The DB is at max_connections=100 with ~9 services each holding persistent pools (several as the postgres superuser, idle for hours), so peaks hit "too many connections". PgBouncer multiplexes many client connections onto a small fixed set of backends, bounding DB connections regardless of how many app pools exist. Adds (stack-wide infra, parked in this repo for now — see README scope note): - pgbouncer.ini: transaction pooling, auth_query pass-through, bounded pool sizes - auth_setup.sql: pgbouncer_auth role + SECURITY DEFINER pgbouncer.user_lookup() so per-app passwords aren't hand-maintained - docker-compose.yml: the service (join the existing DB network) - userlist.txt.example + .gitignore: keep the auth verifier out of git - README.md: deploy steps, incremental cutover (superuser apps first), and the transaction-pooling caveats — including the MCP-specific note (rely on role-level GUCs; simplest to leave the minor MCP direct and pool the heavy superuser apps) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
3.5 KiB
INI
64 lines
3.5 KiB
INI
; pgbouncer.ini — transaction-pooling front for timescale_db (tracksolid_db).
|
||
; ─────────────────────────────────────────────────────────────────────────────
|
||
; Purpose: the DB runs at max_connections=100 and ~9 stack services each hold a
|
||
; persistent pool (several as the postgres superuser, idle for hours), so peaks hit
|
||
; "too many connections". PgBouncer multiplexes MANY client connections onto a SMALL
|
||
; set of real backend connections, so the DB connection count stays bounded no matter
|
||
; how many app pools exist.
|
||
;
|
||
; Auth uses auth_query (NOT a hand-maintained userlist of every app): PgBouncer logs
|
||
; in as `pgbouncer_auth` and looks each user's verifier up via pgbouncer.user_lookup()
|
||
; — see auth_setup.sql. Only the pgbouncer_auth verifier lives in userlist.txt.
|
||
|
||
[databases]
|
||
; Apps point their DSN host at pgbouncer:6432 with the SAME dbname/user/password.
|
||
; `host` here is the real DB (the timescale_db container hostname on the DB network).
|
||
tracksolid_db = host=timescale_db port=5432 dbname=tracksolid_db
|
||
|
||
[pgbouncer]
|
||
listen_addr = 0.0.0.0
|
||
listen_port = 6432
|
||
|
||
; ── Auth (pass-through via auth_query) ──────────────────────────────────────
|
||
auth_type = scram-sha-256
|
||
auth_file = /etc/pgbouncer/userlist.txt
|
||
auth_user = pgbouncer_auth
|
||
auth_query = SELECT uname, phash FROM pgbouncer.user_lookup($1)
|
||
|
||
; ── Pooling ─────────────────────────────────────────────────────────────────
|
||
; transaction mode = a server connection is returned to the pool at COMMIT/ROLLBACK,
|
||
; so a handful of backends serve hundreds of clients. See README for the feature
|
||
; caveats (no session-level prepared statements / SET that persists across txns).
|
||
pool_mode = transaction
|
||
|
||
; Total backend connections PgBouncer will ever open to the DB =
|
||
; (number of [databases] entries) × default_pool_size + reserve_pool_size.
|
||
; Keep the SUM across all poolers well under the DB's max_connections (100).
|
||
; With one database and 20 + 5 reserve = 25 backends max — leaving headroom for
|
||
; superuser/admin/background-worker connections that bypass PgBouncer.
|
||
default_pool_size = 20
|
||
min_pool_size = 0
|
||
reserve_pool_size = 5
|
||
reserve_pool_timeout = 3
|
||
|
||
; Clients can be plentiful (cheap) — only backends are scarce.
|
||
max_client_conn = 2000
|
||
|
||
; Recycle idle/old server connections so none linger for hours.
|
||
server_idle_timeout = 300
|
||
server_lifetime = 3600
|
||
|
||
; ── Robustness ──────────────────────────────────────────────────────────────
|
||
; Apps set per-connection GUCs via the `options` startup param (e.g. the analytics
|
||
; MCP sends `-c default_transaction_read_only=on -c statement_timeout=...`). In
|
||
; transaction pooling those can't be honored per-shared-backend, so ignore them and
|
||
; rely on ROLE-level settings (ALTER ROLE ... SET ...) instead. See README.
|
||
ignore_startup_parameters = extra_float_digits,options,search_path
|
||
|
||
; Admin/stats console (psql -p 6432 pgbouncer) — restricted to the auth role.
|
||
admin_users = pgbouncer_auth
|
||
stats_users = pgbouncer_auth
|
||
|
||
; Quiet by default; flip to 1 temporarily when debugging.
|
||
log_connections = 0
|
||
log_disconnections = 0
|