Skip to content

Comments

feat(server): Add HTTP concurrency limit with 503 rejection#326

Merged
jan-auer merged 2 commits intomainfrom
feat/http-concurrency-limit
Feb 23, 2026
Merged

feat(server): Add HTTP concurrency limit with 503 rejection#326
jan-auer merged 2 commits intomainfrom
feat/http-concurrency-limit

Conversation

@jan-auer
Copy link
Member

@jan-auer jan-auer commented Feb 23, 2026

Adds a web-tier flood protection limit to the objectstore HTTP server. When the number of in-flight requests reaches http.max_requests (default: 10,000), new requests are rejected immediately with HTTP 503. /health and /ready are excluded so orchestration probes always get through.

The Http config struct groups HTTP-layer settings separately from the service-layer Service struct. This keeps concerns cleanly separated and gives a natural home for future HTTP-level settings (timeouts, body size limits, etc.). The env var is OS__HTTP__MAX_REQUESTS.

Why direct 503 instead of readiness-based backpressure: failing /ready under load lets Kubernetes drain the pod, but probe intervals (5–10s) leave a window of continued overload, recovery wastes capacity waiting for probe cycles, and multiple pods failing probes simultaneously concentrates traffic onto remaining pods. A busy pod is also semantically still ready — conflating load with readiness muddies alerting. Direct rejection responds in milliseconds, recovers instantly, keeps every pod in the pool, and works in any deployment environment.

Tested with two unit tests (pass-through and rejection/exemption via a Notify-based gating pattern) and an integration test via TestServer with max_requests = 0 for deterministic coverage without needing concurrent requests.

Ref FS-171
Closes #315

Adds a web-tier flood protection limit to the objectstore server. When
the number of in-flight HTTP requests reaches `http.max_requests`
(default: 10,000), new requests are rejected immediately with HTTP 503.
Health and readiness endpoints are excluded from the limit.

The `Http` config struct groups HTTP-layer settings separately from the
service-layer `Service` struct, providing a natural home for future
HTTP-level settings such as timeouts and body size limits. Configurable
via `OS__HTTP__MAX_REQUESTS`.

Includes two unit tests (pass-through and rejection/exemption) using a
`Notify`-based gating pattern, and an integration test via `TestServer`
with `max_requests = 0` for deterministic coverage.
@jan-auer jan-auer marked this pull request as ready for review February 23, 2026 10:43
@jan-auer jan-auer requested a review from a team as a code owner February 23, 2026 10:43
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Router::layer() ordering is the inverse of ServiceBuilder — the last
.layer() call is outermost (runs first). The test had in_flight_layer
as outermost, so it incremented the counter before limit_web_concurrency
checked it, causing the first request to be rejected immediately and the
handler to never call paused.notify_one(), hanging the test indefinitely.

Swap to apply in_flight_layer first (inner) and limit_web_concurrency
second (outer), matching the production middleware order in app.rs.

Also add a 5-second timeout around paused.notified() so the test fails
with a clear message rather than hanging in CI.
Copy link
Member

@lcian lcian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@linear
Copy link

linear bot commented Feb 23, 2026

@jan-auer jan-auer merged commit a437d3f into main Feb 23, 2026
20 checks passed
@jan-auer jan-auer deleted the feat/http-concurrency-limit branch February 23, 2026 12:35
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