Skip to content

moq-lite-05: move immutable track props to a Track Stream (TRACK_INFO)#1609

Open
kixelated wants to merge 1 commit into
devfrom
claude/inspiring-hawking-baf03d
Open

moq-lite-05: move immutable track props to a Track Stream (TRACK_INFO)#1609
kixelated wants to merge 1 commit into
devfrom
claude/inspiring-hawking-baf03d

Conversation

@kixelated
Copy link
Copy Markdown
Collaborator

What

Implements moq-dev/drafts#25 in rs/moq-net: move a track's immutable publisher properties out of SUBSCRIBE_OK and onto a dedicated, on-demand Track Stream. Scoped to the WIP Lite05 version; Lite01-Lite04 keep SUBSCRIBE_OK unchanged, so this is backward-compatible.

Why

Priority, Ordered, Cache, Timescale, and Compression are fixed for a track's lifetime, yet were echoed on every SUBSCRIBE_OK. Fetching them once over their own stream and caching them removes that per-response repetition (and would let group-by-group FETCHes reuse a single lookup). Keeping them immutable also avoids a relay fan-out problem: a publisher-side change would otherwise have to propagate out to every downstream subscriber.

What changed (rs/moq-net)

  • New Track Stream (0x6) (lite/stream.rs, lite/track.rs): a TRACK request (broadcast path + track name) answered with a single TRACK_INFO (Priority, Ordered, Cache, Timescale, Compression), then FIN, or a reset on error / missing track. With roundtrip tests.
  • SUBSCRIBE_OK slimmed (lite/subscribe.rs): the static props are gone. On Lite05 a subscription is accepted implicitly (rejection is a stream reset) and the publisher sends nothing on the subscribe stream.
  • Publisher (lite/publisher.rs): recv_track/run_track_info resolve the track's .info (via a short-lived subscribe) and reply TRACK_INFO. run_subscribe no longer sends SUBSCRIBE_OK on Lite05 but still computes the same compression/timescale for serving.
  • Subscriber (lite/subscriber.rs): flights TRACK and SUBSCRIBE in parallel, so the first group still arrives in one round trip. A pending TrackEntry is inserted before SUBSCRIBE, so group streams that race ahead of TRACK_INFO park on a resolved channel (buffered by QUIC flow control) instead of being dropped. The resolved (producer, compression, timescale) is reused for every group's decode and fetched once per upstream-subscription lifetime (linger), rather than re-derived per response.

Design notes / open questions

  • Publisher resolves TRACK_INFO by subscribing-and-dropping. In the model, immutable props are only delivered on accept, so there's no cheap "peek info" path. At an origin this reuses the live producer instantly; at a relay it coalesces with the parallel SUBSCRIBE (linger covers the gap). A dedicated TrackConsumer::info() would make TRACK cheaper.
  • Priority/Ordered are sent as 0/false. The model Track has no publisher priority/order field yet (the old SUBSCRIBE_OK echoed the subscriber's priority and hardcoded ordered=false), so nothing real is lost. Making them meaningful means adding two Track fields (+ catalog/serde + js sync).
  • Not included: SUBSCRIBE_START/SUBSCRIBE_END and the SUBSCRIBE_DROP renumber. The Rust impl never resolves a group range or emits drops, so there's no functional gap, only a wire-spec one.

Cross-package sync (deferred)

Per the sync table, rs/moq-net wire changes also need js/net and doc/concept. Left out here while we gauge the approach; happy to follow up in this PR.

Test plan

  • cargo test -p moq-net (350 passing) including new TRACK/TRACK_INFO roundtrip tests
  • cargo clippy -p moq-net --all-targets clean
  • cargo fmt (pinned nix toolchain)
  • cargo check -p moq-relay -p hang -p moq-cli (downstream still builds)
  • End-to-end lite session test (none exists in-crate yet; the parallel TRACK+SUBSCRIBE flow is currently only covered by unit/roundtrip + model tests)

(Written by Claude)

Replace the per-response publisher metadata in SUBSCRIBE_OK with a
dedicated, on-demand Track Stream, per moq-dev/drafts#25. Scoped to the
WIP Lite05 version; Lite01-04 keep SUBSCRIBE_OK unchanged.

- New Track Stream (0x6): a TRACK request (broadcast path + track name)
  answered with a single TRACK_INFO carrying the immutable publisher
  properties (Priority, Ordered, Cache, Timescale, Compression), then a
  FIN (or reset on error / missing track).
- Removed the static props (compression/timescale/cache) from SUBSCRIBE_OK;
  on Lite05 a subscription is accepted implicitly (rejection is a reset)
  and the publisher sends nothing on the subscribe stream.
- Subscriber flights TRACK and SUBSCRIBE in parallel, so the first group
  still arrives in one round trip. A pending TrackEntry is inserted before
  SUBSCRIBE, so group streams that race ahead of TRACK_INFO park on a
  resolved channel (buffered by QUIC flow control) instead of being
  dropped. The resolved (producer, compression, timescale) is reused for
  every group's decode instead of being re-derived per response, and is
  fetched once for the upstream subscription's lifetime (linger).

Publisher resolves TRACK_INFO by subscribing to read the track's .info and
dropping the subscription; a parallel SUBSCRIBE coalesces onto the same
upstream producer. Priority/Ordered are sent as 0/false for now since the
model Track carries no publisher priority/order field yet.

Not included (no functional gap in the Rust impl, which never resolves a
group range or emits drops): SUBSCRIBE_START/SUBSCRIBE_END and the
SUBSCRIBE_DROP renumber. Cross-package sync to js/net and doc/concept is
also deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <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