Skip to content

fix(moq-hls): release retired renditions instead of force-closing them#2394

Merged
kixelated merged 2 commits into
devfrom
claude/moq-hls-retire-not-force-close
Jul 18, 2026
Merged

fix(moq-hls): release retired renditions instead of force-closing them#2394
kixelated merged 2 commits into
devfrom
claude/moq-hls-retire-not-force-close

Conversation

@kixelated

@kixelated kixelated commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Release broadcaster-owned rendition references without force-closing their segment timelines. A forced close can win the race with the rendition watcher's clean end(), leaving the live edge unfinalized and silently dropping the recording's last segment.
  • Keep force-close behavior for renditions removed or replaced by the catalog, where the old live edge is incomplete and its cursor must terminate.
  • Make the broadcaster-drop regression deterministic by dropping the broadcaster before finishing the publisher, then assert the retained cursor drains the final segment.
  • Update Rendition::close documentation to describe its catalog-removal lifecycle.

Public API changes

None. The changed producer lifecycle is crate-private, and the existing recording API is unchanged.

Test plan

  • nix develop --command just fix
  • nix develop --command just ci
  • Confirmed the reordered regression test fails with the old force-close behavior and passes with this fix.

No cross-package synchronization is required because this only changes internal moq-hls teardown behavior.

(Written by Opus 4.8 and GPT-5)

`renditions::Producer::clear` (called from `Broadcaster::drop`) closed every
rendition's segment timeline before dropping it. That races the rendition's own
watcher, which ends a cleanly-finished timeline with `end()` *then* `close()` --
and `end()` is what promotes the live-edge record into the final segment. Since
`end()` is a no-op on an already-closed channel, a `close()` that lands first
strands that record forever: the last segment of a recording is silently dropped.
moq.pro's recorder hits exactly this shape, dropping the broadcaster right before
it finalizes its uploads.

Dropping the renditions instead is enough for what `clear` is actually for. A
rendition nobody else holds drops here and its `Drop` aborts its watcher, so the
map can no longer pin a standing timeline subscription after the broadcaster is
gone. A rendition a cursor still holds is left to finish on its own, keeping its
final segment, and still reaches a terminal state once its source ends.

Retiring a rendition the CATALOG dropped keeps the force-close (added with the
cursors): there the media continues into a replacement, so the live edge is
genuinely incomplete and there is no `end()` to preserve -- only a cursor that
would otherwise park forever.

Also documents why `renditions::Producer::close` deliberately doesn't cascade
into the segment channels: on a clean end it would race the same `end()`, and on
a catalog-stream error it would truncate recordings whose media tracks are fine.

Tests: the ordering hazard is pinned directly (`end()` after `close()` never
finalizes the live edge), plus a broadcaster-drop test asserting a cursor still
drains its final segment and then terminates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @kixelated, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

Co-Authored-By: OpenAI Codex <codex@openai.com>
@kixelated
kixelated merged commit 209b155 into dev Jul 18, 2026
2 checks passed
@kixelated
kixelated deleted the claude/moq-hls-retire-not-force-close branch July 18, 2026 17:40
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.

1 participant