Files
Vitor Pamplona f512478bcc test(nests): sweep packet-loss rates + Linux docker harness for latency bench
`AudioLatencyComparisonTest`:
  - Three new scenarios: 10 %, 20 %, 30 % loss in addition to the
    existing clean and 5 %. The whole benchmark now sweeps a useful
    arc for finding the breakdown point.
  - Frame-count floor now scales with loss instead of a fixed 60 %
    threshold. QUIC streams are reliable so loss surfaces mainly as
    latency, but at high rates the retransmit + congestion-control
    feedback can eat into the 10 s window, so the floor relaxes to
    `max(50, (1 - 3*lossRate) * EXPECTED_FRAMES)`. The 50-frame
    absolute floor (= 1 s of audio) catches a catastrophic publisher
    failure without flapping on the loaded loss-path runs.

Linux benchmark harness (`nestsClient/tests/hang-interop/linux-bench/`):
  - `Dockerfile` for an arm64/amd64 image with JDK 21 + rustc 1.95 +
    cmake + the *-sys-crate build deps (libssl-dev, build-essential,
    pkg-config). Pins rustc 1.95 explicitly so moq-relay v0.10.25's
    Cargo.lock-requested `constant_time_eq@0.4.3` builds cleanly.
  - `run.sh` wrapper that:
    * builds the image,
    * unpacks a filtered tarball of the host repo into the container
      (mac host `nestsClient/build/` and `cargo target/` stay
      untouched),
    * symlinks `nestsClient/build` -> `/persistent-gradle-build` and
      `nestsClient/tests/hang-interop/target` ->
      `/persistent-cargo-target` so successive runs reuse compiled
      artefacts (~3-5 min cached vs ~7-15 min cold),
    * runs the gradle test invocation and surfaces the JUnit XML
      stats to `linux-bench/out/`.

Linux numbers observed (arm64 Docker on a MacBook M2, 10 s window):

  | Loss | Stack  | p50   | p95   | p99   | max   | Frames |
  |------|--------|-------|-------|-------|-------|--------|
  |  0 % | Kotlin |  61.1 |  63.1 |  65.5 |  79.1 |   573  |
  |  0 % | Rust   | 100.7 | 102.7 | 104.9 | 140.2 |   495  |
  |  5 % | Kotlin | 380.9 | 383.4 | 385.7 | 390.5 |   556  |
  |  5 % | Rust   |   0.7 |   7.3 |  23.2 |  84.2 |   500  |
  | 10 % | Kotlin | 120.5 | 122.4 | 124.0 | 132.6 |   568  |
  | 10 % | Rust   |   0.5 |  21.9 |  60.4 |  84.4 |   500  |
  | 20 % | Kotlin | 159.7 | 164.3 | 170.3 | 183.1 |   566  |
  | 20 % | Rust   |   1.6 |  61.3 | 101.9 | 144.9 |   499  |
  | 30 % | Kotlin | 181.0 | 184.2 | 188.1 | 194.0 |   566  |
  | 30 % | Rust   |   1.2 |  80.7 | 105.3 | 140.8 |   491  |

Notes for future readers: Rust's p50 collapsing to ~1 ms under loss
is the SUBSCRIBE-late-arrival signal, not "Rust is 100x faster" — by
the time the SUBSCRIBE lands at the publisher, the in-flight
encoder has moved past those early frames, so the listener starts
receiving in near-real-time. Read p95/p99/max for the per-frame
story (Rust expands 7 -> 22 -> 61 -> 80 ms p95 across loss rates).
Frame delivery is stable for both stacks even at 30 % loss on Linux,
unlike the macOS run which hits a 3.4 s Rust catastrophe at 30 %.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:01:55 -04:00
..