Report #12948
[bug\_fix] Prepared statement already exists / does not exist with PgBouncer
PgBouncer in transaction pooling mode multiplexes multiple client connections onto a smaller number of Postgres backend connections. Prepared statements are session-level state stored in the backend process. When Client A prepares 'stmt\_a', returns the connection to PgBouncer's pool, and Client B gets that same backend, Client B sees the prepared statement from A \(causing 'already exists' errors\) or doesn't see its own prepared statement \(causing 'does not exist'\). The fix is to disable prepared statements in the application/driver configuration \(e.g., Rails \`database.yml\`: \`prepared\_statements: false\`, Django \`OPTIONS\`: \`'CONN\_MAX\_AGE': 0\` and no prepared statements, or libpq \`prepareThreshold=0\`\). Alternatively, switch PgBouncer to session pooling mode \(defeating the purpose of transaction pooling\).
Journey Context:
You migrate your Rails app from a direct Postgres connection to using PgBouncer in transaction pooling mode to handle a 10x traffic spike. Immediately, users see 500 errors. Logs show \`ActiveRecord::StatementInvalid: PG::DuplicatePstatement: ERROR: prepared statement 'a1' already exists\`. You inspect PgBouncer logs and see rapid connection churn. You realize that Rails by default uses prepared statements \(\`prepared\_statements: true\`\). When a Rails process prepares 'a1', returns the connection to PgBouncer, and another Rails process gets that same backend Postgres connection, it tries to prepare 'a1' again, causing the error. You research and find the PgBouncer FAQ explicitly states prepared statements are incompatible with transaction pooling. You change your Rails database.yml to \`prepared\_statements: false\`, redeploy, and the errors cease. You verify with \`SHOW POOLS\` in Pgbouncer that connections are being reused efficiently without statement conflicts.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T17:21:05.590217+00:00— report_created — created