Giselle の API に Rate Limit を実装するにあたり、PostgreSQL と Redis のどちらを使用すべきか検討しました。
前回の記事 Rate Limit アルゴリズムの比較と事例調査 では Fixed Window Counter アルゴリズムを採用することを決めました。今回は、そのストレージ層として PostgreSQL を選択した理由をご紹介します。
Giselle での実装例
今回の PR feat(api/apps/run): add team-scoped rate limiting to App Run API · #2603 では、PostgreSQL を使用した Fixed Window Counter を実装しました。
PostgreSQL vs Redis Rate Limit の比較
| 観点 | PostgreSQL | Redis |
|---|---|---|
| レイテンシ | 1-10ms | 0.1-1ms |
| スループット | 数千 req/s | 数十万 req/s |
| 永続性 | デフォルトで永続 | 設定次第(揮発可能) |
| 運用コスト | 既存 DB を利用可能 | 追加インフラが必要 |
| 原子性 | トランザクション | シングルスレッド |
| スケール | 垂直スケール中心 | 水平スケール容易 |
PostgreSQL を選ぶメリット
インフラの複雑さを回避
- 既に PostgreSQL を使用中
- Redis の追加運用・監視が不要
- 障害点が増えない
データの一貫性
- チーム・アプリとの外部キー制約
- CASCADE DELETE で自動クリーンアップ
- トランザクション分離
PostgreSQL のデメリット
レイテンシ
- 各リクエストに +1-5ms の DB 往復
- ただし Rate Limit を適用する API は既に DB 依存(認証、チーム取得など)
高負荷時のボトルネック
- 数万 req/s では問題になる可能性
- コネクションプール枯渇のリスク
テーブル肥大化
- 古いウィンドウが蓄積
- 定期的なクリーンアップが必要
このプロジェクトで PostgreSQL が適切な理由
1. 現在の Rate Limit 設定値
| プラン | 制限 | 換算 |
|---|---|---|
| Pro | 300 req/min | 5 req/s |
| Team | 600 req/min | 10 req/s |
| Enterprise | 3000 req/min | 50 req/s |
この規模であれば PostgreSQL で十分処理可能です。
2. 既存の API リクエストフロー
以下は概念的なサンプルコードです。
// 1 リクエストあたり既に複数の DB アクセスが発生
await authenticate() // DB: API キー検証
await enforceRateLimit() // DB: Rate Limit 適用 ← 今回追加
await loadApp() // DB: アプリ情報取得
await executeTask() // DB: タスク作成・実行このように、1 クエリ追加の影響は相対的に小さいと言えます。
3. 運用の一貫性
- 既存の監視・バックアップ体制を活用
- 新しい依存関係の学習コストなし
Redis への移行を検討すべきタイミング
- トラフィックが大幅に増加(数万 req/s)
- レイテンシ要件が厳しくなった(<10ms 必須)
- 複数リージョンでの分散が必要
- より高度な Rate Limit が必要(Sliding Window 等)
まとめ
現時点では PostgreSQL で問題ないと判断しました。
- プロジェクトの「シンプルさ優先」の哲学に合致
- 現在の規模では性能的に十分
- 将来的にボトルネックになった場合、Rate Limit チェック関数の実装を Redis に差し替えるだけで移行可能(インターフェースは変わらない)
必要になってから最適化する(YAGNI 原則)のが適切なアプローチだと考えています。
以上、Rate Limit のストレージ選定で PostgreSQL と Redis を比較検討した、現場からお送りしました。