kakakakakku blog

Weekly Tech Blog: Keep on Learning!

AWS CLI / AWS SDK から実行できるようになった Amazon SQS の「DLQ 再処理」

2023年6月8日にリリースされた Amazon SQS の新しい API 3種類によって,Amazon SQS のデッドレターキュー (DLQ: Dead Letter Queue) に移動されたメッセージを "元のキュー" や "別のキュー" に戻す「DLQ 再処理」を AWS CLI / AWS SDK などから実行できるようになる.今まではマネジメントコンソールでのみ使えていたけど,今後はアプリケーションからも実行できるため,例えば「リカバリ処理」の自動化などに活用できて良さそう❗️

  • StartMessageMoveTask
  • CancelMessageMoveTask
  • ListMessageMoveTasks

さっそく AWS CLI で検証してみた✌️

aws.amazon.com

aws.amazon.com

構成

今回は以下のように3つの Amazon SQS キューを作った🎲

  • main-queue: ビジネスロジックで使うキュー
  • main-queue-dlq: ビジネスロジックにエラーが繰り返し発生したときなどにメッセージを移動するデッドレターキュー
  • recovery-queue: リカバリ処理用のキュー

1. main-queue にメッセージを送信する

まず AWS CLI を使って main-queue にメッセージを5件送信する.メッセージ本文は適当に messageA のようにしておいた.

$ MAIN_QUEUE=https://sqs.ap-northeast-1.amazonaws.com/123456789012/main-queue

$ aws sqs send-message --queue-url ${MAIN_QUEUE} --message-body messageA
$ aws sqs send-message --queue-url ${MAIN_QUEUE} --message-body messageB
$ aws sqs send-message --queue-url ${MAIN_QUEUE} --message-body messageC
$ aws sqs send-message --queue-url ${MAIN_QUEUE} --message-body messageD
$ aws sqs send-message --queue-url ${MAIN_QUEUE} --message-body messageE

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "5"
    }
}

2. main-queue から main-queue-dlq にメッセージを移動させる

次にメッセージを繰り返し取得しながら main-queue-dlq に移動させる.

# メッセージ件数が少ないと1回で全件取得できない場合もある
# https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ReceiveMessage.html
$ aws sqs receive-message --queue-url ${MAIN_QUEUE} --max-number-of-messages 10 | jq '.Messages[] | del(.ReceiptHandle)'
{
  "MessageId": "c20308ff-46da-4a02-86cd-219dcf6d5511",
  "MD5OfBody": "1c8dcdcdaa1600f013e3e15fc4f95d29",
  "Body": "messageA"
}
{
  "MessageId": "cd32e238-ea25-4274-a06a-0cdd7dbc2be4",
  "MD5OfBody": "8c036043f4dd0bbe092697b126321081",
  "Body": "messageB"
}
{
  "MessageId": "71296c42-02c2-4327-9f2e-de87d2aaeb6a",
  "MD5OfBody": "be57b336bfd78dd780ed85564672e003",
  "Body": "messageC"
}
{
  "MessageId": "341490cc-7bcc-49e5-808f-8c04f957b7ab",
  "MD5OfBody": "1430d0d674dc41d3f7710cab7881d685",
  "Body": "messageD"
}
{
  "MessageId": "ebbab43e-262a-4711-95c4-c87ba03fc088",
  "MD5OfBody": "df4f6feddd75cebc20548e6750427ee0",
  "Body": "messageE"
}

最大受信数に達したため main-queue のメッセージ件数は0件になった.

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "0"
    }
}

3. main-queue-dlq のメッセージを確認する

期待通りに main-queue-dlq のメッセージ件数が5件になっている👌

そして,デッドレターキューに移動されても MessageId は同じになる.もちろん MD5OfBody も同じ👀

$ MAIN_QUEUE_DLQ=https://sqs.ap-northeast-1.amazonaws.com/123456789012/main-queue-dlq

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE_DLQ} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "5"
    }
}

$ aws sqs receive-message --queue-url ${MAIN_QUEUE_DLQ} --max-number-of-messages 10 | jq '.Messages[] | del(.ReceiptHandle)'
{
  "MessageId": "c20308ff-46da-4a02-86cd-219dcf6d5511",
  "MD5OfBody": "1c8dcdcdaa1600f013e3e15fc4f95d29",
  "Body": "messageA"
}
{
  "MessageId": "cd32e238-ea25-4274-a06a-0cdd7dbc2be4",
  "MD5OfBody": "8c036043f4dd0bbe092697b126321081",
  "Body": "messageB"
}
{
  "MessageId": "71296c42-02c2-4327-9f2e-de87d2aaeb6a",
  "MD5OfBody": "be57b336bfd78dd780ed85564672e003",
  "Body": "messageC"
}
{
  "MessageId": "341490cc-7bcc-49e5-808f-8c04f957b7ab",
  "MD5OfBody": "1430d0d674dc41d3f7710cab7881d685",
  "Body": "messageD"
}
{
  "MessageId": "ebbab43e-262a-4711-95c4-c87ba03fc088",
  "MD5OfBody": "df4f6feddd75cebc20548e6750427ee0",
  "Body": "messageE"
}

4. "元のキュー" に DLQ 再処理を行う

さっそく aws sqs start-message-move-task コマンドを使って DLQ 再処理を行う.今回は "元のキュー" にメッセージを戻すため,指定するオプションは --source-arn のみ.また今回は --max-number-of-messages-per-second を指定して,1秒間に1メッセージを戻す(デフォルトだと自動的に最適化される).レスポンスの TaskHandle には Base64 でエンコードされた再処理情報が入る(今回はマスキングした).

$ MAIN_QUEUE_DLQ_ARN=arn:aws:sqs:ap-northeast-1:123456789012:main-queue-dlq

$ aws sqs start-message-move-task --source-arn ${MAIN_QUEUE_DLQ_ARN} --max-number-of-messages-per-second 1
{
    "TaskHandle": "XXXXX"
}

awscli.amazonaws.com

期待通りに main-queue-dlq のメッセージ件数が0件になって,main-queue のメッセージ件数が5件になった👌

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE_DLQ} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "0"
    }
}

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "5"
    }
}

注意点は DLQ 再処理を行うと異なる MessageId になるところ(別メッセージとして再度送信されたイメージ).特にアプリケーション側で「冪等性」を考慮するときは MD5OfBody と組み合わせて同一性を確認すると良さそう❗️

$ aws sqs receive-message --queue-url ${MAIN_QUEUE} --max-number-of-messages 10 | jq '.Messages[] | del(.ReceiptHandle)'
{
  "MessageId": "0533eee6-3aa6-4ec9-8d3a-c66672933886",
  "MD5OfBody": "1c8dcdcdaa1600f013e3e15fc4f95d29",
  "Body": "messageA"
}
{
  "MessageId": "10ee8c32-642a-4b16-88d5-9411fa71ecf5",
  "MD5OfBody": "8c036043f4dd0bbe092697b126321081",
  "Body": "messageB"
}
{
  "MessageId": "8842fc6e-3098-4d34-825c-661d9c2b34d5",
  "MD5OfBody": "be57b336bfd78dd780ed85564672e003",
  "Body": "messageC"
}
{
  "MessageId": "86a51a9b-2c02-486a-85bd-6eca74823995",
  "MD5OfBody": "1430d0d674dc41d3f7710cab7881d685",
  "Body": "messageD"
}

{
  "MessageId": "69114df1-0062-49a6-b22e-4b6181299505",
  "MD5OfBody": "df4f6feddd75cebc20548e6750427ee0",
  "Body": "messageE"
}

5. "別のキュー" に DLQ 再処理を行う

もう一度 main-queue-dlq のメッセージ件数に5件にして,今度は "別のキュー" にメッセージを戻す.その場合は aws sqs start-message-move-task コマンドに追加で --destination-arn オプションを指定する.あとは同じ❗️

$ MAIN_QUEUE_DLQ_ARN=arn:aws:sqs:ap-northeast-1:123456789012:main-queue-dlq
$ RECOVERY_QUEUE=https://sqs.ap-northeast-1.amazonaws.com/123456789012/recovery-queue
$ RECOVERY_QUEUE_ARN=arn:aws:sqs:ap-northeast-1:123456789012:recovery-queue

$ aws sqs start-message-move-task --source-arn ${MAIN_QUEUE_DLQ_ARN} --destination-arn ${RECOVERY_QUEUE_ARN} --max-number-of-messages-per-second 1
{
    "TaskHandle": "XXXXX"
}

期待通りに recovery-queue のメッセージ件数が5件になっている👌

$ aws sqs get-queue-attributes --queue-url ${RECOVERY_QUEUE} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "5"
    }
}

さらに aws sqs list-message-move-tasks コマンドを使うと DLQ 再処理の状況を確認できる.今回は実行後に確認したため COMPLETED になっていた.

$ aws sqs list-message-move-tasks --source-arn ${MAIN_QUEUE_DLQ_ARN}
{
    "Results": [
        {
            "Status": "COMPLETED",
            "SourceArn": "arn:aws:sqs:ap-northeast-1:123456789012:main-queue-dlq",
            "MaxNumberOfMessagesPerSecond": 1,
            "ApproximateNumberOfMessagesMoved": 5,
            "ApproximateNumberOfMessagesToMove": 5,
            "StartedTimestamp": 1686875002170
        }
    ]
}

awscli.amazonaws.com

6. DLQ 再処理をキャンセルする

最後に aws sqs cancel-message-move-task コマンドを試すためにメッセージを30件まで増やして main-queue-dlq に移動した.

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE_DLQ} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "30"
    }
}

そして aws sqs start-message-move-task コマンドを実行した直後に aws sqs cancel-message-move-task コマンドを実行した.そのときに TaskHandle を指定する必要がある.

$ aws sqs start-message-move-task --source-arn ${MAIN_QUEUE_DLQ_ARN} --max-number-of-messages-per-second 1
{
    "TaskHandle": "XXXXX"
}

$ aws sqs cancel-message-move-task --task-handle XXXXX
{
    "ApproximateNumberOfMessagesMoved": 1
}

awscli.amazonaws.com

DLQ 再処理のキャンセル中に aws sqs list-message-move-tasks コマンドで DLQ 再処理の状況を確認したところ,大きく CANCELLINGCANCELLED の2種類を確認できた.最終的にメッセージが11件移動されてからキャンセルされていた.

$ aws sqs list-message-move-tasks --source-arn ${MAIN_QUEUE_DLQ_ARN}
{
    "Results": [
        {
            "Status": "CANCELLING",
            "SourceArn": "arn:aws:sqs:ap-northeast-1:123456789012:main-queue-dlq",
            "MaxNumberOfMessagesPerSecond": 1,
            "ApproximateNumberOfMessagesMoved": 5,
            "ApproximateNumberOfMessagesToMove": 30,
            "StartedTimestamp": 1686875997267
        }
    ]
}

$ aws sqs list-message-move-tasks --source-arn ${MAIN_QUEUE_DLQ_ARN}
{
    "Results": [
        {
            "Status": "CANCELLED",
            "SourceArn": "arn:aws:sqs:ap-northeast-1:123456789012:main-queue-dlq",
            "MaxNumberOfMessagesPerSecond": 1,
            "ApproximateNumberOfMessagesMoved": 11,
            "ApproximateNumberOfMessagesToMove": 30,
            "StartedTimestamp": 1686875997267
        }
    ]
}

期待通りに main-queuemain-queue-dlq それぞれのメッセージ件数も中途半端な状態で止まっていた👌

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "11"
    }
}

$ aws sqs get-queue-attributes --queue-url ${MAIN_QUEUE_DLQ} --attribute-names ApproximateNumberOfMessages
{
    "Attributes": {
        "ApproximateNumberOfMessages": "19"
    }
}

参考

今回は AWS CLI v2.11.27 を使ったけど,今回の API は v2.11.26 でサポートされている🆕

github.com

boto3 だと v1.26.148 でサポートされている🆕

github.com

\( 'ω')/ 試す前にアップデートをお忘れなく〜