Postgres: PgBouncer の transaction モードではセッション状態が別の接続に漏れる。SET LOCAL と xact 系ロックを使う
transaction モードの PgBouncer は、トランザクションが終わるたびにサーバー接続を別のクライアントに回す。SET、セッション単位のアドバイザリロック、LISTEN、一時テーブルなど「セッションに残る状態」は、他人の接続に残ったり自分の次のクエリから消えたりする。
壊れる例
sql
SET search_path = tenant_42; -- 次のトランザクションは別のサーバー接続かもしれない
SELECT pg_advisory_lock(123); -- ロックを持ったサーバー接続が他のクライアントに渡るSET statement_timeoutを付けたつもりが、別のクライアントの接続に効いてしまう。- セッション単位のアドバイザリロックを解放できない/他人が解放してしまう。
安全な書き方
sql
BEGIN;
SET LOCAL search_path = tenant_42; -- トランザクション終了で元に戻る
SET LOCAL statement_timeout = '60s';
SELECT pg_advisory_xact_lock(123); -- COMMIT / ROLLBACK で自動解放
-- ...
COMMIT;- 状態はトランザクションの中だけに閉じ込める(
SET LOCAL、pg_advisory_xact_lock)。 - ロールやデータベース単位で決まる設定は
ALTER ROLE ... SETにしておく(Postgres: statement_timeout はアプリ用ロールに ALTER ROLE で設定する)。
注意点
LISTEN/NOTIFYの受信、WITH HOLDカーソル、セッションをまたぐ一時テーブルは transaction モードでは使えない。必要なら session モードのプールか直接接続を別に用意する。- 名前付きプリペアドステートメントは、PgBouncer 1.21 以降なら
max_prepared_statementsを 0 より大きくするとプロトコルレベルで扱える。それより古い版ではドライバ側で無効にする必要がある。 - マイグレーションツールがセッションロックを使う場合(多くのツールが使う)は、PgBouncer を通さず直接接続で流す。
出典
- @amitsinha__07 · 2026-10-24
- PgBouncer「Feature matrix for pooling modes」: pgbouncer.org/features.html