おはようございます、YASUです。
DVAの模擬試験で、SQSの問題がどうにも苦手でした。標準とFIFOの違いは前に整理しましたが、「メッセージの処理に失敗したら、どうなるのか」が、整理できていなかったからだと気づきました。今日は、そこをシンプルにまとめます。
結論
処理に失敗したメッセージは、すぐには消えず、可視性タイムアウトが過ぎるとキューに戻って、再び処理されます。何度か失敗すると、デッドレターキュー(DLQ)に移されます。この「戻る→再挑戦→DLQ」という流れを押さえると、SQSの問題が整理しやすくなりました。
失敗した時の流れ(LambdaでSQSを処理する場合)
1. LambdaがSQSからメッセージを受け取る(メッセージは他から見えなくなる)
2. 処理が成功したら、メッセージが削除される
3. 失敗(エラー、スロットリング、応答なし)したら、可視性タイムアウトのあとキューに戻る
4. 戻ったメッセージを、もう一度受け取って、処理する
5. 失敗が続いて、maxReceiveCountの回数に達すると、DLQに送られる
ポイント1:可視性タイムアウトは、関数のタイムアウトの6倍以上
前に整理した可視性タイムアウトは、受け取ったメッセージを、他から隠しておく時間でした。LambdaとSQSを組み合わせる場合は、ソースキューの可視性タイムアウトを、関数のタイムアウトの少なくとも6倍にするよう、AWSのドキュメントで案内されています。余分な時間があることで、Lambdaがスロットリングされた時にも、再試行できるためです。
ポイント2:バッチで失敗すると、全部戻る
LambdaはSQSのメッセージを、まとめて(バッチで)受け取ります。デフォルトでは、バッチの処理中にエラーが起きると、そのバッチの中のメッセージが、すべてキューに戻ります。成功していたメッセージまで、もう一度処理されることになります。
これを避けるのが、部分的なバッチレスポンスです。イベントソースマッピングで有効にしておくと、失敗したメッセージだけがキューに戻るようにできます。
ポイント3:DLQは、ソースキュー側に設定する
DLQは、何度処理しても失敗するメッセージの、退避場所です。設定は、ソースキューの再処理ポリシーで行い、そこでmaxReceiveCount(何回まで受け取りに失敗したらDLQに送るか)を決めます。AWSのドキュメントでは、maxReceiveCountを少なくとも5にするよう、推奨されています。回数が少なすぎると、一時的なスロットリングだけで、問題のないメッセージまでDLQに送られてしまうためです。
設定する場所は、Lambda関数側ではなく、ソースキュー側です。ここは、試験で取り違えやすいと感じました。
ポイント4:同じメッセージが、何度か処理されても大丈夫にする
失敗して戻ったメッセージは、もう一度処理されます。途中まで処理が進んでいた場合は、同じメッセージが、2回以上処理されることになります。そのため、関数の処理は、何度実行しても結果が変わらない(べき等にする)ことが、ベストプラクティスとされています。
SQSの数字の整理
- 可視性タイムアウト:初期値は30秒、0秒〜12時間
- メッセージの保持期間:最短1分、最長14日
- 配信遅延(DelaySeconds):0〜900秒(15分)
- メッセージのサイズ:最大256KB
- ロングポーリングの待ち時間:1〜20秒
まとめ
- 処理に失敗したメッセージは、可視性タイムアウトのあとキューに戻り、再度処理される
- LambdaでSQSを処理する場合、可視性タイムアウトは、関数のタイムアウトの6倍以上にする
- バッチで失敗すると全部戻るが、部分的なバッチレスポンスで、失敗したものだけを戻せる
- DLQはソースキュー側に設定し、maxReceiveCountは5以上が推奨されている
- 何度か処理されても大丈夫なよう、処理をべき等にしておく
「戻る→再挑戦→DLQ」という流れで整理すると、SQSの問題の見え方が変わりました。それでは、また明日!

コメント