Repository navigation
PgBouncer connection pooling and horizontal scaling - #10
Merged
Merged
Conversation
- DATABASE_URL may point at PgBouncer; transaction pooling verified: npm test runs the whole PostgreSQL suite again through PgBouncer, then two replicas sharing PgBouncer and Redis (sessions, refresh, logout, login limits, concurrent invoices, shutdown) - DB_POOL_MAX/MIN/IDLE_MS/ACQUIRE_MS size the Sequelize / MongoDB pools; DB_SYNC and node dist/sync.js (npm run db:sync) so replicas don't race to create tables - GET /health is readiness (database, Redis, draining → 503); /health?live=1 is liveness - graceful shutdown on SIGTERM: drain (SHUTDOWN_DRAIN_MS), finish requests, close, hard exit after SHUTDOWN_TIMEOUT_MS; the worker gets the same timeout - Dockerfile, docker-compose.yml (PostgreSQL, PgBouncer, Redis, db-sync, N api replicas, worker, nginx), deploy/nginx.conf; CI installs pgbouncer - docs/17-scaling.md, .env.example, docs 09/11/12, AGENTS.md Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dcqyj37vsGrmgdnp7DLbkn
BackStacked
marked this pull request as ready for review
October 15, 2026 21:15
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dcqyj37vsGrmgdnp7DLbkn
3 of 8 tasks
… the repo table Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dcqyj37vsGrmgdnp7DLbkn
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Himanshu · project thread
Before: the api connected straight to PostgreSQL with Sequelize's default pool, every process created tables on start,
GET /healthonly checked the database, and SIGTERM closed the server at once. Running several replicas was described but not tested or packaged.After: the api runs as N replicas behind a proxy, connecting through PgBouncer in transaction mode.
docker-compose.ymlbrings up PostgreSQL, PgBouncer, Redis, a one-shot table sync,API_REPLICASapi containers, the worker and nginx. Replicas drain cleanly on SIGTERM, and/healthtells the load balancer when a replica can't serve.How:
DATABASE_URLpoints at PgBouncer; no code change was needed in the db layer. db-kit uses unnamed statements only,FOR UPDATEand isolation inside one transaction, one-statement counters, and no advisory locks,LISTEN, or temp tables. Sequelize's connect-timeSET TIME ZONEis a parameter PgBouncer tracks per client.npm testnow runs the whole PostgreSQL suite a second time through PgBouncer (transaction mode, 3 server connections).DB_POOL_MAX/DB_POOL_MIN/DB_POOL_IDLE_MS/DB_POOL_ACQUIRE_MSsize the Sequelize pool (pg) or the MongoDB driver pool.DB_SYNC=falseplusnode dist/sync.js(npm run db:sync) stops replicas racing to create tables.[replicas]test runs two servers on one PgBouncer and one Redis. A session, a refresh and a logout each cross replicas, and login limits are shared (limited at attempt 11, not 21). 16 concurrent invoices with payments from both replicas get unique numbers and the books balance. PostgreSQL saw at most 3 connections.src/lifecycle.ts).GET /healthreturns 503 when the database or Redis fails (2 s timeout each) or the replica is draining.GET /health?live=1is liveness. This stays within the one-route rule. On SIGTERM the replica drains forSHUTDOWN_DRAIN_MS, thenapp.close()finishes in-flight requests, and it exits anyway afterSHUTDOWN_TIMEOUT_MS. The worker gets the same timeout.Dockerfile(kits token as a build secret),docker-compose.ymlanddeploy/nginx.conf, which re-resolvesapiso--scaleneeds no restart and retries a request on another replica after a 503. CI installspgbouncer.docs/17-scaling.mdcovers what each replica needs, where state lives, PgBouncer compatibility, pool sizing, health, shutdown, compose and Kubernetes. Also updates.env.example, docs 09/11/12 and AGENTS.md (rule 14).Verified locally: typecheck;
TEST_BACKENDS=pg npm test193/193 (MongoDB binaries are blocked in this sandbox, so CI runs mongo). The compose stack came up with 3 healthy replicas through nginx. Stopping one replica under constant traffic answered 40 of 40 requests with 200.Overlap with #9 (TLS fingerprint): both PRs touch
.env.example,src/config/env.ts,src/server.ts,test/run.ts, docs 09/12 andllms-full.txt, in different hunks. Whichever merges second needs a rebase andnpm run llms.🤖 Generated with Claude Code
https://claude.ai/code/session_01Dcqyj37vsGrmgdnp7DLbkn
Generated by Claude Code