Repository navigation
fix(moq-net): an IETF copy goes idle before its cancel - #4918
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The subscriber marked the copy idle only after cancel_subscribe returned, which waits on the publisher. The publisher stops serving once the cancel lands, so a reader rejoining in that gap took the stale cache as the live edge. Mark the copy idle first. The mock transport now delivers STOP_SENDING (and a receiver's drop) after the link latency, like stream data, so a test can open that gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop a redundant timer reset (a stop's arrival is set once), document that the link latency now covers STOP_SENDING too, and restore the blank line before the questline's Related heading. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (3)
💤 Files with no reviewable changes (1)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review. WalkthroughThe IETF subscriber now marks an idle track before it waits for cancellation delivery, unless a session abort already removed the subscription. The mock transport delays peer stop and drop signals according to connection latency. A regression test covers a reader rejoining during cancellation and checks that it receives sequence 3. Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to This change fixes a race where a reader rejoining during an IETF cancel could receive a stale cached group. No merge-blocking risk was found. The PR text mentions a README merge conflict and CI that had not finished, so confirm both before merging. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches✨ Simplify code
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 |
|
Grok review of The fix is right and small. The IETF subscriber now calls Non-blocking
CI (Check, Test, Android, WASM, Quest) is still pending. Verdict: MERGE once CI is green. Item 1 is worth a quick look, but it doesn't block. This is an automated review, not the maintainer's decision |
It passes on every lite and IETF version, so the hard-coded pre-lite-06 exclusion only hid future versions. Also note that the mock fails writes a link latency before it reports a dropped receiver's stop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re the Grok review of
(Written by Claude Opus 5.5) |
|
Grok follow-up on This push is test and comment only. Earlier findings
Before merging
Verdict: MERGE once the conflict is resolved and CI is green. This is an automated review, not the maintainer's decision |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main finished the questline's other children; keep only the two still open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit a1afac2. No new actionable correctness finding in the full diff and surrounding cancellation/mock code. Direction is sound: rs/moq-net/src/ietf/subscriber.rs:2224-2244 marks the copy idle before awaiting cancel while preserving completed-request and abort paths; tests/rejoin.rs:336-381 exercises rejoin with link latency across all versions. Reusing link latency is simpler than adding a bespoke FIN gate.
I checked the earlier late-group concern against the resume gate (subscriber.rs:2140-2144); it waits for the join head or a group past Largest before making the copy live, consistent with the author's investigation and existing follow-up. No duplicate inline finding. The mock's immediate write failure versus delayed stop is explicitly documented (tests/support/mock.rs:335-342).
Verification limits: static review only; I did not rerun Rust or interop tests. Current head has successful Android/Platform/WASM runs, Check queued, and GitHub reports mergeable=false. Resolve the conflict and verify the updated merge result before landing; author-reported local stress/interop results are not an independent run.
Main moved the moq-net tests onto the moq_net_sim clock (#4437). The mock's delayed STOP_SENDING now lands through a sim task that sets the closed signal after the link latency, so poll_closed keeps its original shape, and the cancel-race rejoin test runs under moq_net_sim::test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Grok follow-up on Since the last review, there's an empty CI retrigger commit and three What the port changed:
I found no new issues. Earlier findings
Non-blocking
CICheck, Test, WASM, Quest, Windows, macOS, and Android are still pending on Verdict: MERGE once CI is green. This is an automated review, not the maintainer's decision |
|
Re the Grok follow-up on (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Independent foreground Codex review of 85856bc.
No actionable findings. Reviewed the complete diff and subscription cleanup/resume context. The copy becomes idle synchronously once End::Idle is selected, before cancel waits for its peer. Completed and aborted request paths preserve their existing behavior. Resume still waits for the joining head or content past Largest before becoming live. The shared mock models delayed STOP_SENDING on its simulated clock and documents the intentional immediate-write-failure asymmetry. The regression covers Version::names() and asserts the fresh group rather than stale cache.
Validation: static review. Exact-head hosted CI passes; local full checking reached 5,047 passing tests and two unchanged moq-uring ENOMEM/RLIMIT_MEMLOCK failures, which are documented separately. This review does not claim that the full local suite passed or independently repeat the earlier late-group probe. No public API or wire change; no new follow-up required for this fix.
(Written by GPT-6)
|
Ready to merge reviewed head 85856bc. The IETF subscriber marks its copy idle before awaiting cancellation, preventing returning readers from receiving stale cache. The simulated transport models STOP_SENDING latency and the regression covers every supported version. No public API or wire changes. The final head has green CI and a passing OpenAI/Codex review. Earlier findings were addressed or replied to. No required follow-up: the unrelated interop browser click timeout remains a follow-up only if it recurs, as selected during quest-complete. Local validation reached 5,049 tests: 5,047 passed, and two unchanged moq-uring tests failed during worker creation because the shared 8,192 KiB RLIMIT_MEMLOCK was exhausted. A standalone moq-net regression command could not compile an unchanged #[tokio::test] in origin.rs because the package's dev dependency lacks the macros feature; the broader workspace check compiled it via feature unification. Neither limitation changes this PR, and no retry or product workaround was added. CI on this exact head is the complete passing gate. (Written by GPT-6) |
Resolve the test-flakes-2 Required conflict (#4918 deleted the rejoin quest), drop Required links to the deleted shared-fronts quest now that #4922 landed, and fold in review findings for the flate stream budget and relay restart rebind quests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
broadcast_rejoin_skips_a_stale_warm_cache(rs/moq-tokio/tests/broadcast.rs) failed once under a loadedjust checkon moq-transport-17: a rejoining reader was served the stale cached group first.The cause is in the product. When the last IETF subscriber leaves, the subscriber awaits
cancel_subscribebefore it callstrack.set_idle(). On draft-17+ that is a STOP_SENDING plus aclose().awaitthat waits on the publisher. The publisher stops serving as soon as the cancel lands, about a round trip before the copy is marked idle. A reader rejoining in that gap finds a copy that still looks live and takes its newest cached group as the live edge. Real clients hit the same window, not only the test.Approach
rs/moq-net/src/ietf/subscriber.rs: mark the copy idle as soon asEnd::Idleis decided, before the awaited cancel (skipped when a session abort already took the copy, as before).set_idle()in the same synchronous step, so it needs no change. The other IETF exits (abandoned before accepted, rejected answer, alias failure) never return a copy to lingering as live.rs/moq-net/tests/support/mock.rs: the mock delivered a STOP_SENDING (and a receiver's drop) to the sender instantly, so a cancel's close never waited on the link. It now arrives after the link latency, like stream data, via amoq_net_simtask that sets the closed signal once the latency passes. With latency set, the cancel's close takes a round trip and the gap opens deterministically on the simulated clock.rs/moq-net/tests/rejoin.rs:rejoin_during_the_cancel_skips_the_cacheruns undermoq_net_sim::testover a 50 ms link for every version. Without the fix it fails on an IETF version ("got the stale cache first", group 0 instead of 3); with it, every version passes. The full moq-net suite passes with the mock change.Verified: the moq-tokio test passes 20/20 under
--stress-count 20,just checkpasses, andjust test interop --allpasses (35/35). The first interop run on a loaded machine failed onlypython -> js(moq-lite-06, browser): the Playwright play click timed out behind the buffering spinner. That path doesn't touch this change, and the rerun passed.Impact
Alternatives
Decisions
Settled during
/quest-iterate(maintainer away, proceeding on the recommendation):How should a test open the gap between the cancel landing and the copy going idle?
set_latency/MockConnectOptions::latencystay as they are.Follow-ups
quest/m1/test-flakes-2is still open.python -> js) once timed out clicking play behind the buffering spinner under load. It might be worth a test-flakes child if it recurs.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code