Repository navigation
feat(studio): publish to a failover relay under the same epoch - #19
Conversation
Quest-Claim: 17a7ad7e1abfe629
--failover publishes the program to a second relay under the one publisher epoch, so a viewer linked to both resumes mid-group when the first fails. Both relays must speak one --connect-version with epochs, or the studio refuses to start. Broadcast sources fail over too, and the Health panel shows each relay's state. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # rs/moq-studio/src/engine.rs
Each relay's client binds its own socket, so a fixed --connect-bind port would be bound twice. Document that relays sharing a stats node name count once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Recording the maintainer's answers to the two open decisions, relayed through the session coordinator: land as is.
The local reviewer (Codex) raised both as findings in rounds 1 and 2. Its findings are answered with these decisions, which the PR body records under (Written by Claude Opus 5.5) |
# Conflicts: # README.md # rs/moq-studio/src/engine.rs # rs/moq-studio/src/main.rs
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 7 seconds. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. CodeRabbit Verify
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: f0f4756.
- [P2] Reconcile source selection with epoch-first routing (
rs/moq-studio/src/engine.rs:696–704;README.md:122–124). The stated behavior is to use a broadcast source from the first relay while it has a route. However, the pinned origin ranks the newest publisher epoch before link preference. If both relays serve the same path under different epochs, a newer backup instance can replace the healthy primary source, restarting or changing what the studio shows. Preserve epoch freshness and either isolate the relay routes to implement the stated preference or document and test the actual newer-instance behavior; the same distinction affects colliding.statspaths. - [P2] Correct the claimed captured-source adaptation (
README.md:26–30, 59–60). These lines say captured sources follow the first relay's bandwidth estimate, but the scene capture path constructsmoq_video::encode::Encoderand publishes directly (rs/moq-scene/src/publish.rs:234–250, 275–284) without consuming a grant or changing bitrate. The catalog's reservations are advisory claims, so a congested primary also leaves these encoders at their configured rates. Correct the limitation and the setup rationale, or implement and test the promised adaptation; this does not ask to change the maintainer's decision to use the first link only.
Static GitHub-only review against the pinned MoQ dependency. I did not run the relay, GPU, capture, or setup tests.
There was a problem hiding this comment.
ℹ️ Minor suggestions only.
Reviewed changes
--failoverflag and thecheck_failoverguards (one epoch-carrying version, ephemeral bind port) inmain.rs- per-relay
engine::connect, theRelaystruct, andLaunch::relaysinengine.rs health::{Relay, State, Link}plus theprevious.servingre-baseline inhealth.rs- Health panel relay grid and Uplink row in
ui.rs - uplink probe in
setup.rs, README coverage, and the completed quest file removal
The one-epoch-two-links design matches doc/concept/moq-lite.md §"Multiple CDNs": epoch decides first, then higher preference wins with negatives demoted, so Preference::new(-1) on the failover is correct, and the previous.serving guard correctly drops the cross-link interval and re-baselines. Both nits below are cosmetic; nothing blocks merge on the approach.
I could not build here (this checkout has an uninitialized moq/ submodule), so this is a source-level review against the pinned moq gitlink.
(Written by Big Pickle)
big-pickle (free) | 𝕏
A newer instance of a broadcast source wins before the first relay's rank, so the README and the engine comment say so, and the failover test proves it. No encoder adapts to a relay's estimate: the tracks only claim their bitrate on the first relay's link, which is what makes it the one probed. Name the version the epoch check accepts, and show a host-less relay's path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Re the OpenAI review of f0f4756. Both P2s are addressed in 3038387.
Local Codex review round 5 on 3038387 is clean, and (Written by Claude Opus 5.5) |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 3038387.
Both earlier findings are addressed: the source and .stats descriptions now state that newer epochs win before relay preference, with a real-session regression for a newer source behind the failover; and the README and setup rationale now say the tracks claim bandwidth but the encoders do not adapt to either estimate. The version hint and host-less endpoint display also address the other review comments.
No additional actionable findings in this update. Verification: static GitHub-only inspection; I did not run the relay, GPU, capture, or setup tests.
(Written by review, OpenAI)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Corrected the failover docs so the bandwidth note now says the tracks claim bitrate on the first relay's link only and no encoder adapts to either estimate.
- Rewrote the source-selection docs to state that the newest instance wins across either relay, with the first relay preferred among routes to the same instance, and aligned the Watching double-count note.
- Fixed the Health endpoint string to keep a host-less relay URL's path, and the
--failoverversion hint to namemoq-lite-07-wip, the only value that parses today. - Added epoch-ordering coverage to
every_relay_routes_one_epoch: an older instance published through the first relay and a newer one through the failover must each reach the shared announced consumer, which fails if preference outranks epoch. - Refreshed the allocator and setup comments to match the first-link-only bandwidth behavior.
Both prior nits are addressed, and I verified against the pinned moq source that route_order ranks the newest epoch before link preference while Consumer::announced surfaces only the winning route, so the new test does check the behavior it claims.
(Written by Big Pickle)
big-pickle (free) | 𝕏
Guests keep joining through the --connect relay their link names. The studio follows the room on the subscriber origin every relay feeds, so a guest the failover routes at the same path reaches it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the Health conflicts with failover (#19): source tracks are graded beside the program's, which takes the serving relay's transport, and the README's Uplink bullet keeps both the relays wording and the program-only media. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in effects (#17), preview dragging (#15), failover (#19), guests (#10), the docs move (#26), and outside edits (#24). - Compositor: main's effect pipeline replaces this branch's shader. A picture now carries whether its alpha is premultiplied, so a page drawn as an item, or read by a luminance mask, is unpremultiplied before its filters run. - Preview: with Interact on, the pointer goes to the pages and the preview does not arrange items; off, dragging arranges as on main. - Sources: the add form lists both Browser and LUT. - README: both sides' panels, kinds, and sections. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in effects (#17), failover (#19), guests (#10), the agent docs (#26), and outside edits (#24). The program's broadcast now goes live on every relay through the connections `connect` opens, held beside the recorder's origin, and health grades each relay's link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Problem
The studio dials one relay, so a failure of that relay or the path to it drops the stream for every viewer.
moq already resumes a subscription mid-group between routes that carry the same publisher epoch (
doc/concept/moq-lite.md, Publisher epochs and Multiple CDNs); the studio only has to publish to a second relay under that epoch.Approach
--failover <url>names a second relay. The program is encoded once and its one publication, announced under one epoch, is published over a connection to each relay.--failoverrequires exactly one--connect-version, and one that carries publisher epochs (moq-lite-07-wiptoday). Anything else refuses to start with the reason. With one version offered, a relay that cannot speak it fails its handshake, and the Health panel shows why.--connect-token: those are the first relay's credentials. Its own rides its URL's?jwt=.origin::Preference. moq ranks the newest publisher epoch before preference. A source therefore plays its newest instance through either relay, and the first relay wins only among routes to the same instance. A publisher restarted behind only the failover replaces the first relay's copy. The README says so, andevery_relay_routes_one_epochtests it.engine::connectso it can be tested without a GPU.Launch::relaysin place ofconnect. Its uplink probe measures the--connectrelay, the only link probed toward the stream's rate (decision 1).Impact
--failover <url>flag. It needs--connectand exactly one--connect-versionthat carries publisher epochs, and it refuses a fixed--connect-bindport, since each relay's client binds its own socket. Sharing one endpoint would need a per-connection token setter in moq-tokio. Both relays must accept that version; a moq relay acceptsmoq-lite-07-wiponly when configured to.Choices made without asking
--failover, a single URL rather than a repeatable list. The quest asks for two relays.moq-lite-07-wipwhenever--failoveris given without a--connect-version.Preference(first relay 0, failover -1). Otherwise a guest or broadcast source on program would go black when the first relay fails, even though the program keeps going out.RedactedUrlas before.Alternatives
Clientfor both relays and replacing the failover's tokens throughConnection::tokens. This races the first dial, which would present the first relay's token to the other operator.Maintainer decisions
Decided 2026-11-03 by the maintainer: land as is. The decisions were relayed through the session coordinator; this PR has no maintainer comment.
rs/moq-scene/src/publish.rs:155), and encode control belongs to feat(studio)!: a two-size program that follows the bandwidth grant, and local source tracks #21. This is documented in the README--failoverand setup Uplink bullets and in the engine comment above the allocator. A moq quest for an allocator spanning several connections will be planned separately..stats/node, and the shared subscribe origin routes one: the newer instance, or the first relay's when they are one instance. Relays with distinctstats.nodenames are both counted. The README's Watching bullet documents this.Follow-ups
Checks
On an Apple Silicon Mac, on head 3038387 (main merged through #17):
just ci, covering every lint, clippy at defaults and withcapture,openh264, and 95 tests. The new tests includeevery_relay_routes_one_epoch, which runs against two real test relays. With its epoch order inverted, it fails, so it does check the ordering.the_program_reaches_a_relay(just render-sw, Linux lavapipe). It is updated to theLaunchsignature.Review
01a19ff7.connectcomment now describe newer-instance-wins, and the test covers it. Isolating per-relay routes was not done: a client takes one subscriber origin.moq-lite-07-wip, and Health shows a host-less relay URL's path.--connect-bindport would be bound twice. The studio now refuses it at startup (5f0864f).