diff --git a/quest/m1/README.md b/quest/m1/README.md index 6d8b58ea7f1..d6ca7be1136 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -139,6 +139,7 @@ removed). - [Announcement takeover](/quest/m1/announce-takeover.md) - a new independent route explicitly continues instances served through its predecessor, with shared Rust/JS semantics and lite-07 wire - [Subscribe ranges](/quest/m1/subscribe-ranges/README.md) - a lite-07 SUBSCRIBE asks for past and live ranges with absolute or relative ends, in either order, and replaces FETCH; merged ranges never starve a subscriber; relays fill misses by range, including over moq-transport - [Drop set](/quest/m1/drop-set.md) - each cursor judges missing groups against its own budget, so a narrow reader drops promptly and a widening re-offers groups already reported dropped on lite-07 +- [A tightened SUBSCRIBE keeps the start's budget honest](/quest/m1/start-budget-update.md) - a relay copy records the delay its start was resolved under, so a SUBSCRIBE_UPDATE tightening cannot make later readers skip fillable history - [Aborted copies don't hold an end open](/quest/m1/settled-abort.md) - a track's declared end settles when a fetched copy finishes a group whose live copy was cut or evicted - [Broadcast archives](/quest/m1/archive/README.md) - ANNOUNCE, TRACKS, and a SUBSCRIBE per track record every track under a prefix to any object_store, a host serves a stored broadcast back, and the catalog drops its archive pointer for a per-rendition `hls` field - [HLS recorder target](/quest/m1/hls-record-target.md) - the moq-cli HLS recorder takes its target duration from the declared `hls` duration @@ -153,6 +154,7 @@ removed). - [moq-quic's API improves on quinn's](/quest/m1/quic-api/README.md) - moq-quic and both QUIC drivers ship one mirrored API: `send::`/`recv::` namespaces, one error per direction, consuming terminal operations, plain configs, no quinn baggage - [On-demand JavaScript FETCH](/quest/m1/js-fetch-on-demand.md) - browser publishers answer IETF FETCH for evicted or uncached groups through the JS ranges request surface - [JS evicted FETCH groups are unknown](/quest/m1/js-fetch-evicted-unknown.md) - a group evicted between listing and writing goes out as End of Unknown Range on drafts 18 and 22, not a silent skip +- [A cold moq-transport-22 copy serves its absolute floor](/quest/m1/ietf22-cold-floor.md) - a draft-22 relay copy asking from `0..` gets every held group, and `warm_backfill` covers draft 22 again - [Rust FETCH parity](/quest/m1/ietf-fetch-parity.md) - Rust finishes an answered joining FETCH's head, refuses a lost head with DOES_NOT_EXIST, and sends no timestamps without TIMESCALE, as the JS publisher does - [SUBSCRIBE honors the properties opt-out](/quest/m1/subscribe-timestamps.md) - the Rust publisher sends untimed objects to a SUBSCRIBE that declines Track Properties, and the Rust subscriber asks for them when it needs timestamps - [JS FETCH timeout test](/quest/m1/js-fetch-timeout-test.md) - a JS joining FETCH still waiting on its SUBSCRIBE at the deadline is refused with TIMEOUT diff --git a/quest/m1/ietf22-cold-floor.md b/quest/m1/ietf22-cold-floor.md new file mode 100644 index 00000000000..adf73e60567 --- /dev/null +++ b/quest/m1/ietf22-cold-floor.md @@ -0,0 +1,26 @@ +# [S] A cold moq-transport-22 copy serves its absolute floor + +## Goal + +A relay copy that subscribes upstream over moq-transport-22 with an absolute +floor, such as `0..`, receives every group from the floor the publisher +still holds, not only the latest. `warm_backfill` in +`rs/moq-net/tests/integration` covers moq-transport-22 again. + +## Plan + +Decided 2026-10-29 (maintainer, `/quest-plan` of moq.pro's announce ramp +loss follow-ups): its own quest in m1, beside the other IETF fill and FETCH +quests. This quest owns the draft-22 absolute-floor backfill, and +[Rust FETCH parity](/quest/m1/ietf-fetch-parity.md) excludes it. + +Found while landing [#7168](https://github.com/moq-dev/moq/pull/7168), which +dropped moq-transport-22 from `warm_backfill` for this. On `main`, the copy +records start 0, but the publisher's cursor starts at the live edge, so only +the latest group arrives. Find which side loses the floor: the subscriber's +draft-22 SUBSCRIBE or fill mapping, or the publisher's resolution of an +absolute start. Fix it there, and keep draft 18 unchanged. + +## Related + +- [IETF fill timeout](/quest/m1/ietf-fill-timeout.md) - the other draft-22 fill work in Rust diff --git a/quest/m1/start-budget-update.md b/quest/m1/start-budget-update.md new file mode 100644 index 00000000000..04ecc8aafae --- /dev/null +++ b/quest/m1/start-budget-update.md @@ -0,0 +1,54 @@ +# [S] A tightened SUBSCRIBE keeps the start's budget honest + +## Goal + +A relay copy remembers the `max_delay` that was in effect when its +publisher resolved SUBSCRIBE_START, not only the one sent at SUBSCRIBE. If a +SUBSCRIBE_UPDATE tightens the delay before the start is picked, a later +reader whose delay is looser than the tightened one, but no looser than the +original, still fills the history between the two starts. Released wires are +unchanged. + +## Plan + +Decided 2026-10-29 (maintainer, `/quest-plan` of moq.pro's announce ramp +loss follow-ups): its own quest in m1, rather than waiting on +[Drop set](/quest/m1/drop-set.md) or +[Delete the old start bookkeeping](/quest/m1/subscribe-ranges/start-bookkeeping.md), +since it fixes released lite-06 behavior. + +Found by review of [#7168](https://github.com/moq-dev/moq/pull/7168): the +lite subscriber sets `requested_max_delay` once, at SUBSCRIBE +(`rs/moq-net/src/lite/subscriber.rs`). `Misses` then gives up every group +below the start for any reader whose budget is no looser than that value. +After a tightening update, the start was resolved under the tighter budget, +so the recorded looser one makes some readers skip history they could have +filled. It loses history only in that window; it does not bring back #7168's +over-fetch. + +Record the budget the start was actually resolved under. SUBSCRIBE_START +carries a group, not which update it was picked under, and updates can cross +it in flight, so the budget current when START arrives is not enough: an +update sent after the publisher picked the start would be recorded in its +place, too loose after a widening or too tight after a tightening. When the +subscriber cannot tell which of its sent budgets the publisher applied, +record the loosest of them. That keeps today's history loss in the +ambiguous window but never brings back the over-fetch. + +Add integration tests in `rs/moq-net/tests/integration`, direct and through +a relay: subscribe, tighten before the start, then a second reader widens +partly, which fails on `main` and passes with the fix; and a tightening and +a widening that each cross an already-picked START, where the recorded +budget must never be tighter than the one the start was picked under. + +Open question for whoever starts this: which lite-06 ordering, if any, tells +the subscriber the publisher applied a tightening before picking the start. +Without one, every tightening sent before START arrives stays ambiguous, the +loosest-budget rule records the SUBSCRIBE budget as today, and the Goal is +out of reach on the released wire. Then propose rewriting this quest to a +wire change for a later draft, or deleting it. + +## Related + +- [Drop set](/quest/m1/drop-set.md) - records the budget each drop was judged under, the same idea for drops +- [Delete the old start bookkeeping](/quest/m1/subscribe-ranges/start-bookkeeping.md) - moves where a subscription starts into the model, which should carry this field diff --git a/quest/m1/subscribe-ranges/js.md b/quest/m1/subscribe-ranges/js.md index 8908ad28ea5..b622987ebff 100644 --- a/quest/m1/subscribe-ranges/js.md +++ b/quest/m1/subscribe-ranges/js.md @@ -30,6 +30,13 @@ Also mirror Rust's optional `Subscription::max_delay` combine, and goes out as the largest delay the wire allows, where today an omitted one means the live edge only. +Fills mirror Rust's rule from [#7168](https://github.com/moq-dev/moq/pull/7168) +(decided 2026-10-29, maintainer): give up a run below a start already +resolved under a budget at least as loose as the reader's, and on a pre-06 +wire fill below the start only when the reader names a floor. `js/net` +requests no fills today, so nothing over-fetches yet; this keeps it that way +once it does. + Decided in the 2026-10-05 audit: the producer half of JavaScript FETCH folds in here, so `@moq/net`'s on-demand request surface takes range requests from the start instead of the