4.6 KiB
RustProxy 0.34.0 — reverse-proxy hot-path candidate
What was changed
- Routes:
Snapshot.routesandfallbacksnow holdArc<CompiledRoute>handles. The same compiled location is shared across hostname aliases.observeresolves vhost/location once from the accepted listener/TLS-state and stores a request-local handle.forward_requestconsumes the same immutable snapshot/route; internal error fallbacks still resolve the explicitly configured named location independently. - Host matching: wildcard candidate strings are generated lazily only after an exact host failed, without changing longest-specific-suffix order.
- LB: single-backend non-cookie pools use a direct admitted lease without
temporary candidate vectors or scheduler mutex. Multi-backend weighted
round-robin retains its ordering and mutex; least-connection/traffic,
adaptive, IP hash and cookie-affinity retain their behavior. IP hash avoids
repeated canonicalization/temporary
format!strings for each candidate. - Health: a
healthy_cachedatomic allows checking healthy backends without the health mutex; all state transitions remain inside the existing state machine, including passive quarantine and half-open recovery. Resolved active/passive per-member policies are held in shared immutableArchandles so a new request lease does not deep-clone health paths, headers or optional host strings. - Fallback/retry: request body+HeaderMap replay templates are made only for configured error fallbacks. Retry headers are cloned only for eligible safe replay requests where another member or H2 graceful close makes retry possible; writes, streaming bodies and WebSockets are not replayed.
- Telemetry: three per-request mutex-backed scalar tags are now atomic;
their original event strings and
Option<bool>log fields are decoded at exchange completion. Request ID, method, host and path are moved into the terminal log record instead of deep-cloned in Drop; the traffic sample still receives its own required copies. Logs avoid a broadcast clone if there are no subscribers, preserving history, sink and counters.
Preserved boundaries
All vhost, ACL, GeoIP, Basic Auth, Tinyauth, WebSocket, retry, cache,
transform, compression, log, metrics, sendfile, PHP-FPM, HTTPS/H2/H3 and
admin panel configuration paths are intended to remain compatible. Defaults
and SQLite schema are unchanged; no new performance toggle was added. The
optimization reuses route objects within one immutable snapshot but does not
allow stale snapshot cache fills (existing revision ticket barrier remains).
Sticky cookies deliberately retain the general selection path, and upstream
request-count recycling is unchanged (default 25,000 per generation).
Locally executed checks vs required checks
- Executed locally: scoped-cache/sendfile/hot-path source-only guards, Python syntax, SQLite migration and upgrade preservation tests, selected JS panel scripts and simulated editor tests. These do not prove Rust builds.
- Prepared, NOT executed: Rust unit tests for shared routing, single-member LB capacity/health, passive ejection/recovery, atomic log fields, retained sendfile, cache and protocol behavior.
- Unmeasured: 0.34.0 requests/sec, latency, CPU cost, memory and nginx gap. Do not infer a throughput gain from fewer source-code locks/allocations.
Build and acceptance
Run on the intended Linux builder with exactly the same compiler, dependencies, release flags and workload as the 0.33.8 reference:
./dev.sh doctor
./dev.sh check
CARGO_INCREMENTAL=0 ./dev.sh test
CARGO_INCREMENTAL=0 cargo build --release --locked
python3 scripts/tests/proxy_hotpath_034_static_test.py
python3 scripts/tests/cache_scope_034_static_test.py
python3 scripts/tests/sendfile_034_static_test.py
python3 scripts/tests/dashboard_presets_sql_test.py
python3 scripts/tests/upgrade_performance_test.py
Next run scripts/performance/regression.py, lb_regression.py,
transport_regression.py and concurrency_regression.py against both binary
versions. After functional acceptance, run the paired
scripts/performance/benchmark.py suite against the unmodified baseline,
this candidate and nginx; use the instructions in
scripts/performance/README.md including separate CPU affinities.
Measure RPS/p95/p99, CPU microseconds per request, RSS, FDs, pool rotations,
health transitions, cache HIT ratio and logger drops. For each payload report
(0.34.0 RPS / 0.33.8 RPS - 1)*100 and 0.34.0 RPS/nginx RPS*100 from real
medians. A source-only revision is not production-accepted.