Skip to content

feat: request admission control / backpressure middleware (opt-in)#1332

Open
unsupo wants to merge 1 commit into
developmentfrom
feat/admission-control
Open

feat: request admission control / backpressure middleware (opt-in)#1332
unsupo wants to merge 1 commit into
developmentfrom
feat/admission-control

Conversation

@unsupo

@unsupo unsupo commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

What

An opt-in, outermost middleware that sheds excess load early with 429 + Retry-After, so requests back off instead of piling into the anyio threadpool until they time out to 504 (the shared-queue collapse).

Two independent gates (either/both; both off by default):

  • AdmissionController — global per-worker in-flight cap. Pod capacity = workers x max_inflight_requests; shed when the worker's in-flight set is full.
  • PerConsumerRateLimiter — per-credential token-bucket fairness, reusing the existing auth.token_bucket.TokenBucketManager, keyed by a SHA-256 hash of the caller's Authorization header / session cookie so one noisy client can't starve the fleet. Cluster-aware when a PG pool is attached.

Why

Cluster mode adds capacity, but under saturation a node still queues every request until 504; past a critical backlog effectively all new requests fail. This is the complementary graceful-degradation layer.

Config / wiring

  • New AdmissionControlConfig (both switches default False), read from the merged server config at app wiring — not a bootstrap key; a restart applies changes. Classified as runtime in the bootstrap-partition test.
  • Wired as the outermost middleware in app_wiring.create_fastapi_app; not wired when disabled -> zero overhead by default.
  • Health/docs endpoints always exempt so readiness probes and schema fetches are never rejected.

Validation

Validated on a 2-replica EKS deployment: at a small in-flight cap, excess concurrent load was shed as fast 429s instead of collapsing to 504s, and a single heavy consumer was throttled without affecting others.

Tests

Unit tests for both gates, credential keying (no raw secret in the key), exempt paths, and the 429 + Retry-After path (incl. an inf-refill guard so a misconfigured 0-rate can't 500). Config-classification test updated. ruff/mypy clean; 443 config tests unregressed.


🤖 Generated with Claude Code

Cluster mode adds capacity, but under saturation the server still queues every
request in the anyio threadpool until it times out to a 504 -- the shared-queue
collapse where, past a critical backlog, effectively all new requests fail.

This adds an OUTERMOST middleware that sheds excess load early with a fast
429 + Retry-After so clients back off instead of piling up:

- AdmissionController: a global per-worker in-flight cap (pod capacity =
  workers x max_inflight_requests). Shed when the worker's in-flight set is full.
- PerConsumerRateLimiter: independent per-credential token-bucket fairness
  (reuses the existing auth TokenBucketManager, keyed by a SHA-256 hash of the
  caller's Authorization header / session cookie) so one noisy client can't
  starve the fleet. Cluster-aware when a PG connection pool is attached.

Both gates are independent and OFF by default (AdmissionControlConfig, read from
the merged server config at app wiring -- not a bootstrap key; a restart applies
changes). Health/docs endpoints are always exempt so readiness probes and schema
fetches are never rejected. Zero overhead when disabled (middleware not wired).

Validated on a 2-replica EKS deployment: at a small in-flight cap, excess
concurrent load was shed as 429s (fast) instead of collapsing to 504s, and a
single heavy consumer was throttled without affecting others.

Adds unit tests for both gates, credential keying, exempt paths, and the 429 +
Retry-After path (incl. the inf-refill guard); config classification updated.
@unsupo unsupo requested a review from jsbattig July 8, 2026 23:25
@jsbattig

jsbattig commented Jul 9, 2026

Copy link
Copy Markdown

Reviewed via /implement-backlog and integrated into development (merge commit f9e40670, preserving your original commit + a robust-fix commit c7ea2675). Thanks for the contribution!

The AdmissionController (global per-worker in-flight cap) was solid as-is. The PerConsumerRateLimiter was reworked to be genuinely cluster-shared — it was per-worker in-memory while the docs claimed fleet-wide, so a consumer's effective rate was configured x workers x nodes. Changes before merge:

  • Parameterized TokenBucketManager (validated table_name/key_column) backing the consumer limiter with a dedicated consumer_rate_limit_state table (migration 034) — zero collision with the auth login limiter's token_bucket_state; auth path byte-for-byte unchanged.
  • PG pool attached at the middleware-construction site in app_wiring (not lifespan).
  • Docstrings corrected to honest wording (bounded fleet-wide; small transient overshoot possible under simultaneous cross-node bursts).
  • Gate fixes: admission_control_config classified runtime in the partition audit; a brittle migration-max test made adjacency-based; magic numbers extracted; \Z regex hardening.

Gates: fast-automation 12394/0 (lone failure = the known #1323 flake, isolation-confirmed); server-fast all 6 chunks PASS/0-fail; ./lint.sh 0. Code review APPROVED. The real-PG cross-node cluster test is skip-gated on TEST_POSTGRES_DSN and will be validated on staging.

Follow-up filed for strict (zero-overshoot) bounding via an atomic UPDATE ... RETURNING decrement (touches both limiters). Will show as merged once development is pushed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants