Skip to content

vacuum morsels: bound the leader's round consumption at the resume point (fixes #66) - #89

Open
jdatcmd wants to merge 2 commits into
malisper:mainfrom
jdatcmd:fix-issue-66-morsel-doublecount
Open

vacuum morsels: bound the leader's round consumption at the resume point (fixes #66)#89
jdatcmd wants to merge 2 commits into
malisper:mainfrom
jdatcmd:fix-issue-66-morsel-doublecount

Conversation

@jdatcmd

@jdatcmd jdatcmd commented Aug 19, 2026

Copy link
Copy Markdown

Fixes #66. Independent branch off main.

On a §5.2 coverage trip, resume_g rewinds below granules other workers already completed, and the serial arm re-scans from there. Re-pruning a pruned page re-collects the same LP_DEAD items, and dead_items_add's num_items is add-not-replace — so anything the leader consumed from the round at/above the rewound resume point gets counted twice (debug: collect_dead_tids' assert trips; release: silent dead-tuple misreport into progress reporting and downstream decisions).

The issue names the deferred blocking-cleanup pages; while fixing it I found the dead-run merge has the same exposure: merge_dead_runs folds all workers' runs unfiltered, so above-the-hole runs double-count identically once the serial arm re-prunes those blocks. Both are fixed with one bound.

Changes

  • vacuum_morsels: new pure leader_round_work(locals, resume_bound) — the dead runs to merge into the round authority store and the deferred pages the leader processes, both filtered strictly below the resume point (None bound on a completed round). Filtering happens before the merge: on an OverlapAt trip the double-claimed block sits at/above the bound, and the merge's blocks-disjoint-across-Locals contract only holds for the below-bound remainder.
  • vacuumlazy morsels.rs: the leader computes the bound right after the coverage check (block_of(resume_g), i.e. the first block the serial arm will visit) and consumes only leader_round_work's output. At/above-bound deferred pages are left to the serial arm's blocking-cleanup path, which the C body already handles — mirroring how skipsallvis_before(resume_g) already filters skip decisions at the resume point.
  • Healthy rounds are behavior-neutral: the coverage guard errors on ScannedBeyondResume, so nothing above the bound exists unless the guard tripped — pinned by a 200-case random parity sweep (leader_round_work(locals, None)merge_dead_runs + raw ledger; any bound splits both consistently).

Testing (TDD, red→green in the container)

  • Red (431534b): coverage_trip_drops_round_work_above_the_rewound_resume drives the issue's exact scenario — a lost Local holding lower granules (HoleAt rewind) while a surviving worker above the hole carries both a dead run and a deferred cleanup page — against the leader's round consumption as morsels.rs builds it today. Fails: dead runs at/above the resume point double-count num_items once the serial arm re-prunes them.
  • Green (tip): same assertions through the real leader_round_work; below-hole work is kept (the serial arm never revisits it), at/above-bound work is dropped. All 13 vacuum_morsels tests + the vacuumlazy suite pass. Ubuntu 26.04, rustc 1.96.0.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved vacuum recovery after coverage gaps by preventing duplicate processing of dead-item runs and deferred cleanup pages.
    • Ensured resumed processing only includes work below the recovery boundary.
  • Tests

    • Added coverage for recovery scenarios, leader processing, and randomized validation of bounded and unbounded cleanup behavior.

ChronicallyJD and others added 2 commits August 19, 2026 15:46
…he rewound resume (issue malisper#66)

A lost Local rewinds resume_g below granules other workers completed;
the serial arm re-scans from there and re-collects the same LP_DEAD
items. dead_items_add's num_items is add-not-replace, so every dead run
and deferred blocking-cleanup page at/above the rewound resume point
that the leader also consumes in the round double-counts dead tuples
(debug: collect_dead_tids' assert trips; release: silent misreport).
The test drives the exact issue scenario against the leader's round
consumption as morsels.rs builds it today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e resume point (issue malisper#66)

New pure leader_round_work(locals, resume_bound): the dead runs merged
into the round authority store and the deferred blocking-cleanup pages
the leader processes, both filtered strictly below the resume point.
morsels.rs computes the bound after the §5.2 coverage check (first
serial-arm block, None on a completed round) and consumes only that —
on a coverage rewind, at/above-bound work is left entirely to the
serial re-scan, which re-collects the same LP_DEAD items exactly once.
Filtering happens before the merge so an OverlapAt trip's duplicated
block never meets the merge's disjointness contract. Healthy rounds
carry nothing above the bound (ScannedBeyondResume is a coverage
error), so this is behavior-neutral there — pinned by the unbounded
parity sweep.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5356489c-c995-4be3-a8e7-cef48b217d0b

📥 Commits

Reviewing files that changed from the base of the PR and between 438c8c4 and 6b883b2.

📒 Files selected for processing (3)
  • crates/backend/access/heap/vacuumlazy/src/morsels.rs
  • crates/backend/commands/vacuum_morsels/src/lib.rs
  • crates/backend/commands/vacuum_morsels/src/tests.rs

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The vacuum leader now uses leader_round_work to filter dead-item runs and deferred-cleanup pages below the recovery resume boundary. Recovery and randomized tests validate coverage-hole handling and unbounded and bounded results.

Changes

Vacuum recovery filtering

Layer / File(s) Summary
Leader round work preparation
crates/backend/commands/vacuum_morsels/src/lib.rs
Adds leader_round_work, which filters, sorts, and validates dead runs and deferred-cleanup blocks against an optional resume boundary.
Morsel round integration
crates/backend/access/heap/vacuumlazy/src/morsels.rs
Computes the resume boundary and uses the filtered leader results during round finalization and serial rescanning.
Recovery and parity validation
crates/backend/commands/vacuum_morsels/src/tests.rs
Adds coverage-hole recovery and randomized parity tests for bounded and unbounded leader work.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 6b883

The change bounds leader work at the resume point and includes targeted regression coverage; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant VacuumMorsels
  participant VacScanLocal
  participant leader_round_work
  participant SerialLeader
  VacuumMorsels->>VacScanLocal: collect worker dead runs and deferred-cleanup blocks
  VacuumMorsels->>leader_round_work: pass locals and resume bound
  leader_round_work->>leader_round_work: filter entries below resume bound
  leader_round_work->>SerialLeader: return sorted dead runs and cleanup blocks
  SerialLeader->>VacuumMorsels: process retained entries during round finalization
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes bounding leader round consumption at the resume point, which is the primary change.
Linked Issues check ✅ Passed The changes filter deferred cleanup pages and dead runs at or above the resume point, directly addressing issue #66.
Out of Scope Changes check ✅ Passed The implementation and tests remain focused on resume-bound leader processing and coverage-guard double counting.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

Parallel/morsel vacuum: deferred-cleanup pages double-count dead_items_info.num_items on a coverage-guard trip

2 participants