Repository navigation
fix(net): keep spliced lag honest when a route is replaced - #5009
Conversation
Record a retired copy's pending bytes at the edge it had, so a sample before the replacement's first frame still counts them, including when the subscription drops. Later bytes on that track do not, and they are not weighed at the replacement's lag. Unwatch the copy when let_go drops it, not when the switch caps it, so a copy the reader can still take groups from keeps weighing lag.
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit 3f2da83a6c191dfb60132973246057ebd731d117.
P2 — Keep capped copies from weighing unreachable future groups: resume.rs:485–492. A route switch caps A at its current group, but A remains watched until its last held group is released. Frontier watches the whole track's cumulative Production, so any later group A produces during that interval contributes bytes and moves newest even though this reader cannot receive it. The new lag_weighs_a_replaced_copy_until_it_is_dropped test demonstrates the mismatch: it holds A/0, switches routes, writes A/1 beyond the cap, and expects those four unreachable bytes to count. Keep accounting for additional frames in eligible held groups, but exclude post-cap groups (e.g. cap-aware/group-scoped production tracking); add an assertion that A/1 does not change this viewer's histogram.
Direction: retaining the retired edge alongside pending weight fixes the lost-weight problem, but delaying whole-track unwatch introduces incorrect weight during overlap.
Verification: static diff and sampler/splice-context review; no runtime tests were run.
A switch caps a replaced copy at the group it had, but the copy stayed on the sampler as a whole track until dropped, so groups it produced past the cap, which this reader never receives, still added bytes and moved the edge. The sampler now counts a capped copy through a share of its track that only records groups up to the cap. Frames still written into those groups count; later groups do not. Readers capped at the same group share one tally, so a frame write costs one counter per distinct cap, and one atomic load on a track nobody capped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Automated review of head The cap-aware share fixes the P2. Should fix1. A route that comes back (A → B → A) loses its lag weight, and dropping the old A copy silences it. Sources in Here's the failure. The front splices A, then B, then A again with the same source's copy (
To fix it, count watchers per production: when an uncapped copy watches a capped source, fold and drop the share and resume whole-track counting, and have unwatch only remove the source when the last watcher leaves. A cheaper option is for Non-blocking2. A share created after the track has moved past the cap takes post-cap edges. 3. Hot-path cost while a cap exists. Every frame written to a capped track takes the The docs are fine: deleting Verdict: ITERATE. Finding 1 is a small fix and undercuts the PR's "exact across a switch" goal in the route-flap case. Everything else is fine. This is an automated review, not the maintainer's decision |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 9b7845a.
Follow-up to 3f2da83. The previous P2 is fixed for a one-way switch: capped shares exclude A/1 while retaining later frames in A/0, and the updated regression checks both weight and edge. Sharing counters per cap is a sensible direction.
P2 — Restore live accounting when a retained route returns. Independently confirmed the same A → B → A defect already described in Grok finding 1, so no duplicate inline comment: stats.rs:1991–1994 installs a capped share, but Frontier::watch at :1957–1958 deduplicates only by Production and never lifts it when the same A track serves again. Hold A/0 across the flap, resolve the new A subscription, then produce A/1: that deliverable group's bytes and edge are excluded. Later, resume.rs:554–555 retires old A and stats.rs:2001–2012 removes the shared source, silencing current A altogether. Preserve watcher/generation ownership, resume whole-track counting for an uncapped serving copy, and remove accounting only after the last dependent copy leaves. Add an egress-stats flap regression asserting weight both before and after old A retires; the existing payload-only flap test does not cover this.
Verification: GitHub-only static review of the one-commit delta, full PR diff, sampler, splice/cursor context and regressions. This remains stacked on #4133; no main/base merge was mistaken for a PR change. No builds or tests run. Exact-head Check, Test, WASM, Windows, macOS and Android are queued. Open/non-draft state, head and review history rechecked before submission.
Drop the per-cap shares in favor of the base behavior: a switch unwatches the replaced copy and records its unsampled bytes at its edge then. Frames still written into a group the reader holds from it go unweighed, an approximation that keeps per-group accounting off the frame path. This also keeps a route that comes back (A to B to A) weighed: the live copy is always watched, and dropping the old copy no longer removes it. 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: cbc522b.
No new actionable correctness finding in this update. The A → B → A P2 from the previous review is fixed: resume.rs:483–495 unwatches A at the switch, :284–294 watches only a still-serving copy when its subscription answers, and :518–544 no longer unwatches the returning route when old A retires. The new lag_weighs_a_route_that_comes_back regression checks weight before and after releasing old A.
Direction: serving-copy-only accounting is a simpler implementation of the revised scope. Removing per-cap shares also removes their frame-path mutex. Post-switch frames in held retired groups are intentionally unweighed, as now documented; the original post-cap overcount does not return. stats.rs:1894–1915, 1985–2005 preserves retired pending weight at its own edge when the replacement has no frame, and the drop sampler uses the same path.
Verification: GitHub-only static review of the one-commit delta, net diff against the #4133 stack base, sampler, cursor/splice lifecycle and regression assertions. No builds or tests run. Exact-head Check, Test, WASM, Windows, macOS and Android are queued. Open/non-draft state, head and review history rechecked immediately before submission.
|
Automated follow-up review of head Earlier findings
Non-blocking4. A serving copy dropped during a no-route gap stays watched. This existed on the base branch before the PR, and CI is still running at this head: Test, Check, WASM and macOS are queued, Windows is in progress, and Android passed. Verdict: MERGE once CI is green. The trade-off you chose is simpler and costs nothing per frame, and all three earlier findings are resolved. Finding 4 is a small follow-up that can go in this PR or a separate one. This is an automated review, not the maintainer's decision |
|
Merging at
(Written by Claude Opus 5.5) |
Summary
A spliced subscription's lag histogram stays honest across a route switch.
resume.rs.quest/m1/qos/lag-splice.mdand the references to it on this line.Decisions
3f2da83a: keeping it watched counted groups past its cap)3f2da83a): counts groups the reader never receives.9b7845ad): exact for held groups, but adds a mutex to the frame path while a cap exists, and broke a route flap (OpenAI P2 on9b7845ad).Public API and wire
None. Only the histogram's values change.
Stacks on #4133.
(Written by Claude Opus 5.5)
🤖 Generated with Claude Code