Skip to main content

MAX_CONCURRENT_REQUESTS

Constant MAX_CONCURRENT_REQUESTS 

Source
pub const MAX_CONCURRENT_REQUESTS: usize = 512;
Expand description

How many requests this server answers at once before a new one waits for a slot to free up.

Backpressure rather than an outright refusal — tower::limit::ConcurrencyLimit makes a request wait for a permit instead of failing it — because a caller within its rate-limit budget (crate::rate_limit) asking at a bad moment should be slowed, not bounced. The two are complementary: this bounds how much concurrent work every route together may impose (memory, open file descriptors, threads blocked on I/O), where a rate limiter bounds how much one caller may ask for.

Applied by limited_make_service, which both serve_router and serve_tls serve through, not as a layer on the [Router] itself. That placement is load-bearing, not a style choice: axum::Router::layer re-applies whatever [tower::Layer] it is given at more than one internal dispatch point, each application constructing its own middleware instance. A stateless layer does not care — a fresh Duration behaves identically every time, which is why the router’s own request timeout is layered directly onto it. ConcurrencyLimitLayer is not stateless: Layer::layer allocates a fresh Semaphore on every call, so a limit applied through Router::layer ends up enforced against several independent, unshared semaphores instead of the one number a deployment configured — the bound would compile, look correct in a code review, and do nothing. Wrapping the already-built Router once, there, and turning the result into a MakeService the way [tower::make::Shared] would is what keeps every per-connection clone pointing at the same semaphore. See this crate’s tests::concurrency_past_the_limit_waits_instead_of_running_unbounded for the regression this would otherwise be.