diff --git a/quest/m0/broadcast-epoch/README.md b/quest/m0/broadcast-epoch/README.md index c89e64e11e..1b8bb4567f 100644 --- a/quest/m0/broadcast-epoch/README.md +++ b/quest/m0/broadcast-epoch/README.md @@ -52,9 +52,9 @@ Decided: prefix route. Document this rather than promise it works. - Publishers on the default publish path, such as moq-boy and moq-room, inherit the epoch from Origin. moq-stats mints its own through - [Stats epochs](/quest/m1/stats-epoch.md), which is outside the release - gate: until it lands, a restarted stats node under a reused name can stall - its viewers the same way. + [Stats epochs](/quest/m1/stats-epoch.md), which also gates the release + (decided 2026-10-04): a restarted stats node under a reused name stalls its + viewers the same way. - Derived output mirrors the epoch it came from (`.pro/transcode//foo.hang/@e`, per the [wildcard](/quest/m0/wildcard/README.md) line's derived-output layout), so @@ -79,3 +79,4 @@ This README owns: - [Bindings](/quest/m0/broadcast-epoch/bindings.md) - moq-ffi and every wrapper expose the epoch and inherit the default - [GStreamer and OBS](/quest/m0/broadcast-epoch/gst-obs.md) - moqsink and the OBS plugin publish each run under a fresh epoch - [Remove `--hop`](/quest/m0/broadcast-epoch/hop-removal.md) - `moq` takes an optional `--epoch` instead of `--hop`, a plain publisher declares a random Hop ID, and the per-session hop stamp and NO_CAPACITY are gone +- [Stats epochs](/quest/m1/stats-epoch.md) - moq-stats publishes each node under its own epoch, so a restarted node never stalls its viewers diff --git a/quest/m0/wildcard/README.md b/quest/m0/wildcard/README.md index 30556219d2..b0b410588d 100644 --- a/quest/m0/wildcard/README.md +++ b/quest/m0/wildcard/README.md @@ -169,9 +169,14 @@ path, not with a route identity or a generation field. Two workers at one path are then one broadcast, and a relay moving between them does so at a group boundary (decided 2026-10-03: #4741 drops first-hop identity, so routing can no longer tell two workers apart). - Wildcard routing invents neither a lease nor a generation. Ship-order - risk: until that m1 quest lands, two workers at one path can still be - spliced mid-group. + Wildcard routing invents neither a lease nor a generation. That quest is + Required here (decided 2026-10-04), so this line cannot ship with workers + that can be spliced mid-group. +- **A serving front follows the best route.** A join or re-price re-selects + every front below the prefix, and a front moves when another route now + wins: cheaper, or the rendezvous winner among equal costs (decided + 2026-10-04, replacing Stay). This is safe because a path is one broadcast + and any covering route resumes it (#4741). - **No reply Origin.** The lite-07 `Origin` field in SUBSCRIBE_OK and FETCH_OK, and the rule that a relay MUST NOT splice across differing Origins, are dropped (decided 2026-10-03: nothing needs them for correctness). The line @@ -210,13 +215,15 @@ announcement shadows it. A claim names no generation, so a client that must distinguish recording generations reads the catalog's archive entry ([archive](/quest/m1/archive/README.md)) rather than announce state. +## Required + +- [Transcoders start at group boundaries](/quest/m1/transcode-group-start.md) - two claim workers at one path are one broadcast, so a relay never splices them mid-group + ## Related - [archive](/quest/m1/archive/README.md) - an archive claims the root, and its catalog names the generations a claim cannot - [Broadcast epochs](/quest/m0/broadcast-epoch/README.md) - derived output mirrors the source path, `@` segment included -- [Transcoders start at group boundaries](/quest/m1/transcode-group-start.md) - - what makes two claim workers at one path interchangeable - [Announcement shapes](/quest/m2/announce-shapes.md) - moq-lite-only exact, suffix, and prefix+suffix claims that survive relay hops diff --git a/quest/m1/route-wakes.md b/quest/m1/route-wakes.md index 48f04bedb8..1e5352b290 100644 --- a/quest/m1/route-wakes.md +++ b/quest/m1/route-wakes.md @@ -4,13 +4,16 @@ A route change under a prefix (join, withdraw, re-price, restale) makes the origin driver act only on the fronts that could change their selection: -fronts still waiting for a source, and fronts the changed route serves or is -requesting through. Today it wakes every front below the prefix, so one -equal-cost pool member joining or leaving a prefix that serves 10k paths -costs the driver 100 to 600 ms of single-threaded work while no front -switches (`origin/pool_churn`, #4607). A root claim, such as an archive's -catch-all, makes every front on the relay pay for every change to it. Done -when `origin/pool_churn` is flat in served paths. +fronts still waiting for a source, fronts the changed route serves or is +requesting through, and, on a join or re-price, the serving fronts the +changed route could now win. Today it wakes every front below the prefix, +so one equal-cost pool member joining or leaving a prefix that serves 10k +paths costs the driver 100 to 600 ms of single-threaded work while few +fronts switch (`origin/pool_churn`, #4607). A root claim, such as an +archive's catch-all, makes every front on the relay pay for every change to +it. Done when a leave in `origin/pool_churn` is flat in served paths, and a +join or re-price costs one cheap rehash per front below the prefix, with +no `select`, track poll, or deadline scan for a front that keeps its route. ## Plan @@ -21,7 +24,8 @@ Facts from the wildcard line (`rs/moq-net/src/model/origin.rs` there): below the prefix, so a woken front cannot tell what changed. Each re-runs `select` under the table read lock (`retain_routes`, then `best_route`, which hashes every pool member unless a serving front's route still - serves), then the driver polls every track and rescans deadlines. + serves; that `Pin::Stay` short-circuit goes with the follow-the-best-route + rule below), then the driver polls every track and rescans deadlines. - `route_order` is rendezvous-style: FNV over the path and hop ids, lowest wins. A join moves only paths the newcomer wins, a leave only paths the leaver was winning. @@ -45,20 +49,21 @@ Decisions: them when it closes. The lookup walks descendants but skips subtrees whose waiting set is empty, keeping cost in woken fronts rather than served paths. -- A serving front stays on its route while it serves, per the wildcard line's - Stay decision, so a join or re-price never wakes it; it moves only on - failover. (2026-09-30) +- A serving front follows the best route, per the wildcard line (decided + 2026-10-04, replacing Stay). A join or re-price must rehash every front + below the prefix, since rendezvous moves exactly the paths the changed + route now wins; only those fronts re-select. A leave wakes only the fronts + the leaver served or was requesting through. - Builds on shared-fronts' keying, so the index hangs off the final front identity. `origin-front-parks.md` replaces the `routed_broadcast` retry loop, one of the watch consumers here; whichever lands second adapts it. (2026-09-30) -Verification: `origin/pool_churn` flat in served paths at every pool width -it already sweeps, and a unit test that counts `select` calls per route -change and asserts only affected fronts re-select, with zero serving fronts -for an unrelated route's join and leave, and a parked waiter retrying when a -deeper advertise-only claim over a served root is withdrawn. Keep -`pool_resolve` unchanged. +Verification: `origin/pool_churn` leaves flat in served paths at every pool +width it already sweeps, and a unit test that counts `select` calls per +route change: zero for an unrelated route's leave, only the won paths on a +join, and a parked waiter retrying when a deeper advertise-only claim over a +served root is withdrawn. Keep `pool_resolve` unchanged. Public API: none. Wire: none.