feat(server): add opt-in global connection cap to the S3 accept loop (#4957)

Follow-up to backlog#1191 (optional sub-item). The main accept loop
spawned one task per socket with no global bound, so a connection flood
could exhaust file descriptors and memory and take existing traffic
down with it.

- New RUSTFS_API_MAX_CONNECTIONS (default 0 = unlimited, no semaphore
  constructed, accept loop unchanged).
- When set, the loop acquires an owned semaphore permit BEFORE
  accept(): at saturation it simply stops accepting and lets the
  kernel backlog absorb bursts (TCP-native backpressure) instead of
  accept-then-close churn. The permit moves into the connection task
  and is released by RAII on any exit path, including TLS handshake
  failures.
- Saturation is observable via the
  rustfs_http_server_connection_cap_saturated_total counter, a
  connection_cap_state startup event, and a per-wait debug event;
  shutdown stays responsive while parked on the semaphore.
- The cap covers everything on the main listener (S3, admin, console,
  internode gRPC); the constant docs carry sizing guidance.
- E2E coverage: ten sequential Connection-close requests against cap 2
  prove permits never leak; two stalled connections saturating cap 2
  leave a third unserved until their permits are released, after which
  the queued request is accepted and answered.
This commit is contained in:
Zhengchao An
2026-07-18 10:41:51 +08:00
committed by GitHub
parent 569fa3ec87
commit 40c089f31b
4 changed files with 224 additions and 1 deletions
+17
View File
@@ -73,3 +73,20 @@ pub const ENV_API_RATE_LIMIT_BUCKET_BURST: &str = "RUSTFS_API_RATE_LIMIT_BUCKET_
/// Default for `RUSTFS_API_RATE_LIMIT_BUCKET_BURST` (`0` = same as bucket RPM).
pub const DEFAULT_API_RATE_LIMIT_BUCKET_BURST: u32 = 0;
/// Maximum concurrently served connections on the main API listener.
///
/// `0` (the default) means unlimited. When set, the accept loop stops
/// accepting once the cap is reached and lets the kernel backlog absorb
/// bursts, releasing capacity as connections close. This bounds file
/// descriptor and memory usage under a connection flood.
///
/// The cap covers everything on the main listener — S3, admin, console,
/// and internode gRPC — so size it well above peer-node count plus the
/// expected client concurrency.
/// Environment variable: RUSTFS_API_MAX_CONNECTIONS
/// Example: RUSTFS_API_MAX_CONNECTIONS=10000
pub const ENV_API_MAX_CONNECTIONS: &str = "RUSTFS_API_MAX_CONNECTIONS";
/// Default for `RUSTFS_API_MAX_CONNECTIONS` (`0` = unlimited).
pub const DEFAULT_API_MAX_CONNECTIONS: usize = 0;