Repository navigation
fix(moq-tokio): finite default connection receive window (noq reassembly cap, part 1) - #4605
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
noq and iroh leave the connection window unlimited, so one peer could make us buffer max_streams x the stream window (about 12.5 GB). Apply a 64 MiB default when quic.receive_window is unset; an explicit value still wins, and Resolved keeps None so the io_uring refusal still sees an unset window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Outcome of the quest start (background agent, so the decisions below are open for the maintainer):
Decisions, with my recommendations:
(Written by Claude Opus 5.5) |
|
Decisions (from the maintainer, via the coordinating agent):
Status: #22 is rebuilt on #25 and its changelog updated. #21 is not updated yet; see the comment there. (Written by Claude Opus 5.5) |
Releases stay patch-only, so the back-merge ships on both branches instead of waiting for a cut; Cloudflare builds track release; #4605 is backported after it merges on trunk; the main PR lands immediately before step 4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 34 minutes. View limit detailsLimit details: You’ve used all 4 included reviews currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (8)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Merging with maintainer approval. Changes since the last CI run:
Decisions on record: 64 MiB default receive window, all three quinn commits in the port. Filing the upstream (Written by Claude Opus 5.5) |
Review (full)Reviewed head: Positive: finite connection BlockingNone found. Non-blocking
Notes (not actionable here)
Verdict: MERGEReviewed head SHA: This is an automated review, not the maintainer's decision |
|
MERGE — head Finite 64 MiB default connection Non-blocking
Verified: This is an automated review, not the maintainer's decision |
|
On review finding 1 ( (Written by Claude Opus 5.5) |
…bly cap, part 1) (#4605) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
) (#4858) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Quest:
quest/m0/noq-reassembly-cap.mdProblem
moq-tokio's noq and iroh backends leave the connection
receive_windowatVarInt::MAX. The only bound on what one peer can make us buffer ismax_streams(10k) × the 1.25 MB stream window, which is about 12.5 GB per connection.Approach
Landed here: moq-tokio applies a 64 MiB connection receive window when
quic.receive_windowis unset, on both noq and iroh. An explicit value still wins. The io_uring workers already run a fixed 16 MiB.Landed elsewhere: the RUSTSEC-2026-0185 reassembly cap, with all three quinn commits (quinn-rs/quinn#2694, quinn-rs/quinn#2789, quinn-rs/quinn#2814), shipped in
moq-noq2.0.1 (pinned onmainby #4699) and 1.3.3 (pinned onreleaseby #4702). That also coversrs/moq-uring.Quest trimmed here: the quest now only tracks the iroh build's upstream
noq-proto1.3.0, which stays unfixed until n0 releases n0-computer/noq#828 (still open).Picking the window
A standalone harness ran one noq connection (BBRv3, 32 uni streams, default stream window) through
moq-shaper. It sweptreceive_windowand measured goodput after a 3 s warmup, over an 8 s window:When the window is the limit, goodput is about 0.65 to 0.8 × window/RTT. Above that point the window stops mattering.
The host had a load average around 100 on 32 cores (about 15 agents building), so the shaper topped out near 130 Mbps. That is why only small windows could be tested directly, and why the link-bound rows are noisy. Extrapolating the measured efficiency:
To rerun on a quiet host, build the harness and invoke it as
window-sweep <rtt_ms> <rate_mbps|0> <streams> <secs> <window_MiB|0>...(source below). A higherrateshows where 16/32/64 MiB actually bind.Harness (standalone crate, not committed)
It depends on
moq-noq(the fork, with the reassembly-cap port),moq-shaper(path dep onrs/moq-shaper),rcgen,rustls, andtokio. It opensstreamsuni streams, writes 64 KiB chunks until the deadline, and drains and counts them on the server. The transport mirrors moq-tokio:max_concurrent_*_streams(10_000),Bbr3Config, andreceive_window(window << 20)unless 0. The shaper appliesdelay = rtt/2each way andRate { bits_per_second: rate * 1e6, burst: 64 KiB, queue: max(rtt, 20ms) }.The shaped runs built against moq-dev/noq#25, so they also exercised the assembler cap under queue-overflow loss. Every connection reported
close_reason() == Noneat the end, so noINTERNAL_ERROR.Impact
quic::Resolved::receive_windowis unchanged: it staysNonewhen unset, and the backend applies the default. This keeps the io_uring refusal of explicit windows working. The new constant ispub(crate).MAX_DATAinstead of 2^62 - 1. A peer can have at most 64 MiB of unread data across all of its streams.Alternatives
Config::resolvesoResolved::default()shows 64 MiB. That makes the io_uring workers refuse every config, because they reject anySomewindow. It also needsOptionto becomeu64, a breaking change.Follow-ups
noq-protoonce n0 releases fix(proto): bound assembler chunk count (port of quinn RUSTSEC-2026-0185 fixes) n0-computer/noq#828 (the trimmed quest).Assembler::defragmentstarts atbytes_read(quinn starts at 0), so unordered readers can lose unread chunks. MoQ reads in order and is unaffected. Filing it on n0-computer/noq needs separate approval.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code