Skip to content

mod_proxy: Add a per-worker maxbusy limit on concurrent requests, glo… - #857

Open
arturobernalg wants to merge 1 commit into
apache:trunkfrom
arturobernalg:proxy-worker-maxbusy
Open

arturobernalg wants to merge 1 commit into
apache:trunkfrom
arturobernalg:proxy-worker-maxbusy

Conversation

@arturobernalg

Copy link
Copy Markdown
Member

max= on a BalancerMember only bounds the per-child connection pool; nothing today provides a process-global limit on concurrently active requests to a backend, so under multiple children/threads a backend can receive far more concurrent requests than an admin configuring max=N would expect.

This adds maxbusy=N to BalancerMember: a hard cap on requests concurrently active against that specific member, counted across all child processes and surviving a graceful restart. 0 (the default) keeps current behavior.

Design: a new admission table is kept separate from worker->s->busy (which stays exactly as it is today, including its use by bybusyness and its reset() semantics) and separate from proxy_worker_shared, which is per-restart-generation shared memory and so cannot hold live cross-generation state. The table lives in an apr_shm retained via ap_retained_data_get()/_create() (already used in this file for proxy_start_time), owned by the parent process, so it survives both a graceful restart and the unload/reload of mod_proxy.so that a graceful restart causes. Every statically configured maxbusy member is known by post_config time (the balancer-manager dynamic-worker form does not expose maxbusy), so every admission slot is pre-registered once in mod_proxy_balancer's post_config, after balancer->s->sname is final; the request path only ever looks up an existing slot and does an atomic CAS reserve/release, never creating one, so there is no creation race between processes or generations. A saturated member is treated like a disabled/error member for selection purposes (sticky redirect, hot spares, balancer timeout), not marked in error itself. Reservation release is a separate cleanup from the busy-count cleanup, run immediately when proxy_handler() abandons a worker for a failover attempt, rather than only at the end of the whole request.

Tested manually with real multi-child builds (prefork/worker/event), real graceful restarts with in-flight requests, BalancerPersist on and off, sticky/redirect/hot-spare/standby topologies, failover chains, and backend HTTP/2 and WebSocket/tunnel traffic. Added test/modules/proxy/test_07_maxbusy.py.

MMN bumped to 20211221.34 for the new proxy_worker_shared::maxbusy field and the new ap_proxy_* exports.

This PR does not report or address a security vulnerability, follows the httpd style guide, uses an empty APLOGNO() tag for its new log messages, includes a changes-entries/*.txt file, and includes pyhttpd test cases in test/modules/proxy/.

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.

1 participant