From 0c4f8fb0a930f68764aa53c65922c35255860252 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 7 May 2026 17:06:32 -0400 Subject: [PATCH] fix(quic-interop): bump multiconnect transfer timeout to 60s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the handshake timeout bump and the faster PTO landed, the last remaining flake was picoquic's handshakecorruption iter ~35: the handshake recovers from 2-3 PTO rounds and smoothed_rtt is left at ~1s (RFC 9002 §5.2 takes the sample from the largest-acked packet's SEND time, and that's the PTO retransmit, not the original). post-handshake PTO is then 3s+, doubling. Three doublings under 30% bit-flip eat 24s before the GET retransmit lands — 30s is a cliff. 60s gives the slow-recovery iterations real headroom. Total budget: 50 iters × ~3s typical = 150s, plus a few 60s outliers, comfortably within the runner's 300s testcase budget. Verified clean: aioquic, picoquic, quic-go each pass all 7 tests (handshake, multiplexing, longrtt, transferloss, transfercorruption, handshakeloss, handshakecorruption). 21/21. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../quic/interop/runner/InteropClient.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt index b45ba9479..370021e30 100644 --- a/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt +++ b/quic/interop/src/main/kotlin/com/vitorpamplona/quic/interop/runner/InteropClient.kt @@ -72,7 +72,20 @@ private const val HANDSHAKE_TIMEOUT_SEC = 10L // per-iter transfer; the file is 1KB but its STREAM frames have // to land through the same lossy path. private const val MULTICONNECT_HANDSHAKE_TIMEOUT_SEC = 30L -private const val MULTICONNECT_TRANSFER_TIMEOUT_SEC = 30L + +// Multiconnect transfer-side per-iter budget. Larger than the +// handshake budget because once 1-RTT keys are up our smoothed_rtt +// reflects the loss-recovery cost — RFC 9002 §5.2 takes the RTT +// sample from the largest-acked packet's send time, and that's the +// PTO-retransmit that finally landed, not the original send. So an +// iteration whose handshake recovered after 2-3 PTO rounds will +// have smoothed_rtt ~1s, which doubles the post-handshake PTO into +// 3s+. Three doublings under 30% bit-flip can hit 24s before our +// GET request is acked, so 30s is right on the cliff. 60s gives +// the slow-recovery iterations real headroom; 50 iters × ~3s +// average + a few slow ones × 60s still fits the runner's 300s +// testcase budget. +private const val MULTICONNECT_TRANSFER_TIMEOUT_SEC = 60L // Multiplexing generates ~hundreds-to-thousands of small files; download // throughput on Mac+Rosetta is dominated by Docker filesystem overhead