diff --git a/quest/m1/README.md b/quest/m1/README.md index 12192ca55f..d5a22c1091 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -62,6 +62,8 @@ were done or not worth their cost. - [Dogfood hosted worklets](/quest/m1/dogfood-assets.md) - moq.dev and the moq.pro dashboard host the worklets and call `assets()` after the release - [Media audio-tone check](/quest/m1/media-audio-tone.md) - the media lane's audio-tone check passes under load, fixed at its cause - [More tests under load](/quest/m1/test-flakes-2/README.md) - the second round of load-only failures, one quest per flake, fixed at the cause +- [IETF request headers](/quest/m1/ietf-dispatch-headers.md) - each bidi request reads its header in its own task, so a slow one never blocks the next +- [END_OF_TRACK placement](/quest/m1/ietf-end-of-track-placement.md) - END_OF_TRACK rides the upstream's Location, and Rust's header stops claiming END_OF_GROUP - [moq-uring tests under load](/quest/m1/uring-tests-under-load.md) - uring tests pass while parallel checks share locked memory - [FFI runtime](/quest/m1/ffi-runtime.md) - moq-ffi drives moq on a multi-thread runtime instead of one thread - [Audio group duration](/quest/m1/audio-group-duration.md) - audio groups span at least 20 ms by default, so small frames don't mint a group each @@ -115,6 +117,7 @@ were done or not worth their cost. - [Benchmark regressions in CI](/quest/m1/bench-ci.md) - PRs get a non-blocking comparison of the Criterion benches they affect, and a nightly trend on main alerts on regressions - [Mergeable bench buckets](/quest/m1/bench-buckets.md) - moq-bench emits per-interval latency buckets that sum across processes and hosts - [Relay session bench](/quest/m1/bench-relay.md) - the same scenario through moq-relay's own connection handling +- [Session burst hang](/quest/m1/session-burst-hang.md) - the burst sweep completes at 16 subscriptions and 16+ groups per round - [Read-only lookup](/quest/m1/read-only-lookup.md) - a live track lookup stops waking the front and every demand watcher - [Browser benchmarks](/quest/m1/browser-benchmarks.md) - measure JS transport, container, decode, and render costs in an identified browser - [Generated @moq/net](/quest/m1/rs2ts/README.md) - the browser runs moq-net as TypeScript generated from the Rust source, retiring js/net's hand-written protocol and model code @@ -131,6 +134,7 @@ were done or not worth their cost. - [Rust papercuts](/quest/m1/papercuts-rs.md) - HTTP refusals are counted, and a `u64::MAX` resume is unbounded - [JS papercuts](/quest/m1/papercuts-js.md) - IETF status 0 keeps the subgroup open, a muted rendition change settles, and bad element attributes warn - [Drop insertTrack](/quest/m1/js-insert-track.md) - `createTrack` is `@moq/net`'s only way to add a track by hand, as in Rust +- [JS subgroup heads race close](/quest/m1/js-head-race.md) - a stalled publisher no longer pins a subgroup reader past unsubscribe - [Front parking](/quest/m1/origin-front-parks.md) - an unroutable request waits on a front instead of re-asking on every route-table move - [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 diff --git a/quest/m1/ietf-dispatch-headers.md b/quest/m1/ietf-dispatch-headers.md new file mode 100644 index 0000000000..223125d9b2 --- /dev/null +++ b/quest/m1/ietf-dispatch-headers.md @@ -0,0 +1,24 @@ +# [S] A slow IETF request header never blocks the next request + +## Goal + +`rs/moq-net`'s moq-transport session reads each incoming bidi request +stream's header in its own task, so a request stream whose header stalls +behind flow control never holds up accepting the next one. + +## Plan + +[#5086](https://github.com/moq-dev/moq/pull/5086) fixed the same pattern for +uni streams: `run_unis` waited for each stream's type before accepting the +next, and stalled under connection flow control. `run_dispatch` +(`rs/moq-net/src/ietf/session.rs`) still reads `id` and `ietf::Body` inside +the accept loop. Move the header read into the per-stream task, keeping the +current error handling: a stream that dies before its header is dropped, and +any other header error still fails the session. JS already handles each bidi +stream in its own task (`#runBidis` in `js/net/src/ietf/connection.ts`), so +this is Rust only (decided 2026-10-08). + +Test, beside #5086's `a_silent_stream_does_not_hold_up_the_next`: a silent +bidi request stream does not block a SUBSCRIBE on the next one. + +Public API: none. Wire: none. diff --git a/quest/m1/ietf-end-of-track-placement.md b/quest/m1/ietf-end-of-track-placement.md new file mode 100644 index 0000000000..61546c904c --- /dev/null +++ b/quest/m1/ietf-end-of-track-placement.md @@ -0,0 +1,29 @@ +# [S] END_OF_TRACK rides the upstream's Location + +## Goal + +When a moq-transport publisher ends a track, `rs/moq-net` and `js/net` send +END_OF_TRACK at the upstream's final Location (5/5 on group 5's stream), +rather than as 6/0 on a new stream. Rust's END_OF_TRACK stream header clears +END_OF_GROUP, as JS's already does. + +## Plan + +Deferred from [#5077](https://github.com/moq-dev/moq/pull/5077) (decided +2026-10-08 there as conformance polish with no lost data), from Fastly's +follow-up on #5020 (cells `status-end-of-track` and `publish-done`). +Decided 2026-10-08: both in one quest. + +- Rust's `write_end_of_track` (`rs/moq-net/src/ietf/publisher.rs`) builds its + header from `GroupFlags::default()`, which sets END_OF_GROUP; JS clears it. +- Moving onto the upstream's Location means writing the status on the last + group's own stream when it is still open; settle what happens when that + stream has already finished. + +Test in both languages: a track ending after group 5 object 5 delivers +END_OF_TRACK at 5/5, and an END_OF_TRACK on its own stream does not claim +END_OF_GROUP. Fastly's moq-relay-interop harness is external, so the PR +notes the change for their next rerun of those two cells. + +Public API: none. Wire: no format change, but a peer sees END_OF_TRACK at a +different Location. diff --git a/quest/m1/js-head-race.md b/quest/m1/js-head-race.md new file mode 100644 index 0000000000..6ba7a6d303 --- /dev/null +++ b/quest/m1/js-head-race.md @@ -0,0 +1,24 @@ +# [S] JS subgroup heads race the subscription closing + +## Goal + +In `@moq/net`'s IETF subscriber, every await on a subgroup stream before its +group producer exists (the FIRST_OBJECT `peekU62()` and the first +`decodeMaybe`) ends as soon as the subscription closes. A stalled publisher no +longer keeps the Reader, its handler, and the Tail entry alive until the +stream moves or the session closes. + +## Plan + +Found by the review of [#5069](https://github.com/moq-dev/moq/pull/5069) +(the FIRST_OBJECT backport). Rust's `recv_group` already races these reads +against the subscription; mirror it in `js/net/src/ietf/subscriber.ts`. + +Decided 2026-10-08: `main` only, no `release` backport. Only a stalled +publisher is affected, and closing the session clears it. + +Test: a publisher opens a subgroup stream and stalls before its first object; +unsubscribing releases the reader and its tail entry without waiting on the +stream. + +Public API: none. Wire: none. diff --git a/quest/m1/session-burst-hang.md b/quest/m1/session-burst-hang.md new file mode 100644 index 0000000000..6835a8149e --- /dev/null +++ b/quest/m1/session-burst-hang.md @@ -0,0 +1,21 @@ +# [S] The session burst bench completes at every shape + +## Goal + +`rs/moq-net/benches/session.rs`'s `burst` sweep runs to completion at 16 +subscriptions and 16 or more groups per round, with or without the kio +budget, and the sweep covers those shapes again. + +## Plan + +Found while landing serve-budget +([#5088](https://github.com/moq-dev/moq/pull/5088)), which adds the `burst` +axis: with that axis applied, the bench hangs at those shapes without the +budget too, so the sweep was limited to shapes that complete. Decided 2026-10-08: +treat it as a possible session stall, not a bench setup limit. Find whether +it is cache size, stream credit, or a real stall, fix it at the cause, and +add a regression test if the cause is in the session rather than the bench. + +## Required + +- [A busy task yields within a budget](/quest/m0/serve-budget.md) - adds the `burst` axis