世界を動かす技術を、日本語で。

なぜ私たちは新たにPostgres接続プールを構築したのか

2026年7月8日原文(pgdog.dev)

概要

PgDog はPostgresのスケーリング用プロキシで、 従来のコネクションプーラーの課題 を解決 SET文やLISTEN/NOTIFY など重要なPostgres機能の互換性を確保 Tokioベースのマルチスレッド化 で高効率・高スループットを実現 アプリ変更不要 で既存コードを活かしたままスケール可能 オープンソース かつ自由に利用・改変・デプロイが可能

PgDog: なぜ新しいPostgresコネクションプーラーが必要か

  • PgDog はPostgresのための新しい コネクションプーラー兼プロキシ
  • 既存の代表的なコネクションプーラー: PgBouncer, RDS Proxy, Pgpool-II, Supavisor
  • 従来のプーラーは "leaky abstraction"(抽象化の漏れ) 問題が発生
    • インフラ追加により アプリケーションコードの大幅な変更 が必要になる場合
    • 特に SET文やセッション変数 に依存したアプリで顕著

セッション状態とSET文の課題

  • 従来のプーラーでは セッション制御(SET文) が機能しなくなる
    • コネクション再利用時に 状態が他クライアントへ漏れる リスク
      • 例: statement_timeoutRow Level Security (RLS) の誤動作
    • 結果として SET文の利用禁止 が推奨される現状
  • PgDog は独自の SQLパーサー でSET文を検出・管理
    • 各クライアントごとに 変数名・値を保存 し、必要時に自動同期
    • クエリパイプライン で複数変数も効率的に更新
    • Postgres本来の機能を犠牲にせず、そのままスケールが可能

LISTEN/NOTIFYの完全対応

  • LISTEN/NOTIFY はPostgres内蔵の pub/subキュー
    • 従来プーラーでは トランザクションモード利用時に非対応
  • PgDog は内部で両コマンドを処理し、 複数プロセス間でメッセージ転送
    • Tokioのbroadcast channel で同一プロセス内のクライアント間通信
    • 専用コネクション 経由でPostgresと全プロセスを接続
    • Postgresをブローカー としつつ、PgDogがpub/subクライアントとして機能
    • NOTIFYのトランザクションセマンティクスも完全維持

マルチスレッド設計とスケーラビリティ

  • PgDogはTokio上で動作 し、 マルチスレッド対応
    • 各クライアントごとに 非同期タスク を割り当て
    • CPUコア数分だけ直線的にスケール
  • PgBouncerのSO_REUSEPORTやRDS Proxyのオートスケール との違い
    • 従来は 各プロセスごとに独立したコネクションプール が必要
      • クライアントは一度接続すると インスタンス移動不可
      • オーバーロード時に 他クライアントも巻き込まれる
    • PgDogは1プロセスで多くのクライアントを効率的に捌く
    • バーストトラフィックにも柔軟対応 し、 低レイテンシ維持
    • 運用もシンプル で、 メトリクスやヘルスチェックも一元管理

PgDogの特徴とメリット

  • 20年の歴史あるPgBouncerに代わる新世代コネクションプーラー
  • 2M QPS超の本番運用実績
  • オープンソース で、 自由な利用・改変・デプロイ が可能
  • 既存アプリの大規模改修不要 で導入可能
  • Postgresの主要機能を犠牲にしない 設計

まとめ

  • PgDog は従来のコネクションプーラーの課題を解決し、 本来のPostgresの機能を活かしつつスケール
  • 大規模・高負荷環境での運用 や、 既存コードベースの維持 に最適
  • エンジニアの負担軽減運用の簡素化 を実現

Hackerたちの意見

コネクションプーラーはクライアント間でコネクションを再利用するから、あるクライアントの接続状態が別のクライアントに“漏れ”ちゃうんだよね。これ、マジでヤバい。普通のPostgresの設定で実際に起こるの?

定義上、コネクションプーラーはコネクションを再利用するから、PGに限らずどんなコネクションプーリングの設定でも起こり得るよ。pgbouncerでは、カスタマイズ可能なコマンド[0]を使ってコネクションをリセットするから、クリーンな状態に戻せるはずだよ。[0] https://www.pgbouncer.org/config.html#server_reset_query

そうだね、pgbouncerがPostgresのワイヤプロトコルに対してどれだけ透明性を持とうとしているかの結果だよ。このことをうまく説明している記事があるよ: https://www.augusteo.com/blog/how-pgbouncer-works

こういうのは「永続接続」をサポートしている他のデータベースでも見られるよ。起動するとき、データベースの状態が全くわからないからね。前のプロセスがエラーになってたら、壊れたトランザクションの真っ只中にいるかもしれない。前のセッションが何か変なSETマジックを使って動かしてた?一時テーブルを作ってた?まあ、全部そのまま残ってるからね!

PostgreSQLに特有の話じゃないよ。他の人も言ってるけど、MySQLのプーラーやプロキシでも同じことが起こる可能性がある。接続の挙動は動的に変わることができて、その接続のライフタイム中は持続するからね。例えば、レガシーなクライアントAがバウンサーを通じてMySQLに接続して「会話は全部latin-1を使いたい、utf-8じゃなくて」って言うと、MySQLがクエリを解析する際の文字セットが変わるんだ。レガシーなクライアントがいくつかクエリを実行してから切断すると、今度は新しいクライアントがMySQLに接続するんだけど、バウンサーは前の接続のまま割り当てちゃう。新しいクライアントは完全にUTF-8に対応してるし、デフォルトがそれだから明示的に言わないんだよね。ただ、データベースサーバーはまだlatin-1で考えてるから、新しいクライアントがUTF-8データを送ると、それがlatin-1として解析されちゃう。latin-1はUTF-8のサブセットだから、クエリは実際にはうまくいくけど、latin-1の範囲外の文字を使うとエラーが出たり、サーバーから壊れたデータが返ってきたりする。これを回避するための解決策は以下の通り。1. すべてのクライアントが同じ設定を使うことを確認する。もしデータベースが同じORMを使う単一のアプリ用なら、これは自動的にできる。2. すべてのクライアントが、必要な変更を毎回明示的に設定することを確認する。UTF-8クライアントは、デフォルトでも明示的にUTF-8接続を設定するし、utf8mb4が必要なクライアントはそれを明示的に要求する。これを確実にする一つの方法は、サーバー(またはバウンサー)を、誰にも有効でないデフォルトを使うように設定することだ。例えば、デフォルトの文字セットを7ビットのスウェーデン語に設定すると、頻繁にエラーが起こる。3. これらの変更を禁止するか、元のクライアントが切断された後に検出して元に戻すことができるバウンサーを使う。MySQL用にこれが存在するかは分からないけど。4. 異なるアプリケーションごとに別々のバウンサーを使う(#1の延長)。つまり、データベースサーバーのプールごとにバウンサーを持つのではなく、各アプリケーションごとに持つってこと。ウェブアプリには1つ、レガシーなレポートツールには1つ、ODBCコネクタには1つ、みたいに。理論上はかなりの混乱だけど、実際には多くのインストールが#1のケースに当てはまるから、あまり問題にならない。ただ、たまに問題になるときはデバッグがめちゃくちゃ難しくなる。

pgbouncerには、漏洩の可能性に影響を与えるいくつかのプールモードがあるよ。

Clickhouseもpgbouncerのスケーリングについて面白い記事を出したよ。so_reuseportをスケールアウトしながら、あまり厳しくシャーディングしなくてもいい方法について話してる。[https://clickhouse.com/blog/pgbouncer-clickhouse-managed-pos...] https://news.ycombinator.com/item?id=48814152

まあ、正直言ってpgdogは見た目がめっちゃすごいし、マルチスレッドを超えてるよ。notify/listenの修正や、リードレプリカへの自動クエリルーティング、自動シャーディングがあれば、Postgresがついにvitessに近づくかもね。

このNOTIFYのパフォーマンス修正って、もうトランザクショナルじゃないってことになるんじゃない?

一番厳密なCAP定理の定義から言うと、そうだね、トランザクショナルではない。でも、かなり近いよ。データベースの世界ではそれは良い答えじゃないって分かってるけど、実際には大多数のメッセージを届けるから、まあそれで十分かもね?どうなるか見てみよう。DBやアプリを壊さずに近づくことが可能だって示してるけど、まだ_耐久性_のあるワークキュー、例えばKafkaから期待されるレベルには達してないと思う。すぐにそれを置き換えることはないだろうね。

Select用のクエリキャッシュを追加する予定はあるの?

ないと思う。面倒すぎる。キャッシュは文脈なしでは本当に難しい問題で、通常その文脈はアプリ特有なんだよね。

Hacker Newsで議論の続きを見る