Skip to content

fix(moq-cli): an export failure on a live broadcast does not linger - #4947

Merged
kixelated merged 5 commits into
moq-dev:mainfrom
t0ms:cli/linger-export-failure
Oct 8, 2026
Merged

kixelated merged 5 commits into
moq-dev:mainfrom
t0ms:cli/linger-export-failure

Conversation

@t0ms

@t0ms t0ms commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Problem

moq export ts --linger reads every end of the export as the broadcast ending. An export that fails
on its own while the broadcast stays up, such as a catalog whose video codec TS cannot carry, waits
out the whole linger for a return that cannot come, since Source::returned waits for the ended
broadcast to close first, and then exits 1. With --linger 20s against an AV1 publisher, main
exits after 23.3 s. Planned as Export linger, from
#4645; the quest is deleted here.

Approach

  • On a failed end with a non-zero linger, run_ts waits up to CLOSE_GRACE (1 s, capped at the
    linger) for the broadcast consumer to close. If it closes, the failure was the broadcast ending,
    and the linger starts as before. If it is still up, the failure is the export's own: it logs a
    warning and exits with the error at once.
  • The grace settles the race the quest names: a killed publisher's tracks can error before its
    broadcast's close arrives, so the state is waited for rather than read once.
  • A clean catalog finish is unchanged, and so is --linger 0s.

Impact

  • An export failure on a live broadcast exits 1 within the grace instead of after the linger.
  • A broadcast that closes within 1 s of the failure lingers and resumes exactly as before.
  • No API, wire, catalog or flag change. One new warning line; the --linger help gains a sentence.

Alternatives

  • Classifying by the error: rejected, since a drop and an export failure both surface as track
    errors.
  • Reading is_closed() once: rejected, since the close can trail the track errors.
  • Waiting for the close for the whole linger is today's behaviour, and the case this fixes.

Validation

  • Two unit tests on closes_within, with paused time: a live broadcast does not close within the
    grace, and one closing a quarter of the grace after the failure counts as an end.
  • A relay-backed CLI test, an_export_failure_with_the_broadcast_up_exits_without_lingering:
    --linger 20s against moq import fmp4 of the AV1 fixture, with the publisher kept up. It fails
    on main ("exited after 23.27s") and passes here. All four export CLI tests pass, including the
    interrupted-publisher resume, which ends on a failure and then closes.
  • A local relay, export ts --linger and a PCR-paced broadcast capture, compared with main at
    edd671fff on the same arms. With the publisher SIGKILLed and replaced, three sessions resume
    in 3 of 4 runs here and 2 of 3 on main, and two sessions in none of 3 on either. One relay
    restart lingers and resumes once on both. Clean replay and continue, run here only (×3 each),
    resume at every return. The new warning fired in none of them. Across the nine failed ends that
    lingered, the broadcast closed within 13.8 ms of the first track error, so on loopback the grace
    has about 70× margin; a cross-host relay adds transport delay to that gap, which is what the
    margin is for.
  • The runs that do not resume are relay: a publisher replacing a killed one is sent the old session's subscriptions before it has published, and moq import ts exits with "rendition is not published" #4945, not this change: on both builds the replacement publisher
    is handed the killed session's subscriptions, and each such run ends on rendition is not
    published
    or a catalog protocol violation, then lingers once the broadcast closes and exits 1.
  • just check origin/main passes.

Follow-ups

`export ts --linger` waited out the whole linger after any failed end, so
an export that failed on its own while the broadcast stayed up waited for
a return that cannot come. A failed end now lingers only if the broadcast
closes within a 1 s grace, since a killed publisher's tracks can error
just before its close arrives; otherwise it exits with the error at once.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator

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: 7a7118b

No actionable correctness issue found. rs/moq-cli/src/subscribe.rs:399-408 separates a live-broadcast export failure from a broadcast end without using a racy single state read; zero-linger and clean-end paths remain unchanged. The bounded grace plus existing resume timeout is a sensible direction, and the live AV1 regression exercises the original failure.

Verification: reviewed the full diff, run/resume control flow, paused-time tests and relay-test additions. Static review only; I did not rerun CLI tests or cross-host publisher-loss trials. The one-second classification grace is a deliberate heuristic, so a slower close can still be classified as an export failure. This is a draft COMMENT review, not merge approval.

@kixelated
kixelated marked this pull request as ready for review October 8, 2026 00:15
@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: d42ceb46-3b3c-4833-bb32-993604db8e4d
📥 Commits

Reviewing files that changed from the base of the PR and between c6356be and c9e1ddd.

📒 Files selected for processing (6)
  • doc/bin/cli.md
  • quest/m1/README.md
  • quest/m1/export-ts-linger.md
  • rs/moq-cli/src/args.rs
  • rs/moq-cli/src/subscribe.rs
  • rs/moq-cli/tests/export.rs
💤 Files with no reviewable changes (2)
  • quest/m1/README.md
  • quest/m1/export-ts-linger.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 1 remain after this review.


Walkthrough

For nonzero linger, Subscribe::run_ts now checks whether the broadcast closes within min(CLOSE_GRACE, linger) after an export error. If it stays open, the command returns the export error. If it closes, the existing return-and-resume flow applies. The change adds unit and integration tests and updates CLI documentation. It also removes the m1 quest entry and its design note.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to c9e1d

A failed export on a live broadcast exits promptly, while a broadcast that closes can still wait for a restarted publisher. No actionable merge blocker remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: preventing export failures on live broadcasts from waiting through the full linger period.
Description check ✅ Passed The description directly explains the problem, implementation, impact, alternatives, and validation for the live-broadcast export failure fix.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
✨ Simplify code
  • Create a new PR
  • Autopilot · 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.

kixelated and others added 3 commits October 7, 2026 22:32
# Conflicts:
#	rs/moq-cli/src/args.rs
#	rs/moq-cli/src/subscribe.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator

Automated review: fix(moq-cli): an export failure on a live broadcast does not linger

On a failed end with a non-zero --linger, run_ts now waits up to CLOSE_GRACE (1 s, capped at the linger) for the broadcast to close. If it closes, the failure was the broadcast ending and the linger starts as before. If it stays up, the export failed on its own and exits 1 right away. There are two paused-time unit tests on closes_within and a relay-backed CLI test (AV1 into export ts --linger 20s) that fails on main at 23.3 s. CI is green.

This is small and correctly scoped: clean finishes and --linger 0s are untouched, and classifying by broadcast state rather than by error is the right call, since both cases surface as track errors.

Findings (non-blocking)

  1. Low: a fixed 1 s grace across hosts. CLOSE_GRACE (rs/moq-cli/src/subscribe.rs:446) was measured with about 70x margin on loopback (13.8 ms). Across a WAN relay, the gap between a dropped upstream's track errors and its broadcast close is set by the relay's own detection, not the RTT. If those ever land more than 1 s apart, a real drop exits 1 instead of lingering, which is the behaviour --linger exists to prevent. The new warning makes it visible. Consider scaling the grace with the linger (for example min(linger / 10, 5 s), floored at 1 s) so long lingers tolerate slower closes.
  2. Nit: the grace isn't deducted from the linger. A real end now waits up to grace plus linger before giving up, so up to 1 s past what --linger says. That's harmless, but resume_within(linger - waited) would make the flag exact.

Verdict: MERGE (reviewed head d0c5f439)

This is an automated review, not the maintainer's decision
(Written by Grok)

# Conflicts:
#	doc/bin/cli.md
#	quest/m1/README.md
@kixelated

Copy link
Copy Markdown
Collaborator

Merged main again (c9e1ddd) to clear new conflicts. Changes since the reviewed 7a7118b:

  • Earlier conflict merges with main, and the request_broadcast(.., None) fix in the export test.
  • doc/bin/cli.md: main's docs: keep the site on features and correct publisher restarts #5033 condensed the retention section, so this keeps main's text and adds the PR's one fact: an export that fails while the broadcast stays up exits 1 without lingering.
  • quest/m1/README.md: drops both the finished Wide varint tests line (done on main) and the Export linger line (done here).

The maintainer accepted the OpenAI review of 7a7118b as covering these follow-ups. just check passes locally, including the four moq-cli::export linger tests.

(Written by Claude Opus 5.5)

@kixelated
kixelated enabled auto-merge (squash) October 8, 2026 15:02
@kixelated
kixelated merged commit 1ec21ac into moq-dev:main Oct 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants