代表的な面接トピック

データエンジニアリング面接:コンシューマーを壊さずにデータコントラクトを進化させるには?

データ難しい
Offer.cc 編集チーム公開日 更新日

質問

12個のプロデューサーが注文イベントを書き込み、40個のダウンストリームコンシューマーがそれを読み取っています。ビジネス要件として、amountを整数のセントから10進数の金額に変更し、currencyとcustomer_tierを追加したいと考えています。データコントラクトをどのように定義し、互換性を分類し、段階的に変更を展開し、コンシューマーが壊れていないことをどのように証明しますか?

質問とシナリオ

これはミドル〜シニア向けのデータエンジニアリングにおけるスキーマ進化に関するプロンプトです。このイベントはリアルタイムメトリクスとレイクハウスにデータを提供します。コンシューマーは異なるバージョンを実行しており、1日ですべてをアップグレードすることはできません。at-least-once(少なくとも1回)の配信と再生可能(replayable)なイベントを前提とします。コントラクトは、フィールド型、セマンティクス、品質、鮮度(freshness)、オーナーシップ、およびセキュリティ境界をカバーする必要があります。

面接官がテストしていること

  • 優れた回答は、「フィールドがパースできること」と「ビジネス上の意味が不変であること」を明確に区別します。
  • フィールドの追加は常に安全であると主張するのではなく、プロデューサーとコンシューマーの互換性マトリクスを構築できますか?
  • リネージと使用状況のエビデンスを使用して、影響を受けるカラム、クエリ、ダッシュボードを特定できますか?
  • リバーシブルな移行を維持しながら、CI、リリースのゲート、およびランタイムでコントラクトを強制できますか?

最初に確認すべき明確化のための質問

イベントフォーマットとレジストリ、amountの現在の単位と範囲、コンシューマーが未知のフィールドを拒否するかどうか、古いメッセージがリプレイされるかどうか、および通貨の欠落が何を意味するかを確認します。単位や丸めの変更は、通信上の型がパースできたとしてもセマンティックな破壊です。オプションのメタデータフィールドの移行方法は異なります。また、コンシューマーのバージョンが観測可能か、一時的なデュアルバージョントピックが許容されるか、どのような鮮度とバックフィルのウィンドウが適用されるかも質問してください。

30秒の回答フレームワーク

私はコントラクトをスキーマ、フィールドセマンティクス、品質ルール、鮮度、オーナーシップ、セキュリティ制約として定義し、リネージとコンシューマーの機能を棚卸しします。amountを暗黙的に変更することはせず、バージョンまたは新しいフィールドを公開し、古いプロジェクションを読み取り可能な状態に保ち、互換性CIとシャドウ検証を実行し、コンシューマーをバッチ単位で移行します。ランタイム検証では、バージョン管理されたエビデンスとともに違反を拒否または隔離します。移行後は、測定された非推奨ウィンドウを維持し、保持されたイベントから古いプロジェクションを再構築し、照合とリプレイによって同等性を証明します。

ステップバイステップの詳細解説

  1. コントラクト境界を記述する。 名前と型に加えて、単位、精度、NULL許容性、enum、キー、イベント時刻、鮮度目標、PIIラベル、オーナー、および破壊的変更の承認を記録します。OpenMetadataは、スキーマ、セマンティクス、SLA、セキュリティ、品質テスト、およびオーナーシップを1つの統制されたデータコントラクトオブジェクトとしてモデル化します。
  2. 変更を分類する。 オプションフィールドの追加は、寛容なリーダーにとって多くの場合後方互換性があります。削除、型変更、範囲の縮小、単位変更、またはオプションから必須への変更は、初期状態では破壊的です。整数のセントを10進数の金額に変更するとセマンティクスが変わるため、古いものを暗黙的に置き換えるのではなく、正規化されたフィールドまたはバージョンを追加します。
  3. 影響分析を実行する。 OpenLineageのDataset、Job、Run、およびSchema Facetメタデータを使用して、ジョブ、ダウンストリームテーブル、カラムリネージ、および最近の実行を特定します。40個のコンシューマーすべてについて、パーサーバージョン、フィールドの使用状況、リプレイの挙動、および移行オーナーをコンシューマーごとの変更マトリクスに記録します。
  4. 移行を設計する。 一定期間、amount_minoramount_decimalを公開するか、v2イベントを公開します。古いコンシューマーは古いプロジェクションを読み取り続け、新しいコンシューマーは新しいフィールドをシャドウ読み取りします。通貨は、デフォルト値によってビジネス上の意味が変化しないと証明できる場合にのみ、オプションとして公開します。
  5. ゲートを設定する。 CIは、候補コントラクトを登録済みバージョンと比較し、型、必須性、enum、およびセマンティクスの変更を確認します。次に、サンプルリプレイ、品質アサーション、およびコンシューマーコントラクトテストを実行します。本番のイングレス(受信部)はイベントバージョンを検証し、無効なメッセージはプロデューサー、コントラクトバージョン、および理由とともに隔離(quarantine)に送られます。
  6. 切り替えとロールバック。 パースエラー、欠落、金銭の照合、レイテンシ、リプレイの差異を監視しながら、コンシューマーをバッチ単位で移行します。新しいプロジェクションが正しくない場合は、新しいバージョンの書き込みを停止し、古い読み取りパスを復元します。保持されたイベントから古いプロジェクションを再構築できます。最後のコンシューマーとリプレイウィンドウが非推奨ラインを通過するまで、古いフィールドを削除しないでください。
  7. 追跡可能にする。 OpenLineage実行イベントは、ジョブ、実行、入力、および出力を記述します。Schema Facetはデータセットのフィールドを記録します。コントラクトバージョン、Gitリビジョン、および検証結果をリネージイベントに含めることで、後の調査でどのリリースがどのコンシューマーの結果を変更したかを特定できるようにします。

高品質な回答サンプル

私はこれを通常のフィールド追加として扱いません。まず、amountの単位、精度、および丸めルールをコントラクトに記載し、次にリネージを調査して、40個のコンシューマーがそれを整数のセント、表示値、または集計キーとして使用しているかを確認します。OpenMetadataのデータコントラクトモデルは、スキーマ、セマンティクス、SLA、セキュリティ、品質テスト、およびオーナーシップをカバーしており、「パーサーが受け入れる」チェックをビジネス上の保証と誤認することを防ぎます。

v2または互換性のあるデュアルフィールドバージョンを登録します。amount_minorを保持し、明示的な精度を持つamount_decimalを追加し、オプションのcurrencyを追加します。CIは互換性チェックを実行します。コンシューマーコントラクトテストは、未知のフィールド、通貨の欠落、古いメッセージのリプレイ、および精度の境界をカバーします。新しいプロジェクションをシャドウ計算し、コンシューマーをバッチ単位で移行し、イングレスで有効なコントラクトバージョンがないイベントを拒否し、失敗したものをアラート付きで隔離します。

切り替え中は、金銭の照合、フィールドの欠落、パースエラー、レイテンシ、リプレイの差異を監視します。不一致が生じた場合は、新しいバージョンの書き込みを停止し、古い読み取りパスを復元し、保持されたイベントから再構築します。すべてのコンシューマーが移行し、リプレイウィンドウが閉じ、非推奨メトリクスがゼロに達した後にのみ、古いフィールドを削除します。OpenLineageのJob、Run、Dataset、およびSchema Facetメタデータがコントラクトバージョンと検証結果を保持するため、影響分析と監査の再現性が保たれます。

よくある間違い

  • JSONがパースできるかどうかの確認のみを行う → 型の互換性をセマンティックな互換性として扱う → 単位、精度、NULL許容性、および範囲をコントラクトに含め、個別にレビューする。
  • 「単なる新しいフィールド」だからと公開する → 厳密なパーサーや必須フィールドチェックが失敗する → コンシューマーを棚卸しし、必要に応じてバージョンまたはデュアルフィールドを使用する。
  • 本番後にログのみを監視する → 不正なデータの復旧がすでに困難になる → CIでゲートを設定し、サンプルをリプレイし、ランタイムで隔離する。
  • 古いフィールドを直ちに削除する → リプレイ中や遅延しているコンシューマーが読み取りパスを失う → コンシューマーとリプレイをカバーする非推奨ウィンドウを設定する。
  • リネージを静的なカタログとして扱う → どの実行が影響を受けたかに答えられない → Job、Run、Dataset、Schema Facet、バージョン、および検証エビデンスを関連付ける。

フォローアップの質問と回答

レガシーコンシューマーがアップグレードできない場合はどうしますか?

互換性のある古いプロジェクションまたは変換レイヤーを維持し、新しいイベントからも古いビューが生成されるようにします。そのコンシューマーにオーナー、期日、およびエラーバジェットを割り当てます。コントラクトを永久に凍結したり、トランスレーターが金銭的な意味を暗黙的に変更したりしないようにします。

通貨が欠落しており、安全に推測できない場合はどうしますか?

コントラクト違反または明示的な未知の状態として扱い、それらしいデフォルト値を勝手に作らないようにします。イベントを隔離し、プロデューサーに通知します。ビジネスで許可されている場合は、明示的な「未指定通貨」の値を公開し、ダウンストリームのメトリクスから除外するかグループ化します。

ロールバックによって金銭の二重計上が発生しなかったことをどのように証明しますか?

イベントID、コントラクトバージョン、およびプロジェクションバージョンをべき等性キー(idempotency keys)として使用します。両方のプロジェクションをリプレイし、注文および通貨ごとの集計を比較します。差異サンプル、丸めルール、および入力スナップショットを保存します。照合が成功した後にのみ、新バージョンの書き込みを再開します。

公開情報ソース

関連する質問