skills-ws
Version:
87 agent skills for AI coding assistants — marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.
89 lines (66 loc) • 3.83 kB
Markdown
## Contents
- 5. Connection Pooling — PgBouncer
- Why you need it
- Configuration
- Transaction mode gotchas
## 5. Connection Pooling — PgBouncer
### Why you need it
PostgreSQL creates a process per connection (~10MB RAM each). 100 connections = 1GB RAM just for connections. PgBouncer multiplexes thousands of app connections over a small pool.
### Configuration
```ini
; /etc/pgbouncer/pgbouncer.ini
[databases]
myapp = host=10.0.1.100 port=5432 dbname=myapp
[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
; Pool mode:
; transaction — releases connection after each transaction (recommended)
; session — holds connection for entire session (needed for LISTEN/NOTIFY, advisory-lock sessions)
pool_mode = transaction
; Prepared statements in transaction mode (PgBouncer 1.21+):
; PgBouncer tracks protocol-level (extended-protocol) prepared statements per server
; connection. Set this > 0 to enable them in transaction mode.
max_prepared_statements = 200 ; 0 disables; per server connection
; Pool sizing
default_pool_size = 25 ; Connections per user/db pair
max_client_conn = 1000 ; Max client connections
reserve_pool_size = 5 ; Emergency extra connections
reserve_pool_timeout = 3 ; Wait before using reserve
; Timeouts
server_idle_timeout = 600 ; Close idle server connections after 10min
client_idle_timeout = 0 ; Don't close idle client connections
query_timeout = 30 ; Kill queries running > 30s
query_wait_timeout = 120 ; Wait 2min for a connection before erroring
; Stats
stats_period = 60
log_connections = 0 ; Don't log every connect/disconnect
log_disconnections = 0
```
### Transaction mode gotchas
```sql
-- These DON'T work reliably in transaction mode (a later statement may land on
-- a different server connection that never saw the session-level command):
LISTEN channel; -- LISTEN/NOTIFY
SET search_path = myschema; -- Session-level SET
CREATE TEMP TABLE ...; -- Session-scoped temp tables
-- Session-level advisory locks (pg_advisory_lock); use *_xact_ versions instead.
-- Workaround: use SET LOCAL (transaction-scoped):
BEGIN;
SET LOCAL search_path = myschema;
SELECT * FROM my_table;
COMMIT;
-- Or use session mode for specific apps that need these features.
-- Explicit SQL-level "PREPARE stmt AS ..." still won't survive across
-- transactions in transaction mode — only protocol-level prepared statements
-- (the extended query protocol your driver uses) are pooled, when
-- max_prepared_statements > 0.
```
**Prepared statements & drivers (2026).** PgBouncer 1.21+ pools protocol-level prepared statements in transaction mode when `max_prepared_statements > 0`. Driver caveats:
- **node-postgres / pg, asyncpg, JDBC, libpq** — use the extended protocol; named prepared statements work once `max_prepared_statements` is set. asyncpg also lets you disable its own statement cache (`statement_cache_size=0`) if you prefer.
- **Prisma** — for transaction-mode poolers, append `?pgbouncer=true` to the `DATABASE_URL` (disables Prisma's prepared statements). Prisma's own Accelerate / pooled `prisma://` URLs already handle this.
- **Serverless (Lambda, Vercel, Cloud Run)** — many short-lived clients overwhelm direct connections; route through a transaction-mode pooler (PgBouncer, RDS Proxy, Supabase pooler, Neon's pooled endpoint). Keep per-instance client pools tiny (often `max: 1`) and let the pooler do the multiplexing.
Session mode is still required when a feature genuinely needs connection affinity for its whole lifetime: `LISTEN`/`NOTIFY`, session-level advisory locks, `SET` that must persist across transactions, or session-scoped temp tables.
---