Skip to content

fix(net): read each IETF uni stream's type in its own task (backport #5086) - #5125

Merged
kixelated merged 1 commit into
releasefrom
backport/ietf-uni-dispatch
Oct 10, 2026
Merged

kixelated merged 1 commit into
releasefrom
backport/ietf-uni-dispatch

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Problem

On release, run_unis in rs/moq-net/src/ietf/session.rs waits for each accepted uni stream's type varint before accepting the next one. QUIC opens lower-numbered streams implicitly, so the stream it waits on can be one whose bytes are still held back by connection flow control. That credit is used up by later streams' unread data, which the loop never gets to. The connection deadlocks. This affects any IETF peer and was seen in about 1 in 50 runs on an impaired link. moq-lite already reads each stream's type in its own child.

What was ported

Only the ietf/session.rs half of #5086 (da1aa61fb):

  • Each accepted uni stream reads its type and is served in its own child (Uni::serve).
  • A stream that breaks the session (unknown type, second SETUP, undecodable header) records its error in a shared kio::Shared<Option<Error>> slot. The accept loop polls that slot and returns the error, so error handling stays as loud as before.
  • A stream that dies before its type is still dropped without failing the session, as before.
  • Regression test ietf::session::tests::a_silent_stream_does_not_hold_up_the_next: a uni stream that never sends its type, followed by a group stream for a retired alias. The second stream must be read and stopped with CANCELLED.

Adapted to release APIs: poll_decode_peek and Context::from_waker(waiter.waker()), and the test uses #[tokio::test(start_paused = true)] like its neighbors.

Left out

The model/resume.rs half of #5086 (fetch Recover treating Old / Evicted as non-verdicts). It fixes code that comes from main-only #4741. The drill and quest changes are left out too.

Impact

  • Public API: none.
  • Wire: none.

Tests

  • The new test fails on release without the fix (the stream behind the silent one is never stopped) and passes with it.
  • All 18 ietf::session tests pass.
  • just check: lint and clippy clean; 4990 of 4992 workspace tests pass. The 2 failures, moq-tokio::broadcast::broadcast_rejoin_{skips_a_stale,replays_a_current}_warm_cache, run over moq-lite with real sockets and a 10s wall-clock bound. They don't touch this IETF path, and they passed 6 of 6 times when rerun on their own, so they look like load flakes from the full parallel run.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

…5086)

run_unis waited for each accepted uni stream's type before accepting the
next. QUIC opens lower-numbered streams implicitly, so the stream it waited
on could be one whose bytes sat behind connection flow control held by later
streams' unread data, deadlocking the connection with any IETF peer.

Each stream now reads its type and is served in its own child. A stream that
breaks the session records the error in a shared slot, which ends the loop as
before. Backports only the session.rs half of #5086; the resume.rs half fixes
main-only code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-09T22:20:36.359982Z 7711e52 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Automated review of 1afd492a (backport of #5019 to release)

The adaptation is sound: on drafts 14-17 the subscriber now peeks the first object id, drops a stream whose first id is non-zero (keeping the subscription up), and still treats an empty stream as an empty group. Draft-18+ behavior is unchanged since legacy is false there. The ported test covers the drop, the next group at object 0, and a later gap still failing only that group.

No blocking issues.

Non-blocking:

  • subscriber.test.ts subscribeTrack: ALPN selection maps any non-DRAFT_16 version to ALPN.DRAFT_19. Fine for today's callers, but a future draft-14/15/17 test would silently negotiate the wrong ALPN. A small version→ALPN map (or a throw for unsupported versions) would make this harder to misuse.
  • There's no draft-16 test for the empty-stream case (legacy && id === undefined yields an empty group). It's the one branch this backport adds that isn't exercised; a two-line test would lock it in.
  • CI (Check, Test, Replay) is still pending on this head. Per the body, chore(js): bump @moq/net to 0.4.3 on release #5119 (0.4.3 bump) should merge after this one.

Verdict: MERGE once CI is green.

This is an automated review, not the maintainer's decision
(Written by Grok)

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: 7711e52 (full PR).

No actionable introduced bugs found. Reviewed the complete rs/moq-net/src/ietf/session.rs diff and surrounding task-set, reader, subscriber, and test-transport code, including fatal-error wakeups, duplicate SETUP rejection, early streams, cancellation, and stream-local failures.

Direction: this is a focused backport. Reading each type inside an existing owned child removes the uni-stream head-of-line dependency without introducing runtime-specific spawning. The private Uni helper retains the existing protocol/error policy; no public API or wire-format change. Leaving the unrelated fetch-recovery changes out is appropriate. The silent-stream regression exercises the intended scheduling fix.

Verification limits: static GitHub source review only; I did not compile or rerun tests. At publication, Android passed; Check, Test, and macOS were running, with Windows and WASM queued. The PR's reported local test results were not independently reproduced.

(Written by OpenAI)

@kixelated
kixelated merged commit c771ce0 into release Oct 10, 2026
7 checks passed
@kixelated
kixelated deleted the backport/ietf-uni-dispatch branch October 10, 2026 00:27
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.

1 participant