SQS FIFO キューへ同じ内容のメッセージを連続して送る方法を教えてください

2021.10.30

困っていた内容

SQS FIFO キューを使っている環境で、同じ内容のメッセージを連続して送信できません。

しばらく待つと送信できるようになるのですが、意図して同様のメッセージを送信したい場合はどう対応したらいいでしょうか?

現在のメッセージ送信方法は以下です。

同様のメッセージが送信できない例

readonly QUEUE_URL=<キューの URL>

for i in {0..2}; do
    aws sqs send-message \
        --queue-url ${QUEUE_URL} \
        --message-body '{"message": "fifo-queue-duplicate-test"}' \
        --message-group-id Group1
done

出力結果

{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "30555aba-16a2-453a-8e8e-2197dbc01d99",
    "SequenceNumber": "18865450273486831616"
}
{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "30555aba-16a2-453a-8e8e-2197dbc01d99",
    "SequenceNumber": "18865450273486831616"
}
{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "30555aba-16a2-453a-8e8e-2197dbc01d99",
    "SequenceNumber": "18865450273486831616"
}

何度メッセージを送信しても MessageId がしばらく同じ値になります。

どう対応すればいいの?

以下のいずれかの対応をすれば、同様のメッセージが連続で送信できます。

  • メッセージ送信時の重複排除 ID(MessageDeduplicationId)を分ける
  • メッセージの本文をコンシューマーに影響のない範囲で一部変える
    • 新たなキーと値を追加した場合はメッセージのサイズが大きくなる
  • 重複排除スコープをデフォルトの「キュー」から「メッセージグループ」にし、メッセージグループ ID(MessageGroupId)を分ける
    • メッセージはメッセージグループ毎に 1 つずつ処理されるので順序性に注意

重複排除 ID を分ける例

for i in {0..2}; do
    aws sqs send-message \
        --queue-url ${QUEUE_URL} \
        --message-body '{"message": "fifo-queue-duplicate-test"}' \
        --message-group-id Group1 \
        --message-deduplication-id "Deduplication${i}"
done

出力結果

{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "5930ed9c-87fe-411d-9a22-2a409048d705",
    "SequenceNumber": "18865450250396913408"
}
{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "c2313537-e276-4631-9e57-fa2cade69a3d",
    "SequenceNumber": "18865450250637040640"
}
{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "bd464df3-0755-4cdf-ac0f-e64a7e3ab862",
    "SequenceNumber": "18865450250856945152"
}

メッセージの本文を変える例

for i in {0..2}; do
    aws sqs send-message \
        --queue-url ${QUEUE_URL} \
        --message-body "{\"uuid\": $(uuidgen), \"message\": \"fifo-queue-duplicate-test\"}" \
        --message-group-id Group1
done

出力結果

{
    "MD5OfMessageBody": "272e249ab0b5f90e8d88135682d94760",
    "MessageId": "dda73899-6505-49c1-8254-bc5c42d414af",
    "SequenceNumber": "18865450558375920128"
}
{
    "MD5OfMessageBody": "af64cb408086d9b0cdc50ea87888c249",
    "MessageId": "d90abfcb-bb34-4b67-8c9b-a2fd4d05e22d",
    "SequenceNumber": "18865450558585584896"
}
{
    "MD5OfMessageBody": "bcd526c7a8fca87ab049bcbf130a3d5e",
    "MessageId": "1a339f14-9d8e-45fb-9be9-b58b57840bf6",
    "SequenceNumber": "18865450558797807616"
}

メッセージグループ ID を分ける例

for i in {0..2}; do
    aws sqs send-message \
        --queue-url ${QUEUE_URL} \
        --message-body '{"message": "fifo-queue-duplicate-test"}' \
        --message-group-id "Group${i}"
done

出力結果

{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "dd26dafd-dafe-49c4-b0ae-a257826d4f86",
    "SequenceNumber": "18865450926135280896"
}
{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "d544c8e9-0db1-41af-8236-6a648403ecce",
    "SequenceNumber": "18865450926342128128"
}
{
    "MD5OfMessageBody": "a14eb7d0681d6dd4a8332f5636a0d84c",
    "MessageId": "da6a6526-a59f-4996-b468-5dee06265651",
    "SequenceNumber": "18865450926566383616"
}

いずれの方法でもメッセージ ID が重複せず、コンシューマーでは別のメッセージとして取得可能です。

どんな仕組みなの?

SQS FIFO キューは設定でコンテンツの重複が排除できます。
重複排除期間は 5 分間です。

SQS コンソールの設定画面

この「コンテンツ」とはメッセージの本文(メッセージの属性ではなく)を SHA-256 でハッシュ化したものを指します。

「コンテンツに基づく重複排除」設定をオフにすると、重複排除 ID(MessageDeduplicationId)の指定が必須になります。
重複排除 ID を指定しない場合、メッセージ送信時に以下のエラーメッセージが出力されます。

An error occurred (InvalidParameterValue) when calling the SendMessage operation: The queue should either have ContentBasedDeduplication enabled or MessageDeduplicationId provided explicitly

FIFO キューを使うとコンシューマー側で重複排除、順序保証などを含むメッセージの同期処理の実装コストが下がります。
また、プロデューサー側も重複を意識せずにメッセージを送信できますが、意図して同じ内容のメッセージを送信したい場合は今回のような対応が必要になります。

FIFO キューは元来、プロデューサー・コンシューマー側で実装していた処理を SQS サービス側で担うため、標準キューよりオーバーヘッドが生じることを意識し、計画的に利用してください。

参考資料