Skip to content

fix(net): resolve subscription starts past skipped groups - #6028

Merged
kixelated merged 6 commits into
mainfrom
quest/m1/subscribe-ok-start
Oct 20, 2026
Merged

kixelated merged 6 commits into
mainfrom
quest/m1/subscribe-ok-start

Conversation

@kixelated

@kixelated kixelated commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A warm relay could name stale cached groups in SUBSCRIBE_OK that it had already given up, while a cold relay named the actual delivery start.

Approach

Record missing-head, stale, and upstream-dropped ranges before resolving the start. Reuse the cursor's drop notifications and keep ranges compact. Skip recording a missing head at u64::MAX, which has no successor, without rejecting the supported sequence.

Once the start resolves, any recorded range above it is sent as SUBSCRIBE_DROP right after SUBSCRIBE_START, as it would have been had it been given up after the start. Without this, consuming the cursor's drop notifications before the start would hide them until the end sweep.

Mock-time tests cover cold/warm publisher caches, an actual two-hop relay on lite-05/06/07, a million-group upstream drop, a range dropped above a start resolved from the source, and the maximum missing-head sequence. The session join benchmark sweeps publisher count against retained history and runs in the existing nightly benchmark harness.

Impact

  • Public API: none.
  • Wire: no encoding or negotiation change. Lite-05+ resolves the start past groups already given up, matching the existing draft definition, and names a range given up above the start promptly instead of at the end. The implementation calls this SUBSCRIBE_START; the draft calls it SUBSCRIBE_OK.
  • JS already derives the start from the selected group. IETF SUBSCRIBE_OK reports a live-edge snapshot; neither shares this gap.

Decisions

Picked without a maintainer decision, recommended options:

  • Ranges recorded before the start but above it:
    • ✅ Send them as SUBSCRIBE_DROP right after SUBSCRIBE_START (prompt, like any later drop, and also covers missing-head skips that main left to the end sweep).
    • Leave them for the end sweep (a regression for upstream drops, which main forwarded once the start resolved).
  • poll_forward_drop guard after moq-dev/moq#5983 made served present on every lite-05+ version:
    • ✅ Reduce it to !self.emit_range.
    • Keep the served.is_none() clause, which can no longer be true there.

Alternatives

Port only this independent fix from closed moq-dev/moq#5931. SUBSCRIBE_ALL and resolved upstream demand changes remain separate work.

Follow-ups

Full browser and GStreamer interop remain unverified on this host. There are no open implementation decisions.

Checks

  • Merged origin/main, including moq-dev/moq#5924 and moq-dev/moq#5983; the benchmark history loop now uses Shape::group_frames.
  • Reproduced the stale-cache bug before its fix: cold/warm starts [2, 0], expected [2, 2].
  • Reproduced the maximum missing-head panic before the checked-end correction.
  • a_range_skipped_above_the_start_is_dropped fails without the forwarding ([], expected [(2, 3)]).
  • Five focused regressions pass, including the actual two-hop relay acceptance test.
  • just check passes on 127f92e1d (7,662 Rust tests). Two earlier runs on a host at load 40+ hit wall-clock flakes outside this change: moq-uring clock::tests::host_tracks_moq_clock (1 ms calibration slack; passes alone) and moq-cli export::redundant_importers_switch_through_bufferbloat_on_lite_07 ("stitching export exited"), which also fails with main's publisher.rs (1/1 under load) and passes 2/3 here.
  • Earlier validation at 0901088b7: all 27 new benchmark points pass as a debug smoke measurement (no speedup claim), and the available native interop matrix passes (publishers rust,python,go,cpp; subscribers rust,python,go,cpp,js-native-node,js-native-bun,c). just test interop --all could not run browser or GStreamer cells on this host (pinned Chromium 1248 lacks libglib-2.0.so.0; GStreamer plugin discovery unavailable), so it is not a full-all interop pass.

(Written by GPT-6.1-Sol, updated by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 3 commits October 19, 2026 18:05
@kixelated

Copy link
Copy Markdown
Collaborator Author

Validation at 0901088b7: just check, four focused regressions, all 27 debug benchmark points, and the 28-cell available native interop matrix pass. Browser interop is blocked by pinned Chromium 1248's missing libglib-2.0.so.0; GStreamer plugin discovery is also unavailable, with loader stderr discarded by the harness. The required all-platform attempt was stopped with exit 143. All owned test processes have exited. The PR remains draft as requested.

(written by GPT-6.1-Sol)

kixelated and others added 2 commits October 19, 2026 22:51
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ranges given up before SUBSCRIBE_START but above the resolved start were cleared, so lite-05+ subscribers waited until the end sweep for them. Forward them as SUBSCRIBE_DROP right after the start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review October 20, 2026 06:20
@coderabbitai

coderabbitai Bot commented Oct 20, 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 23 minutes.

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
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c0422a6b-5a7e-4351-a1b0-18bef5487894
📥 Commits

Reviewing files that changed from the base of the PR and between 5284d96 and d91db85.

📒 Files selected for processing: 5
  • quest/m1/README.md
  • quest/m1/archive/tracks.md
  • quest/m1/subscribe-ok-start.md
  • rs/moq-net/benches/session.rs
  • rs/moq-net/src/lite/publisher.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 20, 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-20T16:16:56.209171Z d91db85 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 added this pull request to the merge queue Oct 20, 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 20, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d91db850d0

ℹ️ 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".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +3966 to +3967
for range in skipped.iter() {
self.drop_range(range.start.max(start)..range.end, writer)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not drop a recovered group that is still served

When poll_forward_drop drains a drop before SUBSCRIBE_START and that sequence is re-created before the start resolves, skipped still contains the held group's sequence. This loop emits SUBSCRIBE_DROP for it, but start() immediately calls self.serve(group), so the peer accounts the group as permanently unavailable and then receives its stream, potentially settling or discarding it. This can occur when an initial group is cut and recovered before any later group resolves the start; exclude the held group from the buffered drops or do not serve it.

Useful? React with 👍 / 👎.

@kixelated
kixelated added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 06765f4 Oct 20, 2026
4 checks passed
@kixelated
kixelated deleted the quest/m1/subscribe-ok-start branch October 20, 2026 16:48
kixelated added a commit that referenced this pull request Oct 20, 2026
#6028 added a `history` argument to the bench id without a specifier,
which fails to compile the bench on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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