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>
40 lines
1.9 KiB
PL/PgSQL
40 lines
1.9 KiB
PL/PgSQL
-- auth_setup.sql — create the PgBouncer auth_query plumbing on tracksolid_db.
|
|
-- ─────────────────────────────────────────────────────────────────────────────
|
|
-- Run ONCE as the postgres SUPERUSER (the SECURITY DEFINER function must be owned by
|
|
-- a superuser to read pg_shadow). Apply with:
|
|
-- docker exec -i <timescale_db> psql -U postgres -d tracksolid_db \
|
|
-- -v ON_ERROR_STOP=1 -v pgb_pw="$(cat ~/.pgbouncer_auth.pw)" < auth_setup.sql
|
|
--
|
|
-- This lets PgBouncer authenticate ANY app user by looking its stored SCRAM verifier
|
|
-- up at connect time — so you never hand-maintain a userlist of every app password.
|
|
-- Only the pgbouncer_auth role itself needs an entry in userlist.txt.
|
|
|
|
\set ON_ERROR_STOP on
|
|
|
|
-- 1) A minimal LOGIN role PgBouncer uses to run the lookup. No other privileges.
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgbouncer_auth') THEN
|
|
CREATE ROLE pgbouncer_auth LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE;
|
|
END IF;
|
|
END $$;
|
|
ALTER ROLE pgbouncer_auth WITH LOGIN PASSWORD :'pgb_pw';
|
|
|
|
-- 2) The lookup function. SECURITY DEFINER (owned by postgres) so it can read
|
|
-- pg_shadow; returns exactly (username, scram_verifier) for one user.
|
|
CREATE SCHEMA IF NOT EXISTS pgbouncer AUTHORIZATION postgres;
|
|
|
|
CREATE OR REPLACE FUNCTION pgbouncer.user_lookup(
|
|
IN i_username text, OUT uname text, OUT phash text
|
|
) RETURNS record
|
|
LANGUAGE sql
|
|
SECURITY DEFINER
|
|
SET search_path = pg_catalog
|
|
AS $$
|
|
SELECT usename, passwd FROM pg_catalog.pg_shadow WHERE usename = i_username;
|
|
$$;
|
|
|
|
-- 3) Lock the function down to ONLY pgbouncer_auth.
|
|
REVOKE ALL ON FUNCTION pgbouncer.user_lookup(text) FROM public;
|
|
GRANT USAGE ON SCHEMA pgbouncer TO pgbouncer_auth;
|
|
GRANT EXECUTE ON FUNCTION pgbouncer.user_lookup(text) TO pgbouncer_auth;
|