出題の入口 に先に回答し、必要な節から復習する。以下はAIによる一般化した設計例。数値と障害は演習上の追加条件で、本人の実務・実測ではない。対象はPostgreSQL 18、Amazon SQS Standard(更新型ドキュメント)、Stripe公開API資料(APIバージョン未固定)。資料確認日は2026-09-08。
解説図:Receive後も残るメッセージ
https://github.com/MFQWKMR4/tech2026 の CHATGPT.md、AGENTS.md、
src/content/docs/career/index.md と次のファイルを読み、
architecture-queues の振り返りを一緒に読むlearnセッションを始めてください。
- src/content/docs/architecture/queues.mdx
- public/diagrams/architecture-queues/visibility-state.svg
- reviews/architecture-queues.yaml
- sessions/2026-09-08-architecture-queues-01.yaml
- diagrams/architecture-queues/outbox.json
- public/diagrams/architecture-queues/outbox-snapshots.svg
- diagrams/architecture-queues/external-effect.json
- diagrams/architecture-queues/lifecycle.json
- src/content/docs/practice/queue-recovery.md
- sessions/2026-09-10-int-burst-01-01.yaml
reviewに新しいsessionがあればそちらも確認してください。
参照できたファイルとcommitを示し、不明はunknownとしてください。
図JSONを読んだことと描画を確認したことを区別してください。
まず希望する節を一つだけ聞き、回答を待ってください。
指定がなければneeds_revisitから、一節ずつ説明→追加疑問の深掘り→理解確認へ進めてください。
一問ずつ回答を待ち、ヒント後と自力の説明を区別してください。
復旧演習は問題だけを提示し、模範解答を先に出さないでください。
終了時は実際の回答を根拠に、CHATGPT.mdと既存テンプレートに従ってYAML+Session narrativeを含む [Session Sync] GitHub IssueをMFQWKMR4/tech2026に作成し、URLを返してください。作成できない場合は未作成と明示し、コピー可能なタイトルと本文を返してください。
2026-09-10の横断面接:回答・支援・不足別の再学習
最終レビュー:2026-09-10 · 説明した(Explained)。ヒント後の理解と自力の説明を区別して記録しています。
次回、自力で確かめたいこと 2026-09-08 (2026-09-08-architecture-queues-01): 次回、Transactional Outboxを名称なしで再出題し、orders更新とoutbox event作成を同一transactionに置く理由、およびoutbox→queue間では重複を許容する理由を自発的に説明できるか確認する。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-10 [2026-09-10-int-burst-01-01] 受付のDB正本への変更は観点ヒント後。DB/queue間の失敗条件に未投入再送・重複許容を説明したが、受付commitと照会契約は次回独力再確認。 2026-09-08 (2026-09-08-architecture-queues-01): at-least-once / at-most-once / exactly-once side effectの違いを、queue deliveryと実際の外部副作用を分けて再度説明する。特に『exactly-once delivery』という語に引きずられず、consumer冪等性との組み合わせを説明できるか確認する。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-10 [2026-09-10-int-burst-01-01] 外部key非対応で自前exactly-once案が未解決。at-most-once選択は解法説明後。 2026-09-08 (2026-09-08-architecture-queues-01): idempotency keyの実装をさらに深掘りする。ローカルDB内でUNIQUE制約+transactionにより実現できる範囲と、外部副作用をまたぐときに下流のidempotency / transaction ID / status query / reconciliationが必要になる理由を再出題する。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-08 (2026-09-08-architecture-queues-01): source of truth / authoritative ledgerとreconciliationを、銀行・決済以外の例でも説明できるか確認する。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-08 (2026-09-08-architecture-queues-01): SQS等を題材にvisibility timeout、ack/delete、receive count、retry、DLQ、redriveのライフサイクルを図なしでも説明できる状態を目指す。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-10 [2026-09-10-int-burst-01-01] 原稿Q3は未出題。今回の不足認定ではなく未実施の再確認として残す。 2026-09-08 (2026-09-08-architecture-queues-01): queue運用ではqueue depthだけでなくoldest message age、enqueue/consume rate、retry count、error rate、DLQ depth/ageをどう組み合わせて見るかをproduction scenarioで再出題する。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-08 (2026-09-08-architecture-queues-01): DLQ大量復旧時に、redrive rate・consumer concurrency・下流rate limit・新規トラフィックの公平性をどう調整するか、より深いcapacity planning問題へ進む。 根拠: 同sessionの回答・説明後の理解。自力での再確認は未実施。 2026-09-10のセッション INT-BURST-01 v1(2026-09-09)の口頭interview。開始問題「イベント開始時に受付が集中し、受付後に外部サービスを使って処理するシステム」で、初回独力回答ではAPI serverのhorizontal scaling、queueによるproducer/consumer分離、外部APIの処理能力に合わせたconsumer側rate調整、queue容量設計、受付完了と処理中/完了の状態表示を提案した。進捗表示では当初「受付APIではDB書込みを発生させたくない」とし、queue投入時に受付成功を返し、consumer開始後にDBへprocessing、完了後にcompletedを記録して状態取得APIで見せる案を出した。
Q1初案は受付DBなし。中立的追質問→原稿外DBなし条件→正本の観点ヒント後にDB導入へ変更。続く障害条件に未投入再送・重複許容・processing復旧を提案。SQS Q3は未出題。
問い・回答の流れをIssueで読む
2026-09-08のセッション 非同期ジョブ・キューについて、アプリ内非同期実行から開始し、プロセスクラッシュ、DB更新とenqueueの隙間、workerの重複実行、Transactional Outbox、at-least-once、consumer冪等性、外部副作用に対するexactly-onceの限界、source of truthとreconciliation、visibility timeout、DLQ、queue監視、redriveまで条件を一つずつ変えて検討した。特に、冪等性の最終的な根拠が副作用のauthoritativeな状態を持つシステムにあることを、説明後に本人の言葉で整理できた。一方、キューやDLQの具体的な動作は今回初めて学んだ部分が多く、次回はより深い再出題が必要。
クラッシュ条件後に注文と未送信状態の同時commitを提案(名称未提示)。外部副作用とのatomicityを本人が問い直した。ledger・visibility timeout・DLQは説明後の理解。段階的redriveを提案。実装・実測なし。
問い・回答の流れをIssueで読む
回答で示せたこと(ヒントの有無を含む) 2026-09-08 (2026-09-08-architecture-queues-01): プロセスクラッシュ条件を追加すると、アプリ内の別スレッドだけではジョブが消失し得ることを認識し、処理を別の永続的な実行基盤へ渡す方向へ判断を変更した。回答要約: 『プロセスが落ちることを考えると、メールを送信しようとした段階で別のところに投げる。このアプリケーションでは投げたら完了』。ヒント: あり(レスポンス直後にプロセスが落ちる条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): 注文DB更新と非同期処理要求を同じDB上に残す方向を自発的に提案した。回答要約: 『注文完了済みと同時にメール送信前みたいなのをコミットし、他のやつがDBを参照しながらメール送信して更新する』。ヒント: あり(DB更新後・enqueue前に落ちる条件のみ提示、Transactional Outboxという名称はこの時点では未提示)。 2026-09-08 (2026-09-08-architecture-queues-01): 外部メールAPI成功後・DB更新前にworkerが落ちるケースから、冪等でない外部副作用では重複可能性が残ることを認識した。回答要約: 『送信まで完了してその後にワーカーが落ちた場合が冪等性を担保できない』『ロック保持なのかな、いやそれは筋が悪い』。ヒント: あり(processing状態後のクラッシュ条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): 『送信漏れの方が重複より問題』という要件では、at-least-once寄りに再試行し、重複を許容する判断をした。回答要約: 『多少送られてないほうが良くないのであれば、重複しても良いので再トライする』。ヒント: なし(選択肢と要件のみ提示)。 2026-09-08 (2026-09-08-architecture-queues-01): at-least-once配送とconsumer側の冪等性という役割分担を、説明後に自分の言葉で整理した。回答要約: 『キュー側でat-least-once deliveryをやって、引っ張ってきたデータのIDをキーにconsumer側で冪等性の実装をする』。ヒント: あり(at-least-onceでは重複配送があり得ることを提示)。 2026-09-08 (2026-09-08-architecture-queues-01): 長時間DBロックを保持して外部APIを呼ぶことへの違和感から、短いtransactionでprocessing状態をcommitしてから外部処理する案を提案した。回答要約: 『送信中みたいな状態でロック取りながら処理を開始したことを書き込んでコミットすればいい』。ヒント: あり(外部APIが30秒遅延する条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): Transactional Outboxの説明後、outboxからqueueへのenqueueが重複しても同じevent IDをconsumer側で吸収すればよいと理解した。回答要約: 『同じIDのデータがキューに複数入ってもconsumer側で何とかできる』。ヒント: あり(Transactional Outboxの構造と名称を説明)。 2026-09-08 (2026-09-08-architecture-queues-01): queue depthが増え続ける状況について、enqueue rateがconsumer capacityを上回ればbacklogが増加し続けると判断した。回答要約: 『毎秒100件ずつ増える。それは問題、パンクする』。ヒント: あり(enqueue 1000/s、consume 900/sという演習条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): oldest message ageが高止まりするケースから、一部メッセージだけが処理失敗している可能性を推測した。回答要約: 『特定のやつだけ処理できてないとか、エラーが出てる可能性』。ヒント: あり(depthは減るがoldest ageが30分から下がらない条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): DLQの説明後、失敗メッセージを隔離し、エラーログ等から原因を調査して修正後に再処理する運用を理解した。回答要約: 『うまくいかなかったデータとして保存できる。それをもとにエラーログを見て原因調査し、原因がわかったらDLQから同じジョブをやる』。ヒント: あり(DLQ・visibility timeout・redriveの基本動作を説明)。 2026-09-08 (2026-09-08-architecture-queues-01): 重要イベントがDLQに長時間残る条件では、時間ベースのアラートとon-call対応を設計する考えを示した。回答要約: 『要件次第だけど、10分以上滞在とかでon-callを呼んで対応させる』。ヒント: あり(24時間以上で業務影響という条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): 大量のDLQ backlog復旧では、一気にredriveせずrate limitをかけ、必要ならconsumerを一時的にscale outする案を出した。回答要約: 『rate limitかけて少しずつ戻す。あとは一時的にconsumerを増やしたい』。ヒント: あり(10万件のDLQという演習条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): 冪等性の最終的な根拠について、説明後に『副作用のsingle source of truthを持つシステムが、一意なoperation IDと状態遷移をatomicに管理し、その保証を上流へ伝播させる』と整理した。回答要約: 『決済で言ったら銀行とかがその一意なoperation IDと状態遷移をatomicに管理できるので、あとはその冪等性さえ提供してくれれば、それを伝播してつなげることができる』『銀行的にはtransactionの中で残高を動かせばそれで取引ができたということ』。ヒント: あり(authoritative ledger、reconciliation、外部境界について説明)。 2026-09-10 [2026-09-10-int-burst-01-01] 受付と後段処理をqueueで分離し、外部serviceの処理能力に合わせてconsumer rateを制御する方針を初回独力で提示した。 支援・留保: Q1初回独力。ヒントなし。 2026-09-10 [2026-09-10-int-burst-01-01] DB保存成功→queue投入失敗に対して、受付状態とqueue投入済み状態を分け、未投入状態を後から再投入する方針を提案した。 支援・留保: DB導入への変更(観点ヒント後)に続き、DB成功/queue失敗の障害条件を提示。方式名や解法の提示は記録なし。 2026-09-10 [2026-09-10-int-burst-01-01] queue投入成功→DB投入済み更新失敗では、同じ業務IDの重複投入を許容し、consumer側でDBを参照して重複処理を避ける方針を自発的に提示した。 支援・留保: queue成功/DB更新失敗という条件提示後。重複許容の方式は本人が提案。 2026-09-10 [2026-09-10-int-burst-01-01] consumerがprocessing状態で停止した場合、一定時間processingのままのrecordを検出して再実行するrecovery jobを提案した。 支援・留保: processingのまま停止という条件提示後。再実行の安全性が保証されたという評価ではない。 自力では説明しきれなかったこと 2026-09-08 (2026-09-08-architecture-queues-01): セッション開始時点では、queue consumerがmessageをReceiveした瞬間にqueueから削除されるのか、成功後に明示的に削除するのかを知らなかった。回答: 『consumerが取得した時点でqueueからもうなくなるのか、それとも送信完了してから削除する操作があるのか全く使ったことがない』。ヒント: なし。ChatGPTがvisibility timeout / Delete / retryの基本動作を説明した。 説明後の理解はdemonstratedを参照。次回は自力想起を確認する。 2026-09-08 (2026-09-08-architecture-queues-01): セッション開始時点ではDLQの具体的な役割・redrive方法を知らなかった。回答: 『Dead Letter Queueってやつか。この辺あんまわかってない』『そもそもどうやって戻すかわかってない』。ヒント: なし。説明後は調査・修正・再処理という運用イメージを理解した。 説明後の理解はdemonstratedを参照。次回は自力想起を確認する。 理由・設計を詰めたいこと 2026-09-08 (2026-09-08-architecture-queues-01): 最初の案では『別スレッドでメールAPIを実行し、結果をDBに記録する』としており、プロセスクラッシュ時のジョブ消失を考慮していなかった。回答要約: 『非同期でメール送信APIを実行して結果を待たず処理を終える。別スレッドでやって結果が返ったらDBに記録』。ヒント: なし。クラッシュ条件提示後に設計を変更した。 2026-09-08 (2026-09-08-architecture-queues-01): consumerの重複実行対策として当初はDB row lockを長時間保持する案を出したが、外部API呼び出しをtransaction内に含めても外部副作用とのatomicityは得られない点までは整理できていなかった。回答要約: 『処理開始時にDBのロックを取って完了したら更新しながらロックを解放』『行ロックであればあまり影響ないのかな』。ヒント: あり(30秒の外部API遅延、processing commit後のクラッシュ条件を提示)。 2026-09-08 (2026-09-08-architecture-queues-01): queue depthとoldest message ageの関係は初見で、自発的にはユーザー影響まで結び付けられなかった。回答: 『正直よくわかりません。古いやつから順に処理されてるから問題ないってこと?』。ヒント: あり(depth=100万、oldest=45秒という条件を提示し、その後説明)。 2026-09-10 [2026-09-10-int-burst-01-01] 初回の進捗設計では受付成功をqueue投入だけで返し、consumerがDBへ記録するまで状態取得APIから受付済みを証明できない問題が残った。本人も『DBに記録がない状態なので見分けがつかず』『正本としてデータベースとキューの2つができてしまう』と問題を認識し、その後DBを受付時に使う方針へ変更した。支援: 中立的追質問→演習上の追加条件→観点ヒントの後。 修正が必要な説明 記録された項目はありません。
失ってはいけない仕事なら、HTTP応答前に仕事の存在を永続化する。別スレッドに渡すだけではプロセス終了で失う。注文をcommitしてからqueueへ送る方式にも二つの書込みの隙間がある。
Transactional Outboxでは、注文更新と一意なevent IDを持つoutbox行を同じDB transactionでcommitする。relayはcommit済み行だけを読み、queueへの送信成功を確認して送信済みにする。送信確認を失った場合は同じevent IDで再送する。queueが新しいmessage IDを振っても、業務event IDは維持する。AWS Outbox
注文とoutboxは一緒にcommitできる。しかし、queueへの送信とoutboxの送信済み更新は別々に成立する。
既存のシーケンス図を開く:Outboxの通信順序(C1〜C3)
図では Send E → Accepted → mark E sent の順序に注目する。queueが受け付けた後でも、DBを更新する前にrelayは停止し得る。これがC3である。最後の resend E は正常系の続きではなく、C3から復旧した場合の別経路として読む。
通信順序が分かっても、「再起動したrelayは何を根拠に再送するのか」が残る。そこで同じ境界を、DB行とqueue内のメッセージで見る。
演習上の追加条件 :注文は O17、業務event IDは E42。単一relayが未送信行を走査して送り、成功後に sent へ更新する設計例とする。差分を見るためconsumerは停止中、保持期限内で、他の送信・再配送は省略する。以下は実測ではない。
上から、送信前 → 送信成功直後の停止 → 再起動して再送・送信済み更新後。同じevent IDを青でそろえている。図を拡大する 。
最初の二つのコマでは、左側のDBは同じなのに、右側のqueueが違う。 pending は「送信済みとして記録されていない」という自DBの状態であり、「queueに絶対に届いていない」という証拠ではない。このDB行だけを読むrelayには、C2とC3を区別できない。
relayは仕事を失わないために、残っている E42 を再送する。3コマ目で増えたのはoutbox行や注文ではなく、同じ業務イベントを運ぶメッセージ である。最初のメッセージが既に消費されていれば2通が同時にqueueに並ぶとは限らないが、consumerが同じeventを再び受け取る問題は残る。
逆順にしても隙間は消えない。先に sent をcommitし、送信前に停止すると、queueに未着なのにrelayの再送対象から外れてしまう。この方式では漏れを避けるため「送信成功 → sent更新」を選び、重複に備える責任をconsumerにも持たせる。
consumerが行う変更も一つのDB内で完結するなら、処理済み E42 の記録と業務変更を同じtransactionで確定する。外部メールや決済まで一緒にcommitできるという意味ではない。その境界は次の「Why」「Trade-off」で分けて考える。AWSのOutbox解説 も、重複配送を考慮してconsumerを冪等にすることを求めている(更新型資料、2026-09-09再確認)。
シーケンス図で停止位置を確かめ、SVGで見たC2・C3の状態を、ほかの停止位置と比較する。
クラッシュ位置
残る状態と再開方法
従来方式:ordersだけcommit、enqueue前
注文だけ残り、仕事の記録がない。照合等がなければ送信漏れ
C1:ordersとoutboxのcommit前
両方rollback。片方だけ成立させない
C2:同時commit後、enqueue前
outboxに未送信eventが残る。relayが再開して送れる
C3:enqueue成功後、sent更新前
queueには存在するがoutboxは未送信。同じeventを再送し得る
保証は「業務更新と仕事の記録が一体」であり、queueや外部APIを含めた一つのcommitではない。DBの耐久性設定・復旧、relayの再試行、retention内の処理が前提になる。outboxの最古未送信時刻も監視する。queueへ届く前の停止はqueue depthだけでは見えない。複数relayのclaimは短いtransactionで行い、期限付きleaseで停止workerの仕事を回収する。期限切れの旧workerも動く可能性があり、leaseだけで外部副作用の重複は防げない。
問い:受付成功を返した直後、consumerがまだ動いていなくても何を確かめられるか。 永続的なqueueへの送信確認がある設計は、それ自体で耐久性を持ち得る。しかし「仕事が保存された」と「状態取得APIで受付済みと確認できる」は別の契約である。queueの検索機能やretentionを確認せず、任意のjob IDで照会できるDBとして扱わない。
演習上の追加条件として、DBを状態取得APIの正本にし、受付DB書込みは可能とする。受付時にjob ID・受付状態・必要な入力と未送信の仕事を同じtransactionへ保存し、commit確認後に受付成功と状態照会先を返す。独立したoutboxが不要な単純系なら、job行そのものが送信待ちの仕事を兼ねてもよい。重要なのは、受付状態だけをcommitして仕事の記録を失う隙間を残さないこと。AWS Transactional Outbox (更新型資料、2026-09-10確認)。
停止位置(この設計例)
状態照会と再開
DB commit前
受付成功を確定して返さない。clientが結果不明なら同じ操作IDで確認・再送
commit後、queue publish前
DBに受付済み+未送信がある。relayが再開して送れる
publish後、投入済み更新前
DBの未送信は「未到着」の証明ではない。同じ業務IDで再送し得る
consumer開始前
DBの受付済みを表示できる。処理中・処理完了と混同しない
既存Outbox図 のC2/C3は、この二つのpublish前後の状態を比較している。受付IDを持っていても、DBに行がないという事実だけで「未受付」と断定してよいかは、read replicaの遅延、認可、retention等の契約にも依存する。ここでは受付commit済み行を読めることを前提にしている。
202 AcceptedというHTTP statusを使うだけでdurableな処理保証が生まれるわけではない。受付応答には処理状況と状態を追う方法を示し、保証内容はAPI契約として定める。RFC 9110 §15.3.3 (2022、2026-09-10確認)。DB書込みの待ち時間・負荷は増えるので、受付latencyやDB能力を測る。その代わり、queue到達前も状態と再開対象を管理できる。
「処理済みキーを保存した」と「業務変更が成立した」を別commitにすると、どちらを先にしても隙間ができる。ローカルDBで完結する例なら、同じtransaction内でscopeとoperation IDの一意性を確保し、リクエスト内容を照合し、業務状態と再送用結果を保存する。commit前の停止なら全体をrollback、commit後の再送なら既存結果を返す。並行した同一キーは一意制約で競合させる。詳細な並行処理は既存のHTTP API解説 を参照する。PostgreSQL 18 Transactions
決済以外なら、ゲーム内ポイントの正本を一つのDBが持ち、operation IDとポイント移動を同一transactionで確定できるモデルを考えられる。これがauthoritativeな状態の境界である。外部メール送信や外部サービスへの特典付与を加えた瞬間、別の境界が生まれる。「銀行ならすべてDB内」は一般化できず、単一台帳内の記帳と他行への送金・決済網の確定は区別する。
外部副作用の図 は、正常時の後半と、F1〜F3で止まった場合を示す。row lock中に外部APIを呼んでも、自DBのrollbackでは送信済みメールを取り消せない。長いlockは待ちと接続占有も増やす。短いtransactionでprocessingをclaimする方式は占有時間を減らすが、結果不明を解消する仕組みは別に必要になる。
位置
自DBから分かること
再開時の判断
F1:processing commit後、呼出し前
processingのみ
実際には未実行でも、再起動後にF2と区別できるとは限らない
F2:下流で成功、応答喪失/done commit前
processingのみ
同じoperation IDで照会、または下流契約に従って再送
F3:done commit後、queue Delete前
結果を永続保存済み
再配送で同じeventを認識し、副作用を再実行せずDelete
下流が冪等性を提供する場合でも、キーのscope・保持期間・同一payload・エラーの再利用条件を確認する。Stripeは同一キーの結果を再利用し、キーを少なくとも24時間経過後に削除し得る。古いjobを新しいキーで無条件に再送することは、同一操作の保証を失わせる。Stripe idempotent requests
言葉
この教材で区別する意味
at-most-once
再実行を避ける代わりに、未実行のまま失う可能性を許す
at-least-once
成功を目指して再試行する。配送/実行は重複し得る。保持期限等で無期限の完遂を保証しない
exactly-onceの業務効果
どの状態変更を一度だけ成立させるかを明示。同じDB内の冪等処理と外部副作用は別
メールAPIに冪等性も結果照会もなく結果不明なら、漏れを避けて重複を許すか、重複を避けて未送信リスクを許すかを要件で決める。SQSのFIFOやdeduplicationという名称だけでは、外部メールまで一度だけ届くとは言えない。
reconciliationは自システムの未確定操作と、下流のoperation ID・結果一覧・確定記録を照合して差異を収束させる処理。設計例ではpendingを走査→同じ操作を照会→成功を記録/安全な再試行/不一致を隔離する。照会が一時的にnot foundでも未実行と即断せず、下流の可視化遅延と契約を考慮する。照合は過去の二重実行を無かったことにはせず、必要なら補償や人の判断が残る。金融の照合も記録間の一致を確認するもので、単一transactionの代替保証ではない。Stripe reconciliation
ライフサイクル図 の分岐は代替経路。Receiveで一時的に不可視になり、処理成功後にreceipt handleでDeleteする。Deleteしなければvisibility timeout後に再取得可能になる。Standardでは不可視期間中も重複が絶対にない保証ではなく、consumerの冪等性は必要。SQS visibility timeout
既存ライフサイクル図でReceive・Delete・timeoutの分岐を追ったら、同じ E42 がどこに残るかを見る。演習上の追加条件 :1通だけに着目し、保持期限内、他workerの受信やStandard queueで起こり得る別の重複は省略する。
図を拡大する
②の灰色のE42は、queueから削除されたのではない。workerに処理を任せるため一時的に取得されにくくなった状態で、Deleteがなければ③へ戻る。処理の副作用だけが成功してworkerが落ちた場合にもこの再取得は起こり得るので、先ほどのOutboxと同じく業務event IDによる冪等性が必要になる。
visibility timeoutは「業務失敗と確定する時刻」ではなく、再取得可能になるまでの時間。処理が終わったかはアプリの永続状態や下流との照合で判断する。SQS visibility timeout (更新型資料、2026-09-09再確認)。
設定
Trade-off
短すぎるvisibility
処理中に再配送され、重複と負荷が増える
長すぎるvisibility
worker停止後の再試行が遅れる
延長heartbeat
可変処理時間に合わせられるが、延長失敗と停止検知を設計する
receive countは業務成功数ではない。redrive policyのmaxReceiveCountに従い繰り返し失敗をDLQへ隔離する。DLQは自動修復ではなく、調査と期限付き復旧の待機場所。入力の業務上の不正とconsumerの実装不足を分け、修正・少量再試行・結果確認へ進む。StandardではDLQに移しても元のenqueue時刻を基に保持期限が進むため、DLQ retentionを元queueより長くする。SQS DLQ
redriveは元queue等へ戻す操作。SQSでは移動したmessageは新しいmessage IDとenqueue時刻を持つため、本文の業務event IDを使い続ける。原因未修正で戻すと失敗を循環させる。少量から速度を上げ、エラー増加時には移動を止める。SQS redrive
以下はすべて説明用の仮定で、正常判定には業務SLOが必要。
パターン
判断と次に見るもの
depth=100万、oldest=45秒、期限5分、完了rateが流入を上回る
多くても回復可能な候補。完了遅延分布とretry、DLQを確認して判断
depth=10、重要jobの発生から2時間、期限10分
少なくても重大。特定jobの失敗・欠落と下流結果を追跡
enqueue=1000/s、正常完了=900/s
単純モデルで毎秒100件増える。継続すれば遅延・保持期限が問題
depth減少、oldestが30分のまま
一部失敗の仮説。event ID、job種別、receive count、error、DLQを照合
queueが空、outbox未送信age増加
relay停止やenqueue失敗を調べる
SQSメトリクスは近似値。visibleとin-flightを分け、Receive/Deleteの件数を重複排除済み業務完了数と同一視しない。Standardのpoison messageはoldest ageから除外され得る。DLQのApproximateAgeOfOldestMessageはDLQへ移動してからの時間であり、業務発生からの総時間ではない。本文のcreated_atからend-to-end遅延を測り、成功率と合わせる。「oldest=45秒」だけで全job正常とは言えない。SQS CloudWatch metrics
一時障害のretryは上限付きexponential backoffとjitterで分散する。full jitterの設計例は、再試行番号nに対して0〜min(cap, base × 2^n)の待ち時間を選ぶ。上限回数・総期限も置き、恒久的な入力エラーを同じ頻度で再試行しない。AWS Backoff and Jitter
DLQから戻す速度、consumerの同時実行数、下流APIへの実呼出し数は別に制御する。worker追加は処理能力を増やせても下流quotaは増やさない。新規job用の枠と復旧用の枠を分け、必要ならjob種別ごとにqueueを分ける。retryを含む総呼出し数、429、latency、通常jobのageを見て復旧を減速する。受入れを絞るbackpressureや期限切れjobの扱いも要件に沿って決める。
大量復旧の問題 で、数値を使った判断と停止条件を練習する。最初は問題だけを扱い、回答後に条件を一つずつ追加する。
session 2026-09-08-architecture-queues-01(Issue #9 )の5つの質問を一般化した。keyと副作用のatomicity、commit/enqueueの隙間、Receive/DeleteとDLQ、depthとage、大量redriveをそれぞれ上記の節へ統合。既存の通信順序・ライフサイクル図は diagrams/architecture-queues/*.json が正本で、固定Archify CLIでHTMLを生成する。2026-09-09の図表改善試作では、本文の説明と組み合わせる状態比較図 public/diagrams/architecture-queues/outbox-snapshots.svg を追加した。この説明図はSVG自体を正本とする。外部API・SQSの実験は未実施。読了や図の追加を本人の習得実績にしない。
図表改善(2026-09-09):説明用SVGはSVG自体が正本。前提と判断は本文、状態・比較は図、通信順序・依存関係は既存図、実測は元の記録を参照する。教材編集を新しい学習実績にはしない。
2026-09-10補強:2026-09-10-int-burst-01-01(Issue #13 )の受付直後の状態確認という問いを一般化し、「受付成功の耐久性と、状態を照会できること」を追記。既存Outbox図を再利用。DB/queue実験や本人の習得証拠は追加していない。