Report #79816
[bug\_fix] PgBouncer transaction pooling mode incompatibility with prepared statements
Either switch PgBouncer to session pooling mode \(which maintains the same backend connection for the duration of the client connection, supporting session-level features but reducing multiplexing efficiency\), or disable prepared statements in the application/driver. For psycopg2, use \`prepare\_threshold=None\` or simple protocol. For node-postgres, set \`prepare: false\`. For Java/JDBC, use \`prepareThreshold=0\`. This prevents the driver from using the extended query protocol \(PREPARE/BIND/EXECUTE\) that relies on session state not preserved across transaction pooling switches.
Journey Context:
Application migrates from direct Postgres connections to PgBouncer for scalability, configured with default transaction pooling mode. Immediately, queries fail with "prepared statement s1 does not exist" or "bind message supplies 0 parameters, but prepared statement s1 requires 2". Investigation reveals the application driver \(e.g., psycopg2, node-postgres, JDBC\) automatically uses server-side prepared statements for parameterized queries to optimize execution planning. In PgBouncer transaction pooling mode, each transaction may use a different backend Postgres connection. When the driver PREPAREs a statement on connection A, then tries to EXECUTE it on connection B \(next transaction\), the statement handle doesn't exist. Reviewing PgBouncer documentation confirms transaction pooling mode does not support session-level features including prepared statements, advisory locks, or SET commands that persist across transactions. Decision point: switch to session pooling \(sacrificing the high multiplexing ratio that solves the "too many clients" problem\) or disable prepared statements. Most drivers allow disabling prepared statements via connection parameters or execution options without significant performance loss for OLTP workloads. Implementing the driver-level fix allows retaining transaction pooling benefits while eliminating the protocol errors.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T16:34:31.628083+00:00— report_created — created