代表的な面接トピック

データエンジニアリング面接:重複する副作用を出さずにスタックしたRedis Streamメッセージを復旧するには?

データ普通
Offer.cc 編集チーム公開日 更新日

質問

注文を処理するRedis Streamワーカーが、確認応答(acknowledgment)の前に頻繁にクラッシュします。復旧を設計し、アイドル状態の保留中エントリを再要求(claim)する方法、二重請求を防ぐ方法、およびポイズンメッセージを隔離する方法を説明してください。

プロンプトとコンテキスト

注文タスクはRedis Streamに書き込まれ、1つのコンシューマーグループ内の複数のワーカーによって消費されます。外部APIが成功した後、XACKの前にワーカーがクラッシュすると、メッセージが保留中エントリリスト(PEL: Pending Entries List)に残る可能性があります。面接官は、重複する副作用、無限リトライ、サイレントな損失を起こさずにこれを復旧する方法を求めています。

これは、ストリーム処理の配信セマンティクスと復旧設計をテストするものです。XREADGROUPは配信されたものの未確認のエントリをPELに記録し、XACKはグループに対して処理完了のみを確認応答します。XAUTOCLAIMはアイドル状態の保留中メッセージをあるコンシューマーに移転しますが、ビジネス操作そのものを冪等にするわけではありません。

面接官が評価している点

  • 新規エントリ、保留中エントリ、PEL、およびコンシューマーの所有権を説明できているか。
  • XACKXPENDINGXCLAIMXAUTOCLAIMに適切な役割を割り当てているか。
  • 再要求(claim)、副作用、確認応答(acknowledgment)が一連のリトライ可能なシーケンスを形成しているか。
  • 冪等性キー、リトライ回数、デッドレターストリームでポイズンメッセージを処理しているか。
  • アイドル時間、配信回数、PELサイズ、および復旧レイテンシを監視しているか。

最初に明確にすべき質問

  • 1つのメッセージが引き起こす副作用は何か? 請求、フルフィルメント、通知では重複のリスクが異なります。
  • 冪等性キーと信頼できる正統な(authoritative)状態ストアはあるか? それらがなければ「少なくとも1回(at-least-once)」は安全ではありません。
  • 再要求までにワーカーがアイドル状態でいられる時間はどれくらいか? 閾値は通常の処理時間とネットワークジッターを超える必要があります。
  • Streamはトリムまたは削除される可能性があるか? ペイロードの欠落には独自のメトリクスとアラートが必要です。
  • Redis 8.4のXREADGROUP CLAIMを使用しているか、それとも古いバージョン向けの互換性のあるスキャン&クレームフローを使用しているか?

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

「at-least-once配信を定義し、ビジネスの冪等性をStreamの外部で保持します。ワーカーはXREADGROUPで読み取り、冪等なビジネス状態をコミットし、その後にのみXACKを実行します。復旧ワーカーはPELをスキャンし、安全な閾値を超えてアイドル状態になっているエントリに対してXAUTOCLAIMを使用します。配信回数とエラータイプによってリトライを制限し、ポイズンメッセージをデッドレターStreamに送信し、PEL、アイドル時間、クレーム数、確認応答レイテンシ、重複抑制を監視します。」

詳細な回答

メッセージのライフサイクルを描く

XADDがStreamにエントリを追加します。XREADGROUPがそれを配信し、そのコンシューマーのPELに記録します。ビジネス処理が成功した後、XACKがグループのPELからそれを削除します。ワーカーがその前にクラッシュした場合、アイドル条件が満たされた後に別のワーカーがそれを要求(claim)できます。

安全なアイドル閾値を設定する

XAUTOCLAIMに対するmin-idle-timeは、通常の処理p99、妥当な依存関係リトライ、およびネットワークジッターを超える必要があります。そうでなければ、処理が遅いだけで正常なワーカーがまだ処理中であるにもかかわらず再要求されてしまう可能性があります。返されたカーソルを使用して0-0までスキャンし、その後最初から別のサイクルを開始します。以前はアイドル時間が短すぎたエントリが後から対象になる可能性があるためです。この閾値は運用パラメータであり、Redisの普遍的なデフォルト値ではありません。

クレームと確認応答の順序

復旧ワーカーがエントリを要求した後、外部の副作用を呼び出す前に、メッセージIDまたはビジネス冪等性キーによって状態を確認します。成功した結果と完了状態を書き込み、その後にXACKを実行します。状態の書き込みと外部呼び出しが1つのトランザクションを共有できない場合は、意図、結果、および補償タスクを記録します。リトライによって呼び出しが繰り返される可能性があることを認め、XACKをビジネスコミットが発生したことの証拠として提示しないでください。

重複と競合するクレームの処理

複数の復旧ワーカーが同時にスキャンする可能性があり、ネットワークのリトライがクレームと競合する可能性があります。注文ID、支払いリクエストID、またはビジネスキーで冪等性を強制します。保留中から処理中、完了への有効な状態遷移のみを許可します。「完了」と読み取られた重複は、再請求することなく確認応答され、抑制としてカウントされます。配信回数だけではビジネス上の重複を識別できません。

ポイズンメッセージを隔離する

配信ごとに配信回数がインクリメントされます。持続的な失敗は、不正な形式のペイロード、恒久的なビジネスルール違反、または依存関係の利用不可に起因する可能性があります。エラーを分類します。不正な入力はデッドレターに直接送信できます。一時的な依存関係エラーはバックオフを使用します。リトライ閾値を超えたエントリは、元のID、最後のエラー、試行回数、およびビジネスキーとともにデッドレターStreamに移動します。デッドレターフローに担当者と補償手順を割り当てます。

トリムまたは削除されたエントリの処理

保留中エントリのStreamペイロードがトリムされたか、XDELで削除された場合、XAUTOCLAIMはペイロードを再配信することなくPELからIDを削除することがあります。復旧メトリクスは、「リトライして完了した」と「ペイロードがもう存在しない」を区別する必要があります。注文の場合、クリーンアップされたIDがビジネス上の成功として報告されないように、保持、アーカイブ、または外部ペイロードストアを計画してください。

復旧品質の監視

PELサイズ、最大アイドル時間、クレーム率、配信回数の分布、XACKレイテンシ、デッドレター量、グループごとの冪等性抑制ヒット数を監視します。アラートには、ストリーム、グループ、コンシューマー、およびビジネスキーを含める必要があります。制御された訓練中にワーカーを停止(kill)し、副作用が1回だけ完了し、エントリが最終的に確認応答されるかデッドレター化されることを検証します。コマンドの成功だけでは不十分です。

質の高い模範解答

「私はat-least-once配信を使用し、注文の冪等性状態をビジネスストアに保存します。ワーカーはXREADGROUPで読み取り、注文を処理中としてマークし、依存関係を呼び出し、結果と完了状態を書き込み、その後にのみXACKを送信します。これらのステップの間にクラッシュが発生すると、エントリはPELに残ります。

復旧ワーカーは、通常のp99にジッターを加えた時間を超えてアイドル状態のエントリに対してXAUTOCLAIMを使用します。注文または支払いリクエストキーをチェックします。完了したエントリは確認応答され、抑制としてカウントされます。未完了のエントリはワークフローを続行します。不正な形式や恒久的なエラーは無限ループしません。一時的な依存関係エラーはバックオフし、配信閾値を超えたエントリは元のIDとエラーコンテキストとともにデッドレターStreamに送られます。

PEL、アイドル時間、クレーム数、確認応答レイテンシ、デッドレター、および重複抑制を監視し、トリミング、ワーカーのクラッシュ、依存関係のタイムアウトの訓練を実施します。ペイロードがなくなったIDをXAUTOCLAIMがクリーンアップした場合、Redisはペイロードが利用できないことしか伝えず、注文が成功したことを証明するものではありません。保持ポリシーまたはアーカイブでそのケースをカバーする必要があります。」

よくある間違い

  • Redis Streamsをexactly-onceと呼ぶこと: 確認応答には外部の副作用が含まれません → at-least-onceを明記し、ビジネスの冪等性を追加してください。
  • 完了前に確認応答すること: クラッシュにより作業がサイレントに失われる可能性があります → ビジネス状態が成功した後に確認応答してください。
  • アイドル時間をp99より短く設定すること: 正常なワーカーが横取り(claim)されます → 処理分布とジッターから調整してください。
  • XAUTOCLAIMを無限に呼び出すこと: ポイズンエントリがリソースを浪費します → エラーを分類し、リトライおよびデッドレターポリシーを使用してください。
  • 配信回数を重複検出として使用すること: 1つのビジネスアクションが異なるメッセージIDを持つことがあります → ビジネスキーとステートマシンを強制してください。
  • トリムされた保留中IDを無視すること: クリーンアップが成功として報告されます → 欠落したペイロードについてアラートを発行し、外部アーカイブを保持してください。
  • 計画なしに復旧ワーカーを実行すること: クレームと副作用が競合します → 冪等な状態、リース、または制限された並行性を使用してください。
  • Streamの長さのみを監視すること: ブロックされたPELは見えません → PEL、アイドル時間、確認応答レイテンシ、およびデッドレターを監視してください。

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

フォローアップ 1: なぜXCLAIMを直接使用しないのですか?

XCLAIMは、呼び出し側がどのメッセージIDを要求するかを知っている必要があります。XAUTOCLAIMは最小アイドル時間によってPELをスキャンし、カーソルを進めるため、復旧ワーカーに適しています。どちらもビジネスの冪等性やエラーの隔離を代替するものではありません。

フォローアップ 2: 0-0カーソルは、古いメッセージも新しいメッセージもないことを意味しますか?

これは、今回のスキャンがPELカーソル範囲の終端に達したことを意味します。以前はアイドル時間が短すぎたエントリが現在はアイドルになっている可能性があり、新しいPELエントリが出現している可能性もあるため、最初から別のサイクルを開始します。

フォローアップ 3: 請求は成功したもののXACKがタイムアウトした場合はどうなりますか?

エントリは再度配信される可能性があります。冪等性キーにより、2回目の試行では完了状態が読み取られ、2回目の請求が回避される必要があります。1つのビジネス結果と確認応答のリトライを記録します。確認応答のタイムアウトを請求の失敗と解釈しないでください。

フォローアップ 4: min-idle-timeはどのように選択しますか?

通常のp99処理時間、許容される最長の依存関係リトライ、およびネットワークジッターをベースラインとして使用し、安全マージンを追加します。誤クレーム率、復旧レイテンシ、およびPELの増加で検証します。1つの固定値ですべてのタスクに対応できるわけではありません。

フォローアップ 5: 不正な形式の入力は何回リトライすべきですか?

パース失敗や不変のビジネスルール違反は通常、直接デッドレターに送られます。一時的な依存関係の失敗はバックオフしてリトライします。元のID、ペイロードの要約、最後のエラーを保持しながら、エラータイプ、コスト、復旧可能性に応じて閾値を設定します。

フォローアップ 6: Redis 8.4 XREADGROUP CLAIMで何が変わりますか?

新しいエントリの読み取りとアイドル状態の保留中エントリの再要求が1つのコマンドに統合され、古いバージョンで必要だった複数コマンドのループが削減されます。PELのセマンティクス、確認応答の順序、冪等性、補償、およびポイズンメッセージの処理は、引き続きコンシューマーの設計に委ねられます。

公開情報ソース

関連する質問