Pub/SubとTask Queue、非同期処理の適切な使い分けとは?
はじめに
本記事は、Luup Advent Calendar 2025の4日目の記事になります。
こんにちは、株式会社LuupのSoftware部でPlatform / Technology Enabling (Backend TL) を担当している安元(やっすー)です。
急遽2日連続で記事を作成することとなってしまったため、もし誤りがあればお気軽にコメントください🙏🔥
Luupのバックエンド開発では、Firebase (GoogleCloud) をメインに活用しており、Cloud Functionsを用いたサーバーレスアーキテクチャを採用しています。
日々の開発において、ユーザー体験を損なわないために重い処理をバックグラウンドに逃がす「非同期処理」は欠かせません。
Firebaseで非同期処理を実装する場合、主に Pub/Sub (Cloud Pub/Sub) と Task Queue (Cloud Tasks) の2つの選択肢がありますが、皆さんはどのように使い分けているでしょうか?🤔
今回は、Luupの開発現場でも議論になるPub/SubとTask Queueの使い分けについて、その概念の違いと具体的な判断基準をまとめました。
概念の違い:イベント駆動 vs コマンド駆動
両者の最大の違いは、誰に向けて・何をしてほしいかというスタンスにあります。
1. 📡 Pub/Sub:イベント駆動 (Event-driven)
- 概念: "To whom it may concern" (関係者各位)
- スタンス: 「何かが起きました(イベント)。興味がある人は反応してください」
- 特徴: 発行側は、誰が受信するかを知りません(疎結合)。
2. 📦 Task Queue:コマンド駆動 (Command-driven)
- 概念: "Do this" (これやって)
- スタンス: 「この宛先に、この設定で、この処理を実行してください」
- 特徴: 発行側は、実行してほしい処理(宛先)と実行条件を明確に指定します。
機能比較:得意・不得意を知る
それぞれの特性を表にまとめると以下のようになります。
| 項目 | 📡 Pub/Sub (イベント) | 📦 Task Queue (コマンド) |
|---|---|---|
| 宛先 | 不特定多数 (トピックを購読している全員) | 特定 (指定したURL/Function) |
| 流量制御 | なし (来ただけ全部すぐに送る) | あり (レートリミット/同時実行数) |
| 実行タイミング | 基本即時 | 即時 または 日時指定(スケジュール) |
| リトライ制御 | 可能 (設定で待機時間を調整可) | 高度 (回数制限やレート制御も可) |
| 主な用途 | データ同期、ログ集約、システム間連携 | メール一括送信、外部API連携、予約処理 |
現場で役立つ「使い分けのチェックリスト」
では、実際の開発時にどちらを選ぶべきか?例として以下の4つのポイントを参考にしています。
1. 外部APIを叩く処理か? (API Rate Limit)
-
Pub/Subの場合:
- イベントが大量発生すると即座に配信されるため、サブスクライバー側のスケールアウトに対する考慮が必要になります。
- 外部APIのレート制限(例:1分間に100回まで)を容易に超えてしまい、エラーが多発するリスクがあります。
-
Task Queueの場合:
- 「1秒間に最大10件まで」といったレートリミットを設定可能です。
- スパイクが発生しても、受信側(外部API)を守りながら平準化して処理できます。
2. 処理を遅らせたいか? (Scheduling)
- 「会員登録の24時間後にフォローアップメールを送る」
- 「ライド終了の5分後にアンケート通知を送る」
こういった要件の場合、Task Queueのスケジュール実行機能・遅延実行一択です。Pub/Subでこれを実現しようとすると、別途スケジューラーを組む必要があり構成が複雑になります。
3. 複数の処理を同時に発火させたいか? (Fan-out)
- 「ユーザー登録」というイベントに対して:
- Welcomeメールを送る
- BigQueryへデータを保存する
- 検索インデックスを更新する
これらを互いに依存させず独立して行いたい場合、Pub/Subでイベントをファンアウトさせるのが最も綺麗な設計です。発行元は「ユーザー登録された」という事実だけを投げればよく、後から処理が増えても発行元のコードを修正する必要がありません。
4. 順序性は重要か?
厳密な順序保証が必要な場合、実はどちらも完璧ではありません。
Pub/Subは基本的に順序を保証しません。Task Queueの方が制御はしやすいですが、分散システムである以上、基本的にはどちらを採用するにせよ冪等性を意識した設計が不可欠です。
Luupでの現状とこれから
今回このような記事を書きましたが、Luupの現状(2025年11月時点)のサーバーコードでも、「とりあえず非同期処理はPub/Sub」で実装されている箇所も少なからず存在します。
改善が必要な例として、Pub/Subなのにイベント名が関数名(コマンド)のようになっているケースも存在します。
- 🚫 Bad (Pub/Sub): トピック名
sendWelcomeEmail- これは実質「コマンド」であり、イベント駆動の疎結合なメリットが活かせていません。
- ✅ Good (Pub/Sub): トピック名
userCreated- これなら「メール送信」以外の処理が増えても自然です。
- ✅ Good (Task Queue): 関数名
sendWelcomeEmail- メールを送りたいという明確な意図があるなら、Task Queueで制御しながら送るべきです。
現在は、レート制限が必要なバッチ処理や、明確なコマンド実行が必要な箇所から、徐々にTask Queueへの移行も検討しています。💪❤️🔥
まとめ
- 「何かが起きた」を広く知らせたい → 📡 Pub/Sub
- 「いつ」「どのペースで」処理させたい → 📦 Task Queue
Firebase/GoogleCloud環境では、この2つを適切に使い分けることで、スケーラビリティと安定性を両立したシステムを構築できます。
Luupでは、こうしたアーキテクチャの改善や、マイクロサービスの設計に興味があるエンジニアを募集しています!!
Discussion