Request deadlines and bounded background work¶
router_settings.timeout is the total inference timeout in seconds (default 600).
It begins before the ingress queue, upload, authentication, prompt resolution and
guardrails. Cache hits, routing, retries/backoff, MCP execution, normal accounting
and downstream response delivery consume the same monotonic budget. It applies to
chat and compatible aliases, embeddings, rerank, images and audio. Health and admin
requests do not use the inference deadline.
When routing is reached, a route's shorter timeout is measured from inference ingress and shortens the remaining request budget, including finalization. A route cannot extend the global budget. Reloads affect subsequent requests; an active request keeps its pinned routing generation and deadline. A cache hit uses the global ingress budget without creating a provider execution plan. Set the global timeout high enough for the longest supported modality before choosing shorter route limits.
HTTP connect, pool, write and read limits remain explicit and are capped by remaining
time. A provider read timeout measures idle time between received chunks; frequent
chunks do not extend the total stream lifetime. Local expiry returns HTTP 408 with
request_deadline_exceeded before response headers. The messages compatibility API
keeps its native error envelope. After headers, an incomplete stream is aborted;
there is no replacement response, success terminal marker or provider retry.
Normal finalization is awaited in both outbox and legacy spend modes. Legacy writes no longer escape the request as fire-and-forget tasks, and a failed write produces the existing persistence-unavailable error. This does not make legacy accounting transactional or recoverable: use the durable spend rollout for production. Cancellation still gives resource cleanup its existing short, shielded grace. The deadline can therefore expire before cleanup finishes; that grace cannot start a replacement provider attempt. PR6's durable operation remains available for recovery when a provider outcome or receipt is ambiguous.
Optional callbacks¶
Callback delivery is best effort and has finite per-process capacity. Required audit, spend, notifications and business webhook delivery keep their existing durable owners. The manager snapshots accepted callback payloads, bounds recursive size/depth before copying, and discards oversized/full work without delaying an inference response. Request exception objects and their tracebacks are not retained; failure callbacks receive a safe error and the logging payload's error information.
| Startup setting | Default | Meaning |
|---|---|---|
callback_max_pending |
128 | Total queued and running deliveries |
callback_max_concurrency |
4 | Simultaneous async deliveries and maximum synchronous adapter threads |
callback_max_payload_bytes |
262144 | Maximum recursively measured payload before copying |
callback_max_bytes |
8388608 | Delivery payload allocation, including a three-times copy allowance |
callback_timeout_seconds |
5 | Per-delivery time including slot wait; also the inline hook limit |
callback_shutdown_seconds |
5 | Total optional delivery/thread drain grace |
The synchronous adapter executor has a separate allocation of the same
callback_max_bytes; it retains that charge if a timed-out delivery stops waiting
while its thread continues. A callback list accepts at most 32 handlers per outcome.
The manager retains at most 64 handlers across active and retired configurations.
Retired SDK clients close under the same blocking allocation and per-handler lock;
queued deliveries cannot reopen them. A stuck or failed close keeps its handler
allocation. Further registrations are rejected with a resource metric and warning
until cleanup succeeds or the process restarts. Cleanup shares the delivery shutdown
grace and never starts an unbounded collection of close tasks.
OpenTelemetry uses a handler-owned provider and synchronous span processor inside
this executor, with a five-second exporter timeout and explicit provider shutdown.
S3 uses one pooled connection per handler, five-second connect/read limits, one
attempt and explicit client close. The existing Langfuse v2 adapter uses one SDK
consumer configuration, five-second requests, one retry limit, serialized flush,
and explicit shutdown; its SDK may create ingestion/media threads in addition to
the gateway executor. SDK internals and custom integrations still need their own
memory/connection limits when operators enable them.
Configured custom integrations are trusted code: async implementations must yield
to the event loop and propagate cancellation, and synchronous implementations must
use the manager's bounded run_blocking facility rather than create their own
tasks/executors. Pre-call hooks retain their existing ordered mutation behavior
inside the request deadline. They are distinct from required guardrail policy.
Callback errors or overflow do not change a successful provider response. Delivery does not retry integrations. Capacity remains charged until actual task/thread completion, including cancellation-resistant extensions. Shutdown stops admission, waits for its grace, then cancels pending async/queued thread work. Cancelling or timing out a queued synchronous call keeps its slot and byte charge until a worker dequeues and skips it. Repeated disconnects cannot create replacement capacity while the queue still retains those payloads. Executor shutdown physically removes queued calls after closing admission. Python cannot kill an already-running thread; a permanently blocked extension can retain a bounded slot until process termination. Monitor this condition and fix or disable the extension before restarting. The managed process lifecycle bounds termination and reports unfinished cleanup separately from a graceful exit.
CPU guardrails¶
Presidio inspection and anonymization run in a bootstrap-owned executor shared by registered Presidio guardrails, including replacements loaded by configuration. At most 32 guardrails may be registered. Reload replaces configured entries, including removal through an empty list, and keeps manually registered handlers. A failed reload preserves the previous complete guardrail policy. Each instance serializes access to its optional NLP engine. Synchronous callback adapters have their own allocation and cannot consume guardrail execution slots.
| Startup setting | Default | Meaning |
|---|---|---|
guardrail_max_concurrency |
2 | Worker threads per process |
guardrail_max_pending |
8 | Queued plus running inspections |
guardrail_max_bytes |
33554432 | Retained payload allocation, including a four-times transformation allowance |
guardrail_timeout_seconds |
5 | Maximum wait for an inspection, including queue time |
guardrail_shutdown_seconds |
5 | Executor drain grace |
The earliest request/guardrail timeout stops waiting. A running inspection keeps
its slot and byte charge until completion; late results are discarded. Cancelled
queued inspections retain the same charge until dequeued and skipped, so a blocked
worker can keep the allocation full even after its callers have disconnected. Full,
oversized or unavailable execution returns local HTTP 503
gateway_work_unavailable and does not call or penalize the provider. Required
guardrails are never skipped because execution capacity is exhausted.
All callback/guardrail limits above are startup-only. Explicit file values override
environment defaults. Environment names use DELTALLM_ plus the uppercase setting.
Dynamic changes, including adding an explicit default that changes environment
precedence, return restart_required before persistence. Update and roll the
deployment to change these allocations.
Capacity and observation¶
For each API process the defaults allocate at most six executor threads, 128
callback deliveries, four unfinished synchronous callbacks and eight guardrail
inspections. Payload allocations total at most 8 + 8 + 32 = 48 MiB, in addition
to ingress buffers, Python task/object overhead, thread stacks, NLP engines,
integration clients and their own SDK buffers. These are payload budgets, not an
RSS limit. Offloading keeps yielding/native CPU work off the event loop. Threads do not
guarantee parallel speedup for Python code limited by the GIL, and arbitrary C
extensions that hold the GIL require process isolation for strict latency bounds.
The fallback email scan also avoids retrying every suffix of a long local part.
Multiply by every process and overlapping API replica that constructs these owners. The managed rollout example reserves 37 API processes, allocating up to 222 threads and 1776 MiB of payload capacity. Its seven overlapping worker processes also construct these runtimes, bringing the deployment total to 264 threads and 2112 MiB. This change adds no PostgreSQL, Redis or provider HTTP pool and no data-plane SQL/Redis/network call for deadline propagation.
deltallm_request_deadline_expirations_total{response}distinguishes expiry before and after response headers.deltallm_bounded_work_in_flight{allocation}anddeltallm_bounded_work_bytes{allocation}include unfinished cancelled work.deltallm_bounded_work_rejections_total{allocation,reason}reports full, oversized and closed allocations;callback_resourcesadditionally exposes exhausted handler capacity and failed SDK cleanup.deltallm_bounded_work_seconds{allocation,outcome}records thread work through actual completion, including queue wait.deltallm_callback_outcomes_total{integration,outcome}reports optional delivery outcomes. Custom handlers use the singlecustomintegration label.
Investigate sustained saturation and callback drops before increasing limits. Compare event-loop lag, received throughput, all-response latency, timeout/rejection counts and retained-work recovery under the same offered workload. These defaults are bounded allocations; PR10 still supplies production throughput qualification.
The source PR 7 measurements include HTTP and component samples, callback retention, event-loop lag, latency, dependency counts, and shutdown drain. This integration does not copy the historical sample archive. The complete integrated image requires new load qualification on the reproducible kind cluster.
See Process lifecycle for migration-before-rollout ordering, the managed 80-second shutdown budget, interrupted streams, and recovery of committed records. Keep the managed image command in production.