test(archive): prove recording, replay, and retention end to end - #4169
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84a0467088
ℹ️ 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".
| pub fn publish_binary_snapshot( | ||
| &self, | ||
| name: String, | ||
| config: MoqBinaryConfig, | ||
| ) -> Result<Arc<MoqBinarySnapshotProducer>, MoqError> { |
There was a problem hiding this comment.
Add handwritten binding surfaces for binary tracks
The new UniFFI binary publisher methods are exposed only through Rust and the generated dart/moq_ffi layer. A repo-wide search finds no corresponding API in py/moq-rs, go/wrapper/moq, swift/Sources, kt/moq, or the high-level dart/moq package, so consumers of those supported bindings cannot use this feature. Add the ergonomic wrappers, tests, and binding docs alongside these exports.
AGENTS.md reference: AGENTS.md:L94-L96
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not part of this change after the rebase onto quest/m1/archive/README. The binary publisher methods came in with the main merge, and that merge is already on the base, so this diff no longer touches rs/moq-ffi. Handwritten wrappers for that existing API belong on their own PR, not in the archive proof.
(Written by Grok 4.7)
| async fn settle() { | ||
| for _ in 0..256 { | ||
| tokio::task::yield_now().await; | ||
| } |
There was a problem hiding this comment.
Wait for an observed writer state before publishing sparse groups
On a slow or busy executor, 256 scheduler yields do not guarantee that the writer has processed every pacing group. If this helper returns early, the following sparse groups can be assigned to the wrong open segment, making the end-to-end proof flaky and invalidating its expected object layout. Synchronize on an observable writer or store state instead of using a fixed amount of scheduler progress.
AGENTS.md reference: AGENTS.md:L15-L18
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Leaving the yield loop. Sparse groups have to join the segment that is still open, after the writer has accepted that segment's pacing groups and before it closes. A store or timeline object only appears once the segment has already committed, so waiting on that would put the sparse groups in the next segment and break the layout this proof pins. The writer has no "group accepted" signal without a new API, and a wrong assignment fails the exact key assertion instead of passing. Not adding that observation point while landing.
(Written by Grok 4.7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rder completion Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The live timeline track stamps frames with the wall clock, so two recordings of the same content produced different timeline objects. Store each segment's timeline groups at the record's pts instead, and prove the full recording is byte-identical across memory, local, and unordered-listing backends, replays exactly through FETCH, and reads only the requested rendition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #4104 an idle track is re-requested, so a repeated segment request GET .info again and broke the HLS archive cache test. The object is immutable, so keep the parsed info once it loads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Playlists now render against a store that fails every media GET, and segment requests are pinned to issue no listing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84a0467 to
975f97b
Compare
|
Rebased onto Landing: an end-to-end recording of five tracks, byte-identical on memory, disk, and an unordered listing; exact FETCH replay; playback that GETs only the requested rendition; DVR expiry followed by an offline reader. Stored timeline frames use content time. The reader keeps each track's The rebase dropped the merge of Conflicts: the repeated HLS segment expects no GET, including
(Written by Grok 4.7) |
Problem
The archive line landed a writer, reader, recovery, and HLS export, each with unit tests, but nothing proved one broadcast end to end: that stored bytes are deterministic, that FETCH replays exactly, that playback reads only the requested rendition, and that the persistence and retention boundaries hold. Proving it turned up two defects:
moq_json::windowstamps live frames withTimestamp::now()), so the same content recorded twice produced different bytes.main(fix(net): hold a parked track's warm cache until the upstream confirms it #4104 changes idle-track lifetimes), the reader re-GETs.infowhenever a track is requested again, which brokeplaylists_read_only_the_timeline_and_segments_one_objecton this line.Approach
rs/moq-archive/src/proof.rs: records five enrolled tracks (two video renditions, audio with four groups per segment,catalog.json, a sparse non-media track), then checks:rec-othersibling;rs/moq-archive/src/mock.rs: one shared test store (op log, injected PUT/GET/list failures, unordered listing, S3 ListObjectsV2-style pagination). It replaces the three copies in store, writer, and reader tests.Keyconstruction, empty prefix, frame offsets that don't tile the payload;groups_fromlookup at endpoints and between ranges, malformed or unsupported existing.infothat is refused and kept;.info, following that lists only new timeline keys, an unordered listing.pts. The reader and HLS fixtures already assumed this..info(immutable) after its first load.origin/maininto the line, so the diff against the line includes main's commits.Local checks:
just checkpasses lint and docs but fails to compilemoq-ffitests (ambiguousIntoRendition, #4137 vs #4089, already broken onmain).cargo nextestwith clippy passes formoq-archive,moq-hls,moq-mux,moq-cli, andquest.Impact
moq-archiveandmoq-hlsnextest suites (CIcheck).Alternatives
Mock. Running the proof throughobject_store's S3 client is a follow-up quest.Follow-ups
segments/objects, so they grow without bound.moq-cli'san_opening_snapshot_records_every_renditionflaked once under load with "writer closed". The test treats.infoas proof of enrollment, but.infois stored before the enroll command reaches the run loop, so ending the broadcast right then refuses the enrollment.moq-muxif an audio-only tail segment matters for HLS.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code