Module P-11·22 min read

The silent bugs that only appear in production: prepared statements, temp tables, and serverless connection spikes.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

P-11 — Connection Pooling Failure Modes: PgBouncer, Serverless, and the Edge

What this module covers: P-8 introduced PgBouncer. This module goes deeper into the failure modes that silently break applications when pooling is misconfigured — the session vs transaction mode divide that breaks prepared statements and temporary tables, the specific ways Next.js and serverless environments create connection spike patterns that exhaust pools, and the correct architecture for each deployment context.


Why Connection Pooling Breaks Things

A connection pool is not a transparent proxy. It multiplexes multiple application connections onto fewer Postgres connections, which means application-level state tied to a connection is not preserved between statements.

This creates a class of bugs that are invisible in development (where the pool is often not used, or uses session mode) and only appear in production under load. The symptoms look like data corruption, missing data, or mysterious errors — not "this is a pooling problem."


Session vs Transaction vs Statement Mode: The Exact Breakage

Session Mode

One Postgres backend is assigned to one application connection for the entire session. The application and the Postgres backend have a 1:1 relationship for the session's duration.

What works: everything. Prepared statements, temporary tables, advisory locks, SET parameters, LISTEN/NOTIFY, cursors, transaction control.

Pool savings: minimal — you need roughly as many Postgres backends as peak concurrent application connections. Session mode pooling is mainly useful for reducing connection establishment overhead (TLS handshake, authentication).

ini

Transaction Mode

A Postgres backend is held only during an active transaction. Between transactions, the backend is returned to the pool and may be given to a different application connection.

What breaks:

1. Prepared statements:

sql

2. Temporary tables:

sql

3. Session-level SET parameters:

sql

4. Advisory locks:

sql

5. LISTEN/NOTIFY:

sql
ini

Statement Mode

Each statement gets its own backend. Multi-statement transactions are broken: each BEGIN, UPDATE, COMMIT goes to a different backend.

sql

Statement mode is almost never correct for application use. It exists for very specific query-routing scenarios. Avoid it.


The Prepared Statement Problem: The Correct Solutions

If you need prepared statements with transaction-mode PgBouncer, there are three correct approaches:

Solution 1: Disable Prepared Statements at the Driver Level

Most database drivers support disabling prepared statements. When disabled, every query is sent as a simple query string without preparation.

javascript
python
python

Solution 2: Use max_prepared_statements

PgBouncer 1.21+ supports transparent prepared statement handling in transaction mode:

ini

This makes prepared statements work transparently in transaction mode — PgBouncer handles the routing. Check your PgBouncer version before relying on this.

Solution 3: Session Mode for Prepared-Statement-Heavy Connections

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.