こんにちは。ディベロップメントサービス1課の山本です。
今回は、Amazon Simple Queue Service(以下、SQS)でメッセージがデッドレターキュー(以下、DLQ)に遷移するタイミングについて解説します。
意外なタイミングで遷移したので記載しました。
この記事の対象者は?
- SQS や AWS Lambdaを使用している方
- DLQ の設定について理解を深めたい方
- 処理の失敗に対する挙動について詳しく知りたい方
DLQ とは
まず、DLQ について簡単に説明します。 AWS ドキュメントに丁寧に記載されておりますので、本記事ではその内容を抜粋します。 デッドレターキューとは何か - デッドレターキュー (DLQ) の説明 - AWS
デッドレターキュー (DLQ) は、エラーのためにソフトウェアシステムが処理できないメッセージを一時的に保存する特別なタイプのメッセージキューです。
具体的に SQS のメッセージを Lambda で処理する例で書いてみます。
正常な場合、Lambda での処理が完了すると SQS のメッセージを削除して一連のメッセージ処理が完了となります。

異常な場合、DLQの設定をしていると Lambda 側の処理が失敗しても SQS のメッセージは削除されないまま残り、再取得・処理されます。

X回(最大受信数)処理が失敗したメッセージを退避して、解析もしくは後ほど再実行するための一時格納用キューが DLQ となります。
DLQ に遷移するタイミング
ここで問題です。 最大受信数が 2 回の場合、 どのタイミングで メッセージは DLQ に格納されるでしょうか。
- A:2回目の処理失敗時
- B:3回目の処理開始時
答えは
なんと
ななんと!
B:3回目の処理開始時 です!

てっきり、2回処理に失敗したタイミングで DLQへ遷移するものだと思っていました。
AWS 公式ドキュメント内にはこの記載を見つけられなかったのですが、公式ブログ(英語)にはそれらしきことが書いておりました。
When you create a source queue, you can specify a DLQ and the condition under which SQS moves messages from the source queue to the DLQ. This is called the redrive policy. The redrive policy condition specifies the maxReceiveCount. When a producer places messages on an SQS queue, the ReceiveCount tracks the number of times a consumer tries to process the message. When the ReceiveCount for a message exceeds the maxReceiveCount for a queue, SQS moves the message to the DLQ. The original message ID is retained.
機械翻訳してみます。
ソースキューを作成する際に、DLQと、SQSがソースキューからDLQにメッセージを移動する条件を指定することができます。これを redrive ポリシーと呼びます。redriveポリシーの条件は、maxReceiveCountを指定します。プロデューサが SQS キューにメッセージを配置すると、ReceiveCount は、コンシューマがメッセージを処理しようとする回数を追跡する。メッセージの ReceiveCount がキューの maxReceiveCount を超えると、SQS はメッセージを DLQ に移動する。元のメッセージ ID は保持される。
つまり、処理開始時に ReceiveCount(受信数) と maxReceiveCount(最大受信数)を比較して、受信数が超過している場合 DLQ へと転送する処理のようです。
検証
準備
テスト用 Lambda 関数
SQS からメッセージを取得し、メッセージが存在すれば例外。存在しなければ正常終了するコードとなります。
import boto3 import os # 環境変数からキュー URL を取得 QUEUE_URL = os.environ.get("SQS_QUEUE_URL") sqs = boto3.client("sqs") def lambda_handler(event, context): try: # SQS からメッセージを1件取得 response = sqs.receive_message( QueueUrl=QUEUE_URL, MaxNumberOfMessages=1, WaitTimeSeconds=1 ) messages = response.get("Messages", []) if not messages: return # 必ず失敗させる raise Exception("強制失敗") except Exception as e: raise
テスト用 SQS
以下設定のtest_move_QUEUE
を作成します。
- 最大受信数:2
- デッドレターキュー:test_move_DLQ

メッセージの格納
test_move_QUEUE
に適当なメッセージを送信します。
確認
1回目の Lambda 実行
Lambda は例外終了。
元々のキューにメッセージが再格納。
2回目の Lambda 実行
2回目も Lambda は例外終了。
最大受信数失敗しても、元々のキューにメッセージが再格納。
3回目の Lambda 実行
3回目はメッセージが取得できないため、Lambda は正常終了。
このタイミングで、DLQ への遷移を確認。
おまけ
今回は Lambda 関数内で SQS メッセージの取得処理を書いたので、3回目を実行するまでは元のキューにメッセージが残り続ける形となりました。
Lambda トリガーに SQS を設定している場合は2回(最大受信回数)失敗しても、すぐポーリングでメッセージ取得が発生するので DLQ への遷移が時間差なく行われます。
まとめ
- DLQ の遷移タイミングは、最大受信回数 + 1 回目にメッセージを取得する時
- Lambda トリガーに SQS を設定している場合は、そこまで気にしなくていい
最後に
2回失敗したらすぐ DLQ に送られるものだと思ってました。。。
本ブログがどなたかのお役に立てれば幸いです。