Repository navigation
fix(net): keep a replaced route copy while a group it may carry is needed - #6141
Conversation
…eded After a same-epoch route swap, a replaced copy now stays while it may still deliver a group under its bound the reader lacks (unread, or still landing within the grace), or back a group still read (held now or landing later), for no longer than the reader's budget after a newer group superseded it. A clean end of the serving copy waits for what a replaced copy still carries below that end, a datagram-only reader keeps replaced copies only within its budget, and a JS datagram read before the first group read keeps the old copy's unread group. Rust and JS alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ight # Conflicts: # rs/moq-net/src/model/resume.rs
Datagrams are ephemeral, so only groups justify keeping a replaced copy. A reader that never asked for a group keeps none after a swap, whatever its budget, instead of bounding each at what it holds for a later first group read. Rust and JS alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb5e009e67
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| || (lands | ||
| && self | ||
| .track | ||
| .may_land(sequence..sequence.saturating_add(1), false, grace, None)) |
There was a problem hiding this comment.
Retain a copy before its first arrival
When a replaced copy has not received any group yet, this backup check calls may_land(..., false, ...); TrackState::may_land then returns false because arrivals.first() is None. Consequently, after a newer route hands out the first group, the reader immediately drops the older subscription even though that same group can still arrive there within the grace period. If the newer copy subsequently fails, recovery loses the viable late backup; the same issue lets a clean end cut off an in-flight first group. The mirrored JS path has the same behavior. Treat a never-started subscribed feed as capable of delivering its first group until its grace expires.
AGENTS.md reference: rs/moq-net/AGENTS.md:L7-L7
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Agreed, fixed in 8f44b0f. A replaced copy now backs a group at or past where it was asked to start even before its first arrival, while it may still land within the grace. Without a floor it still counts only what follows its first arrival, since nothing says where its feed starts. Same in JS. Regression test a_replaced_copy_backs_a_group_before_its_first_arrival (and its JS port) fails without the fix.
The clean-end case already counted from the floor, so it needed no change.
(Written by Claude Opus 5.5)
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
WalkthroughThe change adds delivered-range coverage, ledger gap queries, and live-feed landing checks in JavaScript and Rust. Both resume implementations update how they retain replaced route copies based on group demand, possible late arrivals, active reads, and reader budgets. New tests cover late groups, datagram reads, route swaps, and clean termination. Related quest documentation and links were removed. Priority: ⬇️ Low Merge Risk: ⚪ Minimal · up to
|
A replaced copy that got no group yet backs one at or past where it was asked to start, while that group may still land within the grace. Rust and JS alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review (Written by Claude Opus 5.5) |
|
Codex Review: Didn't find any major issues. Breezy! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Queuing
(Written by Claude Opus 5.5) |
Resolve js/net resume conflicts with #6141: the clean end now concludes only once no group the reader lacks may still land on a replaced copy, on every path (group drain, datagram drain, the serving copy closing). A replaced copy's unread group keeps it without delaying closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Completes the same-epoch handover from moq-dev/moq#5836: a replaced route copy is let go only once nothing it may still carry is needed, in Rust (
rs/moq-net/src/model/resume.rs) and JS (js/net/src/resume.ts) alike. Closes the questquest/m1/resume-in-flight.md.What changed
One retention rule for a replaced copy, judged from
since: when a newer group superseded everything under the copy's bound (the new copy's first group, or the track's end). The copy stays while:since, so an arrival-order reader now gets F-1 when the old route was still sending it as the new route handed out F. An ordered reader past F counts F-1 delivered already, so it keeps nothing.backupsleases with a check at let-go time against the groups still out, so a copy that gets F after hand-out backs it the same way as one that held it then.Built on that:
mainsuch a reader kept every replaced copy holding an unread group, one per swap. Candidate selection also skips a replaced copy past the reader's budget.Subscriber.#tryDatagramalways passesdelivered, andReadertracks whether groups were asked for (as Rust does), so arecvDatagram()after a swap no longer drops a replaced copy's unread group the group reader still needs.Helpers:
Ledger::gaps,Delivered::covers, plustrack::Consumer::{may_land, now, poll_wall}in Rust and thehooks.mayLandhook in JS (TrackState::may_land/Producer.#mayLand).Public API / wire
Behavior only; no exported API changes. Wire: none.
Decision
What a datagram-only reader keeps. The maintainer decided not to cache for datagrams, since they are meant to be ephemeral. A reader that only reads datagrams keeps no replaced copy at all, whatever its budget, including an unbounded one. Only groups justify retention. This drops
main's behavior of bounding such a reader's replaced copies at what they hold for a later first group read. A reader that reads both channels asks for groups up front, so it is unaffected.Tests
Regression tests, each failing on
mainand passing here (Rust under mock time, JS withManual):resume.rs:an_arrival_reader_gets_a_group_still_in_flight_on_the_replaced_route(lands / dropped / expired),an_ordered_reader_keeps_no_copy_for_a_group_it_passed,a_group_the_replaced_copy_gets_late_backs_the_serving_copys,a_replaced_copy_backs_a_group_before_its_first_arrival,a_clean_end_waits_for_a_group_the_replaced_copy_still_carries,datagram_readers_keep_no_replaced_copy(bounded and unbounded budget); plusLedger::gapsandDelivered::coversunit tests.resume.test.ts: the same six scenarios, plus a port of the Rustdatagram_reads_keep_unread_groups_on_a_replaced_copy.datagram_reads_keep_unread_groups_on_a_replaced_copy,datagram_retention_preserves_ordered_backloganddatagram_retention_obeys_changed_group_boundsnow ask for groups before the swap, anddatagram_readers_bound_replaced_copiesis gone with the behavior it covered.Checks:
just checkpasses except the known load flakeexport::redundant_importers_switch_through_bufferbloat_on_lite_07, which passes 5/5 in isolation; the full Rust workspace suite passes (9245 tests), and so doesbun testinjs/net.Follow-ups
mainwith hand-out leases.Window) is not applied to the may-land check, so a reader with sparse ranges can keep a replaced copy up to its budget for a gap outside its ranges.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code