クラウド同上

Cloud Pub/Subの新機能Dead-letter topicsに関して

Author
tsk
Lv:6 Exp:1174

MLチームの新人エンジニアです

Google CloudのメッセージングサービスであるCloud Pub/SubにDead-letter topics機能がリリースされました。Pub/Subの挙動に注目しながら見ていこうと思います。

Cloud Pub/SubのDead-letter topics機能

4/20にCloud Pub/SubでDead-letter topicsという機能がGenerally Available(GA)となりました。(リリースノートはこちら
これはBeta版なしでいきなりGAとなった新機能になります。

Cloud Pub/Subを普段使われる方はご存知かと思いますが、ack(確認応答)を返されなかったPub/Subメッセージはサブスクリプションに設定されたメッセージ保持期間(デフォルトでは7日間)を過ぎた場合に破棄されてしまいます。

Dead-letter topics機能では、「指定回数配信のリトライを行ったのに確認応答が返されなかった場合」にDead-letter topicとして指定したPub/Subトピックにメッセージを送信します。
このときDead-letter topicに接続されたサブスクリプションは該当のメッセージを溜め込みます。

なおメッセージ保持期間が切れたメッセージに関してはこれまで通り破棄されます。
(つまり、最大試行回数配信されずにメッセージ保持期間が切れた場合です)

詳しいドキュメントはこちらになります。

実験

Dead-letter topics機能の挙動を実際に確認します。
今回はCloud Shellでコマンドを実行しました。

準備

以下の図のようにPub/Subを設置します。

1.Pub/Subトピックを作成(TOPIC_NAME=test-topic)

gcloud pubsub topics create TOPIC_NAME

2.Dead-letter topicの作成(DEAD_TOPIC_NAME=dead-letter-topic)

gcloud pubsub topics create DEAD_TOPIC_NAME

※これは別プロジェクトに作成してもOKです

3.Dead-letter topicに対するサブスクリプションを作成(DEAD_SUB_NAME=dead-letter-sub)

gcloud pubsub subscriptions create DEAD_SUB_NAME \
--topic=dead-letter-topic \
--message-retention-duration=10m

※今回Dead-letter topicに接続されているサブスクリプションのメッセージ保持期間を10分としました

4.test-topicに対してサブスクリプションを作成(SUBSCRIPTION_NAME=test-sub)

gcloud pubsub subscriptions create SUBSCRIPTION_NAME \
--topic=test-topic \
--dead-letter-topic=dead-letter-topic \
--max-delivery-attempts=5

配信の最大試行回数を5回に指定します。
dead-letter-topicが別プロジェクトにある場合は --dead-letter-topic-project= オプションも指定します。

以上の設定はUI上からも可能ですが、説明は割愛します。

さて、ここで権限の問題が出てきます。
データの流れを見るとPub/Sub(test-sub)からPub/Sub(dead-letter-topic)へのメッセージの配信が必要となることがわかると思います。また、メッセージを配信したらackを返すことも必要となります。(dead-letter-topicからtest-subへのack、返さないとtest-subにメッセージが残存してしまいます)
以上を行うためには(Roleだと)publisher roleとsubscriber roleが必要となります。

今回はCloud Pub/Subのサービスアカウントに2つのRoleを付与します。
dead-letter-topicに対するpublisher Roleと、test-subに対するsubscriber Roleです。
(もちろん上記のUIからポチポチするだけで権限付与も可能です)

PUBSUB_SERVICE_ACCOUNT="service-${project-number}@gcp-sa-pubsub.iam.gserviceaccount.com"

gcloud pubsub topics add-iam-policy-binding dead-letter-topic \
    --member="serviceAccount:$PUBSUB_SERVICE_ACCOUNT"\
    --role="roles/pubsub.publisher"

gcloud pubsub subscriptions add-iam-policy-binding test-sub \
    --member="serviceAccount:$PUBSUB_SERVICE_ACCOUNT"\
    --role="roles/pubsub.subscriber"

project-number はコンソールのホーム画面で確認することができます。

以上で準備は完了です。

メッセージの送信

test-topicに対してメッセージを送信し、5回サブスクライブします。このとき、確認応答を返さないようにします。
サブスクライブにはgcloudコマンドを利用します。

メッセージ送信コマンドの実行

gcloud pubsub topics publish test-topic --message='{"a": "Hello, world"}'

レスポンス

messageIds:
- '1125638311675193'

メッセージ送信が完了しました。
test-subサブスクリプションのUI上でメッセージが1件あることを確認します。

確認応答なしのサブスクライブ

サブスクライブコマンドの実行
以下を5回実施します。–auto-ackオプションなしならば確認応答は返されません。

gcloud pubsub subscriptions pull test-sub --format json

レスポンス(5回目の例)

[
  {
    "ackId": "ISE-MD5FU0RQBhYsXUZIUTcZCGhRDk9eIz81IChFFwYIFAV8fXFfTXVdWBoHUQ0ZcnxmdDtcFQQLE1EvVVsRDXptXFcnUAwbd3tifWlbEgcFQFJ3XHPolqjnz73IYBclSqudwK9vM9b4zeJMZho9XxJLLD5-MSpFQV5AEkw6H0RJUytDCypYEU4E",
    "deliveryAttempt": 5,
    "message": {
      "data": "eyJhIjogIkhlbGxvLCB3b3JsZCJ9",
      "messageId": "1125638311675193",
      "publishTime": "2020-04-23T09:36:50.548Z"
    }
  }
]

配信を行ったメッセージと同じIDのメッセージが取り出されます。
レスポンスメッセージのフィールド deliveryAttempt から最大試行回数メッセージをサブスクライブしたことを確認します。今回は5回となります。
5回の操作によりPub/SubのUIから溜まっていたメッセージ1件が消えたことを確認します。

Dead-letter topics機能によるメッセージ再配信

Dead-letter topicsの機能により、ackが失敗したメッセージはdead-letter-topicへ配信されます。配信されたメッセージはdead-letter-subへ溜まります。
ここで、dead-letter-subの詳細ページへ移動し、メッセージが1件増えていることを確認します。このメッセージを取り出してみます。

dead-letter-subからのサブスクライブコマンド

gcloud pubsub subscriptions pull dead-letter-sub --format json

レスポンス

[
  {
    "ackId": "ISE-MD5FU0RQBhYsXUZIUTcZCGhRDk9eIz81IChFFgQIFAV8fXFfTXVaXhoHUQ0ZcnxmdDtcFFIDE1t_VVsRDXptXFcnUAwbd3tld29eFgEKTFR5WnOM8cWe873IYBclSu-ewK9vM72I_oFPZho9XxJLLD5-MTJFQV5AEkw6B0RJUytDCypYEU4E",
    "message": {
      "attributes": {
        "CloudPubSubDeadLetterSourceDeliveryCount": "5",
        "CloudPubSubDeadLetterSourceSubscription": "test-sub",
        "CloudPubSubDeadLetterSourceSubscriptionProject": "ca-tasukuito-test",
        "CloudPubSubDeadLetterSourceTopicPublishTime": "2020-04-23T09:36:50.548+00:00"
      },
      "data": "eyJhIjogIkhlbGxvLCB3b3JsZCJ9",
      "messageId": "1125642545089775",
      "publishTime": "2020-04-23T10:01:24.960Z"
    }
  }
]

message > attribute フィールド内をみるとtest-subからのメッセージであることが確認できます。
一応メッセージが送信したものと同じであるかどうかを確認します。
上記のレスポンスをtmp.jsonの中に保存し、メッセージの中身をデコードします。

コマンド

cat tmp.json | jq -r .[0].message.data | base64 --decode

結果

{"a": "Hello, world"}

中身も最初に送信したメッセージと一致しました。
Dead-letter topic機能が正しく動いたことを確認できました。

おまけですが、「準備」の手順3で設定したdead-letter-subのメッセージ保持期間(=10分)経過後、メッセージは完全に削除されました。

まとめ

アプリケーションで処理に問題があったデータを分離して、別のPub/Subに保存できることができるのでアプリケーションのデバッグに非常に有用です。
今回はgcloudコマンドでackを返さずにメッセージを引き出しました。実際のアプリケーションを利用する際は処理に問題がある場合にackを返さないようにすれば同じようにDead-letter topicsを活用できるかと思います。
なお、Dead-letter topicsに配信されたメッセージは無期限に保管されるわけではない(Dead-letter topicsに接続されているサブスクリプションの保管期限に依存している)のでこのあたりは注意が必要です。スナップショットなどと上手く組み合わせて使いましょう。