課題とコンテキスト
注文と支払いはユーザーIDおよび注文IDによって結合されますが、支払いは注文の2時間後に到着する可能性があります。Flink Event Time Interval Joinの境界、ウォーターマーク、ステート保持、遅延データパス、および修正戦略を設計してください。イベント時間(event time)、処理時間(processing time)、取り込み時間(ingestion time)を区別し、メッセージの到着順序だけでは不十分である理由を説明してください。
面接官が評価するポイント
- 処理時間から順序を推測するのではなく、ビジネス時間のインターバルを定義できているか。
- ウォーターマークが進捗シグナルであり、グローバルに正確な時間ではないことを理解しているか。
- 2ストリームのステート、クリーンアップ、遅延データ、および重複イベントの処理を説明できるか。
- レイテンシ、完全性、ステートコスト、およびリプレイ性のバランスを取れているか。
確認すべき明確化のための質問
- 注文と支払いのビジネス時間フィールドは何ですか?また、それらのクロックドリフトは発生しますか?
- 支払いはどの程度遅れる可能性があり、インターバル後に修正または手動での消込が必要ですか?
- 結合キー(join key)はユニークですか?また、重複、キャンセル、複数回支払いは発生しますか?
- ダウンストリームはアペンドオンリーのファクト、更新、または最終的な消込テーブルのいずれを受け入れますか?
30秒の回答フレームワーク
注文後0〜2時間の支払いなど、ビジネス時間でインターバルを定義します。両方のストリームをキーでパーティショニングし、イベント時間のウォーターマークを進め、進捗によってこれ以上一致しないことが証明されるまで、結合処理で双方のレコードを保持します。許容範囲内の遅延イベントは結合可能であり、それを超えるイベントはサイド出力(side output)または補償ストリームに送られます。重複排除、チェックポイント、リプレイ、およびダウンストリームの冪等性でまとめます。
ステップごとの詳細解説
1. イベント時間とインターバル境界の選択
各レコードから不変のビジネスイベントのタイムスタンプを抽出し、同じユーザーおよび注文キーを使用します。支払いが注文の後でなければならない場合は下限を0、上限を2時間とし、事前支払いが有効な場合にのみ負の下限を許可します。インターバルは任意に長いウィンドウではなく、ビジネスSLAから導き出します。処理時間は、履歴の順序が重要でない場合にのみ適しています。
2. ウォーターマークと2ストリームのステート
各入力は、自身の順序不同(out-of-order)の境界からウォーターマークを生成します。結合は、レコードが別のマッチを見つけられないことが判明するのに十分な進捗が両側で得られるまで待機します。それまで、レコードはkeyed stateに保持されます。ステートサイズは、入力レート、インターバルの長さ、キーのカーディナリティ、および順序不同の許容境界に依存します。チェックポイントはそのステートを永続化するため、すでに出力された結果を推測することなく、既知の位置からリカバリを継続できます。
3. 遅延、重複、およびキャンセルイベント
許容された順序不同および遅延範囲内のイベントは結合に参加できます。範囲外のイベントは、消込のためにサイド出力または永続的な補償トピックに送られます。イベントIDまたはビジネスキーで重複を排除し、支払いのキャンセルや返金は新しいイベントまたは明示的なリトラクション(retraction)としてモデル化します。ダウンストリームが更新をサポートしている場合はupsertまたはリトラクションを出力し、そうでない場合は履歴をサイレントに書き換えるのではなく修正テーブルを維持します。
4. レイテンシ、ステートコスト、および検証
境界と順序不同の制限を短くするとステートとレイテンシが削減されますが、マッチの取りこぼしが増加します。境界を広くすると完全性は向上しますが、メモリ、チェックポイント、およびリカバリのコストが増加します。本番リリース前に、順序不同、重複、ウィンドウを跨ぐイベント、およびリカバリ障害をリプレイしてテストします。結合のヒット率、サイド出力のボリューム、ウォーターマークのラグ、ステートサイズ、チェックポイント時間、重複率を確認します。エンドツーエンドの冪等性キーにより、再起動やリプレイ時の二重請求を防止します。
模範回答
注文と支払いのビジネスタイムスタンプおよび許容される遅延SLAを確認した上で、ユーザーIDと注文IDでパーティショニングします。支払いが注文後2時間以内にのみ到着する可能性がある場合は、処理時間ウィンドウではなく、0〜2時間のイベント時間インターバルを指定します。各ストリームはウォーターマークを出力し、結合はレコードをkeyed stateに保持し、進捗によって一致が残っていないことが証明された時点でそれらをクリアします。
境界内の遅延イベントは結合に参加し、境界外のイベントはサイド出力または補償ストリームに送られます。イベントIDによって重複を排除し、返金やキャンセルは新しいイベントとして表現します。サポートされている場合はupsertまたはリトラクションを出力し、それ以外の場合は修正テーブルを維持します。リリース前に順序不同、重複、リカバリをリプレイして検証し、ヒット率、サイド出力、ウォーターマークラグ、ステート、チェックポイントを監視し、リプレイの安全性のために冪等性キーを使用します。
よくある間違い
- ビジネスイベント時間をメッセージ到着時間に置き換えてしまうこと。
- ウォーターマークを、上流のすべてのイベントが到着したことの証明として扱ってしまうこと。
- ステートのクリーンアップ、チェックポイント、リカバリを議論せずに大きなウィンドウを選択すること。
- サイド出力や消込パスを用意せずに、ウィンドウ外の遅延イベントを破棄すること。
- 重複排除とダウンストリームの冪等性を省略し、再起動後に重複した支払い結果を生成してしまうこと。
フォローアップの質問と回答
フォローアップ1:なぜ2つの独立したウィンドウと通常の結合を使用しないのですか?
独立したウィンドウでは、2つのストリームのイベント時間の進捗とクリーンアップの境界が失われるため、相対時間のインターバルを表現することが難しくなります。Interval Joinは、この関係のためにキーのマッチングと明示的な下限・上限を組み合わせます。
フォローアップ2:ウォーターマークが停滞した場合はどうしますか?
パーティションのアイドル状態(partition idleness)、ソースのタイムスタンプ、バックプレッシャー、および順序不同の設定を確認します。単一の空パーティションが進捗をブロックしないよう、実際にトラフィックのないパーティションに対してidlenessを設定しますが、ソースの障害を隠すためにウォーターマークを恣意的に進めてはなりません。
フォローアップ3:ビジネス側が2時間以降に到着する支払いを受け入れる場合はどうしますか?
リアルタイムの結合と消込を分離します。暫定的な結果を出力し、遅延イベントを補償ストリームに永続化させ、バッチ処理または2つ目のストリーミングジョブで冪等なupsertを用いて修正を生成します。