From 1593b6589266b3010ebe2193021992b8c706f60d Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 10 Oct 2026 05:37:11 -0700 Subject: [PATCH 1/2] deps: bump moq-noq stack to 2.0.4 Carries moq-dev/noq#32 (reset the PTO backoff on discarding Initial or Handshake keys) and moq-dev/noq#34 (TransportConfig::handshake_idle_timeout, default 10s), which fix the impaired drills' handshake timeouts. Deletes the impaired-handshake quest. Co-Authored-By: Claude Opus 5.5 --- Cargo.lock | 16 +++---- Cargo.toml | 4 +- quest/m1/test-flakes-2/README.md | 1 - quest/m1/test-flakes-2/impaired-handshake.md | 47 -------------------- 4 files changed, 10 insertions(+), 58 deletions(-) delete mode 100644 quest/m1/test-flakes-2/impaired-handshake.md diff --git a/Cargo.lock b/Cargo.lock index 05b9e2c38a..a85eed2632 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4638,9 +4638,9 @@ version = "0.0.0" [[package]] name = "moq-noq" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9f6a4bff164ee93937c76528b1183eb03a89158d4ffad9978ce02f92db18b42" +checksum = "6bbf0af241543ba9abfbee6c436eeae5367efde3037c9c4982aec2567d74b92b" dependencies = [ "bytes", "cfg_aliases", @@ -4660,9 +4660,9 @@ dependencies = [ [[package]] name = "moq-noq-proto" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fe577f261bf4d7f84c35f81028db0d26d47f17c45c3df6a012504c1773c6a2" +checksum = "102495e6fc86b36d47e75947bd136f0e60d0d360cf15fc3b9712b60cb3c979a2" dependencies = [ "aes-gcm", "aws-lc-rs", @@ -4691,9 +4691,9 @@ dependencies = [ [[package]] name = "moq-noq-udp" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a7c34996db77a509617f12db2d735ad5ca013e2d01293e0339afac331f17a7" +checksum = "589717ed483c3b74a5fdb628139191b9a1e1b92877847a0ac9f3ff44df7bbd4e" dependencies = [ "cfg_aliases", "libc", @@ -10136,9 +10136,9 @@ dependencies = [ [[package]] name = "web-transport-moq" -version = "2.0.2" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c84f87ce8e1cfaa50e9f0dfe0f6e8de31c9905388d68ca7cde984375ffc4891" +checksum = "60d31032c966e67089b5369f9aecf925ef7d0f8a155530da7dc355bc930b097d" dependencies = [ "bytes", "futures", diff --git a/Cargo.toml b/Cargo.toml index 34a52f856a..0f6ed09524 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,7 +154,7 @@ moq-mux = { version = "0.10.11", path = "rs/moq-mux" } moq-net = { version = "0.3.10", path = "rs/moq-net" } # The MoQ fork of noq (moq-dev/noq). iroh keeps upstream noq, so a build with the # iroh feature carries both stacks. -moq-noq-proto = { version = "2.0.2", default-features = false } +moq-noq-proto = { version = "2.0.4", default-features = false } # NVENC bindings, forked from ViliamVadocz/nvidia-video-codec-sdk to dlopen the # driver at runtime. Compiles on any platform (macOS included) but only actually # used by moq-video on Linux. @@ -238,7 +238,7 @@ web-transport-iroh = "0.8.1" # default-features off so the QUIC crypto provider is chosen by moq-tokio's # aws-lc-rs / ring features. # The WebTransport adapter released with moq-noq, from the same repository. -web-transport-moq = { version = "2.0.2", default-features = false } +web-transport-moq = { version = "2.0.4", default-features = false } web-transport-proto = "0.6" web-transport-trait = "0.5" web-transport-wasm = "0.7" diff --git a/quest/m1/test-flakes-2/README.md b/quest/m1/test-flakes-2/README.md index fae2e659a5..7ff03bda58 100644 --- a/quest/m1/test-flakes-2/README.md +++ b/quest/m1/test-flakes-2/README.md @@ -32,7 +32,6 @@ Public API: none. Wire: none. ## Required -- [Impaired handshake](/quest/m1/test-flakes-2/impaired-handshake.md) - the impaired drills' clients never time out while connecting - [Interop close code](/quest/m1/test-flakes-2/interop-close-code.md) - the browser close-code test reliably sees `unauthorized` for a refused session - [TS duration fidelity](/quest/m1/test-flakes-2/ts-duration-fidelity.md) - TS compliance captures the whole round-tripped stream - [Cluster burst overrun](/quest/m1/test-flakes-2/cluster-burst-overrun.md) - the impaired cluster burst drill always overruns its bottleneck, so it always exercises it diff --git a/quest/m1/test-flakes-2/impaired-handshake.md b/quest/m1/test-flakes-2/impaired-handshake.md deleted file mode 100644 index 7276528e0a..0000000000 --- a/quest/m1/test-flakes-2/impaired-handshake.md +++ /dev/null @@ -1,47 +0,0 @@ -# [XS] Impaired drill clients connect reliably - -## Goal - -The impaired drills (`bursts_cross_a_cluster::impaired`, -`bursts_cross_a_flapping_peer::impaired`, and -`interrupted_publisher_republishes_new_content::impaired` in -`rs/moq-relay/tests/drills.rs`) never fail while connecting their publisher or -subscriber. - -## Plan - -Both causes are fixed in the QUIC stack, in `moq-quic` (#5163) and in -moq-dev/noq ([#32](https://github.com/moq-dev/noq/pull/32) and -[#34](https://github.com/moq-dev/noq/pull/34), stacked). The drills still run -on `moq-noq`, and [the switch](/quest/m1/quic/fork/switch.md) is not started, -so what remains is to bump the `moq-noq-proto` pin to a release carrying both -noq PRs. Then loop the impaired drills under load (four to eight in parallel, -a few hundred runs each) and confirm that no connect fails. - -The causes, from qlogs of failing runs (2026-10-09). Both are the drills' 2s -client idle timeout meeting a backed-off PTO during the handshake. - -1. **A lost Finished waited on the Initial backoff** (`reset by peer`). - RFC 9002 A.11 resets `pto_count` when Initial or Handshake keys are - discarded; noq and quinn did not. The server, which cannot read 1-RTT - before the Finished, idled out first. Fix: reset `pto_count` in - `discard_space`. -2. **Two Initial flights per idle window** (`timed out`). The idle timer was - armed at `max(2s, 3 * 999ms)` = 2997ms, and the second probe is due at - 2997ms plus timer slop. Decided 2026-10-09: add a separate - `handshake_idle_timeout` (default 10s, as msquic does) that governs until - the handshake completes. Dead-peer detection on established connections is - unchanged. The rejected options were capping the handshake probe interval - (it touches the PTO deadline's monotonic invariant) and flooring the idle - timer at the backed-off PTO (it slows dead-peer detection). - -With the bump, a handshake to an unreachable relay waits 10s instead of about -3s. Check that the crash drill's reconnect loop (`dial()`'s 5s backoff -timeout) still holds. - -Public API: none in this repo; `moq-quic` and `moq-noq-proto` gain -`TransportConfig::handshake_idle_timeout`. Wire: none. - -## Related - -- [Switch](/quest/m1/quic/fork/switch.md) - moves the runtimes from noq to `moq-quic`, which already carries both fixes From a8a443c5eb41c62668ab26f4312db06abfb45b7a Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 10 Oct 2026 06:27:43 -0700 Subject: [PATCH 2/2] test(moq-uring): unanswered dials end at the handshake idle timeout noq 2.0.4 bounds a handshake by handshake_idle_timeout (10s) instead of the idle timeout, so the 500ms idle timeouts no longer shortened these dials. Co-Authored-By: Claude Opus 5.5 --- rs/moq-uring/tests/endpoint.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/rs/moq-uring/tests/endpoint.rs b/rs/moq-uring/tests/endpoint.rs index 03bd8037e5..a4a8f32764 100644 --- a/rs/moq-uring/tests/endpoint.rs +++ b/rs/moq-uring/tests/endpoint.rs @@ -360,10 +360,8 @@ fn an_unanswered_dial_times_out() { let client_sock = handle .udp(UdpSocket::bind("127.0.0.1:0").expect("bind"), udp::Config::default()) .expect("client socket"); - let mut dial = dial_config(hole.local_addr().expect("addr")); - // QUIC stretches this to 3x the initial probe timeout (RFC 9000 ยง10.1), - // so the dial resolves in about three seconds. - dial.transport.idle_timeout = Duration::from_millis(500); + // The dial ends at noq's handshake idle timeout (10s), not the idle timeout. + let dial = dial_config(hole.local_addr().expect("addr")); let result = worker .block_on(async move { quic::client::connect(client_sock, &dial).await }) @@ -399,10 +397,9 @@ fn the_backlog_bounds_pending_handshakes() { let client_sock = handle .udp(UdpSocket::bind("127.0.0.1:0").expect("bind"), udp::Config::default()) .expect("client socket"); - let mut dial = dial_config(server); - // The server never answers, so the dial ends at the idle timeout; keep the - // test short. - dial.transport.idle_timeout = Duration::from_millis(500); + // The server never answers, so the dial ends at noq's handshake idle + // timeout (10s). + let dial = dial_config(server); let result = worker .block_on(async move { quic::client::connect(client_sock, &dial).await }) @@ -438,8 +435,7 @@ fn the_backlog_bounds_queued_connections() { let second_sock = handle .udp(UdpSocket::bind("127.0.0.1:0").expect("bind"), udp::Config::default()) .expect("second client socket"); - let mut second_config = dial_config(server); - second_config.transport.idle_timeout = Duration::from_millis(500); + let second_config = dial_config(server); let second = worker .block_on(async { quic::client::connect(second_sock, &second_config).await }) .expect("worker");