代表的な面接トピック

データエンジニアリング面接:データコントラクトをどのように設計し、適用するか?

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

質問

ある企業には、40のコンシューマーに向けて注文イベントを発行する15のプロデューサーが存在します。スキーマの変更、セマンティックドリフト、鮮度の低下、不正なレコードがダウンストリームのデータをサイレントに破壊する前に検出できるよう、データコントラクトをどのように設計し、適用しますか?

プロンプトと適用範囲

これをデータエンジニアリングのシステム設計問題として扱ってください。ピーク時に毎秒2,000件の注文イベント、15分の鮮度目標、必須フィールドに対する99.5%の完全性目標を想定します。コントラクトは、構造、意味、品質、サービスレベル、オーナーシップ、プライバシータグ、および取り決めを変更するためのプロセスを網羅する必要があります。

有効な境界は、アップストリームのプロデューサーとダウンストリームのコンシューマーの間にあるデータプロダクトです。データベースのテーブル定義だけでは不十分です。total_amount に税金が含まれているか、誰がそのフィードを所有しているか、鮮度が満たされなかった場合に何が起こるかを規定することはできません。

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

面接官は、実行可能でバージョニングされ、オーナーが明確なコントラクトを求めています。AvroやJSON Schemaを列挙するだけの回答は不十分です。優れた回答は、互換性とセマンティックの正しさを分離し、プロデューサーの境界にチェックを配置し、コンシューマーに対して予測可能な違反対応と移行パスを提供します。

各フィールドを、拒否、隔離、変換、アラート、または明示的な縮退状態での継続といったコンシューマー側の判断に結びつける必要があります。また、コントラクトが文書化されていない承認待ちの行列にならないようにする方法についても説明する必要があります。

設計を左右する確認事項

  • ソースは追記専用のイベントストリームか、変更可能なテーブルか、あるいはその両方か?変更可能なスナップショットには、キー、更新セマンティクス、削除ルールが必要です。
  • どの保証が厳格なリリース判定基準(ハードゲート)となるか?決済イベントは無効な通貨で拒否される一方、オプションのマーケティングラベルは隔離される場合があります。
  • コンシューマーは1バージョンの遅延を許容できるか?許容できる場合は、互換期間と変換処理を公開します。許容できない場合は、協調した一括切り替えを要求します。
  • イベントはリプレイ可能か、また個人データを含んでいるか?リプレイ可能性は保持期間に影響し、プライバシータグはマスキング、アクセス、削除処理に影響します。

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

「私はコンシューマーのユースケースから着手し、バージョニングされた機械可読なコントラクトを作成します。これには、スキーマとセマンティクス、必須フィールドおよびドメインのチェック、鮮度と完全性のSLO、オーナーシップ、プライバシータグ、進化ポリシーを定義します。プロデューサーは公開前に検証を行い、レジストリはCIで互換性をチェックし、ランタイムチェックによって不正レコードを隔離してメトリクスを公開します。新バージョンはデフォルトで追加型とし、互換性を破るセマンティック変更にはデュアルパブリッシュまたは変換期間を設けます。すべての違反にはオーナー、リプレイパス、コンシューマーから参照可能なステータスを割り当てます」

ステップごとの設計

1. コントラクトオブジェクトの定義

データセットに識別子とバージョンを付与します。各フィールドについて、レコードタイプ、NULL許容性、単位、ビジネス上の意味、許容値、機密性、未知のフィールドが許可されるかどうかを記録します。注文イベントについては、occurred_at がイベント発生時刻であるか、金額が整数の最小単位であるか、キャンセルされた注文が表示されたままになるかを明記します。

オーナーシップ、サポート連絡先、保持期間、鮮度、配信頻度、可用性を追加します。サービスレベルは測定可能である必要があります。鮮度は受け入れられた最新レコードの経過時間とし、完全性は一定ウィンドウ内の非NULL必須フィールドの割合とすることができます。

2. 障害モードによるチェックの分離

スキーマチェックは、欠落したフィールドや互換性のない型を検出します。ドメインチェックは、0未満の金額やサポートされていない通貨を検出します。リレーショナルチェックは、重複したイベントIDや必須の状態をスキップした注文ステータスの遷移を検出します。鮮度およびボリュームのチェックは、停止したプロデューサーや一部のパーティションの異常を検出します。

テスト結果は、コントラクトバージョン、プロデューサーのビルド、パーティション、サンプルウィンドウ、失敗したルールとともに保持します。そのエビデンスにより、コンシューマーは処理を一時停止するか、バックフィルを行うか、限定的な機能縮退を受け入れるかを判断できます。

3. 公開前後のエンフォースメント

CIにおいて、提案されたスキーマを登録済みバージョンと比較し、代表的なコントラクトテストを実行します。ランタイムでは、イベントが共有ストリームに入る前のプロデューサー境界で検証します。無効なレコードは、元のペイロード、ルール違反内容、コントラクトバージョン、リプレイキーとともに隔離ストリームに送信します。

コンシューマー側でも、自身の境界で重要な不変条件(インバリアント)を検証し続ける必要があります。プロデューサー側での適用は多くのインシデントを防ぎますが、コンシューマー側のチェックは、誤設定されたルーティング、古いプロデューサー、変換バグから保護します。

4. 進化プロセスの明確化

オプショナルフィールドの追加は、古いコンシューマーが未知のフィールドを許容する場合にのみ、互換性を維持する変更として扱います。名前の変更は、意味やフィールド名が変わるため互換性を破る変更(ブレイキングチェンジ)です。追加と非推奨化(add-and-deprecate)を優先します。新しいフィールドを公開し、デュアルライトまたは変換を行い、コンシューマーを移行させ、古いフィールドの読み取りを測定した上で、指定された期間の経過後に廃止します。

total_amount を税込から税抜に変更するようなセマンティックの変更については、フィールド名を再利用するよりも新しいバージョンと新しいフィールドを使用する方が安全です。コンシューマーが古いビューを維持しなければならない場合は、バージョニングされた変換を使用し、変換後の値にラベルを付けます。

5. 違反処理の定義

重大度(シビアリティ)ティアを使用します。不正な形式の決済イベントは拒否され、隔離されます。鮮度違反が発生した場合はオーナーにページャーで通知し、データプロダクトを失効(stale)状態としてマークします。非クリティカルな説明文の不備であれば、メトリクスを記録しつつ処理を継続できます。コントラクトには、誰が、どのくらいの期間ゲートをオーバーライドできるか、またどのようなエビデンスが必要かを規定する必要があります。

レコードをサイレントに破棄してはいけません。プロデューサーおよびコントラクトバージョンごとに、受け入れ、拒否、隔離、リプレイ、重複のカウントを追跡します。リプレイは冪等である必要があるため、シンク側ではイベントIDとコントラクトバージョンを使用して2重のビジネス効果が発生しないようにします。

6. 運用モデルの検証

フィクスチャデータに対するコントラクトテスト、提案されたすべてのバージョンに対する互換性テスト、サンプリングされた本番パーティションに対するカナリア検証を実行します。遅延イベント、重複ID、未知のenum値、NULLの必須フィールド、タイムゾーンの誤り、プロデューサーの送信停止などをテストします。

有効なダッシュボードには、違反率、鮮度の経過時間、完全性、コンシューマーの遅延、隔離キューの深さ、リプレイ成功率、オーナーによる確認までの時間が統合されます。スキーマチェックがグリーンであっても、フィードが失効していれば、それはデータプロダクトとして失敗です。

質の高い模範解答

「私は注文ストリームをバージョニングされたデータプロダクトとして扱います。まずコンシューマーをリストアップし、イベント発生時刻、金額の単位、通貨、アイデンティティ、状態遷移といったイベントセマンティクスを定義します。その上でコントラクトに、スキーマ、ドメインおよびリレーショナルルール、鮮度と完全性のSLO、オーナーシップ、保持期間、プライバシータグを盛り込みます。

レジストリはCIで非互換な変更を拒否します。プロデューサーは公開前に検証を行い、ランタイムバリデータは不正レコードを失敗したルールおよびコントラクトバージョンとともに隔離環境へ送信します。ルーティングや変換の誤りが発生する可能性があるため、コンシューマー側でも最小限の重要なチェックを保持します。

スキーマの進化はデフォルトで追加型とします。名前の変更やセマンティックの変更については、新しいフィールドまたはバージョンを追加してデュアルパブリッシュを行い、コンシューマーを移行し、古いフィールドの読み取りを測定した上で、互換期間の終了後にのみ古いバージョンを廃止します。鮮度および品質の違反には、明示的な重大度、オーナー、アラート、リプレイ手順を設けます。フィクスチャ、カナリア、遅延・重複イベント、そして鮮度、完全性、隔離の深さ、リプレイの正確性に関するメトリクスを用いて設計を検証します」

よくある間違い

  • 間違い → 失敗 → 対策: スキーマをコントラクトのすべてと呼ぶ → セマンティックの変更やオーナーシップが暗黙のままになる → 意味、SLO、オーナー、変更ポリシーを文書化する。
  • 間違い → 失敗 → 対策: すべての無効レコードを同期的に拒否する → 1つの不良イベントがパーティション全体をブロックする可能性がある → 制限付きのバックプレッシャーとリプレイキーを用いて隔離する。
  • 間違い → 失敗 → 対策: フィールドの追加は常に安全であると主張する → 厳密なコンシューマーが未知のフィールドで失敗する場合がある → 変更を許可する前にコンシューマーの互換性を検証する。
  • 間違い → 失敗 → 対策: スキーマの不一致に対してのみアラートを出す → 古いデータや不完全なデータでもスキーマチェックを通過してしまう → 鮮度、ボリューム、完全性、ビジネス不変条件を監視する。
  • 間違い → 失敗 → 対策: 意味を変更した後にフィールド名を再利用する → 過去の値と新しい値の比較ができなくなる → 新しいバージョンを作成するか、明示的に変換されたフィールドを作成する。

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

プロデューサーが一斉にアップグレードできない場合はどうしますか?

古いコントラクトを有効なまま維持し、新しいフィールドまたはバージョンを追加して、計測された期間中は両方を受け入れます。互換性アダプターによって古い入力を変換することは可能ですが、差異を隠すのではなく、変換による欠落と廃止期日を明示する必要があります。

コントラクトテストはパスしたものの、メトリクスが依然として間違っている場合はどうしますか?

それはセマンティックドリフトです。ビジネスレベルの不変条件または照合チェックを追加し、独立したソースと比較して、論争のある定義をコントラクトに記録します。構造的な互換性だけでは、プロデューサーが正しいビジネスルールを適用したことを証明できません。

隔離環境がデータの墓場になるのをどう防ぎますか?

各ルールにオーナーと有効期限の目標を設定し、元のペイロードとコントラクトバージョンを保持し、キューの滞留時間とリプレイの成功率を測定します。日次のレビューを実施し、障害をプロデューサーのバグ、コントラクトの不備、想定内の例外のいずれかに分類します。

データコントラクトを避けるべきなのはどのような場合ですか?

単一のオーナーが存在し、ダウンストリームへの約束がない、プライベートで短命なテーブルの場合、軽量なスキーマとテストの方が低コストである可能性があります。複数のチーム、リプレイ、規制対象のフィールド、または鮮度へのコミットメントによって暗黙の前提がリスクとなる場合に、より完全なコントラクトを導入します。

公開情報ソース

関連する質問