diff --git a/quest/m1/moxygen/README.md b/quest/m1/moxygen/README.md index ce97e996cd..bd040aad9a 100644 --- a/quest/m1/moxygen/README.md +++ b/quest/m1/moxygen/README.md @@ -26,11 +26,26 @@ Out of scope, so they are not reopened as bugs: - A peer that answers SUBSCRIBE_NAMESPACE with unimplemented. The session already continues. +Decided in the review of +[#4276](https://github.com/moq-dev/moq/pull/4276), also not reopened: + +- A FETCH starting mid-group is answered from that object. Each fetch + object carries its own IDs, so it is what was asked, not a partial group + ([r4113671550](https://github.com/moq-dev/moq/pull/4276#discussion_r4113671550)). +- FETCH_OK names the requested end when the range runs past a finished + group's last object. Missing objects there are a hole, like a missing + group ([r4114050992](https://github.com/moq-dev/moq/pull/4276#discussion_r4114050992)). +- An End of Track on a group FETCH_OK that contradicts the cache is + ignored. It only fills a boundary the live subscription has not declared, + and that subscription stays authoritative + ([r4114051032](https://github.com/moq-dev/moq/pull/4276#discussion_r4114051032)). + Docs stay inline in the change that makes them stale. No new guide. ## Required - [Sparse FETCH ranges](/quest/m1/moxygen/fetch-span.md) - a FETCH costs the groups it returns, not the span of its range +- [Group fetch fill](/quest/m1/moxygen/fetch-fill.md) - a cache fill from an IETF upstream is complete or refused, validated against a concrete end signal, and asks from the frame the reader wants ## Related diff --git a/quest/m1/moxygen/fetch-fill.md b/quest/m1/moxygen/fetch-fill.md new file mode 100644 index 0000000000..95b73655cf --- /dev/null +++ b/quest/m1/moxygen/fetch-fill.md @@ -0,0 +1,58 @@ +# [M] Group fetch fill + +## Goal + +A relay filling a cache miss from an IETF upstream caches exactly what the +upstream promised, or nothing. A short or malformed fetch stream fails the +group loudly instead of landing in the cache as a complete one, and the +upstream FETCH asks for the frames the downstream reader actually wants. + +## Plan + +[#4276](https://github.com/moq-dev/moq/pull/4276) added the fill: each cache +miss becomes a standalone FETCH of one group (`run_group_fetch` in +`rs/moq-net/src/ietf/subscriber.rs`), and `recv_group_fetch_objects` writes +the fetch stream into the accepted group. Three of Codex's review findings +merged unanswered, and the maintainer ruled that each blocks the line: + +- **Truncation is cached as complete** + ([r4113942736](https://github.com/moq-dev/moq/pull/4276#discussion_r4113942736)). + FETCH_OK names an `end_location`, but it never reaches the decoder. If the + peer cleanly ends the stream early, the group is finished and cached short, + and later readers see a normal end. Check the exclusive received boundary + (last object ID + 1) against FETCH_OK's `end_location`, which is exclusive, + before finishing the producer, and abort the group otherwise. That only + works when `end_location` names a concrete object: for a whole-group request our own `run_fetch_stream` answers with + the requested boundary (`(group + 1, 0)`), so a stream holding only object + 0 looks like a valid one-object group. The line's accepted declines keep + that requested end, so for this case find a wire signal that marks a + complete group (such as an End of Group status object) and require it; if + the drafts we speak offer none, ask the maintainer rather than guess. +- **A first object with no IDs is accepted** + ([r4113942737](https://github.com/moq-dev/moq/pull/4276#discussion_r4113942737)). + The `(false, None | Some(1))` arm treats omitted Group and Object IDs as + "same group, next object", which only means something when a prior object + exists. On the first object `prior_group` is `None` and `next` is 0, so an + anonymous object is cached under the requested group. The first object must + carry explicit IDs resolving to the requested group and start object; refuse + it as a protocol violation otherwise. The same holds for every field that + inherits from a predecessor (draft-15+ `FetchSubgroup::Prior`, an omitted + publisher priority): on the first object each must be explicit. +- **The upstream FETCH ignores the requested frame offset** + ([r4113942733](https://github.com/moq-dev/moq/pull/4276#discussion_r4113942733)). + `group::Request::frame_start()` carries the downstream reader's start, but + the upstream FETCH always asks from object 0, the decoder numbers from 0, + and the handler never calls `start_at` on the producer `accept` returns, + though `frame_start`'s docs require it. An upstream that evicted the + prefix but holds the suffix refuses a request it could have answered. Ask + from `frame_start`, `start_at` it, and number the fill from it, so the + request, the decoder, and the producer agree on where the group starts. + +Each fix gets a regression test in the existing subscriber test module that +fails without it. Keep the whole-group-only scope of the line: this is about +filling faithfully, not about new FETCH shapes. + +## Related + +- [Moxygen compatibility](/quest/m1/moxygen/README.md) - the line this blocks +- [Fetch without SUBSCRIBE](/quest/m1/ietf-fetch-only.md) - also changes how a relay reaches upstream for fetches