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: 1 addition & 0 deletions quest/m1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ transport, benchmark tooling); worktrees isolate commits, not semantics.
- [Track tail hardening](/quest/m1/track-tail-hardening.md) - Rust and JS wait out a track's tail by the same rules, with the known hang, count, truncation, grace, and memory holes closed
- [JS close end](/quest/m1/js-track-close-end.md) - a JS track's clean close ends at its own last group, not a sibling producer's
- [SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md) - every stream group in a lite subscription arrives or is dropped by name, and lite-07 drops its stream count for it
- [Subscribe ranges](/quest/m1/subscribe-ranges/README.md) - on dev, a lite-07 SUBSCRIBE asks for past and live ranges in either order and replaces FETCH; relays fill misses by range, including over moq-transport
- [FIN wait expiry](/quest/m1/fin-wait-expiry.md) - a group awaiting its FIN ack still expires and follows priority updates on lite and IETF
- [Cross-relay bursts](/quest/m1/cross-relay-bursts.md) - bursty small-group tracks cross two relays without lost groups, unanswered FETCHes, or stalls
- [Session death parity](/quest/m1/session-death.md) - a local close ends tracks cleanly in both languages, and JS group readers see the session's error on session death
Expand Down
4 changes: 2 additions & 2 deletions quest/m1/quic/scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ integer.
Use byte-accounted deficit round robin, or an equivalent bounded-quantum
algorithm, between backlogged groups at equal priority. Round robin by stream
count is insufficient because audio, video, and data streams have different
sizes. Within the chosen group, order streams by the MoQ group order: newest
first, fixed by the draft and never inverted. A blocked stream must not consume
sizes. Within the chosen group, order streams by the subscription's group order
(newest first by default). A blocked stream must not consume
the group's turn, and opening newer groups must not reset its accumulated
fair-share credit.

Expand Down
64 changes: 64 additions & 0 deletions quest/m1/subscribe-ranges/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Subscribe ranges

## Goal

A moq-lite-07 SUBSCRIBE replaces FETCH. One subscription asks for any number
of group ranges, past or live, in ascending or descending order, and the
publisher serves their union from one cursor without sending a group twice.
Every sequence in range either arrives or is named by a SUBSCRIBE_DROP, so
nothing blocks on a cache miss. Relays fill misses upstream with ranges, not
one request per group, including over moq-transport where group numbers may be
sparse. Lite FETCH is removed from lite-07.

## Plan

Decided in planning (09-29), after the fetch-span agent (#4558) found that a
relay bounds a sparse FETCH only if the model can request ranges:

- **Wire (lite-07, unpublished).** SUBSCRIBE carries a list of group ranges
(an open end means live) in place of Group Start/End, plus
`order: asc | desc` (desc, newest first, is today's rule and the default).
Max Age stays as a cap alongside the ranges, since a group's timestamp isn't
always known. SUBSCRIBE_UPDATE replaces the list. Lite FETCH is removed from
lite-07; published versions keep it, answered through the same model.
- **Holes.** SUBSCRIBE_DROP (restored in lite-07 by
[SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md)) names every sequence in the
requested ranges the publisher won't deliver: never existed, expired, or
refused upstream. Datagram groups are always dropped in a past range (the datagrams-are-live-only quest, #4551). The
publisher delivers what it has and fills the rest as upstream answers.
- **Model.** `Subscription` gains `ranges` and `order`, mirroring the wire;
`request_groups` as a separate API was rejected. A publisher's
`track::Dynamic` receives range requests for misses, replacing per-group
`requested_group`, and `fetch_group` becomes a one-range subscription or is
removed. This breaks published moq-net APIs, so the line lands on `dev`.
- **moq-transport.** Some deployments use non-contiguous group numbers, so a
relay never probes upstream one group at a time. Upstream, each run of
locally missing groups becomes one standalone range FETCH, capped below the
upstream's live group (which arrives via SUBSCRIBE), so it never blocks.
moq-transport caps a FETCH at the Largest Object, so it can return the live
group's existing prefix but never wait for its future objects. Open: whether
the current joining FETCH for that prefix (a SUBSCRIBE starting mid-group
whose head isn't cached) stays, which today's bridge relies on. Downstream,
an IETF FETCH is served from the model's ranges, capped at the Largest
Object.
- Ranges are frame-precise (`Position`), not whole groups. The IETF joining
FETCH for a mid-group SUBSCRIBE's uncached prefix stays, because today's
bridge relies on it (maintainer, 09-29).
- The relay half of [fetch-span](/quest/m1/moxygen/README.md) (#4558) moves
here; #4558 lands only the no-handler skip.

This line owns the end-to-end test: a relay with a sparse cache answers a
wide past range plus live in both orders, over lite-07 and moq-transport, with
every sequence delivered or dropped.

## Required

- [Model ranges](/quest/m1/subscribe-ranges/model.md) - `Subscription` carries ranges and order, and `Dynamic` fills misses by range
- [Lite-07 ranges](/quest/m1/subscribe-ranges/lite.md) - the lite-07 wire, Rust publisher and subscriber, and the draft
- [moq-transport ranges](/quest/m1/subscribe-ranges/ietf.md) - range FETCH upstream per missing run, and non-blocking FETCH served downstream
- [JS ranges](/quest/m1/subscribe-ranges/js.md) - `@moq/net` model and lite-07 parity
- [SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md) - publishers name every group they won't deliver

## Related

- [Track priority scope](/quest/m1/track-priority-scope.md) - group order within a track now follows the subscription
21 changes: 21 additions & 0 deletions quest/m1/subscribe-ranges/ietf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# [M] moq-transport ranges

## Goal

A relay fills past-range misses from a moq-transport upstream with one
standalone FETCH per run of locally missing groups, never including the
upstream's live group, so a sparse track costs a handful of requests. It
serves a downstream IETF FETCH from the model's ranges without blocking on the
live group.

## Plan

This absorbs the relay half of fetch-span (#4558) and builds on fetch-fill's
checks (#4544). Receiving a multi-group FETCH stream means several groups on
one stream. Absent sequences between the FETCH's groups become drops, and a
group already cached is discarded as a duplicate. Cap a downstream FETCH at
the Largest Object, as the moq-transport drafts require.

## Required

- [Model ranges](/quest/m1/subscribe-ranges/model.md) - the range requests this answers
16 changes: 16 additions & 0 deletions quest/m1/subscribe-ranges/js.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# [M] JS ranges

## Goal

`@moq/net` matches Rust: `Subscription` carries ranges and order, the lite-07
SUBSCRIBE carries them on the wire, FETCH is gone from lite-07, and
`fetchGroup` becomes a one-range subscription or is removed.

## Plan

Mirror the Rust names. The JS FETCH cancel and JS FETCH quests shape the
current `fetchGroup` surface; fold whatever of them is still open into this.

## Required

- [Lite-07 ranges](/quest/m1/subscribe-ranges/lite.md) - the wire this speaks
22 changes: 22 additions & 0 deletions quest/m1/subscribe-ranges/lite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# [L] Lite-07 ranges

## Goal

moq-lite-07's SUBSCRIBE carries group ranges and an order, SUBSCRIBE_UPDATE
replaces them, and FETCH is gone from lite-07. The Rust publisher serves
ranges from the model and drops what it won't deliver, and a relay fills a
lite upstream by subscribing to the missing ranges.

## Plan

Update `drafts/draft-lcurley-moq-lite.md` in the same PR: the SUBSCRIBE and
SUBSCRIBE_UPDATE fields, the Group Order section (the order is now a field),
the removal of FETCH, and the lite-07 changelog. Fix the stale "offset by 1"
sentence on Group Start while there. Published versions (lite-03..06) keep
FETCH, answered through the model's ranges. Run `just drafts check` and
`just test interop --all`.

## Required

- [Model ranges](/quest/m1/subscribe-ranges/model.md) - the model this serves
- [SUBSCRIBE_DROP](/quest/m1/subscribe-drop.md) - lite-07's DROP for holes
22 changes: 22 additions & 0 deletions quest/m1/subscribe-ranges/model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# [L] Model ranges

## Goal

The Rust model's `Subscription` carries a list of group ranges and an order,
aggregated across subscribers. A track serves the union from one cursor
without duplicating a group, in the requested order, and reports every
sequence it won't deliver. A publisher's `track::Dynamic` receives range
requests for misses instead of one request per group.

## Plan

See the [line](/quest/m1/subscribe-ranges/README.md) for the decisions.
Guidance:

- Aggregating several subscribers' ranges and orders onto one upstream
request needs a rule. For example, the union of ranges, and desc wins when
orders conflict. Decide it and write it down.
- Replace `requested_group` and `fetch_group` rather than keep both. Update
every caller (relay, moq-archive, hls, the ladder) in the same PR.
- Max Age caps every range.
- Benchmark range count and span as separate axes (AGENTS.md fan-out rule).
4 changes: 2 additions & 2 deletions quest/m1/track-priority-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ Where the model stands today, all in `rs/moq-net`:
packs track priority and the group's position within its own subscription
into one send order (`js/net/src/lite/priority.ts`), so two tracks at equal
priority interleave rather than one draining first.
- The draft fixes group order within a track: newest first, with no wire field
to invert it (`drafts/draft-lcurley-moq-lite.md`, Prioritization).
- Group order within a track follows the subscription's `order` (newest first
by default; see [Subscribe ranges](/quest/m1/subscribe-ranges/README.md)).
- A relay forwards the max of its downstream subscriber priorities upstream
(`model/subscription.rs`, `lite/subscriber.rs`), never the publisher's track
priority, so one viewer asking for 255 raises that track above every other
Expand Down