feat(server): make the database pool ceiling configurable - #2828
Conversation
|
All contributors have signed the DCO ✍️ ✅ |
196efc1 to
bfff9b4
Compare
Both stores hardcoded their pool ceiling: 10 connections for Postgres, 5 for on-disk SQLite. One pool is shared by every database-backed RPC, so that number is also the gateway's ceiling on concurrent database work. Once every connection is checked out, callers queue on acquire and sqlx logs "time to acquire exceeded slow threshold"; sandbox creates then time out and are retried, which adds load rather than shedding it. The right ceiling is deployment-specific — it depends on how many gateway replicas share the database and what max_connections the server itself allows — so it cannot be a single number baked into the binary. Expose it on the same config surface as the rest of the gateway's settings: --db-max-connections, OPENSHELL_DB_MAX_CONNECTIONS, and the TOML key database_max_connections, resolved in that precedence order and rendered by the Helm chart from server.dbMaxConnections. Omitting it keeps each backend's previous value, so existing deployments do not move. Values below 1 are rejected rather than silently replaced by the default: a zero pool would block every acquire, and quietly ignoring a typo would reproduce the ceiling the operator is trying to lift. An in-memory SQLite database stays pinned to one connection, since the database lives inside that connection. Refs: NVIDIA#2561 Signed-off-by: Bryce Wilkinson <22760097+bjw123@users.noreply.github.com>
bfff9b4 to
0622e8b
Compare
|
I have read the DCO document and I hereby sign the DCO. |
PR Review: feat(server): make the database pool ceiling configurableReviewed the full diff and verified against the code. Checked out the branch and ran tests locally — all green. What it doesMakes the shared DB pool ceiling configurable. Flag Verified good
Minor notes (non-blocking)
Testing (ran locally on the branch)
VerdictClean, well-tested, and honestly documented (the readyz coupling and the Postgres |
probably contains trailing |
|
I have read the DCO document and I hereby sign the DCO. |
|
The DCO check matches the commit author's GitHub account, and these commits are authored by |
0622e8b to
33517b3
Compare
|
I have read the DCO document and I hereby sign the DCO. |
Summary
Both persistence backends hardcoded their connection pool ceiling — 10 for Postgres, 5 for on-disk SQLite. One pool is shared by every database-backed RPC, so that number is also the gateway's ceiling on concurrent database work, and it could not be changed without rebuilding the gateway.
The ceiling is now configurable, on the same config surface as the rest of the gateway's settings, defaulting to each backend's previous value so existing deployments do not move.
We run a forked build of this change on a single-replica Postgres gateway with 2,000 sandboxes, as we had some issues with HA and some open issues around it. The stock ceiling of 10 was a bottleneck at that scale; 50 resolved it.
Related Issue
Refs #2561. Replaces #2700, which was auto-closed by the vouch gate before I was vouched (thanks @elezar) and which GitHub will not let me reopen after the rebase.
Changes
--db-max-connections,OPENSHELL_DB_MAX_CONNECTIONS, and the TOML key[openshell.gateway] database_max_connections, resolved in the standardflag > env > file > defaultorder via the existing clap/config_filepath.Config→Store::connect_with_pool_size→ each backend.Store::connect(url)keeps the old signature and the backend defaults, so the ~20 test call sites are untouched.min_connectionsis nowmin(configured, 5)instead of trackingmax_connections, so a raised ceiling grows on demand rather than pinning that many file handles open for the process lifetime. Unset behaviour is byte-for-byte the same as before.server.dbMaxConnections(rendered intogateway.toml, chart README regenerated),docs/reference/gateway-config.mdx(new Database Pool section),docs/kubernetes/setup.mdxvalues table, and the gateway man page.The new docs section states the health-check coupling as it works today:
/readyzis answered from the background readiness monitor's cached result, not from a per-requestacquire, but that monitor pings through the same pool — so sustained saturation can push its ping pastDEFAULT_CHECK_TIMEOUTand flip readiness while the gateway is otherwise serving.Addressing the review feedback on #2700
@letv1nnn raised two things, both fixed here:
std::env::vardirectly inpersistence/postgres.rs. It is now a clap arg with anenvbinding plus a TOML field, matchingOPENSHELL_DB_URL(cli.rs,config_file.rs), so the two database knobs live on the same surface and the key shows up in the generated config docs.env/TOML surface are both included, as the triage on feat: make gateway Postgres connection pool size configurable (hardcoded to 10) #2561 flagged. The chart renders the TOML key rather than an extra containerenv:entry, which is how every other non-secret gateway setting is passed.One deliberate reversal from #2700: invalid values now fail fast instead of silently falling back to the default. A zero pool would block every
acquire, and quietly ignoring a typo would reproduce the exact ceiling the operator is trying to lift — the failure mode this change exists to remove. Every other numericOPENSHELL_*knob already rejects bad input at clap parse time, and the TOML loader rejects0the same way it rejects an emptycredential_drivers.Testing
cargo test -p openshell-server -p openshell-core— all green. New tests: TOML value applied when the flag is unset, env wins over TOML, clap rejects0/negative/non-numeric, loader rejectsdatabase_max_connections = 0, SQLite honours an override and defaults to 5, in-memory SQLite pins to 1.helm unittest deploy/helm/openshell— 3 new cases (key omitted by default, rendered when set, negative value fails the template).mise run pre-commitpasses, includingclippy -D warningsandhelm:docs:check.Verified on a kind cluster
with latest helm chart and gateway image built from this branch. Identical load each run — 200 concurrent SDK clients looping
ListSandboxesfor 30s — samplingpg_stat_activityevery 400 ms:server.dbMaxConnections50max_connections=500(omitted → default)max_connections=10The pool reaches the configured ceiling and never exceeds it, and the default path renders no TOML key and keeps the previous value of 10. Both gateways reported Ready, which exercises a real database ping through the configured pool. This environment is not latency-bound on the database, so it demonstrates the ceiling rather than a throughput win.
Confirmed for both postgres and sqlLite
Checklist
mise run pre-commitpasses