「DBのコストが毎月じわじわ上がってるんだけど…」
管理画面の最新データを表示するために、フロントエンドから30秒ごとにAPIをポーリングしている。最初は問題なかった。でもユーザーが増え、画面が増え、エンドポイントが増えるにつれて、DBへのリクエスト数が膨れ上がり、コストが右肩上がりになっている。
本記事では、ポーリングの何が問題で、イベント駆動にするとなぜ解決するのか、移行の基本パターンはどうなるかを整理します。
「30秒ごとにAPIを叩く」のどこが問題か
ポーリングは「定期的にサーバーに問い合わせて最新データを取得する」方式。実装がシンプルで理解しやすいのが利点ですが、システムが成長すると構造的な問題が顕在化します。
【ポーリングの実態】
[00:00] API呼び出し → データ変更なし(無駄)
[00:30] API呼び出し → データ変更なし(無駄)
[01:00] API呼び出し → データ変更あり ✓(これだけ意味がある)
[01:30] API呼び出し → データ変更なし(無駄)
[02:00] API呼び出し → データ変更なし(無駄)
→ 5回のうち4回は「変更なし」を確認しただけデータが変わっていようがいまいが、一定間隔で必ずリクエストが飛ぶ。これがポーリングの根本的な問題です。そして厄介なのは、ユーザー数 × 画面数 × エンドポイント数で掛け算的にリクエストが増えること。
仮に1画面で5つのAPIを30秒間隔でポーリングしていて、同時接続が500クライアントなら、秒間約83リクエストがDBに飛び続けます。画面が3つに増えれば約250リクエスト/秒。そのほとんどが「変更なし」を返すだけの空振りです。
ポーリング vs イベント駆動 ― 何が変わるのか
| 項目 | ポーリング(プル型) | イベント駆動(プッシュ型) |
|---|---|---|
| データ取得のタイミング | 一定間隔で常にリクエスト | データが変わった時だけ |
| 無駄なリクエスト | 大量に発生する | 原理的に発生しない |
| リアルタイム性 | 最大でポーリング間隔分の遅延 | ほぼリアルタイム |
| DB負荷 | ユーザー数に比例して増加 | データ変更頻度に比例 |
| 実装の複雑さ | シンプル | メッセージング基盤が必要 |
| 障害時の影響 | 次のポーリングで自動復旧 | イベント欠損への対策が必要 |
イベント駆動の本質は「変更があった時だけ通知する」というシンプルな発想の転換です。「30秒ごとに聞きに行く」のをやめて、「変わったら教えてもらう」に変える。これだけで無駄なリクエストが構造的に消えます。
移行の基本パターン
イベント駆動への移行は、大きく3つのコンポーネントで構成されます。
【イベント駆動の基本フロー】
1. データ変更が発生
↓
2. メッセージキューにイベントを発行(Pub/Sub, SQS等)
↓
3. イベント処理サービスがリアルタイムDB(Firestore等)を更新
↓
4. フロントエンドがリアルタイムDBの変更を検知
↓
5. 必要なデータをAPIから取得なぜメッセージキューを挟むのか
「データ変更時にフロントに直接通知すればいいのでは?」と思うかもしれません。メッセージキュー(Pub/Sub、SQS等)を挟む理由は3つあります。
- 疎結合:データ変更を行うサービスは「イベントを発行する」だけ。通知先が増えても変更元のコードを修正する必要がない
- 信頼性:メッセージの永続化と再送機能があるため、一時的な障害でイベントが失われない
- 拡張性:将来的に分析基盤やメール通知など、別のサービスにもイベントを流したい時にサブスクライバーを追加するだけで済む
リアルタイム通知の手段選び
フロントエンドにデータ変更を伝える手段には複数の選択肢があります。
| 手段 | メリット | デメリット |
|---|---|---|
| WebSocket | リアルタイム性が最も高い | 接続管理が複雑、スケーリングが難しい |
| SSE(Server-Sent Events) | 実装がシンプル(HTTP上で動く) | 同上。加えて双方向通信は不可 |
| Firestore等のリアルタイムDB | SDK任せで接続管理不要、自動スケール | 読み書きの従量課金が発生 |
WebSocketやSSEは自前で接続管理とスケーリングを実装する必要があるため、運用コストを下げたいならFirestoreのようなマネージドサービスが現実的です。SDKを入れるだけでリアルタイム同期が実現でき、クライアント数が増えても自動的にスケールします。
スロットリングを忘れない
イベント駆動にしても、短時間に大量のデータ変更が起きるケースでは注意が必要です。100件の更新が0.1秒間に発生したら、100回の通知がフロントに飛び、結局APIへの大量リクエストが発生する。
この対策として、「一定時間内の複数イベントを1回の通知にまとめる」スロットリングを入れます。たとえば10秒間隔でバッファリングすれば、その間に何件のイベントが発生しても通知は1回。Cloud TasksやRedisのキー期限を使って実現できます。
ポーリングを残すべきケース
イベント駆動は万能ではありません。以下のケースではポーリングの方が適しています。
- データ変更頻度が高く、ほぼ毎回変更がある:株価のリアルタイム更新など、ほぼ常にデータが変わっている場合はポーリングの「無駄」が少ない
- クライアント数が少ない:同時接続が数十台程度なら、ポーリングの負荷は許容範囲。メッセージング基盤を導入するコストの方が高い
- イベント欠損が許されない:ポーリングなら最悪でも次回のポーリングで最新データが取れる。イベント駆動で欠損が起きると、次のイベントまでデータが更新されない
- 既存システムの改修コストが見合わない:ポーリングからイベント駆動への移行はアーキテクチャの変更であり、一定の開発コストがかかる。コスト削減効果と天秤にかけて判断する
アーキテクチャ設計の知識を深める関連記事
- 「全部POSTでよくない?」がモヤる人へ|HTTPメソッド使い分けの正論と現場のリアル – API設計の判断軸はイベント駆動アーキテクチャの設計にも通じます
- AWS CLIで開発が爆速になる!セットアップからCloudWatch・Lambda活用まで実用コマンド付き – SQSやLambdaの設定・監視をCLIから操作する基本
- IaC(Infrastructure as Code)とは?インフラをコードで管理する3つのメリット – メッセージキューやリアルタイムDBの構成をコードで管理する考え方
- 無料で使えるPostgreSQLサービス8選を比較!2025年版おすすめ – DB負荷の可視化やコスト比較の参考に
- Claude Codeとは?AI搭載のコーディングアシスタントを徹底解説 – イベント処理サービスの実装をAIに相談する活用法
まとめ:「変わった時だけ教えてもらう」に変えるだけ
- ポーリングの問題:データが変わっていなくても定期的にリクエストが飛び、ユーザー数に比例してDB負荷が増える
- イベント駆動の解決策:データ変更時だけ通知する「プッシュ型」に切り替え、無駄なリクエストを構造的に排除
- 基本パターン:メッセージキュー(Pub/Sub等)+ リアルタイムDB(Firestore等)+ スロットリング
- メッセージキューを挟む理由:疎結合・信頼性・拡張性の3つ
- ポーリングが向くケースもある:変更頻度が高い、クライアントが少ない、移行コストが見合わない場合
ポーリングは「とりあえず動くもの」を作る時には最適解です。でもシステムが成長してコストが問題になったら、「聞きに行く」から「教えてもらう」への発想の転換を検討する時期。その移行判断の参考になれば幸いです。
