Pub/Subサブスクリプションにデッドレタートピックを設定してみた

デッドレターって名前は少し物騒ですよね。
2023.09.29

クラスメソッド株式会社データアナリティクス事業本部所属のニューシロです。
今回はGoogle CloudのサービスであるPub/Subサブスクリプションにデッドレタートピックを設定してみました。

デッドレタートピックとは

Pub/Subには、確認応答がないメッセージを再送信してくれる機能があります。

Pub/Sub がメッセージの配信を試みてもサブスクライバーによるメッセージの確認応答ができない場合、Pub/Sub はメッセージを自動的に再送信しようとします。この再配信試行は、サブスクリプション再試行ポリシーと呼ばれます。これは、オンまたはオフにできる機能ではありません。ただし、使用する再試行ポリシーのタイプは選択できます。

ありがたい機能ではありますが、ケースによっては何度も再送信を繰り返して欲しくない場合もあるかと思います。その際に、Pub/Subサブスクリプションにデッドレタートピックを設定しておくと、一定の回数再送信が失敗したメッセージの宛先がデッドレタートピックに変更され、指定した回数以上の再送信を防ぐことができます

Pub/Sub サービスがメッセージの配信を試みても、サブスクライバーが確認応答できない場合、Pub/Sub は配信不能メッセージをデッドレター トピックに転送できます。

では、実際に設定してみましょう。

本題

第2世代Cloud Functionsデプロイ

まずは第2世代Cloud Functionsをデプロイします。
今回は権限エラーを意図的に発生させてPub/Subの挙動を確認したいので、コード内容はなんでも大丈夫です。ただHTTPSトリガーで、認証が必要とだけは設定する必要があります。
ちなみに私は、Google Cloudコンソールから第2世代Cloud Functionsをデプロイする際にデフォルトで書かれているHello World!を表示するPythonのサンプルコードを使用しました。

Pub/Subトピック、サブスクリプションを作成する(デッドレタートピック未設定)

Google Cloudコンソールからでもいいのですが、今回はCloud Shellで作成します。
Pub/Subトピックを作成し、そして挙動の検証のためまずはデッドレタートピックは未設定でPub/Subサブスクリプションを作成します。配信タイプはpushです。

gcloud pubsub topics create devio_topic
gcloud pubsub subscriptions create devio_sub_1 \
  --topic=devio_topic \
  --push-endpoint="Cloud FunctionsのHTTPSエンドポイントURLを入力してね"

本来は、認証が必要な第2世代Cloud Functionsを起動させるためには権限Cloud Run 起動元を持ったサービスアカウントをPub/Subサブスクリプションに設定しなければなりません。しかし前述の通りエラーを意図的に発生させたいため、今回はあえて設定しません。

Pub/Subトピックにメッセージ送信

Cloud ShellでPub/Subに適当なメッセージを送信します。

gcloud pubsub topics publish devio_topic \
  --message="Hello"

狙い通り、こちらはエラーが発生します。
理由としては、第2世代Cloud Functionsを起動させるためのメッセージがHTTPSエンドポイントURL宛に送られるのですが、前述の通りPub/Subサブスクリプションに権限Cloud Run 起動元を持ったサービスアカウントを設定していないためです。
エラー発生のためPub/Subの再試行が働くのですが、再試行までの時間など設定していないためデフォルトの再試行ポリシー:すぐに再試行が適用されています。
よって短時間の間に再試行を繰り返し続け、Cloud Functionsのログが大変なことになります。

(下にも同じログがずっと続いています)

エラー内容はもちろんThe request was not authenticated.です。また、Pub/Subが短期間で何度も再試行している様子がログからわかります。
しかし、今回のようなエラーだともちろん何回再試行しても成功することはありません。またこの現象は、コード内でエラーを検出した際も発生することがあります。

もしこのような状況になってしまった場合は、一旦メッセージをパージしましょう(ただしメッセージ保持設定をしていなければメッセージが失われますので注意してください)。 Google Cloudコンソールでパージできます。

では、デッドレタートピックを設定したサブスクリプションだとどのような挙動になるか検証してみましょう。

デッドレタートピックを設定したPub/Subサブスクリプションを再作成

まずはデッドレタートピックを作成します。メッセージが失われないよう、サブスクリプションも作成しておきます。

gcloud pubsub topics create devio-deadletter-topic
gcloud pubsub subscriptions create devio-deadletter-sub \
  --topic=devio-deadletter-topic

第2世代Cloud Functionsを起動させるPub/Subサブスクリプションを作り直します。

gcloud pubsub subscriptions delete devio_sub_1
gcloud pubsub subscriptions create devio_sub_2 \
  --topic=devio_topic \
  --push-endpoint="Cloud FunctionsのHTTPSエンドポイントURLを入力してね" \
  --dead-letter-topic=devio-deadletter-topic \
  --max-delivery-attempts=5

--max-delivery-attempts=5は最大試行回数です。5~100の間で設定できます。ここで設定した回数に達すると、宛先がデッドレタートピックに変更されます。

ちなみに、サブスクリプション作成コマンドについては以下のリンクが非常に参考になります。

今回はデッドレタートピック関連以外は割愛しておりますが、他にも設定すると良い項目がPub/Subサブスクリプションにはありますので、後日別の記事にしてあげたいと思います。

また、Pub/Subサービスアカウントにパブリッシャーとサブスクライバーのロールがそれぞれ必要ですが、クリックするだけで付与できます(画像はパブリッシャーのロールをクリック後のもの)。

再度Pub/Subトピックにメッセージ送信

再度Cloud ShellでPub/Subに適当なメッセージを送信します。

gcloud pubsub topics publish devio_topic \
  --message="Hello"

再度Cloud Functionsのログを確認します。

想定通り、Cloud Shellで送信した1件 + 再試行5件の計6件で終了しました。

デッドレタートピック確認

この再送信できなかったメッセージはデッドレタートピックに接続されているサブスクリプションから確認できます。
以下の画像を参考に、Google CloudコンソールでPULLをクリックしてください。

先ほど送ったメッセージが見つかりました。 このように、送信失敗したメッセージを後から確認することも可能です。

終わりに

Pub/Subについては他にも指数バックオフ、確認応答期限、メッセージ保持期間などを設定することができます。デッドレタートピックの設定も含め、それぞれのケースに適した設定を見つけていきましょう!

引用・参照まとめ