Amazon SQS と処理の重複

後編 ~ FIFO キューの特徴

2024-03-04
デベロッパーのためのクラウド活用方法

Author : 杉本 圭太

テクニカルインストラクターの杉本圭太です !
最近読んで面白かった漫画は「アンタイトル・ブルー」です。

Amazon SQS と処理の重複 前編 ~ 可視性タイムアウトの役割 で Amazon Simple Queue Service (SQS) の概要、そして可視性タイムアウトの役割やスタンダードキューの特徴を紹介しました。後編では FIFO キューの特徴や機能を学んでいきましょう φ(•ᴗ•๑)

今回も自作した図を交えて解説していきます。全体概要はこちらです。


1. FIFO キューの特徴

Amazon SQS ではキューを作成するときにスタンダードキューか FIFO キューのどちらかのタイプを選択します。キューのタイプは作成後に変更できません。

FIFO キューは前回紹介したスタンダードキューと同じ API でメッセージの送受信をするため、使い方はスタンダードキューと似ている部分が多いです。

  • プロデューサーが SendMessage リクエストで、キューにメッセージを送る
  • コンシューマーが ReceiveMessage リクエストで、キューからメッセージを取り出す
  • キューから取り出したメッセージに可視性タイムアウトが設定できる
  • コンシューマーが DeleteMessage リクエストで、処理が完了したメッセージをキューから削除する


しかし キュータイプ の説明に記載がある通り、配信順序、メッセージ配信時の重複の有無、スループットなどに違いがあります。その中で重要な用語を説明します。

1-1. First-In-First-Out (FIFO)

First-In-First-Out (FIFO) は「先入れ先出し」とも呼ばれます。キューのタイプの名前にもなっている通り FIFO キューを使用することで、キューに送信された順にメッセージを取得できます。スタンダードキューでは送信順にメッセージを取得できる保証はありません。

2020 年に公開された Application Integration Using Queues and Messages というブログ内にあるこちらの比較画像がわかりやすいです。

1-2. Exactly-Once

一般的な FIFO の特性とは関係無く、あくまで Amazon SQS の仕様として FIFO キューは Exactly-Once という特徴を持っています。Exactly-Once は「1 回限り」や「正確に 1 度」などと訳されることもありますが、FIFO キューの場合では「(可視性タイムアウトが切れない限り) 1 つのメッセージはキューから重複して配信されない」という意味です。

前編でスタンダードキューは At-Least-Once であるため、ReceiveMessage リクエストで可視性タイムアウト切れとは関係なく同じメッセージが複数回配信されてしまう可能性があると紹介しましたが、FIFO キューではそうならないように設計されています。しかし可視性タイムアウトの期限が切れるとスタンダードキューと同様に再度メッセージは配信されるため、FIFO キューを使用しただけで処理重複が完全になくなる訳ではない点はご注意ください。

FIFO や Exactly-Once というキューの特徴を確認しましたが、次からは API で FIFO キューを扱うために知っておくべき機能を説明します。


2. FIFO キューでのメッセージ送信 (SendMessage)

FIFO キューへメッセージを送信する場合はスタンダードキューと同じく SendMessage リクエストを使用しますが、FIFO キューではスタンダードキューで使用しないパラメーターを 2 つ意識する必要があります。

※ 複数のメッセージを 1 度のリクエストで送信したい場合は SendMessageBatch リクエストを使用できますが、こちらも FIFO キューで使用する場合は同様の考慮が必要です。

2-1. MessageGroupId (メッセージグループ ID)

FIFO キューへの送信リクエストでは、メッセージごとに MessageGroupId というパラメーターを必ず指定しなければなりません。つまりメッセージはキューへ送信されて保存されると、キューの中のとあるメッセージグループに属します。

このメッセージグループの特徴を挙げます。

  • キューへ送信する時に任意の MessageGroupId を指定するため、事前にキューに設定するものではない
  • 1 つの FIFO キューの中に複数のグループが存在しても良い
  • 同じグループのメッセージは、そのグループに送信された順にキューから配信されていく
  • グループが異なれば、同じキューでも送信された順にメッセージが処理されるとは限らない


こちらの図では 1 つの FIFO キューに、MessageGroupId が A のメッセージと MessageGroupId が B のメッセージを送信しています。

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

もちろん 1 つのキューの中でメッセージのグループは 1 つだけにしても良いです。

複数のグループを扱うことでメッセージグループ ID がどのように機能するかは FIFO キューでのメッセージ受信 (ReceiveMessage) で説明しますので、まずこの時点では「送信時はメッセージごとに MessageGroupId を指定する必要がある」とだけ認識できれば OK です !

2-2. MessageDeduplicationId (メッセージ重複排除 ID)

FIFO キューでは送信時の重複を緩和するため、メッセージごとに MessageDeduplicationId というパラメーターを設定します。
この仕組みがあることで、キュー側では 5 分の間に MessageDeduplicationId が同じメッセージを何度受け取っても、キュー内には 2 度目以降のメッセージが保存されないように制御されます。ただし 2 度目以降のリクエストもレスポンスは成功として返します。また、重複排除のスコープはキュー単位かメッセージグループ単位かで選択できます。

こちらの図は MessageDeduplicationId を b1 という値にして 2 度 SendMessage リクエストしています。

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

結果として 1 つのメッセージしかキュー内に保存されていません。

ちなみに FIFO キュー自体の設定で ContentBasedDeduplication を true にしておくと、送信リクエストで MessageDeduplicationId に任意の値を指定しなくても、自動で MessageBody からハッシュ値を生成して MessageDeduplicationId に設定してくれます (MessageAttributes はハッシュ値の生成に使用されません)。


3. FIFO キューでのメッセージ受信 (ReceiveMessage)

ここからは FIFO キュー内にグループを指定して保存されたメッセージをコンシューマーが受け取る場合の動作を確認しましょう。

重要な点を挙げるとすると、コンシューマーはメッセージを受け取る時にグループを指定できません (ReceiveMessage リクエストで MessageGroupId は指定できないため)。そのため ReceiveMessage リクエストの結果がどうなるかを理解しておくことが、FIFO キューを使いこなす鍵となります。この動作を理解しやすくしてもらうため、キューの状況などによって ReceiveMessage リクエストの結果がどうなるのかを、いくつかのパターンに分けて整理しました。

3-1. 1 つの Group から 1 つのメッセージを受信

まずは FIFO キューの挙動を理解するため、キューにメッセージグループが 1 つ、コンシューマーはメッセージを 1 つずつ取得していく場合の動作を確認します。

ここで押さえて欲しい点は「FIFO キューでは取得中のメッセージがあるグループからは、後続のメッセージが取り出せない」です。
つまりキューの中の Group A のメッセージが 1 つ取得された後は、そのメッセージを DeleteMessage リクエストで削除するか、可視性タイムアウトが切れるまでは Group A からメッセージが取り出せません。

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

FIFO キューでの First-In-First-Out は「取り出したら次」ではなく「メッセージの処理が完了したら次」という意味で捉えていただくとわかりやすいかもしれません。

3-2. 1 つの Group から複数のメッセージを受信

スタンダードキューでも可能ですが、ReceiveMessage のリクエストでは MaxNumberOfMessages パラメーターを指定して、1 度に取得できる最大メッセージ数を指定できます。

この場合も「FIFO キューでは取得中のメッセージがあるグループからは、後続のメッセージが取り出せない」ため、コンシューマー X が Group A から 3 つのメッセージを取得した場合は、3 つ全てが DeleteMessage リクエストで削除か可視性タイムアウト切れになるまで Group A から後続のメッセージは取り出せません。

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

ここまではメッセージグループが 1 つしかない場合でしたが、次からはメッセージグループが複数ある場合の動作です。

3-3. 複数の Group がある中で 1 つの Group から複数のメッセージを受信

すでに書きましたがコンシューマーはメッセージを受け取る時にグループを指定できないため、FIFO キューに複数のメッセージグループがある場合はどうなるのでしょうか ?

実は状況によって「必ずこうなる」とは言い切れないため、「こんなこともあるよ」という例を紹介します。

コンシューマーが 1 リクエストで複数メッセージを取得しようとした場合、キューから複数のグループのメッセージを取得することもありますが、Amazon SQS の FIFO キューではできるだけ同じメッセージグループのメッセージを返すように設計されています。

Group A に 2 つのメッセージ、Group B に 4 つのメッセージがあり、コンシューマー X が最大 3 つのメッセージを取得するリクエストをすると、Group B から 3 つメッセージが取得されることもあります。

画像をクリックすると拡大します

画像をクリックすると拡大します

その後 コンシューマー Y がメッセージの取得リクエストをすると、取得中のメッセージがない Group A からメッセージを受け取れます。

画像をクリックすると拡大します

画像をクリックすると拡大します

Group A も Group B も取得中のメッセージがあり、キュー内に他の Group がない場合はしばらくキューからメッセージを受け取れません。

画像をクリックすると拡大します

ただし、1 リクエストで必ず 1 つのグループのみとは限りません。

3-4. 複数の Group から複数のメッセージを受信

1 つ前のパターンと同様の条件で、Group A に 2 つのメッセージ、Group B に 4 つのメッセージがあり、コンシューマー X が最大 3 つのメッセージを取得するリクエストをしていますが、Group A から 2 つのメッセージ、Group B から 1 つのメッセージが取得されることもあります。

画像をクリックすると拡大します

画像をクリックすると拡大します

このように取得対象になったメッセージから同じグループのメッセージが送信順に取得されていますが、そのグループのメッセージ数がリクエストの MaxNumberOfMessages の数より少ない場合は、レスポンスに他のグループのメッセージも含まれています。

ただし Amazon SQS の FIFO キューではできるだけ同じメッセージグループのメッセージを返すように設計されているため、保証はないですが「Group A から 1 つのメッセージ、Group B から 2 つのメッセージ」のように、同じグループ内で最大数まで取得できていないのに複数のグループのメッセージが取得できるようなレスポンスになることは少ないと考えられます。


FIFO キューでのメッセージ受信パターンをいくつか確認しましたが、確実に 1 つずつメッセージを処理していきたいか、グループ単位でさえ順番に並んでいれば並列処理していけるなど、要件によってメッセージグループ ID の数やコンシューマーの数の設計が変わってくるということを理解しておきましょう !


4. その他の FIFO キューにしかない機能

最後におまけとして、スタンダードキューには存在しない機能を 2 つ紹介します。

4-1. 高スループットモード

スタンダードキューには明示的なクォータの制限はありませんが、FIFO キューでは現時点で SendMessageReceiveMessageDeleteMessage の 3 つの API はそれぞれ秒間 300 トランザクションまでのサポートです。(それぞれの API で 1 リクエストに複数メッセージを処理させれば、最大で秒間 3000 メッセージがサポートされています)

しかし FIFO キューは 高スループットモード に設定することで、クォータの引き上げ申請をしなくてもさらに高いスループットがサポートされています。リージョンによって上限は異なりますので、詳細な値は SQS のクォータ を確認ください。

高スループットモードを使用する場合の制約としては以下などがあるため、使用する場合はドキュメントを確認してください。

  • SendMessage リクエストで指定する MessageDeduplicationId の重複排除スコープはメッセージグループ単位
  • キューに設定できる FifoThroughputLimit はメッセージグループ単位

また、 高スループットモード のドキュメントには Amazon SQS が内部でどのようにパーティションを管理しているかも記載されていますので、興味がある方はぜひ !

4-2. ReceiveRequestAttemptId (受信リクエスト試行 ID)

必須のパラメーターではないですが、ReceiveMessage リクエストで ReceiveRequestAttemptId を指定すると、リクエストは成功してもレスポンスの受け取り時に失敗した場合などで、可視性タイムアウト切れを待たずに同じメッセージを再取得できます。

可視性タイムアウトを長めに設定していた場合は、レスポンスの受け取りを失敗するとリトライまでに時間がかかりすぎることを防げます。1 度目のリクエストから同じ ReceiveRequestAttemptId が 5 分以内である制約や、使い方を間違えると同じメッセージを重複して受け取る可能性もあるため注意が必要ですが、要件に合いそうであればドキュメントを確認してみてください。

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します

画像をクリックすると拡大します


5. おわりに

Amazon SQS の FIFO キューの特徴である First-In-First-Out や Exactly-Once だけでなく、API にどんなパラメーターがあり、どんな役割をするのかなども含めた説明をしました。この部分を理解できれば FIFO キューを使用すると部分的に重複緩和ができるという知識を踏まえて、適切に FIFO キューを活かしたシステム設計ができるはずです。

このようにテクニカルインストラクターは、自学だけではつまずきやすい部分などを含め、みなさんにより AWS を理解してもらいやすくなる工夫を日々行いながら クラスルームトレーニング を提供しております !

質問もリアルタイムでできるため分からない部分はとことんお付き合いしますし、ほとんどのコースには演習の時間も多くあるため学んだ内容を AWS 環境で実践できます !

AWS のトレーニングについてもっと知りたい方は、以下の連載記事も参考にどうぞ。


これまで自分で勉強してきたけど AWS を体系的に学ぶことでもっと詳しくなって業務で活用したい ! という方はぜひ AWS のトレーニングを受講してみてください !


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

杉本 圭太
アマゾン ウェブ サービス ジャパン合同会社
トレーニングサービス本部 テクニカルインストラクター

テクニカルインストラクターとして、知識をつけることが目的ではなく実際に業務で活用できる力を得ることを目指したトレーニングを提供しています。自分自身が新しいことを知るのが好きなので、「AWS を知るのは面白い ! もっと学んでみよう !」と多くの方に感じてもらえる工夫を常に考えながら活動しています。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する