From 4514ae02b20ab5a061d2c0b25815301b86e0348c Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 26 Oct 2026 13:20:11 -0700 Subject: [PATCH 1/2] quest: import the multi-object datagram and redundant-switch issues Plan moq-dev/moq#6885 as m1/datagram-object-drop (a hop or reader that cannot carry an Object ID delivers only object 0) and moq-dev/moq#6877 as m1/ts-reliability/epoch-switch-drain (a stitched switch keeps playing the old instance until the new clock is acquired). Co-Authored-By: Claude Opus 5.5 --- quest/m1/README.md | 1 + quest/m1/datagram-object-drop.md | 55 +++++++++++++++++++ quest/m1/ts-reliability/README.md | 1 + quest/m1/ts-reliability/epoch-switch-drain.md | 50 +++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 quest/m1/datagram-object-drop.md create mode 100644 quest/m1/ts-reliability/epoch-switch-drain.md diff --git a/quest/m1/README.md b/quest/m1/README.md index 0eed1abb522..95cae8162ab 100644 --- a/quest/m1/README.md +++ b/quest/m1/README.md @@ -118,6 +118,7 @@ surface and zero-copy texture import, land before the cut. - [Release audit findings](/quest/m1/release-audit/README.md) - resolve newly identified resource, parser, playback, and interoperability risks before the cut - [Pre-cut audit findings](/quest/m1/cut-audit/README.md) - fix the crashes, hostile-input costs, and correctness bugs the 2026-10-24 audit of main found - [Cancelled moq-transport requests stop](/quest/m1/ietf-request-cancel.md) - a cancelled request releases its state even when the peer withholds credit +- [Datagrams past object 0 never pose as object 0](/quest/m1/datagram-object-drop.md) - a hop or reader that cannot carry an Object ID delivers only object 0 of a moq-transport datagram group - [Empty segments refused](/quest/m1/empty-segments.md) - `room//cam` fails loud instead of naming `room/cam` - [In-band auth](/quest/m1/auth/README.md) - a session tells its peer what it may publish and subscribe to, unions tokens presented in band, and fails loud on an out-of-scope publish - [Cluster routing](/quest/m1/cluster-routing/README.md) - prove shared routing contracts, implement per-origin routes, and connect outbound local clusters diff --git a/quest/m1/datagram-object-drop.md b/quest/m1/datagram-object-drop.md new file mode 100644 index 00000000000..4c543e23457 --- /dev/null +++ b/quest/m1/datagram-object-drop.md @@ -0,0 +1,55 @@ +# [S] Datagrams past object 0 never pose as object 0 + +## Goal + +A moq-transport datagram group with several objects reaches every surface +that keeps Object IDs intact, and every surface that cannot carry an Object ID +delivers only object 0. Today a moq-lite hop without the `ietf` extension +forwards every object as a plain datagram at the same sequence, and the +receiver keeps whichever arrives first. If object 0 is lost or reordered, +object 1 is delivered as the group's only datagram, which is silent +substitution, not loss. The plain Rust datagram reader also hands out every +object's payload at one sequence with no way to tell them apart. + +Out of scope: exposing an Object ID on the public `Datagram` in Rust, JS, or +the bindings; refusing the track; a dropped-datagram stat; multi-object +datagram support in JS. + +## Plan + +Decided 2026-10-26 (maintainer, importing +[moq-dev/moq#6885](https://github.com/moq-dev/moq/issues/6885)): + +- Keep the passthrough from + [moq-dev/moq#5494](https://github.com/moq-dev/moq/pull/5494). Rust relays + already carry Object IDs between moq-transport peers and across a lite-07 + hop with the `ietf` extension. The report was against release 0.17, whose + IETF ingress still dropped every object past 0. Rejected: refusing at + ingress everywhere, which deletes that passthrough. +- Drop, don't refuse. A hop that cannot carry the Object ID sends only + object 0, matching how a hop drops an oversized datagram or a status object + it cannot carry. The rest of the track keeps flowing. Rejected: aborting the + track with `Unsupported`, which would also cost the lite viewer object 0. +- Surfaces: + - `lite::Publisher::serve_datagram` (`rs/moq-net/src/lite/publisher.rs`), + when `ietf` is off: skip a datagram whose `wire.object_id` is past 0, as + it already skips a status. + - `track::Subscriber::poll_recv_datagram` (`rs/moq-net/src/model/track.rs`): + skip a stored datagram past object 0, as it already skips a status. + - JS's IETF subscriber already drops past object 0 and needs no change. +- Debug log like the other per-hop drops; no new stat. +- Fix the docs inline: `doc/concept/standard.md` (the objects and datagrams + bullet says plain readers and lite peers see "normal datagram payloads") + and the `model/datagram.rs` module doc. The cursor's private + `poll_recv_datagram` comment says ranges match "by Object ID", which the + code does not do; make it say what the code does. +- Test both surfaces with a group carrying objects 0 to 2, including object + 0 arriving last and object 0 never arriving. + +## Closes + +- [#6885](https://github.com/moq-dev/moq/issues/6885) - moq-relay silently drops datagram objects after object 0 in a group + +## Related + +- [moq-transport 18 and 22 only](/quest/m1/ietf-drop/README.md) - the reporter's draft 16 is among the drafts that quest deletes diff --git a/quest/m1/ts-reliability/README.md b/quest/m1/ts-reliability/README.md index 09e5a7f65b5..bfb7a0c3181 100644 --- a/quest/m1/ts-reliability/README.md +++ b/quest/m1/ts-reliability/README.md @@ -102,6 +102,7 @@ catalog linger (#5305). - [Cut arrival](/quest/m1/ts-reliability/cut-arrival.md) - frames held through a dead source's silence longer than the delay drop as late instead of ending a stitching export - [TS workflow passes](/quest/m1/ts-reliability/validation-green.md) - the scheduled TS lanes pass on main again, with no lane loosened - [Export SI order](/quest/m1/ts-reliability/export-si-order.md) - a TS export sends SI and media after its PMT and a PCR interval, so a hop keeps first emissions +- [A stitched switch keeps playing the old instance](/quest/m1/ts-reliability/epoch-switch-drain.md) - `export ts --stitch` plays the old epoch until the new one's clock is acquired, so a backup starting leaves no hole ## Related diff --git a/quest/m1/ts-reliability/epoch-switch-drain.md b/quest/m1/ts-reliability/epoch-switch-drain.md new file mode 100644 index 00000000000..683d4dc0896 --- /dev/null +++ b/quest/m1/ts-reliability/epoch-switch-drain.md @@ -0,0 +1,50 @@ +# [M] A stitched switch keeps playing the old instance + +## Goal + +When `moq export ts --stitch` follows a newer epoch, the output keeps playing +the old instance until the new one's clock is acquired, then switches with the +marked program discontinuity it has today. A switch away from a healthy leg, +such as a redundant importer starting or restarting, leaves no hole in the +output. A switch after a failure costs only what the failed leg did not +deliver. Today `Export::followed` +(`rs/moq-mux/src/container/ts/export.rs`) replaces the whole export on a +switch, discarding the old leg's held backlog (up to one `--delay`) and +holding output while the new clock is acquired. + +Out of scope: changing route order, timestamp continuity across epochs, and +hitless failover between importers. + +## Plan + +Decided 2026-10-26 (maintainer, importing +[moq-dev/moq#6877](https://github.com/moq-dev/moq/issues/6877)): + +- Routing stays as decided on 2026-10-19: the newest epoch wins, so starting + or restarting a backup importer is a switch. Rejected: a standby epoch that + routes only when no live epoch remains (a second, publisher-declared + selection policy ahead of epoch on the wire), and receiver-select in the CLI + (a multi-broadcast `export ts`). Separate paths chosen by the subscriber + stay documented in `doc/setup/redundancy.md`. +- The new instance opens a new jitter generation instead of a new export, + reusing the generation machinery `rs/moq-mux/src/jitter.rs` already has for + in-source timeline restarts: generations go out in turn, and the new clock + is never ahead of a deadline already given out. +- The old instance's subscription keeps feeding its generation until the new + generation is acquired, bounded by the `--follow` window, then stops. + Rejected: playing out only what the buffer holds at the switch. +- Distinct epochs promise no timestamp relation, so the switch stays a program + change: new PMT version, every PID flagged, PCR discontinuity. Do not align + the legs' timestamps; that is the cross-importer alignment dropped on + 2026-10-19. +- The redundant failover integration test in + `rs/moq-cli/tests/integration/export.rs` prints each switch's output gap + without asserting it. Assert that a switch away from a healthy leg leaves + no gap, behind a bufferbloat impairment. +- Fix inline: `doc/setup/redundancy.md` and `doc/bin/cli.md` say the switch + is not hitless and that starting a backup causes it; say what a switch now + costs. + +## Closes + +- [#6877](https://github.com/moq-dev/moq/issues/6877) - a redundant ingest pair: let a standby epoch wait behind a live one, and let a follower switch without emptying its delay From 42a5ef00d5e69dc3f45f337d8413f6ca45e24dec Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 26 Oct 2026 23:06:55 -0700 Subject: [PATCH 2/2] quest: describe the datagram reader and the #6877 closure accurately Co-Authored-By: Claude Opus 5.5 --- quest/m1/datagram-object-drop.md | 9 ++++----- quest/m1/ts-reliability/epoch-switch-drain.md | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/quest/m1/datagram-object-drop.md b/quest/m1/datagram-object-drop.md index 4c543e23457..f0aca47dd93 100644 --- a/quest/m1/datagram-object-drop.md +++ b/quest/m1/datagram-object-drop.md @@ -6,10 +6,9 @@ A moq-transport datagram group with several objects reaches every surface that keeps Object IDs intact, and every surface that cannot carry an Object ID delivers only object 0. Today a moq-lite hop without the `ietf` extension forwards every object as a plain datagram at the same sequence, and the -receiver keeps whichever arrives first. If object 0 is lost or reordered, -object 1 is delivered as the group's only datagram, which is silent -substitution, not loss. The plain Rust datagram reader also hands out every -object's payload at one sequence with no way to tell them apart. +plain Rust datagram reader hands out every object's payload at one sequence +with no way to tell them apart. If object 0 is lost or arrives late, object 1 +is delivered as the group's datagram, which is silent substitution, not loss. Out of scope: exposing an Object ID on the public `Datagram` in Rust, JS, or the bindings; refusing the track; a dropped-datagram stat; multi-object @@ -31,7 +30,7 @@ Decided 2026-10-26 (maintainer, importing it cannot carry. The rest of the track keeps flowing. Rejected: aborting the track with `Unsupported`, which would also cost the lite viewer object 0. - Surfaces: - - `lite::Publisher::serve_datagram` (`rs/moq-net/src/lite/publisher.rs`), + - `Subscription::serve_datagram` (`rs/moq-net/src/lite/publisher.rs`), when `ietf` is off: skip a datagram whose `wire.object_id` is past 0, as it already skips a status. - `track::Subscriber::poll_recv_datagram` (`rs/moq-net/src/model/track.rs`): diff --git a/quest/m1/ts-reliability/epoch-switch-drain.md b/quest/m1/ts-reliability/epoch-switch-drain.md index 683d4dc0896..1d2b05d5e24 100644 --- a/quest/m1/ts-reliability/epoch-switch-drain.md +++ b/quest/m1/ts-reliability/epoch-switch-drain.md @@ -47,4 +47,4 @@ Decided 2026-10-26 (maintainer, importing ## Closes -- [#6877](https://github.com/moq-dev/moq/issues/6877) - a redundant ingest pair: let a standby epoch wait behind a live one, and let a follower switch without emptying its delay +- [#6877](https://github.com/moq-dev/moq/issues/6877) - a redundant ingest pair: a follower switches without emptying its delay (the standby-epoch ask is declined)