feat(infra): add PgBouncer connection pooling for production PostgreSQL
Introduces PgBouncer as a connection pooler between the API service and PostgreSQL in docker-compose.prod.yml, reducing connection overhead and improving concurrency under production load. - Add PgBouncer service (edoburu/pgbouncer:1.23.1-p2) with transaction pool mode, max_client_conn=200, default_pool_size=20 - Route API DATABASE_URL through PgBouncer (port 6432), keep direct connection (DATABASE_URL_DIRECT) for Prisma migrations/introspection - Create infra/pgbouncer/ config: pgbouncer.ini, userlist template, and entrypoint script with runtime env-var substitution - Update prisma.config.ts to prefer DATABASE_URL_DIRECT for migrations - Add K6 load test (e2e/load/pgbouncer-pool-test.js) with ramp-up to 200 VUs, pool exhaustion detection, and p95 < 2s threshold - Add PgBouncer env vars to .env.example Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -12,7 +12,9 @@ services:
|
||||
- '${API_PORT:-3001}:3001'
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
||||
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@pgbouncer:6432/${DB_NAME}
|
||||
# Direct connection for migrations (bypasses PgBouncer — required for DDL)
|
||||
DATABASE_URL_DIRECT: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
|
||||
REDIS_URL: redis://:${REDIS_PASSWORD}@redis:6379
|
||||
TYPESENSE_HOST: typesense
|
||||
TYPESENSE_PORT: 8108
|
||||
@@ -27,7 +29,7 @@ services:
|
||||
AI_SERVICES_API_KEY: ${AI_API_KEY}
|
||||
RUN_MIGRATIONS: ${RUN_MIGRATIONS:-false}
|
||||
depends_on:
|
||||
postgres:
|
||||
pgbouncer:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
@@ -165,6 +167,49 @@ services:
|
||||
networks:
|
||||
- goodgo-net
|
||||
|
||||
# ── Connection Pooling ─────────────────────────────────────────────────────
|
||||
pgbouncer:
|
||||
image: edoburu/pgbouncer:1.23.1-p2
|
||||
container_name: goodgo-pgbouncer
|
||||
restart: unless-stopped
|
||||
entrypoint: ['/bin/sh', '/etc/pgbouncer/entrypoint.sh']
|
||||
environment:
|
||||
DB_USER: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
PGBOUNCER_POOL_SIZE: ${PGBOUNCER_POOL_SIZE:-20}
|
||||
PGBOUNCER_MAX_CLIENT_CONN: ${PGBOUNCER_MAX_CLIENT_CONN:-200}
|
||||
PGBOUNCER_ADMIN_PASSWORD: ${PGBOUNCER_ADMIN_PASSWORD:-pgbouncer_admin_secret}
|
||||
PGBOUNCER_STATS_PASSWORD: ${PGBOUNCER_STATS_PASSWORD:-pgbouncer_stats_secret}
|
||||
volumes:
|
||||
- ./infra/pgbouncer/pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro
|
||||
- ./infra/pgbouncer/userlist.txt.template:/etc/pgbouncer/userlist.txt.template:ro
|
||||
- ./infra/pgbouncer/entrypoint.sh:/etc/pgbouncer/entrypoint.sh:ro
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -h 127.0.0.1 -p 6432 -U ${DB_USER}']
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256m
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 64m
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: '5m'
|
||||
max-file: '3'
|
||||
networks:
|
||||
- goodgo-net
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: goodgo-redis
|
||||
|
||||
Reference in New Issue
Block a user