accounts: trim async pool max_overflow 5 -> 2
Some checks failed
secrets-guard / encrypted (pull_request) Successful in 6s
PR build (required check) / changes (pull_request) Successful in 9s
shell-lint / shellcheck (pull_request) Successful in 8s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-backend (pull_request) Failing after 56s
PR build (required check) / gate (pull_request) Failing after 2s
Some checks failed
secrets-guard / encrypted (pull_request) Successful in 6s
PR build (required check) / changes (pull_request) Successful in 9s
shell-lint / shellcheck (pull_request) Successful in 8s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-backend (pull_request) Failing after 56s
PR build (required check) / gate (pull_request) Failing after 2s
This commit is contained in:
parent
707abe033e
commit
2dcfd2aa69
1 changed files with 17 additions and 6 deletions
|
|
@ -62,11 +62,21 @@ def _apply_sqlite_pragmas(dbapi_conn, _rec):
|
|||
#
|
||||
# pool_size=1/max_overflow=1 (the original setting on both) meant a 3rd
|
||||
# concurrent request per worker had to wait out pool_timeout (~30s default) for a
|
||||
# slot — easy to hit with 4 uvicorn workers x real traffic. 5+5 gives each async
|
||||
# engine headroom for a burst of concurrent account requests without coming close
|
||||
# to Postgres's own connection ceiling (two async engines here plus the sync
|
||||
# engine below, times N workers, all share that budget).
|
||||
_ASYNC_POOL_KWARGS = dict(pool_size=5, max_overflow=5, pool_pre_ping=True,
|
||||
# slot — easy to hit with 4 uvicorn workers x real traffic. pool_size=5 keeps that
|
||||
# headroom for the steady state.
|
||||
#
|
||||
# max_overflow is deliberately *tighter* than pool_size (2, not 5), because the
|
||||
# overflow is the part nobody budgets for and it is what saturated Postgres on
|
||||
# 2026-07-26. The connection ceiling is shared far more widely than this process
|
||||
# knows: two async engines here plus the sync engine below, times N uvicorn
|
||||
# workers, times the replica count (web autoscales), and prod and beta are two
|
||||
# databases on ONE Postgres instance. At 5+5 the configured worst case across all
|
||||
# of that exceeded the server's max_connections; at 5+2 prod web's ceiling is
|
||||
# 4x14 rather than 4x20. Steady-state behaviour is unchanged — only the burst
|
||||
# ceiling narrows, and a burst that would have opened a 6th..10th connection now
|
||||
# waits on pool_timeout instead of being refused outright by the server (which is
|
||||
# an error, not a wait). See infra/deploy/db/init/20-tuning.sh for the server side.
|
||||
_ASYNC_POOL_KWARGS = dict(pool_size=5, max_overflow=2, pool_pre_ping=True,
|
||||
pool_recycle=1800, future=True)
|
||||
# Smaller than the async engines: only the notifier leader (one process
|
||||
# cluster-wide, see core/singleton.py) uses the sync engine, driving its periodic
|
||||
|
|
@ -74,7 +84,8 @@ _ASYNC_POOL_KWARGS = dict(pool_size=5, max_overflow=5, pool_pre_ping=True,
|
|||
# statement_timeout (via psycopg's `options`) bound how long a wedged Postgres
|
||||
# can hang the notifier — it runs for the lifetime of that process's leader
|
||||
# flock, so an indefinite hang here would silently stop every subscription
|
||||
# notification until the process is restarted.
|
||||
# notification until the process is restarted. Left at 2+3: one process holds it,
|
||||
# so it is not multiplied by the worker or replica count.
|
||||
_SYNC_POOL_KWARGS = dict(pool_size=2, max_overflow=3, pool_pre_ping=True,
|
||||
pool_recycle=1800, future=True,
|
||||
connect_args={"connect_timeout": 5,
|
||||
|
|
|
|||
Loading…
Reference in a new issue