Files
amethyst/nestsClient
Vitor Pamplona 9fd8cddebf test(nests): one-way latency + 5 % packet-loss path in audio benchmark
Expands `AudioLatencyComparisonTest` from pure inter-arrival jitter
into a real one-way latency measurement against the same
`hang-publish` Rust reference, AND adds a loss-path variant gated
through `udp-loss-shim`.

`hang-publish` (Rust sidecar):
  - new `--log-send-times` flag. When set, the publisher prints
    `SEND frame=<N> send_t_us=<UNIX_EPOCH_MICROS>` to stdout for
    every frame, captured with `SystemTime::now()` immediately
    before `frame.encode(group)`. Lock + flush per line because a
    piped Rust stdout is fully-buffered by default and the JVM-side
    parser needs a live stream. Default off so existing scenarios
    (the I7 reconnect test in particular) keep their stderr-only
    RUST_LOG output untouched.

Kotlin test:
  - `TimestampingOpusEncoder` wraps `JvmOpusEncoder` and stamps an
    `Instant.now()` epoch-micros value per `encode(...)` call into a
    shared map keyed by a monotonic frame counter. encode runs
    immediately before `publisher.send` inside
    `NestMoqLiteBroadcaster`, so the captured timestamp is the
    closest cross-stack anchor we have to "moment the frame entered
    the publisher's outbound buffer" — same anchor the Rust side
    uses.
  - One-way latency = arrival epoch-micros − send epoch-micros per
    matched frame. Both sides read `CLOCK_REALTIME` on linux/macOS,
    so values can be compared directly without a sync handshake when
    the two processes share a host.
  - Frame matching uses `MoqObject.objectId` directly. The listener
    layer already synthesises objectId as a per-SUBSCRIPTION
    monotonic counter, so it equals the publisher's absolute frame
    index; a previous draft of this test did `groupId * 5 + objectId`
    and silently aligned against the wrong frames, producing
    negative latencies.
  - New `under_5pct_packet_loss_pacing_and_one_way_latency` scenario.
    Spawns TWO `udp-loss-shim` instances (one per publisher: the
    shim latches a single client on first datagram, so a shared shim
    would silently swallow the second publisher's traffic), each
    forwarding 1:1 to the moq-relay's UDP port modulo a 5 %
    bidirectional drop rate. The listener stays on the direct path
    so any latency growth comes from publisher-side retransmit /
    ack feedback, not listener-side loss.
  - Both Kotlin and Rust pinned to `FRAMES_PER_GROUP = 5` (Rust's
    default; Kotlin's default is 50). Matched so the per-group
    uni-stream open/close cost is the same on both sides — the
    comparison is about implementation, not about which stack picked
    which group size.

Observed (10 s window, localhost, MacBook Pro M2):

  ===== Audio publisher comparison: clean (loss=0%) =====
  Kotlin speaker     ttf=137 ms  inter-arrival p50/95/99/max=20.17/20.67/20.88/21.16 ms
                                  one-way        p50/95/99/max=80.60/81.31/81.74/83.91 ms
  Rust hang-publish  ttf=325 ms  inter-arrival p50/95/99/max=20.00/21.18/21.49/21.95 ms
                                  one-way        p50/95/99/max=100.32/101.45/102.04/168.58 ms
  ===== Audio publisher comparison: loss-5pct (loss=5%) =====
  Kotlin speaker                  one-way        p50/95/99/max=80.50/81.55/82.31/86.43 ms
  Rust hang-publish               one-way        p50/95/99/max=100.44/119.88/122.16/158.98 ms

  Reading: Kotlin matches Rust on the clean path within ~20 ms
  baseline (and actually sits below it), and absorbs 5 % loss with
  only +0.6 ms p99 growth vs Rust's +20 ms. The 80-100 ms baseline
  is moq-lite's group-buffer floor with 5-frames/group — protocol,
  not stack.

Asserts: each side delivers >= 80 % of expected frames on the clean
path, >= 60 % under loss, and clean-path median inter-arrival sits
in [15, 35] ms. Tail percentiles are printed, not gated.

Gated by `-DnestsHangInterop=true`; both sidecars (hang-publish,
udp-loss-shim) come from `interopBuildSidecars`.

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