Skip to content

feat(lite)!: edit lite-07 subscription ranges by Range ID - #7207

Closed
kixelated wants to merge 1 commit into
mainfrom
lite07-range-deltas
Closed

kixelated wants to merge 1 commit into
mainfrom
lite07-range-deltas

Conversation

@kixelated

@kixelated kixelated commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Status (2026-11-01 takeover)

Not implemented further, and not merged with main. The maintainer asked for an overlap check against the lite-07 subscription work settled today, and it changes this PR's case. No quest claims this PR (its branch is not a quest branch, and nothing under quest/ mentions Range IDs), so it is an unplanned wire prototype. The branch also conflicts with main in five files (the draft, rs/moq-net/src/lite/{subscribe,publisher}.rs, js/net/src/lite/{subscribe,publisher}.ts).

What the settled work means for it:

  • moq-dev/moq#7545 (fetch-range): SUBSCRIBE_END becomes SUBSCRIBE_FINISH, and no message FINs the stream any more, a covered bounded subscription included. The subscriber judges a bounded range complete from its groups, SUBSCRIBE_DROP, and the Range Starts in SUBSCRIBE_OK. That wire half belongs to Lite-07 ranges and JS ranges, which will rewrite the same codec, publisher, subscriber, and draft sections this PR touches.
  • moq-dev/moq#7487 (lite-07 ranges, waiting on fetch-range): a RangeFills set adds each owed run as an absolute bounded range on the upstream SUBSCRIBE_UPDATE and retires it once its streams finish. That makes updates churn more than when this PR was written. But each range is a few bytes, so restating stays cheap, and stateless restating is still the simpler encoding.
  • moq-dev/moq#7575 (datagram tracks): ranges never cover datagrams. This doesn't conflict with Range IDs.
  • Time addressing on lite-07 re-encodes the anchors and the SUBSCRIBE_UPDATE and SUBSCRIBE_OK fields. Range IDs don't depend on the anchor encoding, but they would have to be rebuilt on top of it.

The gap the settled design does expose is range identity, not update size. SUBSCRIBE_OK's Range Starts are positional and sent only once. So a range that a SUBSCRIBE_UPDATE adds after the OK never learns where a Relative or Time start landed. Yet the subscriber now judges completeness from those starts, on a stream that stays open for later updates. Implicit Range IDs, as this PR defines them, are one way to name such a range in a later start report. That question belongs to the lite-07 ranges wire, though, not to a separate delta encoding.

Decisions needed

  1. What to do with this PR.
    • (recommended) Close it unmerged, and add the range-identity question to Lite-07 ranges (in moq-dev/moq#7487's rewrite, or a /quest-plan follow-up). The question to record: a range added by SUBSCRIBE_UPDATE after SUBSCRIBE_OK has no reported start. Decide whether ranges get implicit IDs so a later message can report one. SUBSCRIBE_UPDATE keeps restating the list, and this PR is the reference encoding if IDs are adopted. Reason: restating is small and stateless. Range IDs would add state on both ends, a Resolve hook on Request, and about 680 lines, all under three in-flight reworks of the same wire.
    • Keep it as a planned quest, "Edit lite-07 ranges by Range ID", that requires Lite-07 ranges and is rebuilt on the SUBSCRIBE_FINISH wire once that lands. This PR would be closed now either way.
    • Implement it now: merge main through the five conflicts, update the draft, and run just drafts check and interop. Not recommended, since the code would be reworked again by fetch-range's wire half, RangeFills, and time anchors.
  2. Closing. If option 1 or 2 is picked, confirm closing this PR. A decision about the PR doesn't authorize closing it.

Original description

Problem

A lite-07 SUBSCRIBE_UPDATE restates every Range on each update, even one that only changes the priority or the wanted group. A relay serving many downstream windows resends its whole union whenever any one of them moves.

Approach

Prototype of delta-encoded ranges, mirroring how announces use Announce IDs:

SUBSCRIBE_UPDATE {
  Subscriber Priority (8), Group Order (8), Subscriber Max Age (i),
  Remove Count (i), Range ID (i) ...,
  Range Count (i), Range (..) ...,
  Wanted Group (i)
}
  • SUBSCRIBE's Ranges take implicit Range IDs 0..n-1, and each Range an update adds takes the next ID. IDs are never reused.
  • An update retires Range IDs, then adds Ranges, atomically in one message. Every other field is still restated.
  • Retiring an unknown or already-retired ID, leaving no live Ranges, or growing the live set past what one SUBSCRIBE can carry (65,535 bytes encoded) is a PROTOCOL_VIOLATION. The last rule keeps the publisher's per-subscription state bounded exactly as it is today, and a relay can always restate the set in a fresh SUBSCRIBE.
  • Rust: SubscribeUpdate<R = Vec<Range>> travels as SubscribeUpdate<RangeEdit>. LiveRanges (lite/subscribe.rs) is kept on both ends. The subscriber diffs its new list against it, and the publisher's request loop resolves each edit back to the full list right after decoding, through a new Resolve hook on Request. The rest of the subscription code is unchanged. Resolving at decode time matters because the publisher keeps only the latest pending update.
  • JS: the same LiveRanges. The JS subscriber always sends one span, so an update edits only when the span changes. The JS publisher folds the live set into its covering span as before.

Impact

  • Wire: lite-07 (moq-lite-07-wip) SUBSCRIBE_UPDATE layout changes as above; earlier versions are byte-for-byte unchanged. Both sides must run this build to speak lite-07.
  • Size: an update that keeps its ranges shrinks to 2 range bytes (0, 0). A single-range client whose span changes pays 2 more bytes than before (remove count and one ID).
  • No public API change; lite is private in Rust and not a JS entrypoint.

Alternatives

  • Separate SUBSCRIBE_INCLUDE / SUBSCRIBE_EXCLUDE messages. The subscriber side of the Subscribe Stream has no type byte today, so this would need a new discriminator on every update. It would also split one change across messages, leaving a moment where the publisher holds a half-applied set.
  • Exclusion as negative ranges. Retiring by ID covers shrinking without defining set subtraction over relative and time anchors.
  • Keep restating the full list. It's simpler and stateless, and the range bytes are small, which is the main argument against this PR.

Follow-ups

  • Conflicts trivially with moq-dev/moq#7196 (changelog regroup); whichever lands second moves one bullet.
  • The time-addressing quest reworks anchors in lite-07; Range IDs are independent of the anchor encoding.

Review

No new code or review since d4cea2d. This takeover changed only this description.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

SUBSCRIBE's ranges take implicit Range IDs, and a lite-07 SUBSCRIBE_UPDATE
retires live ranges by id and adds new ones instead of restating the whole
list. Both ends keep the live set: the subscriber writes each update as an
edit, and the publisher resolves it back to the full list before applying
it, so the rest of the subscription logic is unchanged. The live set stays
non-empty and within what one SUBSCRIBE can carry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Closing unmerged, per the maintainer's decision on 2026-11-01. This prototype conflicts with main in five files, and three in-flight reworks rewrite the same code:

The gap it points at is naming a range: a range added by a later SUBSCRIBE_UPDATE never learns where its relative or time start landed. That question is being recorded in quest/m1/subscribe-ranges/lite.md as a follow-up. The branch lite07-range-deltas (d4cea2d) stays as the reference encoding in case implicit Range IDs are adopted.

(Written by Claude Opus 5.5)

@kixelated kixelated closed this Nov 1, 2026
kixelated added a commit that referenced this pull request Nov 2, 2026
#7539 landed, so quic-lb-forward-extras drops its Required on the deleted
relay-quic-lb-forward quest. Follow-ups that extend still-open quest PRs
now require them: ts-small-step-back (#7221), quic-long-type (#7492), and
ts-idle-jitter (#7504). release-fanout-bench notes #7493 landed.

Apply the pending edits whose PRs have landed: lite-07 ranges drops its
fetch-range Required (#7545 landed the miss judging and cached backfill)
and records #7207's range-identity question; quic-lock-contention records
#7531's recommendation to keep the Mutex and RefCell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated added a commit that referenced this pull request Nov 3, 2026
Land part 1 now; fill under the live demand's budget with the one-group
recovery; lite-07 FETCH stays for one group or a slice of one, and SUBSCRIBE
never slices within a group; datagram-only groups wait for datagram tracks;
implicit Range IDs with #7207's encoding, now this quest's next
step. Remove FETCH from lite-07 becomes Lite-07 FETCH is single-group only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant