Skip to content
15 changes: 15 additions & 0 deletions quest/m1/moxygen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
58 changes: 58 additions & 0 deletions quest/m1/moxygen/fetch-fill.md
Original file line number Diff line number Diff line change
@@ -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
Loading