Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ per-frame CPU growth since #5926.
- [Ordered reads can skip owed holes, and snapshot latest() does](/quest/m1/ordered-owed-holes.md) - moq-net's `Ordered` gains a read that skips past an owed hole to the newest buffered group, while keeping the reader's bounds
- [A Rust relay never replays from group 0 to a lite-03/04 subscriber](/quest/m1/relay-inflight-replay.md) - A Rust relay or publisher serving a lite-03 or lite-04 SUBSCRIBE from a track copy that is still resolving starts at the newest group once the copy resolves
- [The Python wrapper publish can be retried by hand](/quest/m1/release-py-dispatch.md) - `.github/workflows/release-py.yml` has a `workflow_dispatch` trigger, so a maintainer can retry a failed `moq-rs` publish from `release` without a new `moq-ffi-v*` tag or a path-matching push
- [A returning route serves past the cap its old copy was given](/quest/m1/returning-route-cap.md) - when a resumed reader's route returns, the reused serving copy asks with the reader's current resolution, not the capped resolution it was left with when it was replaced
- [The resume model passes its long run again](/quest/m1/resume-model-7542.md) - fix #7542's regression that drops groups a long-budget reader can still get
- [A resume more than one group behind never hangs](/quest/m1/ietf-resume-behind.md) - a resumed read gets every group or a drop on every wire version
- [moq-quic's idle connections keep no burst-sized state](/quest/m1/quic-readable-dedupe.md) - readable events are deduplicated and a quiet connection keeps no per-peak-stream bytes
Expand Down
44 changes: 0 additions & 44 deletions quest/m1/returning-route-cap.md

This file was deleted.

66 changes: 65 additions & 1 deletion rs/moq-net/src/model/resume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,8 @@ impl Reader {
// group's in-flight tail. A fresh copy would replay it and retain one per flap. The
// newest is the route's own copy, not a backfill of it.
if let Some(index) = self.copies.iter().rposition(|copy| copy.track.same_channel(&track)) {
// The copy's own resolution ends at its old cap; the reader's does not.
let resolved = self.window().resolved();
let mut copy = self.copies.remove(index);
copy.generation = generation;
copy.replaced = false;
Expand All @@ -1255,7 +1257,9 @@ impl Reader {
self.groups.1.map_or(Bound::Unbounded, Bound::Excluded),
));
}
copy.update(&self.mirrored);
copy.resolved = resolved;
let wire = self.mirrored.clamped(copy.floor, None, &copy.resolved);
copy.send(wire);
self.copies.push(copy);
return;
}
Expand Down Expand Up @@ -3081,6 +3085,66 @@ mod test {
drop(held);
}

#[kio::test]
async fn a_returning_route_keeps_its_relative_end() {
returning_route(Anchor::Relative(991)).await;
}

#[kio::test]
async fn a_returning_route_keeps_its_resolved_time_end() {
returning_route(Anchor::Time(ts(10))).await;
}

/// Its old cap bounds a replaced copy's wire, not the reader's resolved end. Reusing
/// that copy must ask past the cap while keeping the reader's range and local bounds.
async fn returning_route(end: Anchor) {
let routes = Producer::new();
let logical = logical(&routes);
let a = copy();
// Both anchors resolve the reader's exclusive end at 11.
for sequence in 2..=11 {
write(&a, sequence);
}
write(&a, 1001);
routes.serve(a.consume());
let subscription = Subscription::default()
.with_delay(Delay::default().with_max(HOUR))
.with_ranges([track::Range {
start: Anchor::Absolute(2),
end: Some(end),
}]);
let mut sub = logical
.consume()
.subscribe(subscription)
.await
.unwrap()
.with_range(2..3);
let held = recv(&mut sub);
assert_eq!(held.sequence, 2);
assert_eq!(sub.next_in_range(11), None, "the reader resolved its end");
sub.set_range(4..9);

let b = copy();
routes.serve(b.consume());
write(&b, 4);
assert_eq!(drain(&mut sub, false), [4]);
assert_eq!(copies(&routes), 2, "the held group keeps A's capped copy");

routes.serve(a.consume());
for sequence in [1, 12] {
write(&a, sequence);
}
let mut resumed = drain(&mut sub, false);
resumed.sort_unstable();
assert_eq!(resumed, [5, 6, 7, 8], "end={end:?}");
// Widening the local cursor still respects the reader's resolved end.
sub.set_range(4..);
let mut widened = drain(&mut sub, false);
widened.sort_unstable();
assert_eq!(widened, [9, 10], "end={end:?}");
drop(held);
}

/// A returning route resumes where a fresh copy would, and what it buffered while
/// replaced below what another route already handed out comes through the backfill:
/// the late group once, and nothing handed out again.
Expand Down
Loading