Amazon SQS 可視性タイムアウト時のメッセージ取得動作を Standard と FIFO のキューの種類違いで確認してみた

Amazon SQS 可視性タイムアウト( Visibility Timeout )の動作を把握してみよう
2022.07.19

Amazon SQS の可視性タイムアウト(Visibility Timeout)について Standard キューと FIFO キューのキューの種類違いで調べる機会がありました。キューの種類の違いによって挙動が異りますので紹介します。AWS 試験勉強のお役に立つかもしれません。

画像引用: visibility timeout

キューの種類による違いまとめ

Standard と FIFO キューの種類によって可視性タイムアウトによるメッセージの取得時の扱い方が異なります。

Standard Queue

  • 先頭のメッセージが可視性タイムアウトの期間内であれば後続のメッセージを取得する
  • 可視性タイムアウトを過ぎると再度メッセージを取得対象に戻る

FIFO Queue

  • 同一メッセージグループ ID 内で先頭メッセージグループ ID が可視性タイムアウトの期間内であれば後続メッセージを取得できない
    • 先頭メッセージの可視性タイムアウト期間が過ぎるか、可視性タイムアウト期間中のメッセージを削除するまではメッセージを取得できない

When you receive a message with a message group ID, no more messages for the same message group ID are returned unless you delete the message or it becomes visible.

Amazon SQS visibility timeout - Amazon Simple Queue Service

  • 別々のメッセージグループ ID でメッセージが可視性タイムアウトの期間内のメッセージがあれば、他のメッセージグループ ID のメッセージを取得できる
    • FIFO Queue だけどメッセージグループ ID が異なれば順序保証はない

Standard Queue

AWS CLI を使って Standard Queue に対してメッセージの送受信を試します。最初に実行コマンドについて簡単に説明します。

キュー名が長いためコマンド説明の都合、キューの名前を変数に入れています。

$ export AWS_QUEUE_URL=https://sqs.ap-northeast-1.amazonaws.com/123456789012/test-standard-queue

キューへの送信コマンドはとくに変わったことをしていません。

送信コマンド(Standard Queue)

$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-body "test message 1"

キューからの受信コマンドは引数--visibility-timeout 30をつけて可視性タイムアウトを30秒指定します。--max-number-of-messages 1の引数はメッセージの受信件数を最大10件まで指定できます。今回は1件ずつメッセージを取得し可視性タイムアウトの動作を確認します。

受信コマンド

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1

Standard Queue で送受信してみる

標準的な設定の Standard Queue を用意しました。

Standard Queue の特徴を軽く抑えます。順序保証はベストエフォートです。頑張るけど頑張れきれないときもあることは覚えておきましょう。

A standard queue makes a best effort to preserve the order of messages, but more than one copy of a message might be delivered out of order. If your system requires that order be preserved, we recommend using a FIFO (First-In-First-Out) queue or adding sequencing information in each message so you can reorder the messages when they're received.

あとはメッセージの受信は必ず1回という保証はなく1回以上はメッセージを受信できる仕様です、冪等性大事!

If this occurs, the copy of the message isn't deleted on that unavailable server, and you might get that message copy again when you receive messages. Design your applications to be idempotent (they should not be affected adversely when processing the same message more than once).

引用: Amazon SQS Standard queues - Amazon Simple Queue Service

メッセージ送信

可視性タイムアウトの検証のためメッセージを3回送信します。

$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-body "test message 1"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-body "test message 2"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-body "test message 3"

メッセージ受信

メッセージを1件ずつかつ、メッセージを削除せずに受信コマンドを4回実行します。

Standard Queue ですのでおおよそ最初に送信したメッセージから順番に表示され、可視性タイムアウトの期間が過ぎるまで一度受信したメッセージは表示されません。そのため、連続して受信コマンドを実行するとおおよそ後続のメッセージを順番に取得できます。

4回目の実行は4件目のメッセージがないかつ、可視性タイムアウト期間中により3件のメッセージがメッセージ取得対象から外れているため、メッセージを取得できずに結果が返ってきません。

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "0038ea0b-f0b8-4a39-b75e-09f08ba9de12",
            "ReceiptHandle": "AQEB7KFZlycMNvKPGLNJpsJ13vFdvUk8pEnmDxiMuGkbFoznjVWqlGIE7P/Vkb2Simu3dCmc2s4YUQEHZKT0X/CitpBMLmWtaYqzAwvozDy8JNg2WtH2vxEjmKi6QNqm2SfWrt8cwDEyYoXIeiHe3PLppI6J5+6ZfFJi8DJcaovja7AMMyYsWRz6jDKn8zrd5qjXzhcX8sG9V+mCIEz+GrjNbgnSKwOuaMtwdorDwciw6Ssi0ne2MkhrXqSlbNTHc7O2mO2wRdgtJ7L4OHuw/PjGCN3k+v/4P4cRPTRHVldkcGGL+L2CDYwPey7363c/9x7MNWIOgxj9d4hZ8zGlezgH2SjqlzKZXPIFQySsta0C7cXKSJDQa8PEH97o6r5eJKqZ4RCB1ljgHWEUg9Hiu01LluYghSz87lLNzEsXPSH8KKI=",
            "MD5OfBody": "4b1c2ba4e668c5b16b76124b2715fb84",
            "Body": "test message 1"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "129815e4-9e5d-4263-b62f-765e113f8b25",
            "ReceiptHandle": "AQEBa0wxhiJ5RbCksmHOwGHEmWK+3gC+9nFSoh2yuCiBo/GHsXwLQbRn0h+c0Gcu3hl8pmMDWxh4LA4xW/Rtc0HEFUs3ypqBvQJUeL1m4wCQSzQDXbKYV9D4NB08Ln4uvHkzSSR/D9UjTgN2fCwGhkwO1uj9o9AYTYgE5BMIllgJgHv0vlu2el/fjBvCuhdiZWEY+f//4W8Gwy/2PdmPKJH7IsWkrbt+M0nd7LUsrCTKWmYLzXq0kSEwax/o05oiF/BATnpG2B5891lslGsVhu+ip059ichJPD/7t+YYbLehSp0M2glCrkNW726+HejjwOip9DPRvAHSsMU1fG3A/nTvGVLmepfpHY6qO/PEF0Gbk7JlyFRLqRmqYeHU8vRmz/XpaLCxkqeo9NKjVeGPqACsoVF2DXJ+ikCVZViphcDDyc0=",
            "MD5OfBody": "639ce91e3642398aca7f1cb47d1e9ed1",
            "Body": "test message 2"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "3d25846b-99a6-4557-896e-1ddf95db5e30",
            "ReceiptHandle": "AQEBf+msMxo0NHsoi199E82iL96c1brYW+x8W5t0hd++WeJZpu/YI6GaxU58SLyrez748h6qDjrD5uUGRnAb+sEZVF4uNRbJha4BA6mw6QF+/4ESvQUdXdufqmL1XsrQ3oYw/KiNDbyiM6OwrKwf7viIz1GfGccQcC9wsVLWTftch/jFBC7eGyRjz/IoLfi+YKIYQHq2Iiojxu4M3zZ7g3g1esl5ycLbZGbznUu5sr9S1w5qfG3rHWH2KL62vwE93d10jwEDIjLHH9OSE3hYEgQARaIvOGR32rHqTgAVdMfFXeunVCH3HaZn/ur+ieLWbP0vFf009apBjoVg9gLI+wnUyjkk15OXfkptShmY2+NSvUKttbnpipu1EzvTe7kPItIOQSRbfwRfz+4poIdJFJ7Ac/PW/9mFkvgKKsd4+29nLQE=",
            "MD5OfBody": "6ab756a597bb7fd5e62a310fe107f572",
            "Body": "test message 3"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
# メッセージがないため実行結果は表示されません。

可視性タイムアウトの30秒を過ぎてから受信コマンドを1回実行するとおおよそ先頭のメッセージが再度表示されます。これは前回のメッセージを取得後にメッセージを削除していないため正しい動作です。

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "0038ea0b-f0b8-4a39-b75e-09f08ba9de12",
            "ReceiptHandle": "AQEB7KFZlycMNvKPGLNJpsJ13vFdvUk8pEnmDxiMuGkbFoznjVWqlGIE7P/Vkb2Simu3dCmc2s4YUQEHZKT0X/CitpBMLmWtaYqzAwvozDy8JNg2WtH2vxEjmKi6QNqm2SfWrt8cwDEyYoXIeiHe3PLppI6J5+6ZfFJi8DJcaovja7AMMyYsWRz6jDKn8zrd5qjXzhcX8sG9V+mCIEz+GrjNbgnSKwOuaMtwdorDwciw6Ssi0ne2MkhrXqSlbNTHc7O2mO2wRdgtJ7L4OHuw/PjGCN3k+v/4P4cRPTRHVldkcGGL+L2CDYwPey7363c/9x7MNWIOgxj9d4hZ8zGlezgH2SjqlzKZXPIFQySsta0C7cXKSJDQa8PEH97o6r5eJKqZ4RCB1ljgHWEUg9Hiu01LluYghSz87lLNzEsXPSH8KKI=",
            "MD5OfBody": "4b1c2ba4e668c5b16b76124b2715fb84",
            "Body": "test message 1"
        }
    ]
}

Standard Queue の可視性タイムアウトまとめ

おおよそ先頭のメッセージが可視性タイムアウトの期間内であれば、同じメッセージは取得対象から外れおおよそ後続のメッセージを取得する。可視性タイムアウトを過ぎると再度メッセージを取得対象に戻る。

可視性タイムアウトについて少し調べると上記のイメージではないでしょうか?次は FIFO Queue の場合を確認します。

FIFO Queue

今度は FIFO Queue とメッセージの送受信を試します。送信コマンドの引数が変わるため簡単に説明します。

同様にキュー名が長いためコマンド説明の都合、キューの名前を変数に入れます。豆知識としては FIFO Queue は名前の末尾に.fifoをつける必要があるのでリソース名から種類を見分けられます。

$ export AWS_QUEUE_URL=https://sqs.ap-northeast-1.amazonaws.com/123456789012/test-fifo-queue.fifo

キューへの送信は Standard とは異なり、FIFO のみ指定できるメッセージグループ ID の指定が必須になります。

You must associate a non-empty MessageGroupId with a message. If you don’t provide a MessageGroupId , the action fails.

You must associate a non-empty MessageGroupId with a message. If you don’t provide a MessageGroupId , the action fails.

メッセージグループ ID についてはベストプラクティスで以下の記載があります。

  • 同じメッセージグループ ID 内であればメッセージの順序を保証する
  • 異なるメッセージグループ ID 間の順序は保証しない

The message group ID is the tag that specifies that a message belongs to a specific message group. Messages that belong to the same message group are always processed one by one, in a strict order relative to the message group (however, messages that belong to different message groups might be processed out of order).

Using the Amazon SQS message group ID - Amazon Simple Queue Service

FIFO Queue を選択しても異なるメッセージグループ ID を指定してメッセージを送信すると、受信するときに順序が保証されないためご注意ください。

説明が長くなりましたが、送信コマンドは引数にはメッセージグループ ID(--message-group-id "group1")を指定して実行します。

送信コマンド(FIFO Queue)

$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "group1" --message-body "test message test message 1"

ちなみに FIFO Queue に対してメッセージグループ ID を未指定で送信すると以下のエラーメッセージが返ってきます。うっかり指定し忘れても気づけますね。

エラーメッセージ

An error occurred (MissingParameter) when calling the SendMessage operation: The request must contain the parameter MessageGroupId.

受信コマンドは Standard Queue と同じです。可視性タイムアウトを30秒指定し、メッセージは1件ずつ取り出します。

受信コマンド

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1

送受信の実行結果

同じメッセージグループ ID にメッセージ送信

可視性タイムアウトの検証のためメッセージを3回送信します。また、メッセージグループ ID は同じグループ名(group1)を指定して順序を保証させます。

$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "group1" --message-body "test message 1"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "group1" --message-body "test message 2"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "group1" --message-body "test message 3"

メッセージ受信

メッセージを1件ずつかつ、メッセージを削除せずに受信コマンドを4回実行します。Standard Queue の受信のときと同様です。

FIFO Queue で同一メッセージグループ ID 指定のため確実に最初に送信したメッセージを取得できます。

ポイントは可視性タイムアウトの挙動が Standard Queue と FIFO Queue では異なります。後続のメッセージを取得できた Standard Queue に対して、FIFO Queue は後続のメッセージを取得できません。

以下の実行結果を御覧ください。2回目以降の実行はレスポンスがありません。つまり、取得できるメッセージがないのです。

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "7a0e92c9-27d8-49c1-9777-a4be5a610bf5",
            "ReceiptHandle": "AQEBIGszML2k1aCfYnG9rWfBoTsCyXh9I/9OpLMiFtpDXmbXEX68N1YvRWT5qdEeaYEmeYEtsui8TIxCg3SyZm3v99TlQ0GGpYQOLs1zxnIskxQ2Bbpax4TF/rVfXTzFZie3BRy0ozdBrnEivxof+dEva/PijTpFQRGrlGMFN8omEOojf5ABN43G6U2aJrUmHVj+30WxEixEiLXcgPpBh45rFaXrnUHzbDyOPpLy9vT/en+LxYu0udKBGIVS8FX4lei3a6Ac8KZDmwbzzpCeqzT9A0iCBwvseW1sp070ZJXWeQY=",
            "MD5OfBody": "4b1c2ba4e668c5b16b76124b2715fb84",
            "Body": "test message 1"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1

理由は同一メッセージグループ ID を指定した場合、先頭メッセージの可視性タイムアウト期間が過ぎるかメッセージを削除するまで後続のメッセージを取得できないのが FIFO Queue の挙動となります。

When you receive a message with a message group ID, no more messages for the same message group ID are returned unless you delete the message or it becomes visible.

Amazon SQS visibility timeout - Amazon Simple Queue Service

メッセージグループを分けてメッセージ送信

次はメッセージグループ ID を異なるグループに分けてメッセージを送信してみます。前回のメッセージ3件が残っているため一度メッセージをクリアします。

$ aws sqs purge-queue --queue-url ${AWS_QUEUE_URL}

メッセージグループ ID 指定を変更してメッセージグループ毎(divide-group1 - 3)にメッセージを送信します。divide-group1だけは2回メッセージを送信しています。

$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "divide-group1" --message-body "test message 1"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "divide-group1" --message-body "test message 1-2"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "divide-group2" --message-body "test message 2"
$ aws sqs send-message --queue-url ${AWS_QUEUE_URL} --message-group-id "divide-group3" --message-body "test message 3"

メッセージ受信

今度はメッセージグループ ID 毎に可視性タイムアウトが適用されるため次々とメッセージを取得できます。

しかし、4回目の実行では同じメッセージグループ ID(divide-group1)の先頭メッセージが可視性タイムアウトの期間中のため、後続のメッセージを取得できませんでした。

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "2a0722f2-fd23-41c7-862f-07a053f5e5a7",
            "ReceiptHandle": "AQEBqp3fsMo/CnX1usY/KpYsz0qGjj0aOb4aLdOw6MXkYp3vSt9oXke+e20mV867FscWjvSFRVOu/KAWAMsBrmy4AGbWKCvyCkhUAkBSqSYER0AJBEYOCXE6RoecPFLe0ToGM4syNxlPu5D2/6iNRVLDSdx9mfDfcpj31+HBLKrVSMtxS6RDM/w7rHcH+u+wv1UIYTlShCe85Wq4zUtV+bmVoHnjuc/Z4duUgo6LJG4PeiOysdHwcuiZc6sAFtPvxPqJhEM/E7SfByi7MtWd42LZAXibZxPpjkHlq92iRKGprM0=",
            "MD5OfBody": "4b1c2ba4e668c5b16b76124b2715fb84",
            "Body": "test message 1"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "35bcf731-dc4f-4caa-8531-b940392346ea",
            "ReceiptHandle": "AQEB0yilfpKWhEJ2JitBFHu7VHloQen6sv7PD9MYdbTpKKSXzxdhnKmSo7MpfW7WCv0KzECy/rdg0wBotnLfsmomAmj6dxM2GdKT4HDWnTSk7cBzs5qt/dg1XLBkfTLU9v+HqjH6VAED4QLxxGJ6LZeX0HzYtSwDZpwZFFERJKdo3VJFFg7gK3R1mLZ1BrihMKKYTAZvY4vjmYC8Q85EyMZVlcjVZzemB+lvCk+XuUXg5+Nfe5cle4W53lnTajJfCqZ0/c1Dq+MUAyNg66S5Lc60yqDOw0mtp/WrpRqAc5U6GXI=",
            "MD5OfBody": "639ce91e3642398aca7f1cb47d1e9ed1",
            "Body": "test message 2"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
{
    "Messages": [
        {
            "MessageId": "c6668097-037b-4456-ab4b-486a0fc6f8d4",
            "ReceiptHandle": "AQEBS3s8OA+Dr2G2hqJQzMcCTP2YWIAK+tEcmVbx4QAiE4NAUMUgzgDtBQ2A75dIq5R9ne4lMJAMJAdtzcp18FYwCeUrQ+HiXWjfN08X9Pi1NNIb1jGhozi0moRdO4PVIGD+NzxCf+8MHQJ6sQN4qsmnjQcdcNHsAwKAlWVDHcjCROcFAvM60B3iJWs4TD6DBG5jTZAuDf4of00x2W+vS87TCicP/Ankc6MOxeUMqF2FO3+q2ORRfliBHnGBNGSfaMMDbrIkS1ziDrT1yFIKxLht9rVLy4THlFGCkI8rqmEitlM=",
            "MD5OfBody": "6ab756a597bb7fd5e62a310fe107f572",
            "Body": "test message 3"
        }
    ]
}

$ aws sqs receive-message --queue-url ${AWS_QUEUE_URL} --visibility-timeout 30 --max-number-of-messages 1
# FIFO の仕様により同メッセージグループの先頭メッセージ可視性タイムアウト

この点は先の検証(同じメッセージグループ ID にメッセージ送信)と同じことを示しています。同一メッセージグループ ID は可視性タイムアウトが過ぎるか、メッセージを削除するまで後続メッセージを取得できません。

一応送信した順番どおりに3件受信できました。ですが、FIFO Queue でもメッセージグループ ID が異なると順序保証がない点は留意しましょう。

FIFO Queue の可視性タイムアウトまとめ

同一メッセージグループ ID 内の先頭メッセージが可視性タイムアウトを期間中の場合、可視性タイムアウトが過ぎるかメッセージを削除するまで後続のメッセージを取得できない。メッセージグループ ID 内で先頭メッセージがロックされているイメージに近い。メッセージグループ ID が異なれば Standard Queue 同様におおよそ後続のメッセージを取得できる。

可視性タイムアウトの違いまとめ

Standard Queue

  • 先頭のメッセージが可視性タイムアウトの期間内であれば後続のメッセージを取得する
  • 可視性タイムアウトを過ぎると再度メッセージを取得対象に戻る

FIFO Queue

  • 同一メッセージグループ ID 内で先頭メッセージグループ ID が可視性タイムアウトの期間内であれば後続メッセージを取得できない
    • 先頭メッセージの可視性タイムアウト期間が過ぎるか、可視性タイムアウト期間中のメッセージを削除するまではメッセージを取得できない
  • 別々のメッセージグループ ID でメッセージが可視性タイムアウトの期間内のメッセージがあれば、他のメッセージグループ ID のメッセージを取得できる
    • FIFO Queue だけどメッセージグループ ID が異なれば順序保証はない

原文再掲

When you receive a message with a message group ID, no more messages for the same message group ID are returned unless you delete the message or it becomes visible.

Amazon SQS visibility timeout - Amazon Simple Queue Service


The message group ID is the tag that specifies that a message belongs to a specific message group. Messages that belong to the same message group are always processed one by one, in a strict order relative to the message group (however, messages that belong to different message groups might be processed out of order).

Using the Amazon SQS message group ID - Amazon Simple Queue Service

おわりに

AWS Certified Developer - Associate 試験や、AWS Certified DevOps Engineer - Professional 試験で SQS の可視性タイムアウトについての学習する機会があるかと思います。AWS CLI で簡単に動作確認を行えますので試験勉強に合わせて手を動かしてみるときっと記憶に残ると思うので本記事を参考にトライしていただけると嬉しいです。

本文には載せておりませんがいろいろと検証しているとキューのメッセージを受信時に合わせてメッセージを削除したくなります。AWS CLI でスクリプト書くよりは SDK 使って素直に書いた方が楽そうだったので私は SDK 使いました。

参考