Connections release across cognition — a pooled DB connection is never held idle across…
The canonical failure: a daemon (or a request flow) reads from a store,
runs a slow LLM step, then writes back. If it holds one pooled Postgres
connection for the whole flow — the v1.32.0 pin — that connection sits
checked out and idle for the entire LLM call. Multiply by the flow's
concurrency and the pool (say 20 connections behind a bounded session
pooler) is exhausted by connections doing nothing but waiting on an LLM.
A cheap metadata read elsewhere (a rotate sweep's list_class) then
pool timed out while waiting for an open connection.
The law. A pooled database connection is a scarce, coherent resource. A flow holds one only for the duration of a store operation, never across a cognition (LLM) step between two operations. The connection belongs to the
retrieve/persist/rotate, not to theaskthat sits between them.
Why the pin exists — and when it must not
The v1.32.0 connection pin holds one connection per postgresql
axonstore for the flow's whole life, so every store op routes through
the same physical backend. That is REQUIRED under a transaction-mode
pooler (pgBouncer pool_mode=transaction, Supabase Supavisor :6543,
Neon, RDS Proxy): successive checkouts land on different sessions, so a
prepared statement or SET minted on one is gone on the next — the
pin keeps them on one session for coherence.
Under a session pooler (Supabase session mode :5432) or a direct
connection, each pool connection IS a stable, coherent session.
Coherence is automatic; the pin buys nothing and costs everything — it
keeps a scarce connection checked out across the LLM I/O.
AXON_DB_POOLER_MODE makes the pin pooler-aware:
transaction(default, or unset) → pin ON. Unchanged behaviour; the coherence the transaction pooler needs.session|direct→ pin OFF. Store ops acquire per-op and release between them. A flow's connection is returned to the pool the instant a store op finishes — so a slow LLM step (or a whole quiescent stretch of a flow) holds nothing.
Committed data stays globally visible across per-op checkouts (MVCC), and
the tenant GUC is set per-transaction inside begin_tenant_tx — so
read-your-writes and RLS remain correct without the pin. Only the
transaction pooler's cross-checkout session-state loss needs it.
Relation to the other laws
dispatch_vs_cognition(v2.9.0) applied to a RESOURCE: the runtime holds the connection for the dispatch (the store op); cognition (the LLM step) holds nothing. The pin, under a coherent pooler, would blur that line — this law restores it.time_is_an_explicit_input/ graceful degradation: a custody enumeration that can't get a connection fails closed with a witness and retries, rather than blocking a pool slot — the same fail-closed posture asrotation_without_revelation, tuned so a transient contention does not become a permanent quiesce.
The honest test: if a flow's slowest step is an LLM call and it is still holding a database connection during it, the pool's effective size is not its connection count — it is its connection count minus every in-flight flow. Under a coherent pooler, AXON releases the connection so the pool is sized by concurrent DATABASE work, not by concurrent COGNITION.