[アニメーションあり]AWS SQSのコンシューマーを中心とした可視性タイムアウトとFIFOキューの動作についての整理
はじめに
この記事ではSQSのコンシューマー側を中心とした動作について、アニメーションを用いて整理していきます
SQSを使うときに混乱しがちな「可視性タイムアウト」、「FIFOキューの動作」それら2つが組み合わさった時について説明していきます
SQS自体については下記の記事が参考になるかと思います
予備知識
以下では後述の解説を読むにあたっての前提知識を整理しておきます
SQSの処理の流れ
まず以下の2つの用語を整理しておきましょう
- プロデューサー: キューにメッセージを送信する側
- コンシューマー: キューからメッセージを取り出す側
SQSの基本的な流れとしては以下の通りです
- プロデューサーがSQSにメッセージを追加
- コンシューマーがメッセージを取得
- コンシューマーが処理完了後にメッセージを削除
この時、注意点としてエラーなどが発生しコンシューマーがメッセージを処理できないと、キューにはメッセージが残り続けることになります
SQSのキューの種類について
SQSでは標準キュー(Standard queue)とFIFOキュー(FIFO queue)の2つがあります
標準キューでは先に到着したメッセージが先に取得されるという保証はありません
一方でFIFOキューではそれが保証されています
後で詳しく見ていくので今は2種類のキューがあるということを覚えておけば大丈夫です
可視性タイムアウトに関して
可視性タイムアウトはコンシューマーがメッセージを取得する際、重複して同じメッセージが取得されないようにするための設定です
各メッセージに可視性タイムアウトがあり、取得した段階でコンシューマーからはこのメッセージを取得することができなくなります。
一定時間が経過すると、コンシューマーからもメッセージが取得できるようになります。
この時、最初にメッセージを取得したコンシューマーからキューに対して何も操作をしなくても、SQSが自動でメッセージを取得できるようにしてくれます。
SQSの基本的な動作(標準キュー)
まずは、基本的な動作の確認を行います
ここでは標準キューの場合の説明です
シンプルな例
基本的な流れは以下の通りです
- キューにメッセージが到着します。
- コンシューマーがメッセージを取得(GET)します。
- コンシューマー内で処理が進み、完了します
- 最後に削除リクエスト(DEL)が送られ、メッセージが消えます。

複数のコンシューマーが動く場合
これはコンシューマーが複数あっても同様です
メッセージの量が多かったりすると、1つのコンシューマーだけでは処理が追いつかない場合が発生します
こういったケースでは複数のコンシューマーが並行して稼働し、メッセージの処理を行うことになります。
※アニメーションでは簡略化のために1つずつ処理していますが、実際はまとめて取得することも可能です。
それぞれのコンシューマーが別々のメッセージを処理することになります。
ただ、標準キューでは必ずしも到着した順にコンシューマーがメッセージを処理するわけではないということに注意が必要です。

可視性タイムアウト
次に複数のコンシューマーが並行して動作する際に問題になってくる排他制御について説明します
同じメッセージを重複して処理しないようにするための仕組みです
この排他制御を行うのが「可視性タイムアウト」になります。
ここでは標準キューでの説明を行なっていますが、標準キューでは厳密に排他制御が行われるわけではありません
可視性タイムアウトの設定が適切でも、標準キューは少なくとも1回の配信を保証する仕様であるため、稀に同時に複数のコンシューマーに配信されることがあります
厳密に同時に処理するコンシューマーの数を1つにしたいのであれば、FIFOキューを使うべきです。
メッセージの処理に成功した場合
まずはシンプルな例で、メッセージの処理中に他のコンシューマーがメッセージを取得しようとした時のケースです
メッセージが1つ到着し、それをコンシューマー1が処理します
処理中は可視性タイムアウトがまだ切れていないのでコンシューマー2がメッセージを取得しようとしても、メッセージは何も取得できません(コンシューマー2からはキューが空のように見える)
※空に見える挙動はポーリング設定にも依存しますが、ここでは概念的な説明とします

可視性タイムアウトによる再試行
もう少し複雑なケースを考えてみましょう
コンシューマー1がメッセージの処理中にエラーが発生してしまいます
この時、コンシューマー1はメッセージの削除が行えず、キューには見えない状態のメッセージが残り続けています。
一定時間が過ぎて、可視性タイムアウトを迎えるとコンシューマーからメッセージが取得できるようになります。
これは、事実上の再試行処理と同じです。

FIFOキュー
今までは標準キューを使って説明していましたが、次はFIFOキューの説明に移ります。
FIFOキューは先述の通り、メッセージの順序が厳密に保持されます。
この辺りに関連してどんな動作となっているのかみていきましょう。
なお、FIFOキューではメッセージを追加する際に「グループID」と呼ばれるものを指定する必要があります。
一旦、ここでは全てのメッセージは同じグループに到着するとして話を進めます。
順序制御
FIFOキューでは同一グループ内において厳密にメッセージの順序が制御されます。
これは、FIFOキューが先頭のメッセージが残っている間は、他のメッセージの配信を行わないことによって成り立ちます。
つまりは以下のような流れです
- キューにA,Bの順で2つのメッセージが到着
- コンシューマー1がメッセージを取得
- コンシューマー2がメッセージを取得
この時、コンシューマー2はメッセージBがキューにあるにも関わらず、メッセージを取得できません。
※アニメーション中では簡略化のために失敗しているように表現していますが、より厳密にはキューのメッセージが空であるかのように見えます。

FIFOキューにおける可視性タイムアウト
FIFOキューでは同一グループ内において先頭のメッセージが削除されるまで、次のメッセージが取得できません
例えば、コンシューマー1がAの処理中にエラーを起こした場合、先頭のメッセージが取得できるようになるのはキューの中のメッセージの可視性タイムアウトが切れてからになります
標準キューでは前のメッセージが削除されなくても、後ろのメッセージを取得できます。

FIFOキュー(複数グループ)
もう少し話を発展させて、FIFOキュー内に複数のメッセージグループがある場合を考えてみましょう
グループ毎の並行処理
例えば、赤と青2つのメッセージグループがあるとします。
先ほどの説明ではグループが1つしかないケースでしたが、今回は複数のグループがあるケースです。
この時、グループ内では順序が保たれますが、グループ間では順序が保たれません。
例えば、グループAのメッセージがグループBより先に到着しても、グループBの方が先に処理される可能性があります
そのため、2つのコンシューマーがそれぞれ別のグループからメッセージを取得し、並行して処理を行うことが可能です

グループ間では独立しているので、もし仮に片方のグループの先頭のメッセージの処理に失敗しても、別のグループからはメッセージを取得することが可能です

SQSを使う上でのトラブル
ここではSQSを使う際にしばしば発生するトラブルについて説明していきます
可視性タイムアウトに起因する重複処理
可視性タイムアウトがコンシューマーの処理時間より短い場合に発生する問題です。
コンシューマーの処理中に可視性タイムアウトを迎え、他のコンシューマーがそのメッセージを取得してしまうことによって発生します。
適切に可視性タイムアウトを設定する、または可視性タイムアウトの時間を延長することで回避は可能です。
可視性タイムアウトの見積もりが難しく、処理に時間がかかる場合は、処理中にコンシューマーから可視性タイムアウトの延長(ハートビートのような処理)を行うことで、重複取得を防ぐことができます
このために可視性タイムアウトの変更を行うChangeMessageVisibilityAPIが存在します。
原則として標準キューの場合、複数回同じメッセージが処理されてしまうことがあるため、処理内容を冪等にしておくのが好ましいです。

同様の問題はFIFOキューでも同じく発生します
FIFOキューを使用していても、同じメッセージが2回処理されてしまうケースはこのパターンが経験上多いです。

FIFOキューの詰まり
FIFOキューを利用している場合に発生する問題です。
FIFOキューでは前のメッセージがキューから削除されるまで、後続のメッセージは取得できません。
これは先述のとおり、先頭の可視性タイムアウト中であっても同様です。
メッセージ自体に不備があり、そのメッセージがどのコンシューマーでも処理できないといったケースで発生します。
この時、先頭のメッセージが常に取得され、後続のメッセージは永遠に処理されないことになります。
コンシューマー側でエラーハンドリングを行い適切に処理(削除)するか、処理できない場合はDLQへ退避させるといった方法で回避は可能です。

これは複数グループの時にも発生します。
特定のグループ内でメッセージが詰まってしまうといった形になります(それ以外はメッセージが消化される)

一方で標準キューであれば、先頭にメッセージがあっても後ろのメッセージが取得できるため、クリティカルであるケースは少ないはずです。

終わりに
SQSのコンシューマー側の挙動は、時間経過と密接に関係しているので、アニメーションでの説明が良いかと思い本記事を作成しました。
理解の一助となれば幸いです。






