はじめに
Amazon SQS を使っていると、メッセージの処理に失敗したとき
- どういう条件で元のキューに戻って再処理されるのか
- どういう条件でDLQ(デッドレターキュー)に移動するのか
-
maxReceiveCountは結局「何の回数」なのか
このあたりが混乱しやすいです。本記事では、これらの挙動を公式ドキュメントの記載とあわせて整理します。
結論(先にまとめ)
| 観点 | 内容 |
|---|---|
| 元のキューに戻る条件 | 可視性タイムアウト内に DeleteMessage されず、かつ受信回数が maxReceiveCount 未満 |
| DLQに移動する条件 | 削除されないまま受信回数が maxReceiveCount に達したとき |
| カウントの対象 | 「処理失敗の回数」ではなく 「受信された回数」 |
| カウントの単位 | キュー全体ではなく メッセージ1件ごと |
| 移動のトリガー | 異常発生の瞬間ではなく、可視性タイムアウト経過後の再表示 → 次の受信が起点 |
1. SQSは「処理の成否」を見ていない
まず大前提として、SQSはコンシューマー側で異常(例:404エラーやクラッシュ)が起きたことを知りません。
SQSが認識できるのは次の2点だけです。
- メッセージが受信された(
ReceiveMessage)か - 可視性タイムアウト内に削除された(
DeleteMessage)か
そのため「異常が発生した瞬間にDLQへ移動」という動作は原理的に起こりません。SQSから見れば、異常が起きても「削除されないまま可視性タイムアウトが過ぎた」としか見えないのです。
2. 基本の挙動:受信 → 削除されなければ再表示
メッセージを受信すると、そのメッセージは可視性タイムアウト(VisibilityTimeout)の間、キュー上には存在し続けるものの、他のコンシューマーからは見えなくなります。
- 可視性タイムアウト内に処理が完了し
DeleteMessageが呼ばれた → メッセージは削除されて終了 - 可視性タイムアウト内に
DeleteMessageが呼ばれなかった → メッセージはキューに再表示され、再度受信できるようになる
公式ドキュメント:Amazon SQS の可視性タイムアウト
アプリケーションエラー、クラッシュ、接続の問題などで可視性タイムアウトが期限切れになる前にメッセージを処理し、削除しなかった場合、メッセージはキューに再表示されます。そのため、同じコンシューマーまたは別のコンシューマーがメッセージを取得して、別の処理を実行できるようになります。
ポイントは、再表示は「異常が起きた瞬間」ではなく「可視性タイムアウトが期限切れになった後」に発生するということです。
3. maxReceiveCount とは何か
maxReceiveCount は、1つのメッセージが(削除されないまま)何回受信されたらDLQへ送るかを決める上限値です。
公式ドキュメント:Amazon SQS でのデッドレターキューの使用
再処理ポリシーを使用して maxReceiveCount を指定します。maxReceiveCount は、メッセージがデッドレターキューに移動するまでに、コンシューマがソースキューからメッセージを受信できる回数です。例えば、maxReceiveCount を 1 などの低い値に設定した場合、メッセージの受信に 1 回失敗すると、メッセージはデッドレターキューに移動します。システムがエラーに対して回復力を持つようにするには、十分な再試行ができるように maxReceiveCount を高めに設定してください。
誤解しやすいポイント
-
「処理失敗の回数」ではなく「受信された回数」
SQSは成否を判定していないため、カウントされるのは「受信された回数」です。
→ 処理は成功しているのにDeleteMessageを呼び忘れている実装だと、失敗していなくても受信カウントが増え続け、最終的にDLQへ送られてしまいます。 -
カウントはメッセージ1件ごと
キュー全体ではなく、各メッセージがそれぞれ独自のカウンターを持っています。メッセージAが3回受信されてDLQへ移動しても、メッセージBのカウントには影響しません。
4. 具体例:可視性タイムアウト10分・maxReceiveCount=3
異常が起きるたびに「戻る → 再処理 → 異常 → 戻る…」を繰り返すイメージで、流れを追ってみます。
0分 1回目の受信(カウント=1)→ 処理失敗、Delete呼ばれず
↓ 可視性タイムアウト10分が経過するまで他から見えない
10分 再表示される(この時点ではまだ「キューに戻った」だけ)
↓ 次にコンシューマーが受信しに来る
10分〜 2回目の受信(カウント=2)→ 処理失敗、Delete呼ばれず
↓ また10分間見えなくなる
20分 再表示される
↓ 次の受信
20分〜 3回目の受信(カウント=3 = maxReceiveCount到達)→ 処理失敗
↓ 次の受信判定のタイミングで...
DLQへ移動
つまり、異常が発生した0分の時点で即DLQへ移動するのではなく、可視性タイムアウト(10分)の経過を挟みながら受信が繰り返され、理論上の最短でも約30分(10分 × 3周)後にDLQへ移動します。
「30分後ちょうど」とは限らない理由
-
再表示 ≠ 即受信
10分経って再表示されても、それは「見える状態に戻った」だけです。実際にコンシューマーがReceiveMessageを呼びに来て初めてカウントが増えます。ポーリング間隔が空いていれば、その分だけ後ろにずれます。 -
DLQ移動は「次の受信判定」のタイミング
受信カウントがmaxReceiveCountに達した瞬間に移動するのではなく、その後にもう一度受信判定が走るタイミングで「もう上限に達しているのでDLQへ送る」と処理されます。
補足:DLQへの移動が「次の受信タイミングで起こる」点については、AWS公式に明示的な記載は見当たらず、検証ブログ等での報告に基づきます。確実に言えるのは「可視性タイムアウト経過後に再表示される」「
maxReceiveCount回受信されるとDLQへ移動する」の2点です。
「再処理3回」と数えると1回ずれる点に注意
maxReceiveCount = 3 は 「受信3回」 であって「再処理(リトライ)3回」ではありません。
- 初回受信(1回目)→ 失敗
- 再処理1回目(2回目の受信)→ 失敗
- 再処理2回目(3回目の受信)→ 失敗 → DLQへ
つまりmaxReceiveCount = 3の場合、初回を含めて受信3回/リトライとしては2回です。
「再処理を3回やったらDLQ」と捉えると、実際には合計4回受信になり1回分ずれるので注意してください。
ちなみに maxReceiveCount = 1 なら、初回受信で失敗したら即DLQ(リトライなし)になります。
5. DLQに入らないパターン
「失敗 = 必ずDLQ」ではありません。DLQに入らないケースを整理します。
① 正常に処理されて削除された
可視性タイムアウト内に処理が完了し DeleteMessage が呼ばれれば、メッセージは削除されて終了します。受信が1回でも、削除されればDLQには入りません(本来の正常系)。
② 受信回数が maxReceiveCount に達していない
削除されずに再表示されても、受信回数が maxReceiveCount 未満であれば、DLQへは移動せず元のキューで再試行され続けます。
③ そもそもDLQ(リドライブポリシー)が設定されていない
DLQはキューに対して明示的に設定するものです。設定していなければ移動先自体が存在せず、削除されないメッセージは保持期間が切れるまで元のキューで再表示・受信を繰り返します。
④ メッセージ保持期間が先に切れた
保持期間(デフォルト4日、1分〜14日で設定可能)を超えたメッセージは自動削除されます。これはDLQ移動とは別の動作で、保持期間切れによる削除はDLQへは回りません。
受信回数が maxReceiveCount に達する前に保持期間が切れた場合、そのメッセージはDLQに入らず消滅します。
6. (補足)DLQに入った後の保持期間に注意
標準キューでは、メッセージの有効期限は常に元のエンキューのタイムスタンプに基づきます。DLQへ移動してもこのタイムスタンプはリセットされません。
公式ドキュメント:Amazon SQS でのデッドレターキュー保持の設定
標準キューの場合、メッセージの有効期限は常に元のエンキューのタイムスタンプに基づきます。デッドレターキューに移動すると、エンキューのタイムスタンプは変更されません。
例えば、DLQへ移動する前に元のキューで1日費やした場合、DLQの保持期間が4日間でも、メッセージは(残り)3日後にDLQから削除されます。
そのため、DLQの保持期間は元のキューの保持期間より長く設定するのがベストプラクティスとされています。
まとめ
- SQSは「処理の成否」ではなく「受信されたか」「削除されたか」だけを見ている
-
maxReceiveCountは メッセージ1件ごとの「受信回数」の上限(失敗回数ではない) - 削除されないまま受信回数が
maxReceiveCountに達するとDLQへ移動 - 移動は異常発生の瞬間ではなく、可視性タイムアウト経過 → 再表示 → 次の受信を起点に進む
- 「再処理N回」と「受信N回」は1回ずれやすいので注意(
maxReceiveCount=1は即DLQ) - DLQに入らないパターン:正常削除/上限未達/DLQ未設定/保持期間切れ