Files
discord-spywatcher/docker-compose.prod.yml
Copilot 5ba19104eb Enable connection pool monitoring and configure optimized Prisma connection limits (#174)
* Initial plan

* feat: enable connection pool monitoring and configure Prisma connection limits

- Start connection pool monitoring automatically in server.ts
- Add connection pool configuration logging to db.ts
- Update .env.example with detailed connection pool parameters
- Configure connection_limit=5 in Docker Compose for PgBouncer
- Add DATABASE_URL_DIRECT to production docker-compose
- Create comprehensive unit tests for connection pool monitoring

Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com>

* docs: add connection pooling quick start and implementation guides

- Add CONNECTION_POOLING_QUICKSTART.md with practical commands
- Add CONNECTION_POOLING_IMPLEMENTATION.md with implementation summary
- Include troubleshooting tips and monitoring commands
- Document performance tuning strategies

Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com>

* fix: address code review comments

- Fix username consistency in .env.example (use 'spywatcher' instead of 'postgres')
- Add clarifying comment for dynamic import pattern in server.ts
- Both changes improve code clarity and consistency

Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: PatrickFanella <61631520+PatrickFanella@users.noreply.github.com>
2025-11-05 19:33:33 -06:00

270 lines
6.8 KiB
YAML

services:
postgres:
image: postgres:15-alpine
container_name: spywatcher-postgres-prod
environment:
POSTGRES_DB: spywatcher
POSTGRES_USER: spywatcher
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=en_US.UTF-8"
volumes:
- postgres-data:/var/lib/postgresql/data
- ./scripts/postgres-init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U spywatcher"]
interval: 10s
timeout: 5s
retries: 5
networks:
- spywatcher-network
restart: unless-stopped
command:
- "postgres"
- "-c"
- "max_connections=100"
- "-c"
- "shared_buffers=256MB"
- "-c"
- "effective_cache_size=1GB"
- "-c"
- "maintenance_work_mem=64MB"
- "-c"
- "checkpoint_completion_target=0.9"
- "-c"
- "wal_buffers=16MB"
- "-c"
- "default_statistics_target=100"
- "-c"
- "random_page_cost=1.1"
- "-c"
- "effective_io_concurrency=200"
- "-c"
- "work_mem=4MB"
- "-c"
- "min_wal_size=1GB"
- "-c"
- "max_wal_size=4GB"
deploy:
resources:
limits:
cpus: '1'
memory: 512M
pgbouncer:
build:
context: ./pgbouncer
dockerfile: Dockerfile
container_name: spywatcher-pgbouncer-prod
environment:
DB_USER: spywatcher
DB_PASSWORD: ${DB_PASSWORD}
PGBOUNCER_ADMIN_USER: pgbouncer_admin
PGBOUNCER_ADMIN_PASSWORD: ${PGBOUNCER_ADMIN_PASSWORD:-pgbouncer_admin_pass}
depends_on:
postgres:
condition: service_healthy
networks:
- spywatcher-network
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
redis:
image: redis:7-alpine
container_name: spywatcher-redis-prod
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- spywatcher-network
restart: unless-stopped
command: redis-server --appendonly yes
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: spywatcher-backend-prod
volumes:
- logs-backend:/app/logs
environment:
# Connection pool settings optimized for PgBouncer
DATABASE_URL: postgresql://spywatcher:${DB_PASSWORD}@pgbouncer:6432/spywatcher?pgbouncer=true&connection_limit=5&pool_timeout=20
DATABASE_URL_DIRECT: postgresql://spywatcher:${DB_PASSWORD}@postgres:5432/spywatcher
REDIS_URL: redis://redis:6379
NODE_ENV: production
PORT: 3001
ADMIN_DISCORD_IDS: ${ADMIN_DISCORD_IDS}
BOT_GUILD_IDS: ${BOT_GUILD_IDS}
DISCORD_BOT_TOKEN: ${DISCORD_BOT_TOKEN}
DISCORD_CLIENT_ID: ${DISCORD_CLIENT_ID}
DISCORD_CLIENT_SECRET: ${DISCORD_CLIENT_SECRET}
DISCORD_GUILD_ID: ${DISCORD_GUILD_ID}
DISCORD_REDIRECT_URI: ${DISCORD_REDIRECT_URI}
JWT_REFRESH_SECRET: ${JWT_REFRESH_SECRET}
JWT_SECRET: ${JWT_SECRET}
depends_on:
pgbouncer:
condition: service_started
redis:
condition: service_healthy
networks:
- spywatcher-network
restart: unless-stopped
labels:
com.docker.compose.project: "discord-spywatcher"
deploy:
resources:
limits:
cpus: '1'
memory: 512M
command: sh -c "node dist/server.js"
migrate:
build:
context: ./backend
dockerfile: Dockerfile
container_name: spywatcher-migrate-prod
environment:
# Migrations should connect directly to postgres, not through pgbouncer
DATABASE_URL: postgresql://spywatcher:${DB_PASSWORD}@postgres:5432/spywatcher
depends_on:
postgres:
condition: service_healthy
networks:
- spywatcher-network
command: sh -c "npx prisma migrate deploy"
restart: "no"
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: spywatcher-frontend-prod
environment:
VITE_API_URL: ${VITE_API_URL:-http://localhost:3001}
VITE_DISCORD_CLIENT_ID: ${VITE_DISCORD_CLIENT_ID}
depends_on:
- backend
networks:
- spywatcher-network
restart: unless-stopped
labels:
com.docker.compose.project: "discord-spywatcher"
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
nginx:
image: nginx:alpine
container_name: spywatcher-nginx-prod
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- frontend
- backend
networks:
- spywatcher-network
restart: unless-stopped
labels:
com.docker.compose.project: "discord-spywatcher"
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
loki:
image: grafana/loki:2.9.3
container_name: spywatcher-loki-prod
volumes:
- ./loki/loki-config.yml:/etc/loki/local-config.yaml
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yaml
networks:
- spywatcher-network
restart: unless-stopped
labels:
com.docker.compose.project: "discord-spywatcher"
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
promtail:
image: grafana/promtail:2.9.3
container_name: spywatcher-promtail-prod
volumes:
- ./promtail/promtail-config.yml:/etc/promtail/config.yml
- /var/run/docker.sock:/var/run/docker.sock
- logs-backend:/logs/backend:ro
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki
networks:
- spywatcher-network
restart: unless-stopped
labels:
com.docker.compose.project: "discord-spywatcher"
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
grafana:
image: grafana/grafana:10.2.3
container_name: spywatcher-grafana-prod
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_ADMIN_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-admin}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=${GRAFANA_URL:-http://localhost:3000}
- GF_INSTALL_PLUGINS=
depends_on:
- loki
networks:
- spywatcher-network
restart: unless-stopped
labels:
com.docker.compose.project: "discord-spywatcher"
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
volumes:
postgres-data:
redis-data:
loki-data:
grafana-data:
logs-backend:
networks:
spywatcher-network:
driver: bridge