Repository navigation
fix(net): end a resumed track once a bounded copy ran out, and run the Rust resume model with async - #7995
Conversation
Quest-Claim: 4b96be9cdf17b396
…ound Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… model with it The model test is async-only since async became default-off, so the nightly script now asks for the feature. Helpers only async tests and sessions use are gated to match, so moq-net builds without warnings in every feature shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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 20 minutes. View limit details
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. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 80a005a
No actionable findings in the 14-file diff. The bounded-copy drain check in js/net/src/resume.ts:790–797,890–895 requires a completed copy with no unread groups under its replacement bound and preserves an outstanding fill through hooks.awaitsFill. Tracing the subscriber paths confirms that unread groups held back by the reader's own cap still prevent this shortcut. The new pending-fill test and mirrored model regression cover the two key cases. The Rust model's async feature requirement matches test/resume-model.sh, and the additional cfg gates are on internal/test helpers rather than a public API removal.
Overall direction: the fix and targeted regression coverage are sound. Verification limits: GitHub-only static review, including the surrounding drain, fill, retention, feature-selection and test paths. I did not execute tests or independently reproduce the reported seeds. The current head's Check run is still in progress: https://github.com/moq-dev/moq/actions/runs/42201612419. This review does not establish a passing CI result.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- A bounded backfill copy drains at the track's end —
Reader.tryGroupinjs/net/src/resume.tsnow setscopy.done = nullwhen a read comes back idle or parked,copyEndis defined, no fill still owes the copy a group, and nothing under the copy's bound is unread. Only copies bounded byuntil(backfills, and replaced copies bounded by#boundAtEnd) qualify, so the serving copy — and therefore the reader's own cap raising — is untouched. - A pending fill keeps the copy open — the new
hooks.awaitsFillexposesSubscriber.#fillWait, which#askFillssets for a run a fill still owes and each read clears before judging, so the check is read at the right moment. This is the round 1 finding from the PR's Codex review. - Regression coverage in both models — the shrunk counterexample (groups 11 and 1 written, slot down, slot end) is added to
js/net/src/resume.model.test.tsandrs/moq-net/src/model/resume_model.rs, plus a JS unit test for a backfill waiting on a fill the finished route still owes. - The Rust resume model now selects itself —
test/resume-model.shpasses--features async, without which the#[cfg(all(test, feature = "async"))]model was never compiled. moq-netbuilds cleanly withoutasync—EXPORTER_LABEL,frame::Buffer's async-only methods,group::Consumer::poll_read_frames,track::Producer::evict, and the four async-only test helpers are gated to their callers. All arepub(crate)or test-only, andfuzz = ["async"], so no public surface moves.- Quest bookkeeping —
quest/m0/nightly/resume-model.mdretired with its README entry.
I re-ran the important claims rather than only reading them: with MOQ_RESUME_SEED=764723972 the JS model reproduces the nightly failure exactly once the #drained call is removed, and the new unit test fails only when the awaitsFill condition is dropped, so each guard covers its own half. bun test js/net/src/resume.test.ts (137 pass), the JS model on seeds 11, 22, 33, 44, 1539493080 and 183243466, the Rust regressions test, the full Rust model on the failing seed (4096 cases), and cargo check -p moq-net --lib --tests both with and without async all pass here. I did not run just ci, so the check suite remains the authority on the rest.
One residual thought, not a finding: this path sets done = null without setting #settled, so a copy dropped here no longer counts in #end's cross-copy settled clause (it is dropped by #letGo before #end runs). That could in principle tip a later serving-copy error from clean end to failure, but I could not construct it and the model sweep does not hit it.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

Problem
Nightly's
Tests (test resume-model)failed two ways:764723972):reader 0: missing groups 2,3,4,5,6,7,8,9,10. A reader resumed on a new route from group 1. The switch asked a backfill copy for group 0, which the new route never had. That route then finished the track. The backfill copy's own bound (until) is applied as its subscriber's local cap, and group 11 sits past that cap. JS deliberately keeps a capped reader open on a finished track so the cap can rise, so the backfill never drained, the logical track never ended, and the drained serving copy then skipped groups 2..10 when they landed late. Rust's arrival cursor ends on a complete track, so the Rust reader ended cleanly at the finish.moq-netno longer compiles its lib tests withoutasync. moq-dev/moq#7607 madeasyncdefault-off, and the Rust model (#[cfg(all(test, feature = "async"))]) was never selected without it anyway.The earlier symptom (
the track ended cleanly though no route declared an end, seeds1378751129and2002958325) had a separate cause, already fixed by moq-dev/moq#7869. Both seeds pass onmaintoday.Approach
js/net/src/resume.ts: a copy bounded where it was replaced counts as drained (done = null) once its track ended, no fill still owes it a group (new internalhooks.awaitsFill, mirroring Rustawaits_fill), and it holds nothing unread under its bound (hooks.spent). This mirrors the Rust arrival cursor. The reader's own cap still holds the read open, because a group under the copy's bound past that cap stays unread.test/resume-model.shruns the Rust model with--features async.moq-netbuilds without warnings whenasyncis off:EXPORTER_LABELand the test helpers that onlyasynctests use are gated to match their callers.Impact
Choices made without asking
asyncbuild cleanup in this PR. It is 15 lines, and it also removes the only error in Nightly'sfeatures / Features (rs features)job (unused import: EXPORTER_LABEL). It could move to its own PR under the nightly epic if you'd rather keep this one narrow.Validation
MOQ_RESUME_SEED=764723972 just test resume-model: Rust and JS pass (4096 cases).1539493080and183243466. Run on the JS model only: seeds1378751129,2002958325, 11, 22, 33, 44. All pass.cargo clippy -p moq-netwith no features,async, andfuzz(lib and tests): no warnings.just cion80a005a4da: every module passed. Run it unwrapped: wrappingjust ciinquest gatedeadlocks ontest gh, becausesh/local.shtakes its own gate slot.bun test js/net/src: 3020 pass.Follow-ups
moq-netwithoutasyncon the PR gate, which is how this break reachedmainunnoticed. Consider having the gate's feature selection (orjust rs features) compilemoq-net's lib tests with default features whenmoq-netchanges.Review
Codex
gpt-6.1-solat medium effort, viaquest review, head80a005a4da:259a916c65): one finding, medium. A bounded backfill copy that is idle while a fill is pending would count as drained and withdraw the fill. Fixed in80a005a4dawithhooks.awaitsFill, plus a unit test that fails without the check.80a005a4da): no findings.🤖 Generated with Claude Code
(Written by Claude Opus 5.5)