設問とコンテキスト
このデータエンジニアリングの質問は、イベントコントラクトの長期的な進化をテストするものです。ここでの要点は、後方互換性や前方互換性の定義を暗記して暗唱することではなく、実際のリーダー、ライター、シリアライズ形式、履歴データ、そしてロールアウトの順序から安全な計画を導き出すことです。互換性の目標、フィールドの意味論(セマンティクス)、登録とテスト、デュアルバージョン運用、そして最終的なクリーンアップの条件を説明する必要があります。
面接官が見ているポイント
- ライタースキーマ、リーダースキーマ、およびデプロイ時の組み合わせを区別できているか。
- コンシューマーのロールアウト順序から、後方(backward)、前方(forward)、完全(full)、推移的(transitive)の制約を適切に選択できるか。
- 必須フィールド、enum の変更、デフォルト値、未知の値、履歴データの再生を適切に処理できるか。
- レジストリのガバナンス、コントラクトテスト、モニタリング、ロールバック、オーナーシップがリリースプロセスに含まれているか。
質問すべき確認事項
イベント形式、トピックまたはテーブル、すべてのプロデューサーとコンシューマー、外部またはチーム間のサブスクライバー、および各コンシューマーのアップグレード速度を洗い出します。fulfillment_mode が新しい意味を持つのか、それとも古い status から不可逆性なく(可逆的に)導出できるのか、そして新しい enum が未知の値を許容するかどうかを明確にします。また、履歴イベントの再生(リプレイ)が必要かどうか、データレイクが生のペイロードを保持しているか、互換性ポリシーのオーナーは誰か、2つのバージョンを一時的に並行稼働できるかも確認します。
30秒で答えるフレームワーク
コンシューマーのインベントリとリーダー/ライターのロールアウト順序を作成し、互換性の方向を選択してレジストリで強制します。新しいフィールドはオプショナルまたは安定したデフォルト値付きで追加し、拡張可能な enum 表現を使用し、コンシューマーが機能ごとに移行する間、プロデューサーには新旧両方のフィールドを書き込ませます。リプレイ用のマッピングと未知の値の挙動を定義し、コントラクトテスト、リプレイサンプル、ランタイムモニタリングで検証します。すべてのコンシューマーが移行し、保持期間が終了した後にのみ、古いフィールドやバージョンを削除します。
ステップごとの詳細解説
1. リーダー/ライターのマトリクスと互換性の目標を整理する
旧ライターと旧リーダー、旧ライターと新リーダー、新ライターと旧リーダー、新ライターと新リーダーの組み合わせを整理します。コンシューマーが先にアップグレードする可能性がある場合は前方互換性(forward compatibility)が必要です。プロデューサーが先にアップグレードする場合は後方互換性(backward compatibility)が必要です。ローリングデプロイでは通常、移行期間中に両方が必要になります。多くのバージョンにわたる履歴リプレイでは、最新のスキーマとの比較だけでなく推移的なチェック(transitive checks)も求められます。
2. フィールドのセマンティクスを安全に拡張可能にする
新しいフィールドをすべての旧リーダーに対して即座に必須にしないでください。Avro などの形式では nullable union または必要に応じてデフォルト値を使用し、JSON では欠落(missing)、null、未知(unknown)のフィールドの違いを定義します。enum を拡張する場合は、今日現在の値のみが届くと仮定するのではなく、古いコンシューマーが安全な未知(unknown)ブランチを持つようにします。古い status を損失なくマッピングできない場合は、暗黙的に意味を変更するのではなく、新しいイベントバージョンまたは並行フィールドを追加します。
3. レジストリとコントラクトテストを使用して互換性のないリリースを阻止する
サブジェクトまたはイベントタイプごとにスキーマを登録し、命名規則、互換性レベル、オーナーを定義します。プロデューサーのビルド時に新しいスキーマをチェックします。コンシューマーの CI では、新旧の実サンプルをデシリアライズし、ビジネスロジックの挙動をアサートします。パースの成否だけでなく、デフォルト値、未知の enum、時間や単位のセマンティクス、null、フィールド削除後の挙動もテストします。チェックに失敗した場合は、オンラインのコンシューマーエラーになる前にリリースをブロックします。
4. デュアルライトと段階的なアプローチでロールアウトする
まず、両方の形式を読み取れるコンシューマーをリリースします。次に、プロデューサーが古いフィールドを維持しながら新しいフィールドを入力するようにします。コンシューマーのバージョン、パースエラー、デフォルト値の使用状況、イベントラグを監視します。データパイプラインではテーブルの型、パーティション、バックフィルも検証する必要があります。互換性のない enum や構造の場合は、新しいイベントタイプまたはトピックを作成し、ブリッジを使用して両方に発行し、各パスにオーナーと廃止期日を設定します。
5. リプレイ、ロールバック、クリーンアップの条件を定義する
履歴イベントをその当時のライタースキーマでパースし、明示的なバージョニングされたマッピングを使用して現在のモデルを作成します。昨日の事象に対して今日のデフォルト値を暗黙的に適用してはなりません。新旧のスキーマ、変換コード、サンプルスナップショットを保持します。プロデューサーのロールバック時には、古いバージョンがすでに書き込まれたイベントを読み取れることを確認します。すべてのコンシューマーが移行し、古いフィールドの読み取りがゼロになり、リプレイと品質チェックに合格し、通知期間が終了した後にのみ、古いフィールドやブリッジトピックを削除します。
優れた回答例
私なら、十数のコンシューマーと複数のデータパイプラインのインベントリを作成し、ローリングデプロイ中の4つのリーダー/ライターの組み合わせを整理し、fulfillment_mode が古い status から可逆的に導出できるか、それとも新しい事象であるかを判断します。導出可能であれば、安定したデフォルト値を持つオプショナルフィールドとして追加し、古い status を維持します。enum には安全な未知のブランチを含めます。両方の形式を読み取るコンシューマーをリリースした後、レジストリで互換性を強制しながらプロデューサーにデュアルライトさせます。CI では新旧のイベント、欠落フィールド、未知の enum、履歴リプレイをテストし、本番モニタリングではパース失敗、デフォルト値の使用、コンシューマーのバージョン、テーブルの品質を追跡します。構造が完全に非互換である場合は、新しいイベントバージョンを作成し、両方のパスをブリッジします。すべてのコンシューマーが移行し、古いフィールドの読み取りがゼロになり、リプレイ検証に合格し、ロールバック期間が終了した後にのみ、古いフィールドとブリッジを削除します。
よくある間違い
- プロデューサーとコンシューマーのロールアウト順序を整理せずに「後方互換性を有効にする」とだけ言う。
- デフォルト値、欠落フィールドのルール、または旧リーダー向けの安全なパスを用意せずに必須フィールドを追加する。
- 古いコンシューマーが例外をスローしたり、未知の値を誤ったビジネス状態として扱ったりするような方法で enum を拡張する。
- 実際の古いサンプル、リプレイ、テーブル型、ビジネス上の結果をテストせず、スキーマが登録できるかどうかだけをチェックする。
- フィールドの意味を上書きして、履歴リプレイが誤って解釈されるようにしてしまう。
- オーナー、監視、廃止期日、ロールバック条件を定めずに、デュアルライトやブリッジコードを恒久的に残す。
フォローアップの質問と回答
backward、forward、full の互換性をどのように選択しますか?
ロールアウトの順序と消費パターンに基づいて決定します。プロデューサー先行のロールアウトでは、古いリーダーが新しいデータを読み取る必要があるため後方互換性(backward compatibility)を重視します。コンシューマー先行のロールアウトでは、新しいリーダーが古いデータを読み取る必要があるため前方互換性(forward compatibility)を重視します。順序が制御できない場合や双方向の動作が必要な場合は完全互換性(full)を使用します。すべての履歴バージョンに対しては推移的互換性(transitive)を使用し、そのフォーマットの実際のルールを確認します。
なぜ新しいフィールドには通常デフォルト値が必要なのですか?
過去のイベントにはそのフィールドが含まれていないため、古いデータを読み取る際には明示的な挙動が必要だからです。デフォルト値があればリーダーはパースできますが、それは過去の事実であるかのように見せかけるのではなく、「不明」または「提供されていない」という意味である必要があります。意味のあるデフォルト値がない場合は、nullable 型、新しいイベントバージョン、または明示的なバックフィルを使用します。
履歴データのリプレイ時に新しい enum をどのように処理しますか?
ライタースキーマを保持し、まず元のセマンティクスをパースしてから、バージョン管理された変換ロジックを使用して現在のモデルにマッピングします。マッピングできない値は理由を付与して隔離(quarantine)または手動処理にルーティングします。イベントを破棄したり、現在のデフォルト状態を暗黙的に適用したりしてはなりません。
古いフィールドはいつ削除できますか?
すべてのプロデューサーとコンシューマーが移行し、古いフィールドの読み書きがゼロになり、リプレイ、品質、コントラクトテストに合格し、顧客や外部サブスクライバーへの通知期間が終了し、ロールバックおよび監査用の資料が引き続き利用可能な状態であることが確認された後です。段階的に削除します。