Skip to content

Commit b40032c

Browse files
kixelatedclaude
andcommitted
Split viewer count into a SUBSCRIBE_STATS message
Move the telescoping subscriber count out of the subscription's update path into a dedicated SUBSCRIBE_STATS telemetry message, named generically so further cheaply-aggregatable stats can be added later. moq-lite: SUBSCRIBE_UPDATE re-echoes all five subscription parameters on every send, so bumping a counter through it is wasteful. Carry the count in a separate SUBSCRIBE_STATS message instead. This introduces a Type tag on the subscriber's post-SUBSCRIBE messages (0x0 SUBSCRIBE_UPDATE, 0x1 SUBSCRIBE_STATS) to distinguish them, mirroring the publisher's already-typed responses; the count field is removed from SUBSCRIBE and SUBSCRIBE_UPDATE. moq-transport: rename the SUBSCRIBER_COUNT message to SUBSCRIBE_STATS and make its body a list of stats encoded as Message Parameters, so new stats are added without a new message type or a wire break. Subscriber Count becomes one such parameter (even type, count - 1). Draft renamed to draft-lcurley-moq-subscribe-stats. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 92d4442 commit b40032c

2 files changed

Lines changed: 106 additions & 52 deletions

File tree

draft-lcurley-moq-lite.md

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ There MAY be multiple Announce Streams, potentially containing overlapping prefi
300300
### Subscribe
301301
A subscriber opens Subscribe Streams to request a Track.
302302

303-
The subscriber MUST start a Subscribe Stream with a SUBSCRIBE message followed by any number of SUBSCRIBE_UPDATE messages.
303+
The subscriber MUST start a Subscribe Stream with a SUBSCRIBE message followed by any number of SUBSCRIBE_UPDATE and SUBSCRIBE_STATS messages.
304+
The opening SUBSCRIBE is identified by the stream type (0x2) and carries no message Type; every subsequent message from the subscriber begins with a Type that distinguishes SUBSCRIBE_UPDATE from SUBSCRIBE_STATS, mirroring how the publisher's messages on this stream are typed.
304305
When a start group can be resolved, the publisher replies with a SUBSCRIBE_OK message (confirming the subscription and resolving its start group), followed by any number of SUBSCRIBE_END and SUBSCRIBE_DROP messages.
305306
When the accepted track has already ended with no matching groups there is no start group to resolve, so the publisher sends SUBSCRIBE_END with no preceding SUBSCRIBE_OK.
306307
A rejection is a stream reset: if the publisher cannot serve the subscription — the track does not exist, or it otherwise refuses — it MUST reset the stream rather than leave it pending, and SHOULD do so promptly (within roughly a round trip) so the subscriber is not left waiting.
@@ -732,7 +733,6 @@ SUBSCRIBE Message {
732733
Subscriber Stale (i)
733734
Group Start (i)
734735
Group End (i)
735-
Subscriber Count (i)
736736
}
737737
~~~
738738

@@ -768,14 +768,6 @@ The last group to deliver (inclusive).
768768
A value of 0 means unbounded (default).
769769
A non-zero value is the absolute group sequence + 1.
770770

771-
**Subscriber Count**:
772-
The number of subscribers this subscription represents, encoded as the count minus one: the subscription is the implicit `1`, so a leaf subscriber sends `0`.
773-
This is a [subscriber property](#track-info) that fans *in* at a relay: a relay merging multiple downstream subscriptions into one upstream subscription sets this to the **sum** of their counts, so the value telescopes up the fan-out tree.
774-
A publisher therefore learns its total number of downstream subscribers across any number of relay hops by reading the count on its single upstream subscription, without any per-hop coordination.
775-
The count changes as subscribers join and leave; a relay refreshes it via SUBSCRIBE_UPDATE.
776-
A relay holding a subscription open with no live downstream subscribers (e.g. briefly retaining it for reuse) MUST still report at least `1`, since the held subscription cannot represent fewer subscribers than itself.
777-
The count is advisory: a subscriber MAY misreport it, and a relay MUST NOT use it for delivery decisions.
778-
779771

780772
## SUBSCRIBE_UPDATE
781773
A subscriber can modify a subscription with a SUBSCRIBE_UPDATE message.
@@ -784,21 +776,51 @@ The start and end group can be changed in either direction (growing or shrinking
784776

785777
~~~
786778
SUBSCRIBE_UPDATE Message {
779+
Type (i) = 0x0
787780
Message Length (i)
788781
Subscriber Priority (8)
789782
Subscriber Ordered (8)
790783
Subscriber Stale (i)
791784
Group Start (i)
792785
Group End (i)
786+
}
787+
~~~
788+
789+
**Type**:
790+
Set to 0x0 to indicate a SUBSCRIBE_UPDATE message.
791+
792+
See [SUBSCRIBE](#subscribe) for information about each remaining field.
793+
794+
795+
## SUBSCRIBE_STATS
796+
A subscriber sends a SUBSCRIBE_STATS message to report telemetry about a subscription.
797+
Unlike SUBSCRIBE_UPDATE, it does not modify the subscription and the publisher only observes it; it is kept separate so that refreshing telemetry does not re-echo the subscription's delivery parameters on every change.
798+
A subscriber MAY send multiple SUBSCRIBE_STATS messages over the life of the subscription to refresh the values.
799+
800+
~~~
801+
SUBSCRIBE_STATS Message {
802+
Type (i) = 0x1
803+
Message Length (i)
793804
Subscriber Count (i)
794805
}
795806
~~~
796807

797-
See [SUBSCRIBE](#subscribe) for information about each field.
808+
**Type**:
809+
Set to 0x1 to indicate a SUBSCRIBE_STATS message.
798810

799-
A relay SHOULD rate-limit SUBSCRIBE_UPDATE messages sent purely to refresh the `Subscriber Count` (for example, coalescing changes within a short window of roughly a second), so that rapid subscriber churn does not flood the upstream with control messages.
811+
**Subscriber Count**:
812+
The number of subscribers this subscription represents, encoded as the count minus one: the subscription is the implicit `1`, so a leaf subscriber sends `0`.
813+
This is a subscriber-side value that fans *in* at a relay: a relay merging multiple downstream subscriptions into one upstream subscription sets this to the **sum** of their counts, so the value telescopes up the fan-out tree.
814+
A publisher therefore learns its total number of downstream subscribers across any number of relay hops by reading the count on its single upstream subscription, without any per-hop coordination.
815+
Until a SUBSCRIBE_STATS is received, the count is `1`; a leaf subscriber that represents only itself need not send the message.
816+
A relay holding a subscription open with no live downstream subscribers (e.g. briefly retaining it for reuse) MUST still report at least `1`, since the held subscription cannot represent fewer subscribers than itself.
817+
The count is advisory: a subscriber MAY misreport it, and a relay MUST NOT use it for delivery decisions.
818+
819+
A relay SHOULD rate-limit SUBSCRIBE_STATS messages (for example, coalescing changes within a short window of roughly a second), so that rapid subscriber churn does not flood the upstream with control messages.
800820
Because the count is the latest aggregate rather than a delta, a change that reverts within the window requires no message at all.
801821

822+
Future revisions MAY append additional stats to this message; the `Message Length` bounds the message so a receiver can stop after the fields it understands.
823+
802824

803825
## TRACK
804826
TRACK is sent by a subscriber to request a Track's immutable publisher properties.
@@ -1097,7 +1119,7 @@ A generic library or relay MUST NOT inspect or modify the decompressed contents
10971119
# Appendix A: Changelog
10981120

10991121
## moq-lite-05
1100-
- Added `Subscriber Count` to SUBSCRIBE and SUBSCRIBE_UPDATE, a telescoping count of downstream subscribers. Encoded as the count minus one (a leaf sends `0`); a relay sums the counts of the downstream subscriptions it merges, so a publisher reads its total audience across any number of hops from its single upstream subscription. It is a subscriber property that fans *in* like the others, so it refreshes via SUBSCRIBE_UPDATE; relays SHOULD rate-limit count-only updates to absorb subscriber churn.
1122+
- Added a SUBSCRIBE_STATS message carrying a telescoping `Subscriber Count` (count of downstream subscribers, encoded as the count minus one so a leaf sends `0`); a relay sums the counts of the downstream subscriptions it merges, so a publisher reads its total audience across any number of hops from its single upstream subscription. It is kept separate from SUBSCRIBE_UPDATE so that refreshing telemetry does not re-echo the subscription's delivery parameters; relays SHOULD rate-limit it to absorb subscriber churn. This also introduced a `Type` tag on the subscriber's post-SUBSCRIBE messages (`0x0` SUBSCRIBE_UPDATE, `0x1` SUBSCRIBE_STATS) to distinguish them, mirroring the publisher's typed responses.
11011123
- Added a SETUP message, sent once on a unidirectional Setup Stream (0x1) at the start of the session and FIN'd immediately. It carries a list of Setup Parameters for negotiating optional capabilities and extensions per-hop, replacing the prior stream-probing approach (version is still negotiated via ALPN, not SETUP). Endpoints keep exchanging non-Setup streams without waiting for SETUP, buffering only a stream whose encoding a negotiated extension would change; unknown stream types are still reset as a fallback.
11021124
- Added a SETUP `Probe` parameter advertising the publisher's capability level: `None`, `Report` (measure and report the estimated bitrate), or `Increase` (additionally pad to probe for bandwidth above the current sending rate). The levels are nested since probing without measuring is meaningless. A subscriber must not rely on a level the publisher did not advertise.
11031125
- Added `Frame Start` to FETCH so a subscriber can begin partway through a group instead of always at frame `0`, allowing resumption of a partially-received group.

0 commit comments

Comments
 (0)