Skip to content

fix(sync): let a newer floor supersede an unfinished suffix obligation - #1502

Open
skakri wants to merge 2 commits into
mainfrom
fix/purged-suffix-obligation-reroot
Open

skakri wants to merge 2 commits into
mainfrom
fix/purged-suffix-obligation-reroot

Conversation

@skakri

@skakri skakri commented Sep 25, 2026

Copy link
Copy Markdown
Member

Fixes #1489.

Problem. A store that adopts a sender's compaction floor owes the suffix up to the sender's advertised tip (table_sync_suffix_coverage). While it does, the stream is pending:

  • local authoring, compaction and re-adoption on it are skipped;
  • every session reports a continuation.

The obligation cleared only when that exact tip arrived, and a newer floor was refused. But the tip is pinned nowhere, so two ordinary events can make it unsatisfiable, after which the stream stays blocked forever:

  • The writer compacts it away. Compaction knows nothing of peers' obligations.
  • Its device is removed. The device's entries are then refused everywhere.

The block also spreads. A fresh peer adopting the floor from the blocked store inherits the same obligation.

Why the refusal bought no safety. A peer that simply missed the same stretch is re-rooted onto a newer floor, and the floor carries its own contract: every current carrier on the chain is at or above it at the advertising store. Equivocation below a floor is already undetectable once any floor is recorded. MLS (RFC 9420) takes the same position: a member that cannot process the commits it missed rejoins at the current signed state (External Commit, Welcome) rather than waiting for them.

Change

  • A newer floor re-roots an indebted store like any other peer, and the obligation follows the new root. record upserts, so exact presence of the tip still proves the contiguous suffix from the current root.
  • Removing a device drops a suffix still owed on its chain, scoped to that stream. The obligation table carries no account, and the device may still write another account's streams.

Tests

  • a_promised_tip_compacted_away_is_superseded_by_the_senders_next_floor: the writer compacts the promised tip away. Run as-is and after a purge (fix(sync): a purged store with a surviving suffix obligation cannot be re-rooted onto a compacted sender's floor #1489's shape, a RootAdopt past the witness). The store converges and nothing stays owed. It fails with the old floor filter and with the old insert-or-ignore.
  • removing_the_owed_chains_device_drops_the_obligation
  • The segment of interrupted_floor_delivery_needs_the_promised_suffix_before_serving_a_fresh_peer that pinned the old refusal is removed. Its sender offered the promised tip itself as a floor, which no honest store advertises. The rest of that test (the obligation survives a restart and a purge until the suffix arrives) is unchanged.

A running chain digest that would let stores detect diverging copies, analogous to MLS's epoch authenticator, is noted on #1482.

A store that adopts a sender's compaction floor owes the suffix up to the
sender's advertised tip. While it does, the stream is pending: local
authoring, compaction and re-adoption on it are skipped, and every
session reports a continuation. The obligation could only clear when that
exact tip arrived, and a newer floor was refused. But the tip is pinned
nowhere. Once its writer compacted it away, or its device was removed and
its entries refused, no store could deliver it, and the stream stayed
blocked for good. It spread, too: a fresh peer adopting the floor from the
blocked store inherited the same unsatisfiable obligation.

Holding out bought no safety. A peer that simply missed the same stretch
is re-rooted onto a newer floor, and the floor carries its own contract.
Equivocation below a floor is already undetectable once any floor is
recorded. A newer floor now re-roots an indebted store like any other
peer, and the obligation follows the new root. Removing a device drops any
suffix still owed on its chain.

Fixes #1489
The suffix-coverage table carries no account, and the same device may
still write another account's streams, so a removal drops only the
obligation on the stream it is enqueued for. A placeholder removal
reaches a real stream on first contact, which re-enqueues it there.
Also corrects the clear_delivered doc to the new reasoning.

Refs #1489
@github-actions

Copy link
Copy Markdown
Contributor

SCIP oracle — resolution report

Heuristic→compiler edge resolution per corpus. Δ compares resolved-after to the main baseline (only when the corpus profile + tool version match).

corpus tool edges resolved (heuristic → compiler) precision recall monikers Δ vs main
c-libuv scip-clang 12691 33.5% → 45.0% 96.0% 55.3% 661 +0.0pp
cpp-yaml scip-clang 7686 42.0% → 67.7% 82.5% 41.5% 1048 +0.0pp
py-django scip-python 12882 30.3% → 73.8% 93.6% 83.4% 3701 +0.0pp
py-rich scip-python 8383 27.8% → 92.4% 94.6% 81.3% 1196 +0.0pp
rust-time rust-analyzer 9907 37.8% → 59.1% 81.2% 83.6% 574 +0.0pp
ts-rxjs scip-typescript 6533 43.7% → 94.9% 86.2% 76.8% 631 +0.0pp

resolved = Exact/Syntactic + compiler upgrades + resolved-external, over edge candidates with a callee range. precision/recall are the oracle eval metrics.

@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Projectrag-rat
Branchfix/purged-suffix-obligation-reroot
Testbedhetzner-bigmem

⚠️ WARNING: No Threshold found!

Without a Threshold, no Alerts will ever be generated.

Click here to create a new Threshold
For more information, see the Threshold documentation.
To only post results if a Threshold exists, set the --ci-only-thresholds flag.

Click to view all benchmark results
BenchmarkEstimated Cyclescycles x 1e6Instructionsinstructions x 1e6L1 Hitshits x 1e6LL Hitshits x 1e6RAM Hitshits x 1e3Total read+writereads/writes x 1e6
rag_pipeline::pipeline::index cargo_resolver:resolver_config()📈 view plot
⚠️ NO THRESHOLD
1,752.57 x 1e6📈 view plot
⚠️ NO THRESHOLD
1,089.42 x 1e6📈 view plot
⚠️ NO THRESHOLD
1,527.80 x 1e6📈 view plot
⚠️ NO THRESHOLD
36.16 x 1e6📈 view plot
⚠️ NO THRESHOLD
1,256.44 x 1e3📈 view plot
⚠️ NO THRESHOLD
1,565.22 x 1e6
rag_pipeline::pipeline::query_cold cargo_resolver:resolver_built_config()📈 view plot
⚠️ NO THRESHOLD
258.86 x 1e6📈 view plot
⚠️ NO THRESHOLD
171.01 x 1e6📈 view plot
⚠️ NO THRESHOLD
243.46 x 1e6📈 view plot
⚠️ NO THRESHOLD
2.86 x 1e6📈 view plot
⚠️ NO THRESHOLD
30.92 x 1e3📈 view plot
⚠️ NO THRESHOLD
246.36 x 1e6
rag_pipeline::pipeline::query_warm cargo_resolver:resolver_index()📈 view plot
⚠️ NO THRESHOLD
230.63 x 1e6📈 view plot
⚠️ NO THRESHOLD
151.90 x 1e6📈 view plot
⚠️ NO THRESHOLD
216.86 x 1e6📈 view plot
⚠️ NO THRESHOLD
2.53 x 1e6📈 view plot
⚠️ NO THRESHOLD
32.48 x 1e3📈 view plot
⚠️ NO THRESHOLD
219.42 x 1e6
🐰 View full continuous benchmarking report in Bencher

@github-actions

Copy link
Copy Markdown
Contributor

🐰 Bencher Report

Projectrag-rat
Branchfix/purged-suffix-obligation-reroot
Testbedhetzner-bigmem
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
seconds (s)
(Result Δ%)
Upper Boundary
seconds (s)
(Limit %)
index_time/full_rebuild_cargo📈 view plot
🚷 view threshold
6.17 s
(+0.53%)Baseline: 6.14 s
7.17 s
(86.01%)
🐰 View full continuous benchmarking report in Bencher

@codecov

codecov Bot commented Sep 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

This branch has not been deployed

No deployments
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.

fix(sync): a purged store with a surviving suffix obligation cannot be re-rooted onto a compacted sender's floor

1 participant