Repository navigation
fix(net): take a reserved group without asking its cursor again - #7032
Conversation
Quest-Claim: 9b872be0aceacc81
A peek now returns its reservation (the group and the step past it) instead of storing it on the cursor and the resume reader, and a take hands that exact group out. A route returning to a copy resets the copy's bounds, which used to clear the cursor's half of a two-level reservation and panic the next take. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the read path against main's errored track end (#7349) and reader mirror: a plain read that runs out maps to ended(), and resume keeps Mirror beside the peek/take pair. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Taking this draft over at the user's request. Its starter session had been quiet for about 3 days and the body had no
(Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 98701c267c31c7424c5c7c7b7cc1b4b8a3288fa7.
No actionable bugs found in the changed reservation, cursor-commit, and nested-reader paths. Carrying the exact group and cursor step through Reserved/Candidate fixes the split ownership at its source, rather than adding more invalidation exceptions. The copy lease preserves the commit target across route changes; direct and nested route-return tests cover both read orders and the late-arrival case. This is a proportionate internal refactor with no public API or wire change.
Verification: static inspection of the diff and relevant selection, retention, range-update, delivery, and metering code. I did not run tests, builds, or benchmarks; the PR's reported results were not independently reproduced.
(Written by OpenAI)
|
No actionable comments were generated in the recent review.
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
A peek now returns its reservation instead of storing it on two levels, and a take hands that exact group out; this deletes the two-level reservation whose split caused the unreachable! panic when a route returned A→B→A between peek and take.
Cursorreservation is now a value:poll_peekreturnsPeeked { group, arrival: Option<Taken> }andtakecommits it, replacing the storedCursor.peekedand thetake: boolthreading throughpoll_recv_with/poll_next_with.- Subscriber-level peek/take:
track::Subscriber::poll_peek_groupreturnstrack::Reserved(Plain(Peeked)|Resume(Box<Candidate>)); the newtakehands it out, records it indelivered, and meters it.Reservedkeeps the hot plain group on the stack. - Resume path:
resume::Candidatecarries the copy's owntrack::Reservedbeside its lease;Reader::takereplacespoll_group/poll_group_once. - Deletions:
Cursor.peeked,Reader.peeked,resume::Subscriber::skip_ordered/poll_group, every clearing site, and theunreachable!/assert_eq!pair. A take through a nested reader past its declared end now returnsProtocolViolationinstead of panicking. - Tests: new
a_reserved_group_survives_its_route_returningcovering both read orders, direct and nested readers, and each route returning; existing reservation tests take the returned reservation.
I verified the peek+take split stays atomic per track::Subscriber (&mut self, no yield between), that Inner is fixed at construction so the Reserved/Inner mismatch arm is unreachable, and that Reserved::ordered() (from Peeked.arrival.is_none()) matches the requested order. cargo test -p moq-net --lib -- a_reserved_group_survives_its_route_returning and the reservation/peek/skip set (610 tests) both pass. The one intentional semantic drift — a held reservation is handed out even if a later route/bound change moved past it — is documented in the PR body and covered by the new test, so I am not flagging it.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
|
Landing 98701c267c.
(Written by Claude Opus 5.5) |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Since the prior pullfrog review (98701c26), the branch merged main (4a3f7c0d) and adapted the reservation refactor to it. I read the delta and the current read path; every adaptation preserves the pre-merge behavior.
- Merged base via
beb015d72: resolved the read-path conflicts by keeping this PR's peek/take split over main's errored track end. - Threaded the subscriber's
deliveredinto the probe:Cursor::poll_peek/poll_peek_recv/poll_peek_next/poll_dropandprobenow carrytaken: &Delivered, andSubscriber::poll_peek_grouppasses&self.delivered. This is main's miss-judging, evaluated at the same point (the peeked group is not indelivereduntiltake), so a peek does not change which fills are asked. - Kept main's errored track end:
Subscriber::poll_groupmaps a peek that runs out toended(false)and keepsend_error/ended_with_error/ended/reaches, matching main. - Kept main's
Mirrorinresume::Subscriberbeside this PR'spoll_peek_group/take; dropped main'spoll_group. - Quest bookkeeping: deleted the finished
quest/m1/cut-audit/reserved-route-return.mdand its links incut-audit/README.mdandm2/resume-redesign.md.
I confirmed the panic path stays closed: Reader::take still finds its copy by lease, and Copy::retains keeps any copy whose lease strong count exceeds one, so an in-flight Candidate keeps its copy in self.copies across a route flap. No inline findings — the merge adaptation is faithful and the prior review's "no findings" stands.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
|
quest land removed this pull request from the queue: Conflicts with the base: rs/moq-net/src/model/track.rs |
Resolve against #7032's peek/take split: the cursor no longer holds a peeked group, so the idle gate wraps Cursor::poll_peek for both walks, Cursor::parked drops its peeked check, and Inner no longer needs the large_enum_variant expectation. Subscriber::poll_group keeps its pre-check ahead of the meter. Also address pullfrog's review: track_sparse_poll's doc now describes the gated repeat, and Scope::is_tagged's doc says it is a pre-check, not a promise that a meter counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Finishes quest/m1/cut-audit/reserved-route-return.md and deletes it.
Problem
A resumed reader's reservation lived in two places: the resume
Reader.peekedand the copy cursor'sCursor.peeked. When the route returned to an earlier copy (A to B to A),Reader::syncreused A's copy and calledset_rangeon it, which cleared the cursor's half while the reader's half survived. The next take re-queried the cursor and hitunreachable!("the peek reserved a group")inresume.rs. The same split existed one level up: an outer reader's route returning to a nested logical track cleared the nested reader'speekedthroughReader::set_range, so the outer take panicked instead. The window is real in production, sinceProducer::servesyncs every reader from the serving thread, between another thread's peek and take.Fix
The quest's preferred direction: a peek returns its reservation instead of storing it, and a take hands that exact group out without asking the cursor again.
Cursor::poll_peekreturns aPeeked(the group plus the step that moves past it: the arrival walk index, an owed or filled group, or the sequence cursor).Cursor::takecommits it. A plain read is now that peek followed by that take, so thetake: boolplumbing throughpoll_recv_with/poll_next_withis gone.track::Subscriber::poll_peek_groupreturns atrack::Reserved(Plain(Peeked)orResume(Box<resume::Candidate>)), and the newtrack::Subscriber::takehands it out, records it indelivered, and meters it.resume::Candidatenow carries the copy's ownReservedbeside its lease.Reader::takereplacespoll_group/poll_group_once.Cursor.peeked,Reader.peeked,resume::Subscriber::skip_ordered,resume::Subscriber::poll_group, every clearing site (set_rangeat both levels,skip_ordered,learn_end,past_end), and theunreachable!/assert_eq!pair. A take through a nested reader that is now past its declared end returnsProtocolViolationwhere the old code would also have panicked.Tests
a_reserved_group_survives_its_route_returninginresume.rs: peek, evict the group, flip the route A to B to A, then take. It covers both orders; a direct and a nested reader; the nested reader's route returning and the outer reader's route returning; and, for arrival order, a late group that sits below the new floor. All 9 cases panicked before this change.changed_bounds_invalidate_a_peeked_groupis nowchanged_bounds_after_a_peek_decide_the_next_read, since a peek no longer stores anything to invalidate.cargo test -p moq-net --libpasses: 2626 passed, 12 ignored.just checkpasses. Its paired bench reportedrust/kio/lock/4at +40%. This PR does not touchkio, and the host's load average was 25 to 65 during the run.Choices made without asking
cache_refreshbefore the drift check, so groups it then skipped as stale were refreshed too. The stamp now happens only when a group is taken, which matches the sequence path. This only affects how long the cache keeps a group the reader gave up on.sync()on commit. The old stored-reservation take re-synced the cursor because the take could come in a later poll. A take now follows its peek in the same read, anddeliverreads the subscription and servable channels live.serve()can land between them, which is exactly the window the take now tolerates. The nested path always had that window.#[allow(clippy::large_enum_variant)]onReserved.Plainholds agroup::Consumer(about 280 bytes) by value, so the hot plain read does not allocate. It is a stack temporary that is never stored.Impact
pub(super)or private inrs/moq-net/src/model.js/netis unaffected: its resume reader peeks and takes synchronously, with no stored reservation.Benchmarks
No new benchmark: the read path does the same work in a different order. A plain read peeks and then takes, with no extra cursor
sync(), clone, or allocation, and it no longer clones the group into a stored reservation. A resumed read drops theReader.peekedbookkeeping. I saved a base run oftrack_fanout_group,track_parked_read, andorigin/{copy_walk,resume_overlap,passthrough}, but the host's load average stayed at 40 to 65 for over 30 minutes, so I abandoned the head-to-head comparison rather than report noise.just bench origin/mainon an idle host would confirm it.Merge with main
Merged origin/main at c04f7771dc (no rebase). Two conflicts, both in the read path:
track::Subscriber::take/poll_group: main's errored track end (moq-dev/moq#7349) maps a read that runs out toended(false).poll_groupnow does that on the peek'sOk(None), and keeps main'send_error/ended_with_error/ended/reaches. Main'sOption<u64>poll_peek_groupis dropped for this PR'sReservedone.resume::Subscriber: kept main'sMirror(it syncs the reader from another thread, one more caller of the window the take now tolerates) beside this PR'spoll_peek_group/take, and dropped main'spoll_group.After the merge,
just check origin/mainpasses: 9188 nextest tests, lint, and the other gates. Its paired bench again reportedrust/kio/lock/4at +40%; this PR does not touchkio.just rs check-test -p moq-netpasses 3229 tests. Plaincargo test -p moq-net --libnow failssetup::tests::credentials_stay_out_of_traces, since two tests from main each install a global tracing subscriber. nextest runs each test in its own process, so it passes there; see Follow-ups.Review
mechanical fix by codex / Codex:
Merged base; preserved reservation reads and threaded delivered-group state into recovery probes.(head beb015d)quest review), gpt-6.1-sol, medium effort, round 1.Follow-ups
setup::tests::credentials_stay_out_of_traces(traced_test) andmodel::test_tracingboth set a global tracing subscriber, so plaincargo test -p moq-net --libfails. nextest is unaffected. This is pre-existing on main.No other new follow-ups. Resume as producer replacement still deletes
Candidateand the copy cursors altogether.(Written by Claude Opus 5.5; taken over and merged with main by Claude Opus 5.5)
🤖 Generated with Claude Code