Google Cloud 課金停止のトラブルシューティング:Pub/Subの無限ループ対策とエラーハンドリング

| 4 min read

📝 記事について: 本記事は、50近辺のくたびれた貧乏なおっさんの筆者が、「新しい技術」「このサービスおもしろいな~~」「このアプリおもしろいな~~」「ほしいな~~」「かいたいな~~」と思った製品・サービスについて、公開情報を調べてまとめたものです。実際に製品やサービスを使用・体験したわけではありません。内容の正確性については、必ず公式情報やデータソースをご確認ください。

Google Cloudの課金停止自動化で発生するPub/Subの無限ループ問題を解決します。メッセージ再送対策、ACK応答の重要性、メッセージパージ方法を解説します。

Core Insights

  • Pub/Subの無限ループ問題: 関数がエラーで終了すると、Pub/Subが「処理に失敗した」と判断してメッセージを再送し続けます。改善版コードでは、処理前に既に請求先アカウントが解除されているかチェックし、既に解除されている場合は処理をスキップして正常終了するように実装しています。
  • メッセージ再送の原因: Pub/Subは未確認(未ACK)のメッセージを最大7日間保持します。この期間中、メッセージが適切にACKされないと、何度も再送され続けます。
  • 解決策: コード側では冪等性を確保し、既に請求先アカウントが解除されている場合は処理をスキップして正常終了(ACK送信)します。手動では、Pub/Subのサブスクリプション画面からメッセージをパージ(削除)できます。

本記事は、Google Cloud 課金停止の自動化:予算超過で自動停止する設定方法で解説した設定手順を実装した際に発生する可能性のある問題と、その解決方法をまとめたものです。

Pub/Subの無限ループ(再配信)を確実に防ぐ

🔴 重要:Pub/Subの無限ループ(再配信)を確実に防ぐ

Pub/Subは、関数がエラー(例外)で終了すると、「処理に失敗したんだな」と判断して良かれと思って何度もメッセージを再送してきます。

❌ 以前のコードの問題点:

  1. 1回目:予算超過で請求先アカウントを解除 → 成功 ✅
  2. 2回目:同じメッセージが再配信される → 「もう止まってるよ!」というエラー(403 Forbidden)が発生 ❌
  3. 3回目以降:エラーが続く → Pub/Subは「処理に失敗した」と判断して再送 → 無限ループに陥る 🔄

結果として、ログにエラーが大量に記録され、不要な再配信が続いてしまいます。

✅ 改善版コードの解決策:

  1. 1回目:予算超過で請求先アカウントを解除 → 成功 ✅
  2. 2回目:同じメッセージが再配信される → 既に止まっているかチェック → 「既に解除されています。処理をスキップします。」と表示して正常終了(return)
  3. 3回目以降:Google Cloudは「無事に受け取った(ACK完了)」と判断し、再送を止める

既に請求先アカウントが解除されている場合は「処理をスキップ」して正常終了(return)するため、Google Cloudは「無事に受け取った(ACK完了)」と判断し、再送を止めてくれます。これにより、無限ループ(再配信)を確実に防ぐことができます。

⚠️ 重要: 改善版コード(main.py)では、処理前に既に請求先アカウントが解除されているかチェックし、既に解除されている場合は処理をスキップして正常終了するように実装しています。これにより、Pub/Subの無限ループ(再配信)を確実に防ぐことができます。

Pub/Subのメッセージ再送機能について

⚠️ 重要:Pub/Subのメッセージ再送機能について

最初、ACK応答を考えずにトリガーで紐づけカットのみで実装していた場合、再度紐づけしても切れていたという問題が発生することがあります。これは、Pub/Subのメッセージ再送機能が原因です。

Pub/Subのメッセージ保持期間: Google Cloud Pub/Subは、未確認(未ACK)のメッセージを最大7日間保持します。この期間中、メッセージが適切にACKされないと、何度も再送され続けます。

問題の発生パターン:

  1. 予算超過で請求先アカウントを解除 → 成功 ✅
  2. しかし、関数がエラーで終了したため、ACKが送信されない ❌
  3. Pub/Subは「メッセージが処理されなかった」と判断し、再送を続ける 🔄
  4. 請求先アカウントを手動で再度紐づけしても、古いメッセージが再送され続け、再度解除されてしまう 🔄
  5. この状態が最大7日間続く可能性がある ⏰

解決策:

  • コード側の対策: 改善版コードでは、既に請求先アカウントが解除されている場合は処理をスキップして正常終了(ACK送信)するため、再送を防げます。
  • 手動での対策: もし既に古いメッセージが残っている場合は、手動でメッセージをパージ(削除)することができます。

メッセージ確認応答(ACK)を適切に行う方法

🔧 メッセージ確認応答(ACK)を適切に行う方法

Cloud Run関数でPub/Subトリガーを使用する場合、関数が正常に完了すると自動的にACKが送信されます。ただし、以下の点に注意が必要です:

  1. 関数は必ず正常終了させる: エラーが発生した場合でも、例外をキャッチして関数を正常終了させると、ACKが送信され、再配信を防げます。改善版コードでは、エラーハンドリングでこれを実現しています。
  2. 冪等性の確保(重複処理対策): 同じメッセージが複数回配信されても安全に処理できるように、処理前に状態をチェックします。改善版コードでは、請求先アカウントが既に解除されているかチェックしています。
  3. 処理時間を短縮する: Pub/Subの確認応答期限(デフォルト10秒)内に処理を完了させることが推奨されます。処理時間が長い場合は、Cloud Runのタイムアウト設定やPub/Subの確認応答期限を延長することも可能です。
  4. エラーはログに記録する: エラーが発生しても関数を正常終了させることでACKを送信しつつ、エラー内容はログに記録して問題を追跡できるようにします。

改善版コードを使用することで:

  • ✅ 既に請求先アカウントが解除されている場合は、処理をスキップして正常終了(ACK送信)
  • ✅ エラーが発生しても、例外をキャッチして正常終了(ACK送信)
  • ✅ 重複メッセージが配信されても、安全に処理可能(冪等性確保)
  • ✅ 不要な再配信を防ぎ、ログのエラーを削減

💡 重要なポイント:なぜ複数回実行されるのか?

予算アラート自体は複数回発火していません。 Google Cloudの予算アラートは、予算のしきい値を超えた時点で一度だけ通知を送信するのが一般的です。

問題はPub/Subのメッセージ配信特性にあります:

  • Pub/Subは「at-least-once」(少なくとも1回)配信モデルを採用しており、同じメッセージが複数回配信される可能性があります。
  • メッセージの確認応答(ACK)が適切に行われなかった場合、一定時間後に自動的に再配信されます。
  • Cloud Run関数がメッセージ処理後にエラーが発生したり、ACKがタイムアウトしたりすると、Pub/Subはメッセージが処理されなかったと判断し、再配信します。
  • 最初の実行で紐付け解除は成功しましたが、その後の処理で何らかの理由でACKが適切に行われなかった可能性があります。

結論: 予算アラートが複数回発火しているのではなく、Pub/Subからの同じメッセージが複数回配信されていることが原因です。そのため、2回目・3回目の実行時に、既に請求先アカウントが解除されている状態で再度解除を試みるか、または権限チェックが厳密になり、エラーが発生しています。

手動でメッセージをパージ(削除)する方法

🔧 手動でメッセージをパージ(削除)する方法

もし既に古いメッセージが残っていて、再送が続いている場合は、手動でメッセージをパージ(削除)することができます。これにより、未確認のメッセージを一括で削除し、再送を止めることができます。

  1. [Pub/Sub] > [サブスクリプション] を開きます。
  2. サブスクリプション一覧から、stop-billing-topic-sub(または作成したサブスクリプション名)をクリックして開きます。
  3. サブスクリプションの詳細画面で、上部のアクションボタンから 「メッセージをパージ」(Purge Messages)ボタンをクリックします。
  4. 確認ダイアログが表示されます。警告メッセージを確認し、「パージ」ボタンをクリックします。
  5. これにより、サブスクリプション内のすべての未確認メッセージが削除され、再送が止まります。

⚠️ 注意: パージされたメッセージは復元できません。また、確認済みメッセージの保持が無効になっている場合、パージされたメッセージは完全に削除されます。

Pub/Sub サブスクリプション詳細画面 - メッセージをパージするボタン
Pub/Sub サブスクリプション詳細画面(stop-billing-topic-sub)。上部のアクションボタンの中に「メッセージをパージ」(Purge Messages)ボタンがあります(赤いマイナスアイコン付き)。このボタンをクリックすると、サブスクリプション内のすべての未確認メッセージを一括で削除できます。
Pub/Sub メッセージパージ確認ダイアログ
メッセージパージ確認ダイアログ。「すべてのメッセージのパージ」というタイトルで、警告メッセージが表示されています。右下の「パージ」ボタンをクリックすると、すべての未確認メッセージが削除されます。

📚 関連記事

📖 Google Cloud 課金停止の自動化:予算超過で自動停止する設定方法では、5ステップの基本手順を解説しています。
📖 Google Cloud 課金停止の権限設定完全ガイドでは、2つの権限が必要な理由と設定手順を詳しく解説しています。

Artist's Perspective

「Pub/Subの無限ループ問題は、最初は気づきにくい罠です。一度設定したら「動いている」と安心してしまうのですが、実は裏でエラーが発生し続け、メッセージが再送され続けていることがあります。

筆者が実際に遭遇したのは、最初の実行では成功したものの、その後のログを確認するとエラーが繰り返し発生していたという状況でした。これは、ACK応答が適切に行われていなかったことが原因でした。

この問題を解決するために、コードに冪等性を持たせ、既に処理が完了している場合はスキップするようにしました。これにより、メッセージが複数回配信されても安全に処理できるようになりました。エラーハンドリングと冪等性の確保は、Pub/Subを使う上で欠かせない重要なポイントです。」

データソース・参考リンク

本記事は以下の情報源を参考にしています。内容の正確性については、必ず元のデータソースをご確認ください。