Skip to content

feat(native): default quinn and quiche to delay-based congestion control - #2468

Merged
kixelated merged 2 commits into
mainfrom
claude/quinn-quiche-default-cc-66d3ba
Jul 23, 2026
Merged

feat(native): default quinn and quiche to delay-based congestion control#2468
kixelated merged 2 commits into
mainfrom
claude/quinn-quiche-default-cc-66d3ba

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

  • Flip the default congestion control family so an unset --{client,server}-quic-congestion-control lands on BBR for quinn (BBRv1) and quiche (BBRv2 gcongestion) instead of each backend's own CUBIC. Live media wants a send rate an encoder can track, not CUBIC's sawtooth.
  • Move noq and iroh the other way, BBRv3 -> CUBIC. Their shared BBRv3 is the subtract-overflow panic: inflight_at_loss computes (inflight_prev_threshold.round() as u64) - lost_prev with a plain - while every neighboring term uses saturating_sub, so once lost_prev exceeds LOSS_THRESH * inflight_prev it underflows and panics, aborting the process. Since feat(moq-native): expose a congestion control knob for the quinn backend #2432 that has been the default on those backends. A panic is not an acceptable default path, so they sit on CUBIC until the fix lands upstream. delay is still selectable there by name.
  • Each backend now resolves its own default locally, since they no longer agree. quic::Resolved::congestion_control stays Option<CongestionControl> so "unset" remains distinguishable from an explicit request.
Backend Default before Default now
quinn CUBIC BBRv1
quiche CUBIC BBRv2 gcongestion
noq BBRv3 CUBIC
iroh BBRv3 CUBIC

Public API changes

None. Resolved is pub(crate); the pub quic::Client/quic::Server congestion_control fields keep their Option<CongestionControl> type and only their doc comments changed. This is a default-behavior change, not a contract break, hence main rather than dev.

Cross-package sync

doc/bin/relay/config.md updated: the per-backend table's "Default when unset" column now matches, plus a note on why noq/iroh are the exception. No other row in the table applies (no wire, catalog, FFI, or CLI-surface change; the flag names and values are untouched).

Test plan

  • cargo test -p moq-native --all-features — 79/19/11/75 pass, including two new tests, noq::tests::congestion_control_defaults_to_loss and iroh::tests::congestion_control_defaults_to_loss, which pin the CUBIC default so a later edit can't quietly put the panicking controller back on the default path. quiche::tests::apply_settings_writes_cc_algorithm now asserts the unset case reaches bbr2_gcongestion and that an explicit loss still reaches cubic.
  • cargo clippy -p moq-native --all-features --all-targets -- -D warnings, cargo fmt --all --check, RUSTDOCFLAGS=-D warnings cargo doc -p moq-native --all-features.
  • Not run: bun remark (no node_modules in this worktree) and the smoke matrix.

Reviewer note

quinn's BBRv1 and quiche's BBRv2 now ship on by default without the soak time CUBIC has here. The measurements behind this are quinn-only (CUBIC p50 RTT ~558ms vs BBRv1 ~90ms at equal goodput under bufferbloat, via just demo cc compare bloat); quiche's BBRv2 default is going out without a comparable local measurement.

(Written by Opus 4.8)

Live media wants a send rate an encoder can track, so BBR beats CUBIC's
sawtooth as the out-of-the-box behavior. quinn now defaults to BBRv1 and
quiche to BBRv2 gcongestion when --{client,server}-quic-congestion-control
is unset.

noq and iroh move the other way, from BBRv3 to CUBIC. Their shared BBRv3
subtracts without a floor when computing the inflight bytes at the loss
event (noq-proto inflight_at_loss), so a single loss can underflow and
panic, aborting the process. That is not acceptable on a default path, so
delay-based stays reachable there only when asked for by name.

Each backend now picks its own default locally, since they no longer
agree. quic::Resolved keeps congestion_control as an Option so "unset" is
distinguishable from an explicit request.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@kixelated, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dfcff0bf-fa1b-44fc-99f5-3d178698df42

📥 Commits

Reviewing files that changed from the base of the PR and between 7b56dd3 and 015db18.

📒 Files selected for processing (3)
  • doc/bin/relay/config.md
  • rs/moq-native/src/quic.rs
  • rs/moq-native/src/quinn.rs

Walkthrough

QUIC congestion control documentation now specifies backend-specific defaults and warns about the noq/iroh BBRv3 implementation. Quinn and quiche explicitly configure Delay when unset, while noq and iroh use Loss through centralized helpers. Tests verify unset and explicit settings for iroh, noq, and quiche.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title matches a major part of the change, though it omits the noq/iroh default flip.
Description check ✅ Passed The description accurately matches the backend-specific congestion-control default changes and the related documentation and tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/quinn-quiche-default-cc-66d3ba

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
rs/moq-native/src/quinn.rs (1)

70-74: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a regression test for Quinn's unset default.

This changes Quinn's effective default from CUBIC to BBR, but the supplied diff does not test Client::default() through apply_transport. Cover the unset case and explicit Loss mapping so a future change cannot silently restore Quinn's CUBIC default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rs/moq-native/src/quinn.rs` around lines 70 - 74, Extend the regression tests
around apply_transport to exercise Client::default() with no congestion_control
configured, asserting it selects the BBR congestion controller instead of
Quinn’s CUBIC default. Also add coverage for an explicit CongestionControl::Loss
value and assert it maps to the expected loss-based controller, using the
existing transport/test helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@doc/bin/relay/config.md`:
- Around line 197-198: Remove the generic "delay" value from congestion_control
in doc/bin/relay/config.md, leaving it unset or making it backend-specific.
Update the noq/iroh client option near quic.rs lines 139-140 and the noq server
option near lines 254-255 to explicitly warn that selecting delay (BBRv3) can
panic on packet loss and is intended only for deliberate testing.

In `@rs/moq-native/src/quic.rs`:
- Around line 139-140: Update the congestion-control documentation comment in
quic.rs to explicitly warn that selecting delay on noq or iroh may panic when
packets are lost, and state that it should only be used for testing. Retain the
existing default-family information and platform distinctions.
- Around line 254-255: Update the congestion-control option help text in the
QUIC configuration documentation to state that explicitly selecting delay for
the noq server may panic on packet loss, while retaining the existing
explanation that loss remains the default because noq’s BBRv3 is not suitable by
default.

---

Nitpick comments:
In `@rs/moq-native/src/quinn.rs`:
- Around line 70-74: Extend the regression tests around apply_transport to
exercise Client::default() with no congestion_control configured, asserting it
selects the BBR congestion controller instead of Quinn’s CUBIC default. Also add
coverage for an explicit CongestionControl::Loss value and assert it maps to the
expected loss-based controller, using the existing transport/test helpers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 36bf7d7c-cbae-4f0c-ada4-d38acc91b04a

📥 Commits

Reviewing files that changed from the base of the PR and between 7e77cd9 and 7b56dd3.

📒 Files selected for processing (6)
  • doc/bin/relay/config.md
  • rs/moq-native/src/iroh.rs
  • rs/moq-native/src/noq.rs
  • rs/moq-native/src/quic.rs
  • rs/moq-native/src/quiche.rs
  • rs/moq-native/src/quinn.rs

Comment thread doc/bin/relay/config.md Outdated
Comment thread rs/moq-native/src/quic.rs Outdated
Comment thread rs/moq-native/src/quic.rs Outdated
Address review feedback on #2468.

The relay config example set congestion_control = "delay" with no caveat,
which is right for the default quinn backend but is the panicking path on
noq/iroh. Say so in the example, in both clap doc comments (which become
--help), and in the prose warning.

Also extract quinn's default into a named congestion_control() fn so it
can be unit-tested, matching noq/iroh. Nothing else could pin quinn's
unset default: TransportConfig doesn't expose the factory it was given.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) July 23, 2026 23:27
@kixelated
kixelated merged commit 233adab into main Jul 23, 2026
2 checks passed
@kixelated
kixelated deleted the claude/quinn-quiche-default-cc-66d3ba branch July 23, 2026 23:47
@moq-bot moq-bot Bot mentioned this pull request Jul 24, 2026
@t0ms

t0ms commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Thanks, that's a concrete profile I can build to, and the "removes signal" point on random loss is well taken.

I've re-specced our outstanding CC test to exactly your numbers: 10 Mb/s source → 5 Mb/s cap, 100 ms base RTT, 500 ms queue depth, reporting delivered goodput (→5) and standing RTT together — expecting BBR near ~100 ms and CUBIC bloating toward ~500 ms, with an fq_codel/cake counterfactual to see AQM tame CUBIC. I'll also do a 2–3-flow fairness run at the same bottleneck. Dropping reordering and the random-loss sweep from the "CC" framing entirely — agreed they conflate loss-response with congestion-response and reward zero-CC for the wrong reason. I've relabelled our earlier CUBIC-collapse-under-random-loss table accordingly (loss-signal interpretation, not CC quality).

Also saw #2468 land — makes sense, and it clears up something on our side: our earlier "noq-BBRv3 is unstable" datapoint (one run 71%, next 8%) was almost certainly the #768 inflight_at_loss subtract-overflow panic aborting the relay mid-capture, not a CC effect. So I'm parking BBRv3 until that fix is upstream, and pinning --*-quic-congestion-control explicitly on every run now that the defaults have moved. When I get to the bufferbloat run I'll post whether the 558 ms→90 ms delta reproduces on a real EMEA→UK path.

This was referenced Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants