Files
RustProxy/docs/PROXY_1KB_HOTPATH_DEV_0.34.0.md
T
2026-10-18 06:13:42 +02:00

8.4 KiB

RustProxy 0.34.0 DEV: 1 KiB reverse-proxy throughput audit

Goal and evidence boundaries

Priority 1 is short (~1 KiB) HTTP reverse-proxy responses: aim for approximately 100,000 requests/s, and eventually at least 80% of nginx under the same controlled benchmark, without disabling authentication, ACLs, caching semantics, rate limits, traffic accounting, request IDs, logs, body streaming or protocol support. Priority 2 is local WWW serving, to be tuned and measured separately. This is a development revision; Cargo/package version remains 0.34.0.

This source is not compiled in the current environment. Neither cargo nor rustc is available. Only the user's pre-existing executable (SHA-256 453757748455d7b9b50a6140e322ee4d4cf4a030eea29318686f2e28c594b644) was run. Live configuration A/B tests therefore substantiate a configuration bottleneck, not the throughput of this edited source.

Real baseline experiment (existing executable only)

Disposable loopback nginx upstream, 1,040-byte body, HTTP/1.1 persistent frontend connections, caching disabled, identity response encoding, four Tokio workers, ApacheBench (ab -k -n 12000), 16/64 concurrent frontend clients. Checks outside the timed intervals verified HTTP 200, exact 1,040 bytes and SHA-256 62a08d75cdde0416c883d72458385b5e56ae8c14466934ad784587ddb93a548b; 0 failed requests in the shown samples. The host has shared CPU resources; results vary and are not a pinned-CPU or production acceptance benchmark.

Existing executable configuration 16 clients, RPS 64 clients, RPS
max_idle_per_host = 4 18,226 17,935
max_idle_per_host = 64 30,906 24,872
Relative change (configuration only) +69.6% +38.7%

A separate repeat experiment at 64 clients (two runs per variant, in order) produced: idle4 14,028 / 14,834; idle16 22,205 / 24,711; idle32 25,580 / 29,088; idle64 36,521 / 33,847; idle64 + recycle 250,000 31,168 / 29,753; idle32_again 34,798 / 29,194; idle64_again 34,677 / 29,965; idle4_again 23,503 / 23,514 RPS. The order drift warns against reporting a single percentage as a robust speedup. Raising generation recycling from 25,000 to 250,000 did not establish any improvement; its original limit is retained. All shown variants reported zero HTTP failures.

A preliminary nginx comparison gave ~75k/117k RPS at 16/64 clients, but later nginx samples were ~50k/93k on the shared host. Do not compute a definitive "% nginx" from these non-isolated runs. The 100k/80% goals remain OPEN.

Source modifications

Location Change Preserved behavior / caveat
src/balance/model.rs New vhost default max_idle_per_host: 4 -> 64 Configured values 1..64 remain validated; existing explicitly saved 4 stays 4. Disabled keepalive, 30 s idle timeout, 25k-request and 3,600 s recycling rules are unchanged. Increased idle sockets use more file descriptors and memory.
src/admin/web/vhosts/editor.js Match the new 64 default when the field is absent Existing values, limits and all editor settings remain unchanged.
src/proxy/body.rs At Exchange::drop, use exclusive Mutex::get_mut() rather than lock/unlock; move owned route, client_ip, country, upstream_error into final observation where possible Exactly the same log fields, analytics sample, counters, permit lifetime, errors and websocket/H3 fields. Analytics still gets a clone when a vhost sample is queued; history still receives the original.
src/proxy/headers.rs Skip reserved cookie sanitization immediately if the corresponding Cookie/Set-Cookie header is absent Full sanitization, admin-cookie separation and restoration restrictions still apply when the header is present.
src/balance/tests.rs, src/proxy/body/tests.rs, src/proxy/headers/tests.rs Add compatibility, completion and cookie regression assertions These Rust tests require a compiler before they count as run.
scripts/tests/*hotpath* Update source-only guards for the new bounded default Static assertions do not prove functional or runtime performance equivalence.

Persistence note: old vhosts may store keepalive.max_idle_per_host=4 explicitly; the new code default does not rewrite them. To apply the measured configuration improvement before rebuilding, change that option to 64 in the relevant vhost and verify actual idle socket/FD usage. Where hundreds of origins share a host, test 16 or 32 if the FD/memory trade-off is preferable.

Bottlenecks to profile next, not silently disable

  1. src/cache/dashboard.rs::Cache::record: obtains a shared cache-store mutex on every outcome, including BYPASS when cache is disabled through src/proxy/forward/cache_lookup.rs. This may compete with HOT cache operations and admin dashboard reads; profile pthread_mutex/futex and lock hold times. Candidate: separate bounded, sharded domain/reason counters from the HOT object store while preserving exact dashboard totals, reason labels and reset semantics.
  2. src/logging/format.rs::Logs::push: per-request bounded history try_lock, potentially causing recorded log drops under high concurrency. Preserve existing drops/accounting, live subscribers and external sinks. Candidate: worker-local bounded batches or sharded history with deterministic bounded merge; validate log order, recent history, retention and drops.
  3. src/proxy/body.rs::Exchange::drop + src/traffic.rs::Analytics::push: request-sized log strings, sampling queue sends, atomics and lifecycle observations. Profile allocations and CPU per completed request. Candidate: compact shared immutable identity fields or conditional cloning solely where consumers require ownership, without removing logs or analytics.
  4. src/proxy/forward/route_cache.rs: request-target path copies, header policy and several route/cache eligibility steps. Measure with sampling perf record -g before extracting additional fast paths; preserve query handling, canonical host, ACL, authentication, retries and WebSockets.
  5. src/upstream/*: profile H1 pool misses, TCP connect rate, Hyper connection reuse and generation swaps. Increasing recycle thresholds or changing HTTP/2/H2C modes without an isolated performance case is not recommended.
  6. Separate WWW priority: benchmark short static objects independently; existing sendfile: auto applies to eligible cleartext H1 objects >=64 KiB and cannot accelerate 1 KiB bodies.

No lock removal, tracing/log disabling, caching-policy weakening, bypass of security checks, or transformation of complete bodies into buffered copies is proposed without a test proving semantic equivalence.

Acceptance gate on a machine with Rust toolchain and isolated load generator

./dev.sh doctor
./dev.sh check
CARGO_INCREMENTAL=0 ./dev.sh test
CARGO_INCREMENTAL=0 cargo build --release --locked
python3 scripts/performance/regression.py \
  --binary after="$(realpath target/release/rust-proxy)" \
  --out /tmp/rustproxy034-1kb-regression
python3 scripts/performance/lb_regression.py \
  --binary before=/absolute/path/to/old-rust-proxy \
  --binary after="$(realpath target/release/rust-proxy)" \
  --out /tmp/rustproxy034-1kb-lb
python3 scripts/performance/transport_regression.py \
  --binary before=/absolute/path/to/old-rust-proxy \
  --binary after="$(realpath target/release/rust-proxy)" \
  --out /tmp/rustproxy034-1kb-transport
python3 scripts/performance/benchmark.py \
  --binary before=/absolute/path/to/old-rust-proxy \
  --binary after="$(realpath target/release/rust-proxy)" \
  --nginx /usr/sbin/nginx --wrk /absolute/path/to/wrk \
  --out /tmp/rustproxy034-1kb-benchmark \
  --modes proxy --sizes small --concurrency 16,64,128 \
  --duration 30 --warmup 5 --repeats 7

Use the same explicit max_idle_per_host=64 and all other vhost settings for the source A/B test. Run 4 vs 64 as a separate configuration A/B test; never attribute a settings difference to code. Verify full content checksums, HTTP 2xx/4xx/5xx, p50/p95/p99, drop counters, backend connections, FD/RSS trends, CPU/request and packet/TCP retransmit rates. Test H1/H2/H3, TLS, WebSockets, sticky/rate/ACL, cache MISS/HIT/BYPASS, admin sessions, active failover, cancellation, idle timeouts and a sustained memory/connection soak. Profile the freshly built executable separately from timed RPS runs.

Until compilation and those gates pass, this is a development source-only candidate, not a claim of reaching 100k RPS or 80% nginx.