Files
RustProxy/docs/PROXY_RPS_0.34.0.md
T
2026-10-17 23:37:43 +02:00

78 lines
5.7 KiB
Markdown

# 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.zstd` skipped 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 gives `MISS -> HIT` and a transformed representation;
the older Age regression is also reproducible on this old executable.
- Source-only invariants: `scripts/tests/rps_hotpath_034_static_test.py` plus
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`, `rustc` or `wrk`. No post-change RPS gain is claimed.
## Acceptance commands on a Rust-capable Linux host
```bash
./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.