PgBouncer とは、PostgreSQL の前に置いて接続を使い回す、軽量の接続プーラーです。
- PgBouncer のプールモードの違いと、transaction モードで使えなくなる機能
- Prisma の
pgbouncer=trueをいつ付けるかと、supabase pooler・Supavisor について公式の文書で確かめられた事 - 当社が transaction モードのプールで踏んだ
prepared statement already exists・too many clients・接続エラーの記録
この頁は AIエージェント の運用をまとめた頁の束の 1 枚です。公式の文書の定義と、当社が実際に落ちた所を分けて並べます。プーラーの設定値の良し悪しを比べた頁ではありません。
PgBouncer のプールモードと、transaction モードで止まる機能
プールモードは、サーバ側の接続をいつ他のクライアントへ回すかの決まりです。設定は pool_mode で、既定は session です(PgBouncer の文書 https://www.pgbouncer.org/config.html )。
| モード | サーバ側の接続を持てる間 | 気をつける所 |
|---|---|---|
| session | クライアントが繋いでいる間ずっと | 既定。クライアントが切れるまで接続は戻らない |
| transaction | トランザクションの間だけ | セッションに結びつく機能の一部が壊れる |
| statement | 1 文ごと | 複数の文にまたがるトランザクションは禁止 |
出典: PgBouncer「Features」https://www.pgbouncer.org/features.html ・「Configuration」https://www.pgbouncer.org/config.html (どちらも 2026-11-06 に確認)。
transaction モードで使える物と使えない物は、公式の機能表で次のように分かれています。
| transaction モードで使えない | transaction モードで使える |
|---|---|
SET・RESET | NOTIFY |
LISTEN | WITHOUT HOLD のカーソル |
WITH HOLD のカーソル | ON COMMIT DROP の一時表 |
SQL の PREPARE・DEALLOCATE | プロトコル層の prepared statement(max_prepared_statements を 0 以外にした時) |
PRESERVE ROWS・DELETE ROWS の一時表、LOAD、セッション単位の advisory lock | — |
出典: PgBouncer「Features」の SQL feature map https://www.pgbouncer.org/features.html (2026-11-06 に確認)。
pgbouncer prisma:pgbouncer=true は PgBouncer の版で分かれる
Prisma の公式の文書は、Prisma ORM が名前つきの prepared statement を使う事と、外付けのプーラーがそれに対応しない事が多い事を書いています。その上で、PgBouncer は transaction モードで動かす前提で、次のように分けています。
| 場面 | 公式の文書の案内 |
|---|---|
| PgBouncer が 1.21.0 より前 | 接続文字列に pgbouncer=true を付ける |
| PgBouncer が 1.21.0 以降 | pgbouncer=true を付けない事を勧める |
max_prepared_statements | 0 より大きくすると、PgBouncer で prepared statement を使える |
| Prisma Migrate の命令 | Schema Engine はプールに対応しない。CLI の命令には prisma.config.ts で直結の接続を指定する |
| Supabase の Supavisor | PgBouncer に近い振る舞いで、プールの接続文字列に ?pgbouncer=true を足せる |
出典: Prisma「Configure Prisma Client with PgBouncer」(Prisma ORM v7 の版)https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer (2026-11-06 に確認)。
同じ文書は prepared statement "s0" already exists の文言を、PgBouncer 越しに Prisma Migrate を撃った場面で挙げています。当社の記録は下の通り、本番の実行が読みで止まった件で、文書の場面と同じかは確かめていません。Migrate 系の命令の違いは prisma migrate の頁にまとめています。
supabase pooler と Supavisor:公式の文書で確かめられた事
この節は Supabase の公式の文書で確かめた定義だけで、当社の実測ではありません。文書の上では、Supavisor は Supabase の共有プーラーの事で、別に Pro Plan 以上で使える専用プーラー(PgBouncer)があります(https://supabase.com/docs/guides/database/connecting-to-postgres 2026-11-06 に確認)。
| 繋ぎ方 | port | ユーザ名の形 | prepared statement |
|---|---|---|---|
| 直結 | 5432 | postgres | プーラーを通らない |
| 共有プーラー(Supavisor)session モード | 5432 | postgres.[PROJECT-REF] | 対応 |
| 共有プーラー(Supavisor)transaction モード | 6543 | postgres.[PROJECT-REF] | 非対応。接続の道具の側で切る |
| 専用プーラー(PgBouncer)transaction モードのみ | 6543 | postgres | PgBouncer 1.21 以降で対応 |
出典: Supabase「Connect to your database」https://supabase.com/docs/guides/database/connecting-to-postgres (2026-11-06 に確認)。独自のロールで共有プーラーへ繋ぐ時のユーザ名は [ROLE].[PROJECT-REF] です。同じ文書は、共有プーラーの transaction モードで prepared statement を切る Prisma の設定として pgbouncer=true を挙げています。
port 6543 で prepared statement "s0" already exists に落ちた記録
当社は、transaction モードの接続プール(port 6543)へ、接続文字列に pgbouncer=true が無いまま Prisma で繋ぎ、42P05 prepared statement "s0" already exists で落ちました(社の実測 2026-10-17・2026-10-21・2026-10-22・2026-10-23)。
| 順 | 起きた事 | 分かった事 |
|---|---|---|
| 1 | 1 回目の試し撃ちは通った | 偶然だった。通った事は、正しい事の証拠にならない |
| 2 | 本番の実行が 1 件目の読みで止まった | エラーは 42P05 |
| 3 | 読み取り専用で DB を確かめた | 書き込みは 0 |
| 4 | 子のプロセスの環境変数 DATABASE_URL の末尾にだけ &pgbouncer=true を足した | 金庫の値は変えずに通った |
出典: 社の実測 2026-10-17・2026-10-21・2026-10-22・2026-10-23。
足し方は、その実行の環境変数だけを差し替える形です。金庫(接続文字列の保管場所)の値は触りません。
DATABASE_URL="<金庫の接続文字列>&pgbouncer=true" <子のプロセスの命令>
Prisma 用の引数を付けた接続文字列は psql が落とす
Prisma 用の引数を付けた接続文字列をそのまま psql へ渡すと、invalid URI query parameter で落ちます(社の実測 2026-10-04・2026-10-06)。
落とす引数を列挙する形だと、次の Prisma 専用の引数でまた落ちました。libpq が知る引数だけを残す形で渡すと通りました(社の実測 2026-10-04)。別の道具へ同じ文字列を回す前に、誰のための引数かを見ます。
session から transaction へ替えて減った物と、増えた待ち
当社は接続プールを session から transaction へ替え、接続の枯渇のエラーが止まりました。代わりに pgbouncer=true で照会に時間が足され、@prisma/adapter-pg へ替えると p95 の中央が戻りました(社の実測 2026-11-01・2026-11-05)。
| 測った物 | 前 | 後 | 変えた事 |
|---|---|---|---|
| 接続の枯渇のエラー | 旧 revision で 90 分に 36 件 | 0 件 | session → transaction |
| 接続の数 | 16 | 6 | session → transaction |
| 1 照会あたりの時間 | — | +47ms | pgbouncer=true を付けた |
| p95 の中央 | 1,812ms | 668ms | @prisma/adapter-pg へ替えた |
出典: 社の実測 2026-11-01・2026-11-05。当社の環境での値で、他の環境で同じ値になるとは限りません。
Pool の待機を延ばしたら、jest が too many clients で赤になった
Pool の待機を 300 秒にしたところ、jest の file ごとに Pool が積もり、too many clients で 98 本が赤になりました(社の実測 2026-11-05)。
同じ too many clients でも、この件で積もっていたのは試験の側の Pool です。プーラーやサーバの上限の値を見る前に、誰が接続を持ち続けているかを見ます。
transaction モードのプールでは、読み取り専用のトランザクションの中でカタログを読む
当社は、transaction モードのプールではセッションの SET を撃つ道具(pg_dump 等)を避け、次の形でカタログを読みます(社の実測 2026-09-21)。
BEGIN;
SET TRANSACTION READ ONLY;
<カタログを読む SELECT>;
COMMIT;
PgBouncer の機能表では SET は transaction モードで使えず、サーバ側の接続はトランザクションの間だけ割り当てられます(https://www.pgbouncer.org/features.html 2026-11-06 に確認)。Supabase の文書も pg_dump には直結を案内しています(https://supabase.com/docs/guides/database/connecting-to-postgres 2026-11-06 に確認)。DB の権限の側の話は RLS の頁に分けています。
supabase pooler の接続エラーを読み分ける
繋がらない時のエラーの文言で、ユーザ名の形の問題か、DB が在るかどうかを分けられました(社の実測 2026-09-20・2026-10-27)。
| 出たエラー | 当社で当たっていた状態 | 測った日 |
|---|---|---|
ENOIDENTIFIER | ユーザ名を差し替えただけの接続文字列。pooler のユーザ名は role.projectref の形でないといけない | 2026-09-20 |
tenant not found | DB が消えている | 2026-10-27 |
password failed | DB は在る | 2026-10-27 |
出典: 社の実測 2026-09-20・2026-10-27。
よくある質問
pgbouncer=true は付けるべき?
Prisma の文書は、PgBouncer が 1.21.0 より前なら付け、1.21.0 以降なら付けない事を勧めています(2026-11-06 に確認)。Supabase の共有プーラーの transaction モードでは prepared statement を切る設定として挙げられています。当社は付けて通しましたが、1 照会あたり +47ms が付きました(社の実測 2026-11-01・2026-11-05)。
prepared statement already exists が出たら、まず何を見る?
transaction モードのプールへ繋いでいるかと、接続文字列の引数を見ます。当社は port 6543 で pgbouncer=true が無くて落ち、足して通りました(社の実測 2026-10-17・2026-10-21・2026-10-22・2026-10-23)。Prisma Migrate の命令なら、公式の文書の通り直結の接続を指定する話になります。
too many clients が出たら、上限を上げればよい?
当社には上限の値を比べた実測が無いので、値は勧めません。当社の件は、jest の file ごとに Pool が積もったのが原因でした(社の実測 2026-11-05)。先に、接続を持ち続けている側を探します。
Supavisor と PgBouncer はどう違う?
Supabase の文書では、Supavisor は共有プーラーで session・transaction の両方のモードを持ち、専用プーラーの PgBouncer は transaction モードのみです。prepared statement は、共有プーラーの transaction モードでは使えず、専用プーラーは PgBouncer 1.21 以降で使えます(2026-11-06 に確認)。これは文書の内容で、当社が両者を比べた実測ではありません。
出典
- PgBouncer「Features」https://www.pgbouncer.org/features.html (2026-11-06 に確認)
- PgBouncer「Configuration」https://www.pgbouncer.org/config.html (2026-11-06 に確認)
- Prisma「Configure Prisma Client with PgBouncer」https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/databases-connections/pgbouncer (2026-11-06 に確認)
- Supabase「Connect to your database」https://supabase.com/docs/guides/database/connecting-to-postgres (2026-11-06 に確認)
- 社の実測: 2026-10-04・2026-10-06・2026-09-20・2026-09-21・2026-10-17・2026-10-21・2026-10-22・2026-10-23・2026-10-27・2026-11-01・2026-11-05(当社の接続プールと試験で測った記録)
ブルーランプ 記事の編集部/更新日 2026-11-06