Repository navigation
perf(net): insert replayed JS groups in order instead of re-sorting - #7516
Conversation
Quest-Claim: e64b42b04fa47248
Each cached-group mirror pushed onto a sink's unread buffer and sorted the whole buffer, so a subscriber joining G retained groups sorted arrays of sizes 1..G and every arrival re-sorted each subscriber's backlog. Insert with the existing timeline search instead, appending in the usual in-order case. Adds the replay benchmark (Bun nightly and the Chromium micro harness), a GC traced pass and cross-origin isolation for the micro harness, and finishes the cached JS group joins quest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: b8eeb32.
No actionable bugs found. The direction is sound: a small append-fast-path insertion helper removes repeated whole-buffer sorting while retaining the existing mirror, replay and eviction paths. Keeping buffer insertion separate from timeline insertion is justified because their equal-sequence behavior differs; batching replay separately would add a second path without fixing live backlog arrivals. No public API or wire change identified.
Checked the insertion/search and delivery context, sparse-arrival regression test, replay benchmark, shared trace extraction and nightly/browser wiring. Verification was static through GitHub only; I did not run tests, browser benchmarks or independently reproduce the reported performance/GC measurements.
(Written by OpenAI)
|
No actionable comments were generated in the recent review.
|
There was a problem hiding this comment.
ℹ️ No critical issues — one doc-wording suggestion inline.
Reviewed changes
js/net/src/track.ts:#mirrornow inserts a replayed or live group withbufferInsert(append when the buffer's last sequence is<=the new one, else a lower-bound splice that lands after any equal sequence) instead of push + stable sort, removing an O(n log n) sort per arrival.js/net/src/track.test.ts: a deterministic test drives sparse reverse and mixed arrivals into a live backlog and a late joiner, asserting ascending delivery with the right payloads and thatArray.prototype.sortis never called. It fails on main.js/net/bench/replay.tsplusbench/browser/*: a new replay benchmark (retained groups × joining subscribers, and unread backlog × active subscribers, across monotonic/reverse/mixed), trace parsing shared intotrace.ts, cross-origin isolation for finerperformance.now(), and an untimed GC-traced pass.- Nightly/quest wiring:
replayadded to the JS nightly leg and the browser smoke loop; the child quest is closed and its result recorded.
I verified the ordering invariant holds across every mutation of TrackState.groups in track.ts — all insertions route through bufferInsert, the rest are removals or a clear on close, so the append fast path never sees an unsorted buffer, equal-sequence placement matches the old stable sort, and the consumers that assume sorted order (#tryRecvGroup, #tryNextBuffered, #peekGroup, #bufferedLargest) are unaffected. I also confirmed the benchmark prints exactly the 72 data rows ROWS.replay expects, that bench-report.py skips the counts table because its columns are not measurement units, and that the append shape's two publish sets partition 0..total-1 so the drain check passes for all three orders.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
Keeps the finished quest's deletion over main's link rewording, and names the append rows' fan-out directly in bench/README.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
codex / Codex land fixer note:
(written by codex / Codex) |
|
codex / Codex land fixer: Conflicts with the base: bench/README.md, bench/browser/micro.ts, bench/browser/run.sh (written by codex / Codex) |
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 0c48f1d.
No actionable bugs found in this update. Compared with my previous review at b8eeb32, separating upstream merges from the benchmark changes, I checked per-shape replay teardown and clock advancement, asynchronous major collection, saved traced rows and their retention checks, and preservation of the six browser fixtures and nightly registrations.
The direction is sound: expiring each shape outside timing addresses retained prune callbacks rather than masking them with more GC; asynchronous collection retains the serving/concluded controls rather than weakening assertions. Keeping traced samples separate avoids contaminating timing rounds, and restricting tracing to its two consumers avoids unnecessary work. These focused harness corrections justify the added complexity without changing public API or wire behavior.
My previous review had no open findings. The independent documentation wording suggestion is also fixed; no duplicate inline comment is needed.
Verification was static through GitHub only. I did not run tests or browser benchmarks, reproduce the GC probes, or independently verify the reported timing improvements.
(Written by OpenAI)
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Gave each replay shape its own
Manualclock and advanced it past cache expiry after teardown, outside timing, so completed shapes cannot accumulate through prune timers. - Switched resume-history's reachability collection to Chromium's asynchronous major GC (
StackState::kNoHeapPointers), dropped the task yield, and fail loudly when async GC is unavailable. - Recorded traced rows and per-renderer GC summaries in the browser benchmark JSON, restricted tracing to replay and resume-history, and extended the retired-route control to traced rows.
- Merged current main's bandwidth and resume-history benchmark registrations, keeping all six smoke fixtures, nightly selections, and documentation.
The production ordering change in js/net/src/track.ts is untouched since the prior review. I verified the async-GC contract against V8's gc-extension.cc (execution: "async" returns a Promise and collects with kNoHeapPointers), the per-shape Manual.install/close lifetimes against js/clock, the replay/resume-history row counts against ROWS (72 each, bandwidth 44), and that the retired-route slice (slice(5, 9)) still lines up with the resume-history header. Public API and wire impact remain none.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

Problem
Cached-group joins and arrivals into unread backlogs sorted each subscriber's entire buffer after every mirror. Joins repeatedly sorted buffers of sizes 1 through the retained group count.
Finishes Cached JS group joins.
Approach
Append in sequence order, otherwise binary-search the insertion point. Equal sequences stay in their previous stable-sort order. Joins, live arrivals, and re-offers keep one insertion path.
Cover sparse reverse and mixed arrivals for a live subscriber and a late joiner. Sweep retained groups and backlog depth against subscribers in the replay benchmark, checking every delivered sequence and payload and recording sort work separately from timings.
Keep replay and resume-history in the shared browser harness and smoke lane. Share trace parsing with the browser driver, use cross-origin isolated pages, and record an untimed traced pass separately from timed samples for replay and resume-history only. frames, drift, and ledger keep their existing warmup and measured passes.
Merge current main without dropping benchmark suites or controls. PR 7521 landed during local review; the final merge preserves bandwidth, replay, and resume-history registrations, documentation, nightly selection, and the six-fixture browser smoke lane. It does not duplicate the bandwidth implementation. PR 7521 and the earlier main had the same resume-history fixture; its passing run did not exercise the tracing introduced here.
Fix resume-history's traced-run false retention at its source: V8's synchronous
gc()conservatively scans stack slots even from a fresh host task. Await asynchronous major collections, which run withStackState::kNoHeapPointers. Keep the collection count and every serving, concluded, and retired-route assertion. Save traced rows and check their retired counts too. See V8's GC extension.Isolate each replay shape on its own frozen clock, then close its readers and track and advance past cache expiry outside timing. This releases deferred callbacks and prune timers before the next shape. The original whole-run GC totals are withdrawn.
Impact
tracedrows and renderer GC summaries. Timing remains observational.Alternatives
Batching and sorting once per join would leave a separate path from live insertion. Ordered insertion handles both, retaining the append fast path. Increasing GC passes or retrying a failed control would leave conservative stack roots in place; asynchronous collection removes those roots.
Validation
quest check --base origin/main --branch quest/m2/performance-survey/js-cached-groupspassed.0c48f1dc2c2aecac13a7df292460ee51fbe714e5(exit 0), including the PR 7521 integration.nix develop --command python3 .tmp/run-ci.pyrunsquest gate just ciafter removingBASH_ENVand the mbx/mise proxy directories from the process PATH. It changes no repository or host configuration.The corrected fixture was rerun with three alternating rounds against main
f882be3ded89c8861bdc5f643f49128571a2cafd, plus current and identical A/A bundles, in Chromium 156.0.8078.4. All rows still verify every delivered payload and sequence. Raw timing and trace rows remain in.tmp/replay-paired.json. All 36 timing-shape medians improved; current and A/A counted shapes all report 0 sorts, sorted elements, and comparator visits. A reduced weak-reference probe retains 12 completed tracks with the old fixture and zero with the repaired fixture after warmup, measurement, and tracing. The host was shared and A/A samples show substantial scheduling noise; the deterministic counted pass remains the complexity evidence. Original GC totals from the fixture that retained completed shapes are withdrawn.Choices
Follow-ups
/quest-plan: the unchanged announcement testa version without a boundary syncs once the request is outassertssyncedafteryieldNow(). It failed in a full gate here, and untouched main reproduced 12 failures in 100 diagnostic runs. Wait on the actual request/sync event rather than treating one task yield as completion. No existing open PR was found for this exact failure. Subscriber production code and this test have no diff in this PR.BASH_ENVrestores an oversized PATH and triggers mbx E2BIG. Gate commands below clear that hook and resolve Nix Cargo directly; moq-dev/moq#7594 already owns shell PATH deduplication. No host configuration is changed here.Review
quest review,claude-opus-5-5, medium effort, sessionc5c5ec84-194b-4270-a6df-958247636b21.0c48f1dc2c2aecac13a7df292460ee51fbe714e5against maindde50c699f69507ced243754c2f700a6184d1576: no findings. Round 2 also reported no findings before the mechanical integration of PR 7521.(Written by GPT-6.1 Sol)