Skip to content

Dependency capacity budgets

Use separate allocations for gateway authentication, admission and required durable acceptance so reporting, consumers and optional caches cannot exhaust their connection slots. The connection ceilings below are allocation budgets, not measured throughput or a supported concurrency rating. Qualify the complete deployment with real services and representative traffic before increasing admission or replica limits.

PostgreSQL ownership and configuration

Infrastructure bootstrap owns four named allocations in an API process with durable telemetry enabled. The split batch-worker Deployment runs the same infrastructure bootstrap and opens the same enabled allocations; its background tasks are not extra pools. Disabling durable ingestion removes both telemetry allocations.

Separate native clients reserve actual connections. A semaphore or priority queue around one shared native pool cannot reclaim a connection already occupied by a report or consumer. Each allocation therefore has its own client and a bounded admission owner. This adds fixed client/engine overhead, which must fit the pod's memory and the deployment connection budget; it adds no request SQL round trips.

Allocation Consumers Default connections
control Configuration, admin/reporting, ordinary repositories, key-invalidation discovery and background control work db_pool_size: 20
foreground Gateway API-key lookup, organization lifecycle authorization/freshness, budget admission and required legacy audit/spend db_foreground_pool_size: 8
telemetry Required audit/prompt-render acceptance, spend admission, bounded synchronous fallback and selector operation acceptance telemetry_db_pool_size minus the enabled settlement share
telemetry_settlement Owner-fenced receipts for already-admitted ordinary operations spend_settlement_db_pool_size: 1, included in the telemetry total of 5
telemetry_worker Audit/spend consumers, lease refresh, reconciliation, cleanup, optional audit enqueue, audit retention and selector recovery telemetry_worker_db_pool_size: 5

Audit administration reads use the control allocation. Outbox replay administration uses the worker allocation, including its atomic operator audit. Legacy best-effort audit persistence and retention use control capacity. Gateway API-key authentication is distinct from control-plane login, account administration and password reset. Shared database rows and advisory locks remain shared correctness boundaries: connection reservation cannot make a foreground transaction pass a lock held by another transaction. The lock deadline bounds that failure.

Configure startup values in general_settings (config.general_settings in Helm):

general_settings:
  db_pool_size: 20
  db_foreground_pool_size: 8
  telemetry_db_pool_size: 5
  telemetry_worker_db_pool_size: 5
  db_acquisition_timeout_seconds: 0.2
  db_lock_timeout_seconds: 0.2
  db_statement_timeout_seconds: 1.0
  db_transaction_timeout_seconds: 2.0
  db_background_lock_timeout_seconds: 1.0
  db_background_statement_timeout_seconds: 5.0
  db_background_transaction_timeout_seconds: 10.0

Lock deadlines must be no greater than statement deadlines, which must be no greater than transaction deadlines. The shorter budgets apply to foreground and telemetry acceptance; the background budgets apply to control and telemetry workers. Repository transaction options can shorten the configured transaction/acquisition ceilings.

Capacity admission happens before Prisma: each pool permits at most its configured number of operations or transactions and normally has zero capacity waiters. Excess work returns a controlled HTTP 503 with code database_unavailable. Native connection, lock and statement timeouts use the same response; record/constraint errors retain their repository semantics. Local dependency failures never mark a provider unhealthy or trigger provider retries. A transaction owns one slot through commit or rollback, and permits only one outstanding native query on that transaction. Model queries, raw SQL and generated batch execution share this owner. There are no new per-request SQL calls or client retries.

The telemetry acceptance and settlement allocations each reserve one bounded waiting position per configured connection for required writes. With ordinary operation intents enabled, the default telemetry total of five connections splits into four acceptance connections/waiters and one settlement connection/waiter. Waits consume the existing acquisition deadline, including transaction startup; overflow and expired waits return the same explicit unavailable result. No native slot is freed while a cancelled operation is still running.

A readiness probe borrows one existing slot and temporarily permits one business waiter, bounded by the same acquisition deadline. Each allocation permits only one probe; cancellation retains its slot until native work ends. This prevents health traffic from immediately shedding settlement on a one-connection allocation. No extra connection is added. With all allocations enabled, there are at most 3 + telemetry_db_pool_size waiting operations per process, or 352 with the default telemetry total at the illustrative 44-process rollout ceiling below, within the existing bounded request/worker allocations. deltallm_database_allocation_seconds{operation="readiness"} measures probe time; deltallm_database_allocation_events_total{outcome="queue_timeout"} records business acquisition deadlines exceeded in these bounded queues.

PostgreSQL receives native statement_timeout, lock_timeout and idle_in_transaction_session_timeout options on every connection. Startup verifies that the server applied them. Prisma also receives finite connect, socket, pool and interactive-transaction deadlines. Its URL connect/pool timeouts are whole seconds, so a 0.2-second allocation setting becomes a one-second native connect/pool ceiling; interactive transaction acquisition supports the finer deadline. The application also bounds ordinary query callers by acquisition plus statement time. See the Prisma connection options and transaction options.

Cancellation stops the caller without freeing a slot still occupied by native work. The pool owner observes and drains the bounded remaining operation. A bounded expiry task also reclaims an abandoned manually opened transaction and disables its old client; it is cancelled when normal commit/rollback releases the slot. Abandoned transaction starts roll back; ambiguous start/commit acknowledgements retain their slot through the conservative native expiry bound. No failed or uncertain commit is retried by this client layer. Existing event identities, atomic outbox acceptance, privacy locks, claim fencing and replay remain the durability boundaries. Required persistence never falls back to a memory-only acceptance queue.

Set database allocation budgets in the startup file or DELTALLM_DB_* / DELTALLM_TELEMETRY_WORKER_DB_POOL_SIZE environment settings. Explicit file values win for the new allocation fields. Durable configuration must agree with these startup values; startup fails on a mismatch, and live API changes require a restart, including adding an explicit default that would override the environment. Existing database URL selection is preserved. The older db_pool_timeout and telemetry_db_pool_timeout_seconds fields remain for configuration compatibility; allocated clients override those URL waits using db_acquisition_timeout_seconds. TLS, credentials, schema and unrelated URL options are preserved. A proxy must pass through the required startup options; a proxy that strips them fails validation. The legacy timeout fields can be removed in a breaking configuration release after deployment profiles migrate to the allocation deadlines. Direct audit/spend service constructors retain an optional worker-client argument for compatibility; production bootstrap always supplies the isolated worker client. Remove that constructor fallback only after its external callers migrate.

Redis ownership and configuration

general_settings:
  redis_critical_max_connections: 64
  redis_cache_max_connections: 16
  redis_bulk_max_connections: 16
  redis_acquisition_timeout_seconds: 0.2
  redis_socket_timeout_seconds: 1.0
  redis_connect_timeout_seconds: 1.0
  redis_bulk_url: null
Allocation Consumers
critical Authentication/session state, admission/limits, routing coordination and configuration/governance/audit invalidation
cache Prompt, MCP registry, route snapshot and guardrail caches; optional notification deduplication
bulk Response-cache reads, writes and administration, including dynamic cache reloads and MCP result-cache use

Long-lived subscriptions count against critical capacity. No capacity waiters are allowed. Admission precedes the driver's connection lock and has a finite deadline covering setup and validation. Native connect/socket deadlines apply; automatic client retries are disabled. Idle subscriptions use bounded polling without adding Redis commands. Borrowed clients return their lease without closing the owner's pool. Pool saturation preserves each consumer's existing failure policy: optional caches miss/skip, and required controls must not authorize or accept durable work by failure.

These fields use explicit YAML values before environment defaults and reject live changes. The effective durable limits must match that startup allocation snapshot; startup fails before opening additional clients when they differ. The check also covers PostgreSQL pool counts and whether either outbox enables the telemetry pools. This prevents persisted settings from exceeding the connection counts validated by Helm. Before rollout, align persisted allocation values and ingestion modes with the rendered startup configuration. The existing critical endpoint still comes from the startup file/environment so an older durable endpoint value cannot redirect coordination. Capacity/deadline/retry options override conflicting URL query parameters.

Protect critical Redis memory

Both optional allocations use redis_bulk_url when provided. With null, they use the existing endpoint through separate client pools. Separate pools or Redis database numbers do not isolate server eviction. For production isolation, use a separate cache Redis deployment and provision finite memory plus buffer/client headroom on both servers. The critical server must use noeviction; the cache server may use allkeys-lru. Redis documents the memory and eviction behavior.

Supply the cache endpoint through the existing secret resolver, for example redis_bulk_url: os.environ/DELTALLM_REDIS_BULK_URL with a Secret-backed environment variable. The field is secret-typed and the settings API returns **********. Missing, empty or redacted update values preserve it; actual live endpoint writes return restart_required. Set or remove it in startup configuration and restart.

The gateway never changes server memory policy. The eviction regression test requires two dedicated empty test instances, verifies different server identities, fills the cache to eviction, verifies critical lease value/TTL and zero critical evictions, then forces critical noeviction pressure and verifies fail-closed rate admission. It restores server settings and deletes only its own keys. A shared-server deployment must separately qualify memory and CPU/network contention; client separation alone is not evidence of isolation.

Check peak deployment connections

The chart renders a *-dependency-capacity ConfigMap and rejects a peak that exceeds the declared dependencyCapacity PostgreSQL/Redis budgets. It calculates each enabled role from its HPA maximum or fixed replicas, adds surge (rounding percentages up) and a full retiring-generation allowance, multiplies by processes, then sums its enabled pools. It includes reserved connections/clients for migrations, operators, other applications and operating headroom.

peak role processes = (maximum replicas + surge + maximum replicas × retiring generations)
                      × processes per pod
PostgreSQL peak = Σ(peak role processes × enabled database pool sizes) + reserved connections
Redis peak = Σ(peak role processes × all three Redis pool sizes) + reserved clients

The production example has 12 maximum API pods, one surge pod, two retiring generations and one process per pod: 37 peak processes. With both outboxes enabled, this is 37 × (20 + 8 + 5 + 5) = 1406 PostgreSQL connections and 37 × (64 + 16 + 16) = 3552 Redis connections before reserves. Enabling two fixed batch-worker pods adds seven peak processes: 266 PostgreSQL and 672 Redis connections. Including the production example's 100 operating and two migration connections, the API-only totals are 1508 PostgreSQL and 3680 Redis; the split-role totals are 1774 PostgreSQL and 4352 Redis. Additional clients must be declared separately. Both worker and API telemetry jobs share their process allocations. A batch-worker Deployment is not a distinct telemetry-only process role.

The evaluation overlay pins its bundled PostgreSQL server to 100 connections. One steady, one surge and one retiring process use 3 × (20 + 8) = 84 connections, leaving 16 reserved. Enabling outboxes, batch workers or more replicas in that profile requires resizing the actual database and its declared budget; Helm rejects an unchanged 100-connection ceiling. Evaluation settings are not a production profile.

dependencyCapacity.apiProcessesPerPod and batchWorkerProcessesPerPod set WEB_CONCURRENCY and UVICORN_WORKERS; rendered pool-size environment values prevent envFrom from silently raising the two legacy pool counts. Duplicate overrides through explicit env and explicit --workers/-w arguments are rejected. Custom launchers must honor the declared process counts; do not use a separate --workers value or start additional unsized worker processes. The retiring-generation setting assumes rollouts do not overlap beyond its declared allowance; increase it if the deployment process permits more overlap.

The supplied maximum-service budgets are illustrative declarations, not changes to PostgreSQL max_connections or Redis maxclients, and not proof those connections are available. Replace them with the actual allocation supplied by the service owner, including operating headroom, before deployment. The Redis ceiling conservatively sums all allocations even with separate servers; also check each server's own critical/cache connection totals. The extended report also checks declared FD, proxy transport and provider envelopes. Follow the saturation autoscaling preflight to compare those declarations with physical limits and the custom metrics API. Measured N−1 traffic capacity remains a separate qualification.

Rollout, rollback and evidence

Provision the calculated downstream headroom first, then roll the binary/configuration together. Old replicas still have shared pools, so the isolation guarantee starts only after every API and worker process is updated. No database schema migration or durable record rewrite is introduced. Switching the optional Redis endpoint produces cold caches; critical keys and invalidation channels remain on the original server.

Rollback by restoring the previous binary/configuration and restarting after the existing durable-telemetry drain procedure. Keep the old and new process pools inside the retiring budget. Restoring a shared Redis endpoint also restores shared memory pressure; it does not preserve the isolated-server guarantee.

Observe deltallm_database_allocation_occupied, deltallm_database_allocation_events_total, deltallm_database_allocation_seconds, deltallm_redis_allocation_occupied, deltallm_redis_allocation_events_total and deltallm_redis_allocation_acquisition_seconds. Labels contain fixed allocation and operation/outcome classes. Correlate them with server connection/lock/eviction metrics, required acceptance failures, consumer backlog age and request latency.

Readiness probes the critical Redis client and each enabled PostgreSQL allocation, including foreground and telemetry-worker clients. Missing clients, saturation, failures and probe timeouts make readiness fail. These checks run concurrently with one second per dependency: one Redis PING and two SQL SELECT 1 calls in legacy mode, or four SQL calls with an outbox enabled, per probe. They use the existing bounded allocations and add no inference-path calls. Optional cache clients do not gate readiness. Liveness remains independent of all dependencies.

Required validation includes real PostgreSQL reporting/consumer saturation, lock and statement deadlines, transaction expiry, cancellation/recovery, and real Redis cache floods, idle subscriptions, socket failures and memory isolation. Test definitions and rendered arithmetic are not a concurrency certificate. Publish supported traffic only with matching workload, commit/image, resources, error rates and latency evidence.

The managed lifecycle uses a 90-second pod grace. Serialize release and scale operations within the declared terminating-pod allowance; live Helm upgrades reject existing terminating pods, while offline rendering cannot inspect them.