v0.34.0_1
This commit is contained in:
1 parent
9b3d6731cc
commit
00f29f92c8
68 files changed
+2599
-513
No files matched your search
@@ -0,0 +1,216 @@
|
||||
{
|
||||
"kind": "HYPOTHETICAL_AMDAHL_SENSITIVITY_NOT_A_BENCHMARK",
|
||||
"comparison": "Existing 0.33.8 measurements (5 runs, cache off) vs measured nginx",
|
||||
"not_measured": "0.34.0 server performance and sendfile syscall/runtime correctness",
|
||||
"conditions": [
|
||||
"Identical CPU and network assumptions to supplied 0.33.8 baseline",
|
||||
"WWW zero-copy is limited to Linux, cleartext HTTP/1.1, GET >=64KiB, no cache, range, transform, compression or TLS",
|
||||
"WWW low/middle/high: file-copy contributes 20%/40%/60% of total request time; modeled sendfile eliminates 50% of that component",
|
||||
"Reverse proxy low/middle/high: removing response-frame mutex saves a hypothetical 1%/3%/5% of total service time",
|
||||
"These fractions are chosen sensitivity parameters; not measured or inferred from profiling",
|
||||
"No change is assumed for WWW 1040B or out-of-scope protocol paths",
|
||||
"No network saturation, TLS record processing, queueing, origin latency, filesystem cache misses, CPU contention or concurrency effects modeled",
|
||||
"No additional speedup due to cache-retention changes is assumed"
|
||||
],
|
||||
"formula": "RPS_sim = RPS_0.33.8 / (1 - fraction_of_total_service_time_eliminated)",
|
||||
"entries": [
|
||||
{
|
||||
"kind": "reverse_proxy",
|
||||
"bytes": 1040,
|
||||
"baseline_version": "0.33.8",
|
||||
"baseline_rust_rps": 29334.12,
|
||||
"baseline_nginx_rps": 170610.16,
|
||||
"baseline_of_nginx_pct": 17.19,
|
||||
"factor_to_match_nginx": 5.82,
|
||||
"sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false,
|
||||
"scenarios": {
|
||||
"low": {
|
||||
"modeled_total_time_saved_pct": 1.0,
|
||||
"simulated_rps": 29630,
|
||||
"simulated_rps_gain_pct": 1.01,
|
||||
"simulated_of_nginx_pct": 17.37,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 82.63
|
||||
},
|
||||
"middle": {
|
||||
"modeled_total_time_saved_pct": 3.0,
|
||||
"simulated_rps": 30241,
|
||||
"simulated_rps_gain_pct": 3.09,
|
||||
"simulated_of_nginx_pct": 17.73,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 82.27
|
||||
},
|
||||
"high": {
|
||||
"modeled_total_time_saved_pct": 5.0,
|
||||
"simulated_rps": 30878,
|
||||
"simulated_rps_gain_pct": 5.26,
|
||||
"simulated_of_nginx_pct": 18.1,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 81.9
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "reverse_proxy",
|
||||
"bytes": 65536,
|
||||
"baseline_version": "0.33.8",
|
||||
"baseline_rust_rps": 20735.07,
|
||||
"baseline_nginx_rps": 35393.29,
|
||||
"baseline_of_nginx_pct": 58.58,
|
||||
"factor_to_match_nginx": 1.71,
|
||||
"sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false,
|
||||
"scenarios": {
|
||||
"low": {
|
||||
"modeled_total_time_saved_pct": 1.0,
|
||||
"simulated_rps": 20945,
|
||||
"simulated_rps_gain_pct": 1.01,
|
||||
"simulated_of_nginx_pct": 59.18,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 40.82
|
||||
},
|
||||
"middle": {
|
||||
"modeled_total_time_saved_pct": 3.0,
|
||||
"simulated_rps": 21376,
|
||||
"simulated_rps_gain_pct": 3.09,
|
||||
"simulated_of_nginx_pct": 60.4,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 39.6
|
||||
},
|
||||
"high": {
|
||||
"modeled_total_time_saved_pct": 5.0,
|
||||
"simulated_rps": 21826,
|
||||
"simulated_rps_gain_pct": 5.26,
|
||||
"simulated_of_nginx_pct": 61.67,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 38.33
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "reverse_proxy",
|
||||
"bytes": 1048576,
|
||||
"baseline_version": "0.33.8",
|
||||
"baseline_rust_rps": 4461.19,
|
||||
"baseline_nginx_rps": 2730.23,
|
||||
"baseline_of_nginx_pct": 163.4,
|
||||
"factor_to_match_nginx": 0.61,
|
||||
"sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false,
|
||||
"scenarios": {
|
||||
"low": {
|
||||
"modeled_total_time_saved_pct": 1.0,
|
||||
"simulated_rps": 4506,
|
||||
"simulated_rps_gain_pct": 1.01,
|
||||
"simulated_of_nginx_pct": 165.05,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": -65.05
|
||||
},
|
||||
"middle": {
|
||||
"modeled_total_time_saved_pct": 3.0,
|
||||
"simulated_rps": 4599,
|
||||
"simulated_rps_gain_pct": 3.09,
|
||||
"simulated_of_nginx_pct": 168.45,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": -68.45
|
||||
},
|
||||
"high": {
|
||||
"modeled_total_time_saved_pct": 5.0,
|
||||
"simulated_rps": 4696,
|
||||
"simulated_rps_gain_pct": 5.26,
|
||||
"simulated_of_nginx_pct": 172.0,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": -72.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "www",
|
||||
"bytes": 1040,
|
||||
"baseline_version": "0.33.8",
|
||||
"baseline_rust_rps": 24906.2,
|
||||
"baseline_nginx_rps": 351605.13,
|
||||
"baseline_of_nginx_pct": 7.08,
|
||||
"factor_to_match_nginx": 14.12,
|
||||
"sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": false,
|
||||
"scenarios": {
|
||||
"low": {
|
||||
"modeled_total_time_saved_pct": 0.0,
|
||||
"simulated_rps": 24906,
|
||||
"simulated_rps_gain_pct": 0.0,
|
||||
"simulated_of_nginx_pct": 7.08,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 92.92
|
||||
},
|
||||
"middle": {
|
||||
"modeled_total_time_saved_pct": 0.0,
|
||||
"simulated_rps": 24906,
|
||||
"simulated_rps_gain_pct": 0.0,
|
||||
"simulated_of_nginx_pct": 7.08,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 92.92
|
||||
},
|
||||
"high": {
|
||||
"modeled_total_time_saved_pct": 0.0,
|
||||
"simulated_rps": 24906,
|
||||
"simulated_rps_gain_pct": 0.0,
|
||||
"simulated_of_nginx_pct": 7.08,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 92.92
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "www",
|
||||
"bytes": 65536,
|
||||
"baseline_version": "0.33.8",
|
||||
"baseline_rust_rps": 20856.67,
|
||||
"baseline_nginx_rps": 203709.91,
|
||||
"baseline_of_nginx_pct": 10.24,
|
||||
"factor_to_match_nginx": 9.77,
|
||||
"sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": true,
|
||||
"scenarios": {
|
||||
"low": {
|
||||
"modeled_total_time_saved_pct": 10.0,
|
||||
"simulated_rps": 23174,
|
||||
"simulated_rps_gain_pct": 11.11,
|
||||
"simulated_of_nginx_pct": 11.38,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 88.62
|
||||
},
|
||||
"middle": {
|
||||
"modeled_total_time_saved_pct": 20.0,
|
||||
"simulated_rps": 26071,
|
||||
"simulated_rps_gain_pct": 25.0,
|
||||
"simulated_of_nginx_pct": 12.8,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 87.2
|
||||
},
|
||||
"high": {
|
||||
"modeled_total_time_saved_pct": 30.0,
|
||||
"simulated_rps": 29795,
|
||||
"simulated_rps_gain_pct": 42.86,
|
||||
"simulated_of_nginx_pct": 14.63,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 85.37
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "www",
|
||||
"bytes": 1048576,
|
||||
"baseline_version": "0.33.8",
|
||||
"baseline_rust_rps": 2833.71,
|
||||
"baseline_nginx_rps": 12611.49,
|
||||
"baseline_of_nginx_pct": 22.47,
|
||||
"factor_to_match_nginx": 4.45,
|
||||
"sendfile_eligible_only_if_plain_linux_http1_1_no_cache_no_compression": true,
|
||||
"scenarios": {
|
||||
"low": {
|
||||
"modeled_total_time_saved_pct": 10.0,
|
||||
"simulated_rps": 3149,
|
||||
"simulated_rps_gain_pct": 11.11,
|
||||
"simulated_of_nginx_pct": 24.97,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 75.03
|
||||
},
|
||||
"middle": {
|
||||
"modeled_total_time_saved_pct": 20.0,
|
||||
"simulated_rps": 3542,
|
||||
"simulated_rps_gain_pct": 25.0,
|
||||
"simulated_of_nginx_pct": 28.09,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 71.91
|
||||
},
|
||||
"high": {
|
||||
"modeled_total_time_saved_pct": 30.0,
|
||||
"simulated_rps": 4048,
|
||||
"simulated_rps_gain_pct": 42.86,
|
||||
"simulated_of_nginx_pct": 32.1,
|
||||
"simulated_remaining_gap_to_nginx_pct_points": 67.9
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
# RustProxy 0.34.0 — reverse-proxy hot-path candidate
|
||||
|
||||
## What was changed
|
||||
|
||||
1. **Routes:** `Snapshot.routes` and `fallbacks` now hold
|
||||
`Arc<CompiledRoute>` handles. The same compiled location is shared across
|
||||
hostname aliases. `observe` resolves vhost/location once from the accepted
|
||||
listener/TLS-state and stores a request-local handle. `forward_request`
|
||||
consumes the same immutable snapshot/route; internal error fallbacks still
|
||||
resolve the explicitly configured named location independently.
|
||||
2. **Host matching:** wildcard candidate strings are generated lazily only
|
||||
after an exact host failed, without changing longest-specific-suffix order.
|
||||
3. **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.
|
||||
4. **Health:** a `healthy_cached` atomic 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 immutable
|
||||
`Arc` handles so a new request lease does not deep-clone health paths,
|
||||
headers or optional host strings.
|
||||
5. **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.
|
||||
6. **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:
|
||||
|
||||
```bash
|
||||
./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**.
|
||||
@@ -42,6 +42,7 @@ This is the canonical index for the current project documentation. The authentic
|
||||
- [MOCK_SERVER](MOCK_SERVER.md) - local Python mock origin used by development/integration checks.
|
||||
- [MOCK_AUTH](MOCK_AUTH.md) - local fake Tinyauth peer for development only.
|
||||
- [MEMORY_STRESS](MEMORY_STRESS.md) - sustained-memory audit, offline load simulations, real RustProxy staging stress and RSS measurement procedure.
|
||||
- [TEST_REPORT_0.34.0](TEST_REPORT_0.34.0.md) - 0.34.0 source-stage test evidence, simulated cache retention and required acceptance gates.
|
||||
|
||||
## Technical documentation
|
||||
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
# RustProxy 0.34.0 verification report
|
||||
|
||||
## Acceptance status
|
||||
|
||||
**Source candidate, not verified production release.** Rust (`cargo`, `rustc`) is
|
||||
not installed in this execution environment, and the runtime has no access to
|
||||
its dependency registry. Consequently the modified Rust source was **not
|
||||
compiled**, the new Rust tests were **not executed**, and no 0.34.0 RustProxy
|
||||
binary or live throughput figures are supplied. Source inspection and simulated
|
||||
UI/cache tests do not close the runtime acceptance gate.
|
||||
|
||||
## Later 0.34.0 reverse-proxy optimization revision
|
||||
|
||||
The refreshed source archive additionally reuses immutable route handles,
|
||||
limits lazy host wildcard allocation, fast-paths one-backend non-cookie LB,
|
||||
atomically caches healthy state and exchange scalar log fields, shares
|
||||
compiled per-member health policies across leases, and avoids
|
||||
unneeded fallback/retry header clones and empty-subscriber broadcast clones.
|
||||
The existing cache/sendfile work and all panel controls remain. Added Rust
|
||||
regression cases and the source-only hot-path guard are **not a substitute**
|
||||
for compilation and live HTTP, health, capacity, sticky, and H2 retry tests.
|
||||
No RPS improvement, corrected nginx gap, or full functional parity has been
|
||||
measured for this new revision. Details: `docs/PROXY_HOTPATH_0.34.0.md`.
|
||||
|
||||
## Changes and correctness boundaries
|
||||
|
||||
- Operational per-vhost saves (upstream keepalive/recycling, connection limits,
|
||||
load-balancing/health policy, timeouts, request gate and diagnostics) retain
|
||||
HOT and COLD entries. Backend origin, routing, content, cache and transform
|
||||
changes retain conservative invalidation. Vhost-specific changes purge only
|
||||
that vhost; global cache, transform, forwarding, headers and Web Service or
|
||||
PHP-FPM profile changes may conservatively purge all tiers.
|
||||
- Scope-specific cold-cache records persist an origin/content compatibility
|
||||
fingerprint. COLD startup rejects stale scope records and deletes their cache
|
||||
artifacts. Legacy COLD metadata defaults its fingerprint to zero; production
|
||||
vhosts have a nonzero SHA-derived fingerprint. A one-time cold refill can
|
||||
occur on the 0.33.8 -> 0.34.0 upgrade because the global identity changed.
|
||||
- A separate administrative configuration revision acts as a cache-ticket
|
||||
publication barrier: a request bound to a pre-save routing snapshot cannot
|
||||
create a new cache ticket after the save. The data revision stays stable for
|
||||
operational-only saves, so previously cached objects remain available.
|
||||
- Reverse-proxy response-body byte metering captures the backend lease once at
|
||||
wrapper creation instead of acquiring a backend-selection mutex per DATA
|
||||
frame. Request-body accounting, error paths and exchange ownership remain.
|
||||
- An **opt-in `sendfile: auto`** setting is wired through Web Service serde,
|
||||
compiled config, the Delivery UI and documented API. The Linux HTTP/1.1
|
||||
TCP transport adapter calls `libc::sendfile` for eligible static files;
|
||||
cache-enabled, ranged, compressed, transformed and TLS/H2/H3 paths retain
|
||||
existing streaming. It is **disabled by default** and must pass native
|
||||
compilation, correctness, transport and load tests before use. No persisted
|
||||
SQLite schema migration or LB UI change is required.
|
||||
|
||||
## Local verification
|
||||
|
||||
| Check | Outcome | Coverage / caveat |
|
||||
|---|---|---|
|
||||
| `./dev.sh doctor` | PASS | Project source layout and tool inventory |
|
||||
| SQL migration/preset test | PASS | All ten migrations re-applied, preset access and limits |
|
||||
| Upgrade test | PASS | Stop/lock guards, SQLite/.env/language preservation, symlink rejection |
|
||||
| Python compileall | PASS | Python syntax only |
|
||||
| Vhost LB/Cache Chromium test | PASS | Simulated browser quick-view, 320/375/768/1440px |
|
||||
| Other Chromium scripts | 15 PASS | Simulated API/CSS/JS, not a live Rust server |
|
||||
| Sidebar navigation Chromium test | INCOMPLETE | Execution time budget; not counted as a pass |
|
||||
| Three Node dashboard scripts | PASS | Simulated UI logic |
|
||||
| `./dev.sh check` and `./dev.sh test` | BLOCKED | `cargo`/`rustc` unavailable |
|
||||
| Rust unit/integration tests (including new scoped COLD regression) | NOT RUN | Requires Rust toolchain |
|
||||
| Live HTTP/protocol/LB regressions | NOT RUN | Requires built 0.34.0 binary |
|
||||
| 0.33.8 / 0.34.0 / nginx paired RPS benchmarks | NOT RUN | No verified 0.34.0 binary |
|
||||
| Zero-copy sendfile WWW setting | SOURCE IMPLEMENTED / NOT RUNTIME VERIFIED | Linux plain H1 static GET only, `off` by default |
|
||||
| Sendfile editor Chromium save/reload | PASS | 375/1440px, simulated admin API, off -> auto -> off |
|
||||
| Sendfile Rust adapter + fallback test code | ADDED / NOT RUN | TCP marker replacement, persistent socket, HEAD, range, conditional; compiler unavailable |
|
||||
| Amdahl sendfile/nginx sensitivity study | PASS MODEL | No runtime evidence for simulated improvements |
|
||||
|
||||
The 0.33.8 report in the supplied evidence archive recorded **706 passing
|
||||
Rust tests**, **152 HTTP assertions**, **111 transport assertions** and
|
||||
**17 LB assertions**. Those are *prior-version* results and are **not evidence
|
||||
that 0.34.0 passes**. This report never transfers previous pass counts.
|
||||
|
||||
## Synthetic cache-retention scenario (not an RPS measurement)
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python3 scripts/performance/simulate_cache_034.py
|
||||
```
|
||||
|
||||
Assume 20 vhosts, 100 HOT + 100 COLD objects each, all public/cacheable with
|
||||
infinite TTL and adequate cache capacity; 1,000 configuration saves (800
|
||||
operational-only, 200 content changes to one vhost), followed by 500 uniformly
|
||||
random GETs per save. A miss immediately reloads an object into its original
|
||||
tier. No networking, filesystem, CPU timing, origin delay or concurrency is
|
||||
modeled. Results with seed 3400:
|
||||
|
||||
| Synthetic metric | Old global purge | 0.34.0 scoped retention |
|
||||
|---|---:|---:|
|
||||
| Object invalidations | 473,540 | 37,300 |
|
||||
| GET misses | 470,006 | 37,069 |
|
||||
| GET hits | 29,994 | 462,931 |
|
||||
| HIT ratio | 5.999% | 92.586% |
|
||||
|
||||
The modeled change yields **92.123% fewer invalidations**, **92.113% fewer
|
||||
misses**, and **+86.587 percentage points** of synthetic cache HIT ratio.
|
||||
**Measured 0.34.0 requests/s gain: unknown.** These values must not be
|
||||
presented as a real speedup or a projection against nginx.
|
||||
|
||||
## Previously measured comparison (0.33.8, not this candidate)
|
||||
|
||||
From the attached 0.33.8 evidence, five paired benchmark runs with cache OFF,
|
||||
median RPS:
|
||||
|
||||
| Mode / payload | 0.33.8 RPS | nginx RPS | 0.33.8 / nginx |
|
||||
|---|---:|---:|---:|
|
||||
| Reverse proxy 1,040 B | 29,334.12 | 170,610.16 | 17.19% |
|
||||
| Reverse proxy 64 KiB | 20,735.07 | 35,393.29 | 58.58% |
|
||||
| Reverse proxy 1 MiB | 4,461.19 | 2,730.23 | 163.40% |
|
||||
| WWW 1,040 B | 24,906.20 | 351,605.13 | 7.08% |
|
||||
| WWW 64 KiB | 20,856.67 | 203,709.91 | 10.24% |
|
||||
| WWW 1 MiB | 2,833.71 | 12,611.49 | 22.47% |
|
||||
|
||||
The 0.33.8 1 MiB proxy path was **2.77% slower than rebuilt 0.33.7**.
|
||||
Removing a per-response-frame mutex is a performance candidate but is **not**
|
||||
a measured correction of that regression. No throughput gains are attributed
|
||||
here to the new source changes.
|
||||
|
||||
## Hypothetical 0.34.0/nginx gap sensitivity (NOT MEASURED)
|
||||
|
||||
Run `python3 scripts/performance/simulate_nginx_gap_034.py`. Using the **real
|
||||
0.33.8 measured medians** above, assume zero-copy removes 50% of the
|
||||
read/copy part of per-request cost; that part hypothetically accounts for
|
||||
20/40/60% of *total* cost for eligible static files. Model **1/3/5%** total
|
||||
service-time savings from removed proxy-frame mutexes, independently.
|
||||
Amdahl: `RPS_sim = RPS_0.33.8 / (1 - hypothetical_fraction_saved)`. The
|
||||
result is a sensitivity calculation, **not a prediction or 0.34.0 benchmark**.
|
||||
|
||||
| Workload | Measured 0.33.8/nginx | Modeled low | Modeled middle | Modeled high |
|
||||
|---|---:|---:|---:|---:|
|
||||
| Proxy 1040 B | 17.19% | 17.37% | 17.73% | 18.10% |
|
||||
| Proxy 64 KiB | 58.58% | 59.18% | 60.40% | 61.67% |
|
||||
| Proxy 1 MiB | 163.40% | 165.05% | 168.45% | 172.00% |
|
||||
| WWW 1040 B | 7.08% | 7.08% | 7.08% | 7.08% |
|
||||
| WWW 64 KiB | 10.24% | 11.38% | 12.80% | 14.63% |
|
||||
| WWW 1 MiB | 22.47% | 24.97% | 28.09% | 32.10% |
|
||||
|
||||
These ratios apply only to eligible Linux plain HTTP/1.1 static workloads
|
||||
with cache OFF. TLS, HTTP/2, HTTP/3, ranged, compressed and object-cached
|
||||
responses are **not** accelerated by this bridge. Bandwidth, contention,
|
||||
queueing, realistic filesystem and actual CPU profiles are not modeled.
|
||||
Even the favorable modeled 1 MiB case remains ~3.1x slower than nginx WWW;
|
||||
small WWW remains ~14.1x slower. Proxy 1 MiB already exceeds nginx in the
|
||||
**prior baseline**, not as a demonstrated new 0.34.0 improvement.
|
||||
|
||||
## Required rollout gates
|
||||
|
||||
1. On a trusted Linux build worker with the pinned Rust toolchain, run
|
||||
`./dev.sh check`, `CARGO_INCREMENTAL=0 ./dev.sh test`, and a locked release
|
||||
build. Resolve any compiler/test issues before a deployment.
|
||||
2. Follow `scripts/performance/README.md` to run `regression.py`,
|
||||
`transport_regression.py`, `lb_regression.py`, and
|
||||
`concurrency_regression.py` against verified 0.33.8 and 0.34.0 binaries.
|
||||
3. Exercise administration saves during simultaneous requests: save keepalive
|
||||
values and confirm unchanged HOT/COLD entries and hashes; change one backend
|
||||
origin and confirm only its scope is purged, both before/after restart. Check
|
||||
that old snapshots cannot recreate invalidated objects.
|
||||
4. Run the five-repeat, 15-second paired benchmark against 0.33.8, 0.34.0 and
|
||||
nginx on the *same host and affinity*, including large proxy responses, CPU,
|
||||
RSS, resource limits and response correctness.
|
||||
5. Compile the source-level Linux `sendfile` bridge and validate on a running
|
||||
RustProxy process: plain HTTP/1 sendfile path, headers/body exactness,
|
||||
keepalive/pipelining, slow clients, cancellation, HEAD, range, cache,
|
||||
compression, TLS/H2/H3 fallback, symlink security and observability. Do
|
||||
**not** activate Auto on production before that gate passes.
|
||||
6. Complete a mixed-traffic soak and staged rollback rehearsal. Until then,
|
||||
do not describe 0.34.0 as production-accepted.
|
||||
+34
-1
@@ -99,12 +99,45 @@ The Web Service editor is split into stable configuration tabs so later features
|
||||
- **.htaccess** - bounded migration-oriented Apache compatibility, recursive scan and strict-mode behavior.
|
||||
- **MIME** - per-extension MIME overrides and fallback MIME type.
|
||||
- **Cache & headers** - default `Cache-Control`, ordered cache rules and fixed response headers.
|
||||
- **Delivery** - precompressed `.br`/`.gz` sidecars, forced-download extensions and Range/streaming information.
|
||||
- **Delivery** - opt-in Linux `sendfile`, precompressed `.br`/`.gz` sidecars, forced-download extensions and Range/streaming information.
|
||||
- **PHP-FPM** - attach a shared PHP-FPM profile through the selector, open the profile-management modal to expand/edit/create profiles, or choose local configuration for all FastCGI tuning options.
|
||||
- **Security** - symlink policy, request/response hardening summary and filesystem-confinement notes.
|
||||
|
||||
After the Web Service is saved, attach it under **Vhosts** by selecting `Web Server` and the service ID for the main source or a custom location.
|
||||
|
||||
### Optional native Linux sendfile (0.34.0 source candidate)
|
||||
|
||||
Set **Web Server -> Edit service -> Delivery -> Linux sendfile** to **Auto**
|
||||
(or `web_services[].sendfile: "auto"`). The default is `"off"` for full
|
||||
backward compatibility. This option changes only transport; switching it
|
||||
never invalidates the HOT or COLD object cache. No database migration is needed.
|
||||
|
||||
**Platforms other than Linux (FreeBSD, OpenBSD, NetBSD, DragonFly BSD, macOS and Windows):** `sendfile: "auto"` is accepted as configuration but does **not** activate kernel zero-copy in RustProxy 0.34.0. The Linux-specific bridge is compiled only on Linux; on other targets ordinary Tokio `ReaderStream`/Hyper streaming serves the exact file content, including HEAD, ranges, validators, PHP and existing authorization/cache policies. `"off"` and `"auto"` behave the same on those targets. This describes the `sendfile` feature only and is **not** a claim that the entire application currently passes a cross-platform compile/test suite. FreeBSD/macOS native `sendfile` and Windows `TransmitFile` require separate implementations and runtime tests; Linux `libc::sendfile` cannot be reused directly.
|
||||
|
||||
The kernel `sendfile(2)` bridge is selected **only** if all conditions hold:
|
||||
|
||||
- Linux, plain TCP **HTTP/1.1** (no TLS, HTTP/2, HTTP/3 or QUIC);
|
||||
- static **GET** of an already-constrained file, full **200 OK** response;
|
||||
- file at least **64 KiB**, exact `Content-Length`, no `Range`/`If-Range`;
|
||||
- no negotiated compression/precompressed variant, response transform or
|
||||
object cache participation, and no rewritten content/encoding headers.
|
||||
|
||||
The file descriptor is duplicated from the already-authorized confined open
|
||||
file (not re-resolved by path), then attached to a bounded HTTP/1.1 TCP
|
||||
connection bridge. The bridge sends the physical file bytes using Linux
|
||||
`sendfile(2)` and never sends the internal marker bytes to the client. If
|
||||
conditions are not met, RustProxy uses the existing stream/counters unchanged.
|
||||
Only new cleartext connections pick up a newly enabled adapter; this option
|
||||
does not change the listener protocol configuration.
|
||||
|
||||
**Release acceptance:** This source candidate has not been compiled or
|
||||
tested against a running 0.34.0 RustProxy binary in the current environment.
|
||||
The included Rust unit/integration test and real-network test cases must pass
|
||||
on a Linux build worker before enabling in production. Sendfile cannot by
|
||||
itself close the small-response gap to nginx. See
|
||||
`docs/TEST_REPORT_0.34.0.md` and
|
||||
`scripts/performance/simulate_nginx_gap_034.py`.
|
||||
|
||||
### Intercept application HTTP errors (0.29.7)
|
||||
|
||||
Open **Vhosts → Edit vhost → Pages** (not the Web Service PHP tab), enable
|
||||
|
||||
Reference in new issue
Block a user