Skip to content

fix(mux): a TS export drains a left publisher's buffered tail before it ends - #7137

Merged
kixelated merged 4 commits into
mainfrom
quest/m0/nightly/stitch-discontinuity
Oct 29, 2026
Merged

kixelated merged 4 commits into
mainfrom
quest/m0/nightly/stitch-discontinuity

Conversation

@kixelated

@kixelated kixelated commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

The nightly CLI-export job failed export::a_restarted_publisher_is_a_program_switch_with_stitch at "every PID flags the break" ([256, 257, 17]). It still reproduces on current main (53c0dcd685), so neither moq-dev/moq#7049 nor moq-dev/moq#7048 fixed it.

The discontinuity signaling itself was correct. A dump of the export's output showed the new program's first packet on every PID (PCR PID 256, PAT, PMT, SDT 17, audio 257) carrying discontinuity_indicator. The unflagged packets after the test's mark were the old program's: about 200 packets of the interrupted publisher's buffered tail. They went out about 2 s after the mark, in one 30 ms burst, when the replacement arrived.

Root cause: when a publisher left (Unroutable), Export::poll_next returned the error right away, with up to --delay of media still in its jitter buffer. The Follower stops polling the export while it waits for a return. That tail then sat until the replacement (or the same instance) came back, and went out late, behind the gap.

moq-dev/moq#6919 made this visible: it keeps the old program running while a replacement acquires. Before that, a switch discarded the old program, stale tail included. The last passing nightly ran at 22808bbb2f, and #6919 is in the range after it.

Change

  • rs/moq-mux/src/container/ts/export.rs: a publisher that leaves gets its input stopped at once, the way an expired follow stops it. What it already buffered goes out on schedule, and only then does the export report the cause. This applies to failures from prepare and from the in-loop refill. The retirement half of stop_old is now retire(), which both paths share. A program whose publisher left keeps its catalog, because the follower reads its format to subscribe a return. A resumed program clears its pending cause.
  • rs/moq-mux/src/container/ts/export_test.rs: a_publisher_that_leaves_drains_its_buffered_tail_before_the_end covers this deterministically with paused time. It fails on main ("a tail went out after the publisher left"), since the export ends at once with the tail still buffered, and passes with the fix.
  • doc/bin/cli.md: says a publisher that leaves still gets its buffered output out on schedule before the follow waits.
  • Deletes the quest quest/m0/nightly/stitch-discontinuity.md.

The end-to-end assertion is unchanged.

Choices made without the maintainer

  • Play the left publisher's tail out on time instead of dropping it at the switch. The alternative was to discard a dead old program's stale tail when a replacement arrives, which is what happened before fix(mux): retain old program during stitched epoch acquisition #6919. I chose draining because it loses no media, it also fixes the same late burst on a same-instance return, and it matches what a clean finish already does. The cost is that --follow (and exit 1 on a lost publisher) starts up to --delay later.
  • A replacement that is cancelled after the old publisher left still reports the old publisher's cause once the old tail drains.

Validation

  • just check origin/main on the merged head: 4153 Rust tests passed (60 skipped), plus clippy, JS, markdown, quest, shell and toml checks.
  • just rs cli-export on the merged head: 14 of 14 passed, including the previously failing stitch test and the_same_instance_within_the_follow_continues.
  • cargo test -p moq-mux: 1837 passed before the main merge.

Follow-ups

  • The sibling quest quest/m1/ts-reliability/step-restart.md depends on stitch signaling. Signaling was fine; its stitch test needed this drain.
  • If stop_old drops the old catalog on follow expiry and the pending replacement is then cancelled, Follower could panic in catalog_format ("selected catalog"). This PR does not touch that path; it may be worth a deterministic test.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 3 commits October 28, 2026 15:03
…it ends

When the publisher left, the export reported the end at once and left up to
--delay of released media in its jitter buffer. A follower stops polling while
it waits, so that tail went out late, in one burst behind the gap, when the
publisher or a stitched replacement came back. The nightly stitch test caught
it as old-program packets after its mark, so the first packets it checked on
PIDs 256, 257 and 17 carried no break flag.

The export now stops a left publisher's input but plays out what it buffered
on schedule, and only then reports the cause. The retired program keeps its
catalog, since the follower subscribes a return with its format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title A stitched restart signals every TS discontinuity fix(mux): a TS export drains a left publisher's buffered tail before it ends Oct 28, 2026
@kixelated
kixelated marked this pull request as ready for review October 28, 2026 23:13
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You'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 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f1106f1a-1b2b-4e02-9093-c3f7ada14dd6
Commits

Reviewing files that changed from the base of the PR and between 4f7f7c4 and 03d12db.

Files selected for processing: 5
  • doc/bin/cli.md
  • quest/m0/nightly/README.md
  • quest/m0/nightly/stitch-discontinuity.md
  • rs/moq-mux/src/container/ts/export.rs
  • rs/moq-mux/src/container/ts/export_test.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-29T06:38:54.499088Z 03d12db New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)
Reviewed commit: 588dcf6

No actionable bugs found in the changed paths. Draining already-buffered media before reporting a lost publisher is a sound direction: it prevents the follower from parking a live output tail, while preserving the existing pacing and replacement handoff. Sharing retirement logic is simpler than a second drain mechanism; retaining the catalog for resubscription and clearing the saved cause on resume fit the follower's lifecycle. The delayed start of the follow timeout is documented.

The paused-time regression checks both the buffered tail and its release timing without weakening the stitch assertion. Static review of the diff and surrounding export/follower code only; I did not run tests or independently reproduce the nightly failure.

@kixelated

Copy link
Copy Markdown
Collaborator Author

Maintainer decision: keep playing out a departed publisher's buffered media on schedule before following or exiting (no media lost; --follow and the lost-publisher exit start up to --delay later).

(Written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary for head 588dcf6493ef1462745f610faa9603836506c6d3:

  • Change: a TS export whose publisher leaves stops its input at once and plays out the already-buffered tail on schedule before reporting the cause, so the old program's tail no longer bursts out late behind the stitch. Retirement is shared via retire(), the catalog is kept for a return, and a resumed program clears its pending cause. Deletes quest/m0/nightly/stitch-discontinuity.md.
  • Decision: the maintainer chose to keep playing out a departed publisher's buffer (no media lost; --follow and the lost-publisher exit start up to --delay later), as recorded above.
  • Reviews: Codex reacted with no findings and the automated review on this head found no actionable bugs. No unresolved threads.
  • Checks: all 12 required checks passed on this head; quest check --base origin/main passed (647 documents ok).
  • The follow-cancel catalog panic noted in the body is already planned in quest: plan follow-ups from the 2026-10-28 m0/m1 spawn #7195.

(Written by Claude Opus 5.5)

@kixelated
kixelated added this pull request to the merge queue Oct 29, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 29, 2026
@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged origin/main (4f7f7c4) into the branch to clear the merge_conflict that pulled it from the queue. The only overlap was the quest/m0/nightly/README.md Required list, which quest's merge driver resolved: this quest's link is still removed, and main's own removals are kept. No code conflicts, so no new review is needed. quest check --base origin/main passes. Re-landing at 03d12db.

(Written by Claude Opus 5.5)

@kixelated
kixelated enabled auto-merge October 29, 2026 06:37
@kixelated
kixelated added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 66bdbb9 Oct 29, 2026
11 checks passed
@kixelated
kixelated deleted the quest/m0/nightly/stitch-discontinuity branch October 29, 2026 11:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant