Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions doc/bin/relay/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ same way so the cluster converges instead of flapping. Both wire protocols
carry it: natively on moq-lite, and via the [cluster extension](/draft/moq-cluster)
on moq-transport 17+.

Failover routes must carry copies of the same broadcast. For each track, the
Failover routes must carry copies of the same broadcast. A relay moves a
subscription only between sources from the same origin: on moq-lite-07 the one a
source's SUBSCRIBE\_OK or FETCH\_OK names, otherwise the first hop of its route.
A change of origin ends the subscription and the viewer re-subscribes. For each track, the
relay requires matching timescale, retention window, publisher priority, and
group ordering. A source with different properties is refused before its groups
are spliced in. If no compatible source remains, the track fails with
Expand All @@ -43,18 +46,20 @@ link costs 1, which reproduces plain hop counting. Each relay adds the price of
the link an announcement arrived on before forwarding it, so a route's cost is
the sum of what it crossed.

Wildcard advertisements are forwarded and costed the same way as an exact-path
Prefix advertisements are forwarded and costed the same way as an exact-path
route: each hop appends its identity, adds the link price, and passes the
claim on. An advertisement must be contained by one of the publisher's granted
prefixes (`grant/**`); an over-wide pattern is refused rather than clamped.
prefixes (`grant/**`); an over-wide prefix is refused rather than clamped.

Routing prefers the most specific pattern, then a fully identified hop list
Routing prefers the longest covering prefix, then a fully identified hop list
over one that holds a 0 (an anonymous hop) at any depth, then the lowest cost,
then the shortest hop list, breaking any remaining tie toward the newest
announcement so a reconnecting publisher isn't outranked by the session it
replaced. An assigned identity for an anonymous peer is local selection state
and is never written into the hop list. Resolving a non-prefix pattern into a
subscription is not implemented yet.
then the shortest hop list, then a hash of the requested path and the hop list,
breaking any remaining tie toward the newest announcement so a reconnecting
publisher isn't outranked by the session it replaced. Hashing the requested
path spreads equal-cost advertisers of one prefix, such as a transcode pool,
across its paths instead of sending every path to one of them, and every relay
picks the same one for a given path. An assigned identity for an anonymous peer
is local selection state and is never written into the hop list.

```toml
[cluster]
Expand Down
6 changes: 5 additions & 1 deletion doc/concept/moq-lite.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,11 @@ member. The Rust consumer is a `Stream` and the TypeScript one an async iterable

Announcements are hints; requests are the authority. When a subscriber asks
for a covered path the advertiser will not serve, the advertiser refuses that
request rather than narrowing the claim, and no message narrows a route. Token
request rather than narrowing the claim, and no message narrows a route. A
refusal is final: a relay resolves a request against the longest covering
prefix and never retries another advertiser or a broader prefix. An advertiser
running out of capacity withdraws or re-prices its route instead, leaving
headroom for requests already in flight. Token
scope is any pattern union; the session asks for each member's literal head on
the prefix-only wire and filters locally. In Rust and TypeScript,
`origin.scope(root, patterns)` narrows the handle's permissions and presents paths
Expand Down
20 changes: 5 additions & 15 deletions drafts/draft-lcurley-moq-cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,8 @@ A refusal never falls through to a less specific tier.
Within that tier, a receiver SHOULD prefer a HOP_PATH that contains no 0 entry over one that does, then the lowest ROUTE_COST, breaking ties toward the shorter HOP_PATH and then toward the most recently received.
This is advisory: a receiver MAY apply local policy, such as measured RTT, instead.

NO_CAPACITY ({{iana}}) refuses a request the publisher could serve but has no capacity for now.
It permits ONE re-resolution within the same tier, excluding the refusing advertiser: every route with its non-zero first Hop ID, or its session when that ID is 0.
A receiver that has spent its retry, or has no other candidate, MUST refuse downstream with a code other than NO_CAPACITY, so retries cannot compound hop by hop.
Every other refusal, including an unrecognized code, is terminal.
Every refusal, including an unrecognized code, is terminal.
A publisher signals capacity through its advertisement alone: it withdraws or re-prices it before it runs out, leaving headroom for requests already in flight, since a withdrawal and a request for the slot it gave away can cross.
A receiver SHOULD NOT cache refusals.

A relay MUST NOT advertise a namespace merely because it resolved it: the covering advertisement stays the only one until the publisher advertises the concrete namespace, which it SHOULD do once producing, so a later request finds the running content by its exact namespace instead of resolving a second producer.
Expand All @@ -266,7 +264,7 @@ One rule for advertisement and dispatch keeps advertised paths truthful and prev
Under this extension an advertisement is a path, so a session advertises a namespace at most once, a relay forwards only the best path it knows ({{selection}}), and a subscription is served from one source at a time.

A receiver MAY still hold paths to several publishers of one namespace and choose between them as it sees fit: serve from the cheapest and move to the next when it fails.
A refusal moves to another publisher only as {{selection}} allows: once, and only for NO_CAPACITY.
A refusal never moves to another publisher ({{selection}}).
The advertised path and the served source stay the same publisher: a relay that moves to another MUST withdraw its advertisement and advertise the new path ({{updating}}), so the first Hop ID downstream always names the publisher whose Objects flow.
Moving between distinct publishers is a discontinuity: their groups are not one sequence, so a subscriber sees an unrelated Location, and a FETCH that succeeds against one may fail against the other.

Expand All @@ -283,7 +281,7 @@ Because a relay only appends to HOP_PATH, it cannot make a competing path look s
ROUTE_COST has no such protection: it is a single value the sender chooses, so a relay can advertise 0 for content it is not carrying and attract subscriptions it then has to fetch.
Both cost only a suboptimal path choice, and the latter is self-limiting, since the traffic won this way must then be served.

Implementations SHOULD bound the work started by requests beneath a broad advertisement, using NO_CAPACITY when capacity is exhausted.
Implementations SHOULD bound the work started by requests beneath a broad advertisement, withdrawing it before capacity is exhausted.

A receiver MUST NOT make security decisions based on Hop IDs, and a deployment spanning a trust boundary SHOULD treat a peer's ROUTE_COST as a hint to clamp or ignore rather than an accounting figure.

Expand Down Expand Up @@ -314,21 +312,13 @@ Both are carried in PUBLISH_NAMESPACE, in REQUEST_UPDATE of a PUBLISH_NAMESPACE

The Key-Value-Pair parity is load-bearing: HOP_PATH is odd, so its value is a length-prefixed byte string, while HOP_ID, RELAY_COST, and ROUTE_COST are even, so their values are bare varints.

## MOQT Error Codes

This document requests one registration in the "REQUEST_ERROR Codes" registry.

| Value | Name | Reference |
|:--------|:------------|:--------------|
| 0x40B5A | NO_CAPACITY | This Document |


--- back

# Appendix A: Changelog

## moq-cluster-02
- Defined request resolution against the longest covering prefix and the NO_CAPACITY refusal with its single re-resolution; any other refusal is terminal, including between several publishers of one namespace.
- Defined request resolution against the longest covering prefix; every refusal is terminal, including between several publishers of one namespace, and capacity is signaled only by withdrawing or re-pricing the advertisement.
- A relay does not advertise a namespace because it resolved it; the publisher advertises the concrete namespace once producing.

## moq-cluster-01
Expand Down
Loading