設問と適用場面
メッセージが上限付きのリトライ後にデッドレターキュー(DLQ)に入り、オペレーターが原因を修正した後に選択したメッセージをリプレイできる非同期メッセージフローを設計します。分離、調査、バッチ選択、重複による副作用、および通常トラフィックの保護について説明してください。
Amazonのソフトウェア開発面接のトピックでは、問題を解決するための知識の適用が強調されます。AWSはDLQをリトライ制限とアラームを備えた未消費メッセージの分離として文書化しており、Google Pub/Subはデッドレタートピックとリプレイまたはシーク(seek)のセマンティクスを文書化しています。重要なのはキューの構成図単体ではなく、運用上の復旧ループです。
面接官が評価するポイント
- 一時的なエラー、ポイズンメッセージ、ビジネス上の拒否、および有効期限切れの分類。
- バージョン、テナント、パーティション、トレース、試行回数、失敗理由のメタデータ。
- リプレイ制御:べき等性、スコープ、レート、承認、停止条件。
- 順序制御、保持期間、重複配信、およびat-least-onceセマンティクス。
- 単にメッセージを元に戻すだけでなく、復旧を証明するメトリクス。
回答前に明確にすべき質問
- 配信はat-least-once、at-most-once、それともビジネスレベルのexactly-onceですか?
- どの障害がリトライ可能ですか?
- メッセージはどのくらいの期間保持され、いつ価値を失いますか?
- 集約キーに対して順序は必須ですか?
- コンシューマーはべき等な副作用を生成できますか?
- 誰がメッセージの検査、リプレイ、または削除を行えますか?
- 通常のSLO、キューのキャパシティ、リプレイのキャパシティはどのようなものですか?
- 失敗したリプレイは同じDLQに入りますか、それともreplay-DLQに入りますか?
30秒の回答フレームワーク
「まず、配信セマンティクス、保持期間、および順序キーを定義します。リトライ可能なエラーには上限付きのバックオフを使用し、ポイズンメッセージやビジネス上の拒否メッセージは、理由、試行回数、バージョン、トレースIDを付与してDLQに送ります。修正後、オペレーターは対象範囲を絞ったリプレイバッチを作成し、分離した環境でサンプルを検証してから、レートを制限してリプレイします。コンシューマーはべき等性キーによって副作用を防ぎます。DLQの滞留時間、リプレイの失敗、重複、ダウンストリームのレイテンシを監視し、しきい値を超えた場合は一時停止します。」
詳細な回答(ステップバイステップ)
ステップ 1:障害ステートマシンを定義する
通常、リトライ、DLQ、手動修正、およびreplay-DLQの状態を分離します。永続的なビジネス上の失敗を無限にリトライしてはなりません。
ステップ 2:メッセージのメタデータを定義する
イベントID、ビジネス上のべき等性キー、作成日時、テナントまたはパーティション、スキーマバージョン、試行回数、元のトレースID、エラークラスを保持します。元のペイロードはイミュータブルに保持します。
ステップ 3:リトライとDLQのルールを設定する
最大受信回数、バックオフ、保持期間を選択します。AWS SQSは送信元キューおよびRegionの制約を文書化しており、DLQのアラームを推奨しています。期限切れまたは取り消されたメッセージには、監査可能な処理記録が必要です。
ステップ 4:リプレイを安全にする
リプレイリクエストには、フィルター、対象コンシューマーバージョン、レート制限、バッチサイズ、承認者、有効期限を含めます。まずサンプルを検証し、その後バッチでリプレイします。リプレイのキャパシティは通常トラフィックから分離します。
| 制御 | 目的 | 失敗時のアクション |
|---|---|---|
| べき等性キー | 重複した副作用を防ぐ | 拒否または以前の結果を返す |
| レート制限 | コンシューマーと依存関係を保護する | リプレイを一時停止 |
| バッチスコープ | 影響範囲を限定する | フィルターを狭める |
| 承認と監査 | 説明責任を確立する | 不正なアクションをブロック |
| Replay-DLQ | 繰り返される失敗を分離する | 新しい診断バッチを作成 |
ステップ 5:順序制御と並行性を処理する
順序が重要な場合は集約キーでパーティショニングし、通常処理とリプレイのコンシューマーが同一のキーを同時に処理しないようにします。キュー単体でエンドツーエンドのexactly-once配信が実現できると主張してはなりません。
ステップ 6:副作用を保護する
条件付き書き込みにはイベントIDまたはビジネス上のべき等性キーを使用します。決済やメール送信にはべき等なリクエストキーと結果の参照が必要です。メッセージの削除はロールバックではありません。
ステップ 7:可観測性を備えた運用を行う
DLQの深さ、最長滞留時間、エラークラス、リプレイのスループット、リプレイの失敗、重複の影響、ダウンストリームのレイテンシを監視します。すべてのバッチについて、オペレーター、理由、スコープ、タイミング、結果、停止イベントを記録します。
ステップ 8:キャパシティを予算化し、安全に停止する
リプレイのキャパシティを見積もり、新しいコンシューマーが古いスキーマとの互換性を維持できるようにします。原因が解決していない場合、依存関係が過負荷になった場合、または重複レートが上昇した場合は一時停止し、証拠を保全します。
高品質な回答例
「これはat-least-onceの注文イベントフローです。ネットワークタイムアウトはバックオフを用いて最大5回リトライされ、スキーマエラー、認証失敗、および期限切れイベントはDLQに入ります。各メッセージは、イベントID、注文ID、テナント、スキーマバージョン、初回エンキュー時刻、試行回数、エラークラス、トレースIDを保持します。
修正後、オペレーターはテナントと時間枠、対象バージョン、レート制限、承認者、有効期限を選択します。まず分離されたコンシューマーで50件のメッセージを検証し、その後リプレイを通常トラフィックの10%で実行します。注文状態はイベントIDをキーとする条件付き書き込みを使用し、決済リクエストはビジネス上のべき等性キーを再利用します。同一の注文に対する通常処理とリプレイ処理は並行して実行できません。
アラートは最長滞留時間、リプレイの失敗、重複書き込み、依存関係のレイテンシをカバーします。いずれかのしきい値に達するとバッチが一時停止され、繰り返される失敗はreplay-DLQにルーティングされます。バッチレコードにはフィルター、バージョン、オペレーター、結果、停止理由が含まれます。」
よくある間違い
- 根拠となる証拠を残さずにDLQをゴミ箱のように扱うこと。
- ポイズンメッセージを無限にリトライすること。
- スコープ、承認、レート制御なしで再キューイングすること。
- キューがエンドツーエンドのexactly-onceを提供すると仮定すること。
- べき等性を省略し、重複請求や重複メールを発生させること。
- 順序キーを無視すること。
- キューの長さだけを監視すること。
- 保護措置を講じずにリプレイと通常のトラフィックでキャパシティを共有すること。
フォローアップと回答方法
フォローアップ 1:リトライ回数を増やさないのはなぜですか?
リトライは一時的な障害に適しています。ポイズンメッセージや恒久的なビジネス上の失敗はキャパシティを浪費します。障害の種類、保持期間、ビジネス上の待機コストに基づいて制限を設定します。
フォローアップ 2:1つの注文に対する順序をどのように保持しますか?
注文キーでパーティショニングし、通常処理とリプレイ処理の並行実行をブロックし、ステート遷移によって古いイベントを拒否する方法を説明します。
フォローアップ 3:依存関係がべき等でない場合はどうしますか?
ローカルでの重複排除と結果の参照を使用します。それができない場合は、手動での照合、補償処理、またはプロバイダーのべき等性メカニズムを要求します。
フォローアップ 4:リプレイが再度失敗した場合はどうしますか?
別のreplay-DLQに送り、元のバッチと新しいエラーを保持し、影響を受けるフィルターを一時停止して、担当者に通知します。
フォローアップ 5:リプレイレートはどのように決定しますか?
コンシューマーのキャパシティ、依存関係のクォータ、通常のヘッドルーム、および復旧時間を使用します。レートを上げる前に、小規模なバッチで負荷テストを実施します。
フォローアップ 6:メッセージを破棄できるのはどのような場合ですか?
期限切れ、取り消し、または価値がないという明確なビジネス上の決定が下され、監査証拠と保持された理由がある場合にのみ破棄できます。