5.7 KiB
RustProxy 0.34.0: hot-path throughput revision
Status: uncompiled source candidate. This revision does not change the application version, public API, dashboard controls, cache policy or tuning defaults. It must not be labelled production-ready until the new Rust binary passes compile, unit tests, live regressions and paired performance comparisons.
Code changes and intended cost reductions
| Focus | Change | Kept behavior / risk to verify |
|---|---|---|
| Per-request identity | Draw a 256-bit nonce from the OS-seeded/reseeded thread-local CSPRNG instead of asking getrandom on every request. Preserve SHA-256 of nonce+monotonic sequence and the existing 64-hex ID format. |
IDs remain non-predictable. Check ID uniqueness and raw upstream headers. |
| Upstream HTTP request target | Compile the validated scheme/authority http::Uri once with each immutable backend; parse only the request path+query and rebuild using typed Uri::from_parts. |
Never accept a client-supplied authority, bypass DNS/address policies or change SNI. Test H1, H2, IPv6, query and percent-encoding. |
| Load balancing | Do not clone the member ID string for an existing smooth-WRR scoreboard entry. Continue the previously introduced no-mutex single-member non-cookie path. | Sticky-cookie modes, capacity limits, retry exclusions, health and drain remain unchanged. |
| Active health | Use already-compiled Arc<HealthCheck> when dispatching probes, avoiding a clone of its path/headers for each scheduled probe. |
Same 250-ms supervisor, timeout, enabled/claim gate and health state transitions. |
| Per-frame streaming | Capture the leased backend once at first streamed request DATA (response already captured at wrapper construction). Do not take Exchange.backend mutex on every upload chunk. |
Request/response byte counters, mid-stream failures, idle timeouts and lease lifetime remain active. |
| Pool recycling | Preserve exact request-count and elapsed-time recycling checks per request, with the existing independent 250-ms maintenance cycle. The pool factory/connection policy is unchanged. | Exact lifecycle semantics are more important than saving one cheap clock read; tune idle/age limits only after profiling. |
| Log history | Move completed LogEntry directly into bounded history when there are no live viewers or enabled external sinks, instead of deeply cloning its strings. Continue cloning and delivering to live viewers/sinks when enabled. |
Same JSON fields, history size, drop counters, file/Loki/Logstash forwarding. |
| Route/HTTP instrumentation | Remove redundant route-ID mutex/write in forwarding and collapse H1/H2 version tallying into one branch. | Route is recorded once before forwarding; H1/H2 counters remain. |
These changes build on the previous 0.34.0 route-Arc, single-backend LB,
conditional retry/fallback-header cloning, atomic exchange flags and cache HOT
LRU changes. There are no new UI toggles. Pool defaults remain max_idle_per_host
4 and pool_recycle_requests 25,000; tune these only in separately labelled
trials. sendfile: auto remains opt-in and applies only to eligible Linux
plaintext H1 static WWW responses, not reverse proxy or TLS/H2/H3.
Cache transform/query policy
An enabled bypass_query is a deliberate policy: /theme.css?v=... is a
BYPASS even when the origin sends Cache-Control: public. Turning the bypass
off permits a transformed CSS object to follow MISS -> HIT and appear in the
cache dashboard. An existing transformed HOT row with hit count and TTL is
positive evidence of this. This throughput revision does not modify the
cache eligibility logic or discard Age/ETag/Cache-Control headers.
Verification already possible here
- Old/reproducible reference executable only: 110 passed, 0 failed, 1 skipped
(
compression.codec.zstdskipped because the optional decoder was missing), from live proxy/policy/cache/transform loopback regression. This does not exercise the newly modified Rust source. - Old/reproducible reference executable only: query bypass enabled gives
BYPASS -> BYPASS, disabled givesMISS -> HITand a transformed representation; the older Age regression is also reproducible on this old executable. - Source-only invariants:
scripts/tests/rps_hotpath_034_static_test.pyplus existing route/cache/sendfile checks. SQL migration and upgrade scripts pass. - Not performed: Rust compilation or newly added Rust unit tests, live
behavior of the optimized binary, HTTP/2 live TLS, comparative RPS, CPU
profiling and long-duration memory/health soak. The environment does not
include
cargo,rustcorwrk. No post-change RPS gain is claimed.
Acceptance commands on a Rust-capable Linux host
./dev.sh check
CARGO_INCREMENTAL=0 ./dev.sh test
CARGO_INCREMENTAL=0 cargo build --release --locked
python3 scripts/tests/rps_hotpath_034_static_test.py
python3 scripts/tests/cache_age_transform_034_live.py \
--binary "$(realpath target/release/rust-proxy)" \
--output /tmp/rustproxy034-cache-after
python3 scripts/performance/regression.py \
--binary optimized="$(realpath target/release/rust-proxy)" \
--out /tmp/rustproxy034-http-regression
Run scripts/performance/benchmark.py from the documented reproducible
before/after/nginx command in scripts/performance/README.md. Compare the
unchanged old 0.34.0 binary with the newly compiled source, not a baseline
with different feature flags. Record RPS at 1,040 B, 64 KiB, 1 MiB, p95/p99,
CPU/request, RSS, connection counts, errors and exact response-body hashes.
Use perf record -g -p <proxy-pid> during a stable request interval and inspect
perf report --stdio to identify the next true bottleneck.
Do not interpret Python origin self-tests, source inspections, or speculative Amdahl models as measured RustProxy throughput.