ポーリングやめたらDBコスト30%減った話 ― イベント駆動アーキテクチャへの移行パターン

目次

「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のキー期限を使って実現できます。

ポーリングを残すべきケース

イベント駆動は万能ではありません。以下のケースではポーリングの方が適しています。

  • データ変更頻度が高く、ほぼ毎回変更がある:株価のリアルタイム更新など、ほぼ常にデータが変わっている場合はポーリングの「無駄」が少ない
  • クライアント数が少ない:同時接続が数十台程度なら、ポーリングの負荷は許容範囲。メッセージング基盤を導入するコストの方が高い
  • イベント欠損が許されない:ポーリングなら最悪でも次回のポーリングで最新データが取れる。イベント駆動で欠損が起きると、次のイベントまでデータが更新されない
  • 既存システムの改修コストが見合わない:ポーリングからイベント駆動への移行はアーキテクチャの変更であり、一定の開発コストがかかる。コスト削減効果と天秤にかけて判断する

アーキテクチャ設計の知識を深める関連記事

まとめ:「変わった時だけ教えてもらう」に変えるだけ

  • ポーリングの問題:データが変わっていなくても定期的にリクエストが飛び、ユーザー数に比例してDB負荷が増える
  • イベント駆動の解決策:データ変更時だけ通知する「プッシュ型」に切り替え、無駄なリクエストを構造的に排除
  • 基本パターン:メッセージキュー(Pub/Sub等)+ リアルタイムDB(Firestore等)+ スロットリング
  • メッセージキューを挟む理由:疎結合・信頼性・拡張性の3つ
  • ポーリングが向くケースもある:変更頻度が高い、クライアントが少ない、移行コストが見合わない場合

ポーリングは「とりあえず動くもの」を作る時には最適解です。でもシステムが成長してコストが問題になったら、「聞きに行く」から「教えてもらう」への発想の転換を検討する時期。その移行判断の参考になれば幸いです。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次