diff --git a/quest/m0/audio-quality-budgets.md b/quest/m0/audio-quality-budgets.md index 97d8de8544..f3c79b0aa4 100644 --- a/quest/m0/audio-quality-budgets.md +++ b/quest/m0/audio-quality-budgets.md @@ -22,8 +22,8 @@ measures on an `ubuntu-24.04-arm` runner. A red job nobody can act on hides real regressions, so this moved out of Watch into m0 (maintainer, 2026-10-13). -- Measure several nightly runs, or a matrix dispatched on the runner from - the branch, before setting ceilings. A single sample's noise would make +- Measure across several scheduled nightly runs before setting ceilings; + Nightly is never dispatched from a branch. A single sample's noise would make the job flaky. Failing runs keep their summaries as the `audio-quality-run` artifact for 7 days. - Set each ceiling from the observed spread with modest headroom, and record @@ -33,8 +33,9 @@ real regressions, so this moved out of Watch into m0 (maintainer, rows stalling a third of the time look like one. Budget what the player does today, and file each suspected defect as a follow-up quest, so the job goes green without hiding it. -- Rows that are too noisy to bound on a shared runner can be dropped from - `--enforce` instead. That decision needs the maintainer. +- Every row stays budgeted (decided 2026-10-13): a noisy row gets a wider + ceiling from its measured spread. The harness has no per-row opt-out, and + none is added. Public API: none. Wire: none. diff --git a/quest/m1/README.md b/quest/m1/README.md index 38a87c57be..fe0cf984a7 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -105,6 +105,7 @@ disabled rung strands WHEP, HLS, and moq-mux export viewers. - [JSON window timestamps](/quest/m1/json-window-timestamps.md) - Rust and JS window consumers return each event's frame timestamp like the snapshot and stream consumers - [The catalog root clock is the producer clock](/quest/m1/catalog-clock-adopt.md) - a derivative adopts its input's wall mapping through `modify()`, and a root `clock` change after the clock is fixed is refused - [Exact catalog wall clock](/quest/m1/catalog-wall-clock-precision.md) - `hang::catalog::Clock::wall_clock` keeps sub-millisecond precision +- [JavaScript answers TRACK_STATUS](/quest/m1/js-track-status.md) - a browser publisher answers IETF TRACK_STATUS like Rust, so a native fetch-only request needs no SUBSCRIBE fallback - [JavaScript FETCH](/quest/m1/js-fetch.md) - browser publishers answer IETF FETCH through the JS ranges request surface - [IETF fill timeout](/quest/m1/ietf-fill-timeout.md) - native FILL_TIMEOUT bounds upstream waiting on drafts 18-22, alongside local content-age enforcement - [IETF group ingest refuses loudly](/quest/m1/ietf-ingest-refusal.md) - a subgroup other than 0 or a datagram past object 0 aborts the track, and a peer's malformed group is refused rather than dropped at debug @@ -175,6 +176,7 @@ disabled rung strands WHEP, HLS, and moq-mux export viewers. - [Route wakes](/quest/m1/route-wakes.md) - a route change wakes only the fronts it can move, so pool churn stops scaling with served paths - [Publish channel count](/quest/m1/publish-audio-channel-count.md) - forcing a channel count on an Audio.Capture stops costing the subscriber gaps of silence - [JS request deadline](/quest/m1/js-request-deadline.md) - each JS request, PUBLISH_NAMESPACE included, has one fatal 10 s timer from create to answer and fails fast without stream credit, so nothing queues or retries +- [JS cancellation bench](/quest/m1/js-forget-bench.md) - the requests bench sweeps cancellation over competing dynamic routes, so a per-cancel cost that grows with routes shows as a slope - [Rust request credit](/quest/m1/rs-request-credit.md) - a moq-net request fails at once without stream credit instead of waiting - [qmux no-wait opens](/quest/m1/qmux-no-wait.md) - @moq/qmux rejects an over-limit create when waitUntilAvailable is false, like Chrome - [E2EE](/quest/m1/e2ee/README.md) - TypeScript and Rust peers interoperate over encrypted broadcasts no relay can decrypt diff --git a/quest/m1/js-fetch.md b/quest/m1/js-fetch.md index fb0cdc25e7..622d506ec9 100644 --- a/quest/m1/js-fetch.md +++ b/quest/m1/js-fetch.md @@ -25,10 +25,9 @@ no `NotFetchable` code, since nothing would send it. Only lite-07 has the code, and lite-07 drops FETCH for [ranges](/quest/m1/subscribe-ranges/lite.md); lite-05 and lite-06 answer `NotFound`. -Since #4974, a native fetch-only request asks TRACK_STATUS before its first -FETCH, so the `@moq/net` publisher answers TRACK_STATUS too (a #4974 -follow-up, folded in here because answering only matters once JS serves -FETCH). +Answering TRACK_STATUS, which a native fetch-only request asks before its +first FETCH, split out to [JavaScript answers TRACK_STATUS](/quest/m1/js-track-status.md) +(2026-10-13): it does not need FETCH. Verify with an in-memory application responder: a browser publisher serves a native IETF subscriber after a group is evicted or was never cached. Run the @@ -44,6 +43,7 @@ claims that change. ## Related +- [JavaScript answers TRACK_STATUS](/quest/m1/js-track-status.md) - answers the TRACK_STATUS a native fetch-only request sends first - [Browser archive](/quest/m3/archive-browser.md) - supplies memory or OPFS archive data through the same request surface - [IETF fill timeout](/quest/m1/ietf-fill-timeout.md) - native timeout budgets and gap dispositions build on this responder - [FETCH max-delay](/quest/m1/fetch-max-delay.md) - local per-reader age limits also apply to historical data diff --git a/quest/m1/js-forget-bench.md b/quest/m1/js-forget-bench.md new file mode 100644 index 0000000000..791b9f96a2 --- /dev/null +++ b/quest/m1/js-forget-bench.md @@ -0,0 +1,27 @@ +# [XS] Bench JS request cancellation across competing routes + +## Goal + +`js/net/bench/requests.ts` sweeps request cancellation over the number of +`dynamic()` routes competing for one path and the number of cancelled +requests, so a per-cancel cost in `OriginState.forget` that grows with the +routes shows up as a slope. + +## Plan + +#5411 made cancellation release origin demand. Codex flagged on that PR that +`forget` walks every route and failover slot on each cancellation; the merge +argued the walk only covers the cancelled path's own prefixes, the same +entries `bestEntry` already scans. Both cancellation sweeps in the bench use a +single route today, so neither claim is measured. + +Add the two-axis sweep (for example 1/16/128 routes on one prefix by +1/64/1024 cancellations). If it shows a slope, fix `forget` in the same PR, +starting with computing the failover-entry set once instead of copying it in +the inner loop. + +Public API: none. Wire: none. + +## Related + +- [JS request deadline](/quest/m1/js-request-deadline.md) - every timed-out request releases its demand through the same path diff --git a/quest/m1/js-track-status.md b/quest/m1/js-track-status.md new file mode 100644 index 0000000000..98f188247b --- /dev/null +++ b/quest/m1/js-track-status.md @@ -0,0 +1,42 @@ +# [S] JavaScript answers TRACK_STATUS + +## Goal + +A `@moq/net` publisher answers IETF TRACK_STATUS on every draft instead of +refusing every one with NOT_SUPPORTED, so a native fetch-only request on +drafts 14-16 and 18-22 learns the track in one round trip rather than falling +back to SUBSCRIBE. Draft 17 still subscribes, since its answer cannot say +whether the track is timed. + +## Plan + +Since #4974, a native subscriber asks TRACK_STATUS for demand without a +subscription. #5409 found that the relay's IETF publisher can make a reader's +first request look fetch-only, and that against a JS publisher the refusal +failed the track; the Rust subscriber now falls back to SUBSCRIBE on +NOT_SUPPORTED, at the cost of an extra round trip. + +Decided 2026-10-13: mirror the Rust publisher +(`rs/moq-net/src/ietf/publisher.rs`, `run_track_status_stream`). Check the +grant before resolving, resolve the broadcast through the serving origin like +a SUBSCRIBE (dynamic handlers included), wait for the track's info, and answer +TRACK_STATUS_OK. A requester that cancels is owed nothing and releases its +demand through the path #5411 added. Errors map to the same refusal codes as +Rust. + +Split out of [JavaScript FETCH](/quest/m1/js-fetch.md), which is blocked on +JS ranges; answering TRACK_STATUS does not need FETCH. + +Verify with a native IETF subscriber against a browser publisher: on drafts +14-16 and 18-22 a fetch-only request gets TRACK_STATUS_OK with no SUBSCRIBE +fallback, while draft 17 keeps subscribing (`TrackStatusOk::describes_track`). +On every draft, a cancelled request releases origin demand and a request +outside the grant is refused. Run `just test interop --all`. + +Public API: none. Wire: implement the existing TRACK_STATUS and +TRACK_STATUS_OK formats in JS; drop JavaScript from the TRACK_STATUS refusals +in `doc/concept/standard.md`. + +## Related + +- [JavaScript FETCH](/quest/m1/js-fetch.md) - serves the FETCH that follows this answer diff --git a/quest/m2/README.md b/quest/m2/README.md index 4574bf3498..4d90a14ebb 100644 --- a/quest/m2/README.md +++ b/quest/m2/README.md @@ -119,3 +119,4 @@ and bench readiness. Refusal reasons folded the same day into - [Catalog broadcast references in MSF and GStreamer](/quest/m2/catalog-broadcast-reference.md) - MSF conversion and the GStreamer source honor a rendition's `broadcast` or refuse it, instead of resolving it in the catalog's own broadcast - [Binding track resolver](/quest/m2/binding-track-helper.md) - binding apps subscribe to a rendition without inlining `track` or the key - [Exporters follow a rendition to a new track](/quest/m2/export-rendition-rebind.md) - the fMP4, TS, MKV and FLV exporters rebuild a rendition whose resolved `track` or `broadcast` changes under the same ID +- [IETF driver double poll](/quest/m2/ietf-driver-double-poll.md) - a fired deadline costs a moq-transport session's driver one poll, as #5393 makes moq-lite's diff --git a/quest/m2/ietf-driver-double-poll.md b/quest/m2/ietf-driver-double-poll.md new file mode 100644 index 0000000000..cc593c0f05 --- /dev/null +++ b/quest/m2/ietf-driver-double-poll.md @@ -0,0 +1,24 @@ +# [S] A fired deadline costs a moq-transport driver one poll + +## Goal + +A moq-transport (IETF) session's driver is polled once per fired +`time::Deadline`, as #5393 makes a moq-lite session's, and +`rs/moq-net/tests/idle_session.rs` bounds the IETF driver at one poll per +refresh too. + +## Plan + +Start after #5393 (open as of 2026-10-13) merges, and remeasure on top of it. +#5393 adds `kio::Waiter::batch`, which holds a `Tasks` set's wake of its own +owner during that owner's poll. On that branch, IETF sessions still poll +twice: the driver is a boxed future, so its children's sets are woken inside +nested polls that the batch scope does not cover. Find where the nested poll +escapes the scope and cover it, or restructure the driver so its children +belong to the driver's own `Tasks` set. + +Measure with the existing `session_idle` bench and the poll-counting harness +(`rs/moq-net/tests/support/harness.rs`). + +Public API: none expected. Wire: none. +