DLQを積み重ねる

DLQを積み重ねる

はじめに

SQSのデッドレターキュー(DLQ)、使っていますか?
AWSの公式ドキュメントには下記のように書いてありますが、「そんなケースに遭遇したことがない」方がほとんどかもしれません。

正常に処理 (消費) できないメッセージの送信先として、他のキュー (ソースキュー) をターゲットにすることができます

今回、その「そんなケース」に当たったので、キューの動きや送受信されているメッセージに焦点を当てて調べてみました。

デッドレターキューとは

公式ドキュメントが一番端的に説明していますので、日本語ドキュメントのページから引用します。

このキューは、正常に処理 (消費) できないメッセージの送信先として、他のキュー (ソースキュー) をターゲットにすることができます。デッドレターキューは、未使用のメッセージを分離して処理が失敗した理由を特定できるため、アプリケーションやメッセージングシステムのデバッグに役立ちます。

うーん、これだけではどう使えばいいのかイマイチつかみにくいですね。。理解している人からすれば「それ以外に書きようがない」という側面もありますが。。

実験用環境の準備

というわけで、実験用に下記3つのリソースを作ります。

リソース名(論理ID) 役割 指定するDLQ
MainQ 外からメッセージが投げ込まれるキュー SubQ
SubQ MainQのデッドレターキュー SuperSubQ
SuperSubQ SubQのデッドレターキュー なし

CloudFormationでサクッと作成しましょう。

できたものがこちらになります。

MainQ、SubQ、SuperSubQを作成

作成したキュー

ここにテスト用のメッセージを送信しておきます。

メッセージを送信する

メッセージ送受信ボタン

投入するメッセージは下記のとおりです。

これで準備完了です。

本題

MainQの詳細表示画面から「メッセージの送受信」に進みます。

メッセージを受信するために「メッセージのポーリング」ボタンを押下する

メッセージを受信

MainQでの受信1回目

正しく受信できています。受信数が1になっていますね。
メッセージの内容も送った内容そのものです。

MainQでの1回目の受信メッセージ

MainQでの1回目の受信メッセージ

MainQでの受信2回目

受信できました。受信数は2に変わりました。

MainQでの2回目のメッセージ受信

MainQでの2回目のメッセージ受信

MainQでの受信3回目

受信できなくなりました。
DLQに転送されるまでの最大受信回数を2として(CloudFormation経由で)指定しましたので、期待する挙動です。
受信しようと思っていたメッセージはDLQとして指定したSubQに転送されましたので、そちらを見てみます。

MainQでの3回目の受信

MainQでの3回目の受信

SubQでの受信1回目

受信できました。受信数はMainQに住んでいたころの受信回数とあわせて3になっています。
注目なのは、「メッセージ内容がMainQで受信した内容と同一であること」です。
あとで再処理するときも、メッセージの再加工をすることなく「受信先のキューを変更する」だけで同一の処理が再適用できる点がうれしいですね。

SubQでの1回目の受信

SubQでの1回目の受信

SubQでの受信2~3回目

2回目は受信に成功しましたが、3回目で受信できなくなりました。
これも、DLQとして指定したSuperSubQに転送されていますので、そちらを見てみます。

SubQでの3回目の受信

SubQでの3回目の受信

SuperSubQでの受信

受信できました。受信数は、延べ4回の受信を持ち越してきていますので5になっています。
やはりメッセージの内容は当初投入した内容から変わっていません。

SuperSubQでの受信

SuperSubQでの受信

受信回数はいつカウントされる?

ポーリングベースの場合は、ReceiveMessageアクションでメッセージを受信した後にDeleteMessageアクションでメッセージを削除しなければ受信回数が積み上がっていきます。これはわかりやすいですね。

Lambdaのようなイベントソースマッピングの場合は、トリガーの発射とともにカウントアップします。関数が正常終了すればメッセージが削除され、正常終了しなかった場合はメッセージが削除されずキューに残り、再トリガーとともに受信回数が積み上がっていきます。

使い道

後続処理に冪等性があることが条件になりますが、後続の処理がOOMやタイムアウト等の理由により正しく処理を完了できなかった場合に、計算資源をより多く割り当てた別の系統で再処理を行う、などの方法が考えられます。AWSリソースをスケールアップしても、そのうえで動かすロジックは同じものが使える、という点がおいしいポイントですね。

また、DLQと言ってもそれはただの役割でしかなく、キューそのものとしては同じメトリクスが取得できますので、組み合わせ方によっては多段処理も可能になりますね。

まとめ

直前に書いたような再処理が設計しやすいのも、「DLQに転送されるメッセージそのものはオリジナルキューに送信されたメッセージと同一である」からこそですね。SQSよくできてますね!

ではまた!

投稿者プロフィール

hiroo
根っこはインフラ屋な古いおじさん。