Pub/Sub は、Publisher と Subscriber を疎結合にする便利な仕組みです。
ただし、Pub/Sub を挟めば自動的に安全な非同期処理になるわけではありません。Ack、再配信、重複処理、冪等性、Dead Letter Topic などを理解していないと、二重実行やメッセージ滞留で問題が起きます。
この記事では、Pub/Sub を設計するときに考えるべき論点を整理します。
Pub/Sub の基本構造は『Pub/Subとは何か』で、メッセージの流れは『Pub/Subのデータの流れをシーケンス図で理解する』で扱っています。
Ackのタイミングを決める
Ack は、Subscriber が「このメッセージの処理は完了した」と Pub/Sub に伝える操作です。
Subscription
↓ message
Subscriber
↓ 処理成功
Subscription
↑ ack
Pub/Sub は、Subscriber にメッセージを渡しただけでは処理完了とみなしません。
Subscriber が ack して初めて、その Subscription 上で処理済みになります。
設計時に決めるべきなのは、Subscriber がどの処理まで終えたら ack するかです。
- 処理前に ack する場合
- Subscriber が処理中に落ちると、メッセージは失われた扱いになります
- 処理後に ack する場合
- Subscriber が処理中に落ちても、再配信によって別の Subscriber が処理できます
基本的には、DB更新、外部API呼び出し、メール送信などの副作用を含む処理が成功したあとに ack します。
再配信を前提に冪等性を設計する
再配信と冪等性は、別々の論点ではなく1セットで考えます。再配信という仕組みがある以上、同じメッセージが複数回届く可能性が生まれます(前提)。その前提に耐えるための設計が冪等性です(対応)。
前提: 再配信によって同じメッセージが複数回届きうる
Subscriber が ack しないまま一定時間が過ぎると、Pub/Sub はそのメッセージを再配信します。
Subscriber が message を受け取る
↓
処理中に落ちる
↓
ack されない
↓
Pub/Sub が再配信する
再配信は、障害時にメッセージを失わないために必要です。
一方で、同じメッセージが2回以上届く可能性も生まれます。これは、Pub/Sub の基本的な考え方が at-least-once delivery だからです。
At-least-once delivery は「少なくとも1回は届ける」という意味です。0回にはしませんが、2回以上届く可能性はあります。
対応: 冪等性で重複に備える
冪等性 は、同じ処理を複数回実行しても結果が破綻しない性質です。
再配信によって同じメッセージが重複して届く可能性がある以上、Pub/Sub を使う処理では冪等性の設計が重要です。
GCP Pub/Sub には exactly-once delivery の機能もありますが、常に何も考えなくてよいという意味ではありません。外部API、DB更新、メール送信などの副作用を含む処理では、アプリケーション側の冪等性設計が重要です。
たとえば注文完了メールの送信処理を考えます。event_id = evt-001 のメッセージが2回届いたときに、メールが2通送られると困る場合があります。
この場合は、処理済みイベントを記録します。
Email Worker
↓
event_id = evt-001 が処理済みか確認
├─ 処理済みならスキップして ack
└─ 未処理ならメール送信して処理済みに記録して ack
冪等性を作る代表的な方法は以下です。
- 一意な
event_idをメッセージに含める order_idなど業務上の一意キーで処理済み判定する- DBに処理済みイベントテーブルを作る
- 外部APIに idempotency key を渡す
- 更新処理を上書き型にする
Pub/Sub の再配信は異常ではありません。重複しても壊れない処理にすることが重要です。
Dead Letter Topicを用意する
何度再試行しても成功しないメッセージもあります。
たとえば、メッセージ形式が壊れている、参照先データが存在しない、処理側のバグで必ず失敗する、といったケースです。
このようなメッセージを通常の Subscription に残し続けると、同じ失敗を繰り返します。
そこで、一定回数失敗したメッセージを Dead Letter Topic に逃がします。
Subscription
↓ 何度も失敗
Dead Letter Topic
↓
調査・再投入・破棄
Dead Letter Topic を使うと、通常処理の流れから失敗メッセージを隔離できます。
設計時には以下を決めます。
- 何回失敗したら隔離するか
- 隔離されたメッセージを誰が見るか
- 修正後に再投入するか
- 業務上破棄してよいメッセージか
- アラートを出す条件は何か
順序保証が必要かを判断する
Pub/Sub では、通常はメッセージ全体の順序を前提にしないほうが安全です。
多くのシステムでは、全体順序ではなく、特定の単位だけ順序が必要です。
たとえば、注文ごとの状態遷移を考えます。
order-123: created
order-123: paid
order-123: shipped
この場合、全注文を1列に並べる必要はありません。order-123 に関するイベントだけ順序が守られれば十分です。
順序保証を考えるときは、以下を確認します。
- 本当に順序が必要か
- 順序が必要な単位は何か
- 全体順序ではなく、キー単位の順序で足りるか
- 順序保証によってスループットや並列性が落ちてもよいか
順序が不要な処理では、順序保証に寄せすぎないほうがスケールしやすくなります。
PushとPullを選ぶ
Pub/Sub の受信方式には、Push と Pull があります。
Pull は、Subscriber が Subscription にメッセージを取りに行く方式です。
Subscriber → Subscription: pull
Subscription → Subscriber: message
Push は、Pub/Sub が Subscriber のHTTPエンドポイントにメッセージを送る方式です。
Subscription → Subscriber: HTTP POST
選び方の目安は以下です。
| 方式 | 向いているケース |
|---|---|
| Push | Cloud Run や Functions のようなHTTP受信型のサービスで処理したい |
| Pull | GKE、VM、常駐Worker、Dataflowのように処理側で取得量を制御したい |
Push は構成がシンプルです。一方で、処理側が受信レートを細かく制御したい場合は Pull のほうが扱いやすいです。
メッセージに何を入れるかを決める
Pub/Sub のメッセージに、すべての業務データを詰め込む必要はありません。
よくある設計は、イベントの識別情報と参照キーを入れる形です。
{
"event_id": "evt-001",
"event_type": "order.created",
"order_id": "order-123",
"occurred_at": "2026-07-10T10:00:00+09:00"
}
この形式では、Subscriber が order_id を使って必要な詳細情報を取得します。
メッセージ設計で考えることは以下です。
- イベントの種類を表す
event_typeを入れる - 重複排除に使える
event_idを入れる - 業務データを参照するためのIDを入れる
- 大きなファイル本体は入れず、ストレージ上の参照を入れる
- スキーマ変更に備えてバージョンを入れる
Pub/Sub はデータ保存先ではないため、巨大な本体データを運ぶ場所として使いすぎないほうが安全です。
監視する指標を決める
Pub/Sub は非同期処理なので、問題が起きてもユーザーのAPIレスポンスにはすぐ現れないことがあります。
そのため、監視が重要です。
見るべき観点は以下です。
- 未処理メッセージ数が増え続けていないか
- 古い未処理メッセージが残っていないか
- Dead Letter Topic にメッセージが流れていないか
- Subscriber のエラー率が上がっていないか
- 再配信が急増していないか
- publish 側でエラーが出ていないか
Pub/Sub を使うと、上流APIは成功しているのに、下流処理だけ詰まることがあります。
非同期化した処理ほど、下流の滞留や失敗を監視する必要があります。
まとめ
Pub/Sub を設計するときは、メッセージを送ることだけでなく、処理完了、失敗、再試行、重複、隔離、監視まで考えます。
特に重要なのは以下です。
- Ack は副作用を含む処理が成功したあとに返す
- Ack されなかったメッセージは再配信される
- at-least-once delivery により重複配信がありうるため、冪等性をアプリケーション側で担保する
- 失敗し続けるメッセージは Dead Letter Topic に隔離する
- 順序保証は必要な単位に限定する
- Push と Pull は実行基盤に合わせて選ぶ
- 非同期処理の滞留と失敗を監視する
Pub/Sub は、疎結合で壊れにくいシステムを作るための強力な部品です。一方で、再配信や重複処理を無視すると事故りやすい部品でもあります。