You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PR #141 changed std/db/postgres.connect(url) to reuse process-local shared pools keyed by normalized connection target, while returning fresh logical handles for each connect() call. That fixes the request-path physical-pool churn problem, but it introduces a new lifecycle question: shared physical pools currently live for the worker/process lifetime.
Risk / problem
For normal apps with one or a few stable env-backed database URLs, this is fine and desirable.
The risky shape is dynamic connection strings, for example:
Because close(handle) now invalidates only the logical handle and keeps the shared pool cached, many distinct URLs can accumulate:
one shared pool per distinct normalized connection target
up to NTNT_DB_POOL_SIZE server connections per pool
memory/connection growth for the worker lifetime
A related but smaller concern: repeated connect(url) without close(handle) no longer creates physical pool churn, but it can still accumulate logical handle entries in HANDLE_REGISTRY.
A max shared-pool count with LRU eviction seems sound if implemented carefully:
Add an env/config knob, e.g. NTNT_POSTGRES_MAX_SHARED_POOLS, with a conservative default or disabled-by-default behavior if we want maximum compatibility.
Track last-used time / generation per pool key.
On connect(url) when inserting a new shared pool and the registry exceeds the max, evict least-recently-used pools that have no active logical handles and no active transactions.
Never evict a pool while any handle currently points at it, or while a transaction is pinned to a handle backed by it.
Make eviction best-effort; if all pools are active, either keep the new pool and emit a warning, or return a clear recoverable error depending on the chosen policy.
Keep the registry key hashed/redacted; no raw connection strings in logs or errors.
Also consider diagnostics:
warn when shared pool count crosses a threshold
optionally expose internal stats for shared pool count / logical handle count
document that dynamic/user-controlled URLs should be avoided unless bounded intentionally
Context
PR #141 changed
std/db/postgres.connect(url)to reuse process-local shared pools keyed by normalized connection target, while returning fresh logical handles for eachconnect()call. That fixes the request-path physical-pool churn problem, but it introduces a new lifecycle question: shared physical pools currently live for the worker/process lifetime.Risk / problem
For normal apps with one or a few stable env-backed database URLs, this is fine and desirable.
The risky shape is dynamic connection strings, for example:
Because
close(handle)now invalidates only the logical handle and keeps the shared pool cached, many distinct URLs can accumulate:NTNT_DB_POOL_SIZEserver connections per poolA related but smaller concern: repeated
connect(url)withoutclose(handle)no longer creates physical pool churn, but it can still accumulate logical handle entries inHANDLE_REGISTRY.Proposed direction
Consider adding bounded shared-pool lifecycle management.
A max shared-pool count with LRU eviction seems sound if implemented carefully:
NTNT_POSTGRES_MAX_SHARED_POOLS, with a conservative default or disabled-by-default behavior if we want maximum compatibility.connect(url)when inserting a new shared pool and the registry exceeds the max, evict least-recently-used pools that have no active logical handles and no active transactions.Also consider diagnostics:
Acceptance criteria
connect → query → closedoes not leak physical pools beyond the configured bound