Repository navigation
perf(net): a lite-07 session's route teardown costs about linear in what it held - #8555
Conversation
Quest-Claim: f0ba4d7afb1ca0c2
…hat it held WIP checkpoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…st follow-up for the lite-06 join hang Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… can change Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…elay-session-hour # Conflicts: # quest/m2/cluster-routing/routes/scope.md
|
No actionable comments were generated in the recent review. WalkthroughRust route handling now uses region-based lookups and supports grouped stream teardown. New route benchmarks measure teardown and refollow across prefix and claim counts. JavaScript flooring and leaving now refresh only carriers whose prefixes meet the changed carrier’s prefix. The benchmark scope note changed, the relay session-hour note was removed, and a lite-06 join-hang report was added. Merge Risk:
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Single-pass
restrict_all— keeps only the widest regions (a region inside another adds nothing the wider restriction lacks), unions their restrictions, and settles the floors once, replacing a per-region merge fold that could leave a narrower region's presented covering claim behind as a phantom. - Per-segment lookups — new
above/within_any/settlehelpers replace full floor and region scans inas_of,narrow,covering, the merge stale check,retire,Heard::resync,prune's lost floors, andcertified, which now returns a set. - Batch disconnect —
Router::disconnecttakes many neighbors and recomputes each identity once;route::Node::closing()defers each dropped stream's disconnect to aClosingguard, held by the lite subscriber and publisher drivers over their announce-task teardown, andState::closeis now the single close path. homeroute index — routes are bucketed by the prefix their stream asked for, so each shown claim looks up its carriers per segment instead of scanning every route; thecarriesfilter (including the link scope) still runs, so the indexed set stays exact.- js/net
Floors—leave/floorreshow only routes whose prefix meets the changed one, which is sound becausecertifiesrequires the view's prefix to be an ancestor of the queried path and a route's claims lie within its own prefix. - Bench and quests —
fuzz::RouteFollowerplusroutes/teardownandroutes/refollowcells swept over prefixes × claims, the teardown quest retired, and the lite-06 join hang split into its own follow-up.
Checks run here: cargo test -p moq-net --lib --features async route:: (126 passed, 2 ignored, including restrict_all_ignores_a_region_within_another, lookups_match_a_scan_of_every_floor, and closing_streams_together_matches_closing_them_in_turn) and bun test src/lite/floors.test.ts (6 pass). RouteHandoff.tla models only the root interest, so the claim that the model's spec is unchanged holds, and no inbound link to the retired quest slug remains.
deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
ℹ️ No blocking issues — the merge changed nothing in the code this PR owns. One sentence of the PR body went stale against the new TLA+ model that landed with it.
Reviewed changes
- Delta since the prior review —
fb2a9adis a merge oforigin/main(9122f9b) into the branch. The range-diff shows only main's own comment and doc wording arriving alongside; the PR's patch is byte-identical (same per-file diff hashes), so nothing about the teardown fix, the batch disconnect, or theFloorsnarrowing changed. - Merge resolution checked in the code it touches —
cargo check -p moq-net --lib --features asyncis clean atfb2a9adandcargo test -p moq-net --lib --features async route::passes (128 passed, 2 ignored), so the mergedcertifiedset,within_anylookups, andState::closestill compile and hold against main's newShown::presentedwork. - Previously reviewed at
709799c— single-passrestrict_allover the widest regions, per-segmentabove/within_any/settlelookups, batchRouter::disconnectwithNode::closing()guards held by the lite subscriber and publisher drivers, thehomeroute index, the js/net reshow narrowing, and the newroutes/teardown/routes/refollowcells.
ℹ️ The PR body's reason for skipping model work no longer describes the model
The body justifies "/verify-route's spec is unchanged and its result with it" by saying the TLA+ model "only covers the root interest". Main's #8541, merged here, replaced that model with one over two prefixes (root and a) whose whole subject is routes per region. The conclusion still holds, so this is a wording fix only, but the stated reason now reads as a claim about a model that no longer exists.
Technical details
# Restate the model-coverage reason in the PR body
## Affected sites
- PR body, "Choices made without asking" bullet 2: "The TLA+ model only covers the
root interest, so `/verify-route`'s spec is unchanged and its result with it."
- `rs/moq-net/verify/route/RouteHandoff.tla` (at `fb2a9ad`) models the prototype in
`rs/moq-net/src/route/tests/paths.rs`, two prefixes `root`/`a`
(`Keys`, `Covers`, `Restrict`), one floor per view key (`s.view[k].floor`), and has
no `prune`.
- `.claude/skills/verify-route/SKILL.md` `## Coverage`: "The model describes the
prototype ... `route::Router` does not run this rule yet."
## Required outcome
- The sentence should say why this change is outside the model, e.g. that the model
keeps one floor per view key and has no `prune`, so `restrict_all`'s merging of a
view's several floors (and nested-region choice) is not modelled — rather than that
the model is root-only.
## Open questions for the human
- Whether the model, which now has two nested prefixes, should eventually cover the
multi-floor merge; nothing in this PR needs it, since it does not model `prune`.deepseek-v4.1-flash-1017 (free via Pullfrog for OSS) | 𝕏

Finishes quest/m0/ci/nightly-relay-session-hour.md.
Problem
Nightly (Relay session benchmarks)ran out of its hour: each pass spent about 22 minutes in the teardown ofrelay_session_delivery_prefixes/moq-lite-07-wip/broadcasts=1025/follow=1024. A lite-07 viewer holding 1024 announce prefixes has 1024 route streams, each its own router neighbor. Closing each one ranprune, whoseView::restrict_allmerged one restriction per remaining region at claims x floors per merge, so a session's teardown was cubic or worse. A relay pays the same whenever a lite-07 peer holding many prefixes leaves, so a client controlled the CPU.Approach
As decided on moq-dev/moq#8536: batch disconnect and single-pass
restrict_allfirst, indexing only where they left it super-linear.Router::disconnecttakes many neighbors and recomputes each identity once.route::Node::closing()returns a guard that holds back each dropped stream's disconnect, then disconnects them together. The lite subscriber and publisher drivers hold it while their announce tasks drop.restrict_all. It takes the widest regions only (a region within another adds nothing the wider one's restriction lacks), unions their restrictions, and drops covered floors once.as_of,narrow,covering, the merge's stale check and floor settling,retire,Heard::resync, prune's lost floors, and the reader's certified regions look up each segment above a path in thePath-keyed map, instead of scanning every floor or region.certifiedreturns a set. The node'shomelooks up the routes whose asked prefix lies above a claim instead of checking every route. The rebuild in routes/scope.md deletes most of this; it was needed because the first two steps alone left refollow at 3.4 s per op.Floors.leaveandFloors.floorreshow only the routes whose prefix meets the changed route's. A route's claims lie within its prefix, so no other route's can change.Measured (routes bench,
cargo bench -p moq-net --features fuzz --bench routes -- --quick, this host):routes/teardown/1024p_1croutes/teardown/1024p_16croutes/refollow/1024p_1croutes/refollow/1024p_16cprefixes/moq-lite-07-wip/follow=1024, whole cellFloorsteardown, 1024 prefixesOne relay session pass with Nightly's screening args (
--quick --warm-up-time 0.05 --measurement-time 0.2) took 139 s here, excluding the lite-06 join cells below. The 11-11 Nightly's pass took 25 minutes, 22 of them in that one teardown.Choices made without asking:
homeroute index go beyond the decision's preferred minimum; the batch and single pass alone left refollow super-linear.restrict_allover nested regions now ignores the narrower one. The old fold was order-dependent there and could leave the narrower region's presented covering claim as a phantom claim at its own path (restrict_all_ignores_a_region_within_another)./verify-route's model (RouteHandoff.tla) describes the path-per-scope prototype inroute/tests/paths.rs, notroute::Router,View::restrict_all, or the floors this PR changes, so its spec and result are unchanged.closing_streams_together_matches_closing_them_in_turn).Impact
moq_net::fuzz(bench-only,fuzzfeature) gainsRouteFollower.Caused by
restrict_allmerged one restriction per region, each scanning every floor.Alternatives
follow=1024Nightly cell: rejected on quest: the relay session hour is lost to a super-linear lite-07 route teardown #8536, it hides a relay cost a client controls.Path-keyed maps give the same bound with no new structure.Validation
just check: pass (12955 Rust tests, js checks).bun testinjs/net: 3202 pass.relay_session_join_broadcastsandrelay_session_join_stalehang in their moq-lite-06 cells since moq-dev/moq#8476 (bisected;c916855d16passes,f3edb0b8d9hangs, reproduced on main9122f9b6b7with none of this PR). Follow-up below.Follow-ups
Models
Size: M
Implementer: Claude Code, Opus 5.5, medium effort
🤖 Generated with Claude Code
(Written by Claude Opus 5.5 with medium)
Review
Rewritten by
quest reviewafter each round.01a1d02f-c5ae-77e2-b71e-7f818e6c641c, round 2The update merges base-branch changes with no actionable defects identified in the merge resolution or its interaction with the previously reviewed code; tests and model checks were not run as instructed.