From a774748913ba864ffe7a22ce52fd76240f86cfaa Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Thu, 7 May 2026 16:42:34 -0400 Subject: [PATCH] fix(quic-interop): bump multiconnect per-iter timeouts to 30s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 10s HANDSHAKE_TIMEOUT and 60s TRANSFER_TIMEOUT were tuned for single-connection tests against well-behaved peers. multiconnect under 30% packet drop / bit-flip routinely needs three to four PTO rounds just for the handshake — the 10s default hit "handshake_failed" mid- recovery on the unlucky iter. Bump per-iter handshake to 30s and transfer to 30s; 50 iters × ~5s typical = ~250s within the runner's 300s testcase budget, with headroom for the slow-recovery iters. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../quic/interop/runner/InteropClient.kt | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) 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 e580303df..b45ba9479 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 @@ -58,6 +58,22 @@ private const val EXIT_UNSUPPORTED = 127 private const val HANDSHAKE_TIMEOUT_SEC = 10L +// Multiconnect's per-iteration handshake timeout. The runner's +// handshakeloss / handshakecorruption tests run 50 sequential +// connections under 30% packet drop / bit-flip with burst=3, then +// verify the pcap contains exactly 50 distinct handshakes. Under +// that loss intensity, individual handshakes routinely need three or +// four PTO rounds (1s + 2s + 4s + 8s = 15s of doublings) before +// either side's flight finally lands. The 10s default leaves us +// declaring "handshake_failed" mid-recovery on the unlucky iter. +// 30s gives clean PTO headroom while staying under the runner's +// 300s testcase budget for 50 iters (avg ~5s/iter typical, ≤30s +// worst-case before we give up). Same threshold used for the +// 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 + // Multiplexing generates ~hundreds-to-thousands of small files; download // throughput on Mac+Rosetta is dominated by Docker filesystem overhead // per-write. 30s wasn't enough for the larger file counts; the qlog @@ -633,7 +649,7 @@ private fun runMulticonnectTest( driver.start() val handshake = - withTimeoutOrNull(HANDSHAKE_TIMEOUT_SEC * 1_000L) { + withTimeoutOrNull(MULTICONNECT_HANDSHAKE_TIMEOUT_SEC * 1_000L) { runCatching { conn.awaitHandshake() } } if (handshake == null || handshake.isFailure) { @@ -664,7 +680,7 @@ private fun runMulticonnectTest( } val resp = - withTimeoutOrNull(TRANSFER_TIMEOUT_SEC * 1_000L) { + withTimeoutOrNull(MULTICONNECT_TRANSFER_TIMEOUT_SEC * 1_000L) { client.get(authority, url.path) }