Skip to content

fix(net): fill no history below a subscription's start that its reader skips - #7168

Merged
kixelated merged 2 commits into
mainfrom
fix/fill-below-start
Oct 29, 2026
Merged

kixelated merged 2 commits into
mainfrom
fix/fill-below-start

Conversation

@kixelated

@kixelated kixelated commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A fresh subscriber with the default Range::oldest asked a fill for every group below SUBSCRIBE_START. Misses::judge gives up a run only when its newest group already looks stale, and an untimed copy judges by wall-clock arrival at its own hop, so the run looked fresh and went out as a FETCH. A reader then pulled the publisher's whole cached history on every first subscribe, direct or through a relay:

Wire Before (max_delay 2.5s, groups 0..9 one second apart) After
moq-lite-04 6..9 6..9
moq-lite-05 0..9 9
moq-lite-06 6..9 (since moq-dev/moq#7052) 6..9
moq-lite-07 6..9, plus one FETCH below the start that the publisher refuses 6..9, no FETCH
moq-transport-18/22 0..9 9

Bisected to moq-dev/moq#6171 (fills for misses).

Approach

In Misses::judge:

  • Start resolved from a budget (lite-06+). A received copy records the max_delay its SUBSCRIBE carried alongside the SUBSCRIBE_START it answered (Producer::start_resolved). A reader whose budget is no looser gives up every run below that start without asking, through the existing stale path, naming them dropped as lite-07 already did once the publisher refused them. A looser reader behaves as before: lite-06 waits for the re-offer (fix(net): close the relay demand gaps #7052), and lite-07 fills with a FETCH that carries its max_delay.
  • Live-edge start (lite-05, moq-transport). Below a received copy's start, a reader asks a fill only for groups its ranges name (Window::named). An absolute floor still fills (history_groups, late_lower_group), but Range::oldest no longer does. These groups are not stale, only unrequested, so they are skipped rather than dropped.

Tests:

  • widened_max_age::a_fresh_reader_fills_nothing_below_its_start covers lite-04/05/06/07 and moq-transport-18/22, direct and through a relay, and asserts that no lite FETCH goes out. It fails on main.
  • below_a_start_resolved_from_a_budget_only_a_looser_reader_asks pins the budget comparison in the model.
  • range_anchors::every_wire_carries_the_live_and_oldest_starts now expects only the latest group from oldest on lite-05 and moq-transport, and warm_backfill names floor 0.. so it still exercises the backfill path.

Impact

  • Behavior, Range::oldest (the default range) on moq-lite-05 and moq-transport: a subscription gets no history below the live edge unless it names a starting group, whatever its max_delay. An untimed copy cannot bound that history by the budget, so filling it pulled everything cached. To keep reading history there, name a starting group, such as with_range(n..). Maintainer decision (2026-10-28): keep this. Range::oldest's doc and doc/concept/moq-lite.md say so.
  • No public API signature or wire change: Window::named and Producer::start_resolved are crate-internal.

Alternatives

  • Fill below the start only on wires whose FETCH carries max_delay: lite-05/06 readers would lose history they ask for, and lite-07 would keep its refused FETCHes.
  • No fill below the start for any subscription: undoes an intended feat(net)!: dynamic tracks serve fills for misses #6171 behavior.

Checks

  • just ci on this head passes: check and test for rs (moq-net nextest 1243, workspace 9022 passed), js, markdown and gh.
  • cargo nextest on moq-net, moq-net-tests, moq-relay, moq-bench, hang, moq-mux and moq-hls (all features): 5849 passed.

An earlier note here blamed a cargo fmt --all --check failure in demo/carla/bridge on upstream. It was local: that checkout sat under a directory whose rustfmt.toml sets hard_tabs, and rustfmt searches past the repository root. main and this branch are clean.

Follow-ups

  • moq-transport-22: on main, a cold relay copy with an absolute floor 0.. serves only the latest group: the copy records start 0, but the publisher's cursor starts at the live edge. Hence its absence from warm_backfill.
  • JS net may have the same below-start over-fetch. This PR does not check it.

(Written by Claude Opus 5.5)

🤖 Generated with Claude Code

kixelated and others added 2 commits October 28, 2026 15:20
…r skips

A reader with the default oldest range asked a fill for every group below
SUBSCRIBE_START. On an untimed copy each looked fresh, so lite-05 and
moq-transport pulled the publisher's whole cached history on every first
subscribe, and lite-07 sent one FETCH per stale group for the publisher to
refuse.

Below a start the publisher resolved from a budget no tighter than the
reader's (lite-06+), every group is stale: the reader gives the run up
unasked. Below a live edge start, it fills only a floor its ranges name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@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 17 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
  • 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: 0559219b-4b13-424b-ac2f-fe684b52130b
Commits

Reviewing files that changed from the base of the PR and between f652976 and 410a5d5.

Files selected for processing: 9
  • doc/concept/moq-lite.md
  • rs/moq-net/src/lite/subscriber.rs
  • rs/moq-net/src/model/subscription.rs
  • rs/moq-net/src/model/subscription/window.rs
  • rs/moq-net/src/model/track.rs
  • rs/moq-net/src/model/track/miss.rs
  • rs/moq-net/tests/integration/range_anchors.rs
  • rs/moq-net/tests/integration/warm_backfill.rs
  • rs/moq-net/tests/integration/widened_max_age.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-28T22:50:42.026772Z 410a5d5 PR opened
ℹ️ 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: 410a5d5

P2 — Do not attribute a START to the original budget after a pre-START update (rs/moq-net/src/lite/subscriber.rs:8013–8017). The publisher can apply SUBSCRIBE_UPDATE before selecting its first group: RequestServe retains updates while resolving the source, then TrackRun::update changes the cursor's max_delay. For example, SUBSCRIBE at 30s followed by a same-start update to 10s before START can produce a start resolved under 10s, but this code records 30s. A subsequent 20s reader then satisfies drift.budget <= start_budget in Misses::judge and permanently gives up potentially usable history below that start instead of requesting it. Checking only active.start == active.requested does not detect this. Conservatively invalidate the budget proof once an update could have changed the start calculation, or explicitly correlate START with the budget used by the publisher; add a delayed-START regression with tightening followed by partial widening.

Overall direction: suppressing unrequested history is useful, and keeping named-floor fills separate is sensible, but the new permanent-staleness inference needs a trustworthy budget. Verification: static inspection of the changed model, subscriber and publisher control flow and tests only; I did not run tests.

@kixelated
kixelated added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit a3e910e Oct 29, 2026
11 checks passed
@kixelated
kixelated deleted the fix/fill-below-start branch October 29, 2026 00:52
kixelated added a commit to michaelstuurman/moq that referenced this pull request Oct 29, 2026
…low-ups (moq-dev#7179)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
kixelated added a commit that referenced this pull request Oct 29, 2026
Resolves conflicts with #6969 (legacy FIN finishes the track, kept alongside
reopening uncovered demand), #7168 (Window::named also waits on a time start;
its JS fills rule joins the quest's remainder), and #7015's late tail mode.

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