Skip to content

Re-mark the region tracker at the merged order when freeing pages#1294

Merged
cberner merged 1 commit into
masterfrom
claude/region-tracker-merge-bug-x9g3yp
Jul 6, 2026
Merged

Re-mark the region tracker at the merged order when freeing pages#1294
cberner merged 1 commit into
masterfrom
claude/region-tracker-merge-bug-x9g3yp

Conversation

@cberner

@cberner cberner commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Freeing a page could leave the allocator's per-order free-space index (the region tracker) stale, so the database file could grow instead of reusing freed space, and compact() could stop before fully shrinking the file.

Root cause

free_helper() marked the region tracker free only at the freed page's own order. But freeing a page can buddy-merge it into a block of a higher order. Once a region had been marked full at some order K (mark_full, when an allocation of order K failed on it), only a free at order >= K would clear that bit, because mark_free(order) clears orders 0..=order. A free at a lower order that merged up into a free order-K block left the bit set.

With the bit stale, find_free(K) skipped the region even though it held a free block, so allocations grew the file instead of reusing the space, and compact()'s allocate_lowest probe was steered to a higher page and hit its new >= old early break. The buddy allocator itself stayed exact, so no data was corrupted.

Fix

BuddyAllocator::free() now returns the order of the resulting (possibly merged) block, and free_helper() marks the region tracker free at that order. Returning it costs nothing — it is the terminal order of the existing merge recursion — and avoids re-scanning every order to recompute the region's highest free order on each free.

Testing

  • Added free_merge_remarks_region_tracker, which fills a region, frees it so the pages merge back up, and asserts the next allocation reuses the region instead of spilling into a new one. Confirmed it fails before the fix and passes after.
  • cargo fmt --check, cargo clippy --all-targets --all-features (deny warnings), and cargo test --all-features all pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_016n3Q5CXgzQTFNC8qbPheGp


Generated by Claude Code

free_helper() marked the region tracker free only at the freed page's own
order. But freeing a page can merge it with its buddies into a block of a
higher order, and the region tracker is a per-order index of which regions
may have free space. Once a region had been marked full at some order K
(mark_full, when an allocation of order K failed on it), only a free at
order >= K would clear that bit, since mark_free(order) clears orders
0..=order. A free at a lower order that merged up into a free order-K block
left the bit set.

With the bit stale, find_free(K) skipped the region even though it held a
free block, so allocations grew the file instead of reusing the space.
compact()'s allocate_lowest probe was likewise misdirected to a higher page
and hit its "new >= old" early break, terminating before reclaiming
reclaimable space -- a plausible root cause for "compact() doesn't shrink my
file" reports. Full repair rebuilt the tracker and healed it; quick-repair
persisted the staleness across restarts. The buddy allocator itself stayed
exact, so this never corrupted data.

Have BuddyAllocator::free() return the order of the resulting block -- the
order at which buddy merging stopped -- and mark the region tracker free at
that order. This clears the tracker's stale higher-order bits after a merge
without re-scanning every order to recompute the region's highest free order
on each free. Add a regression test that fills a region, frees it so the
pages merge back up, and checks that the next allocation reuses the region
instead of spilling into a new one.

Assisted-by: Claude Code
https://claude.ai/code/session_016n3Q5CXgzQTFNC8qbPheGp
@cberner cberner force-pushed the claude/region-tracker-merge-bug-x9g3yp branch from ca4dba1 to 7b3eb1e Compare July 6, 2026 02:09
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.20%. Comparing base (88881b8) to head (7b3eb1e).

Files with missing lines Patch % Lines
src/tree_store/page_store/page_manager.rs 90.90% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1294      +/-   ##
==========================================
- Coverage   90.21%   90.20%   -0.01%     
==========================================
  Files          36       36              
  Lines       16289    16318      +29     
==========================================
+ Hits        14695    14720      +25     
- Misses       1594     1598       +4     
Files with missing lines Coverage Δ
src/tree_store/page_store/buddy_allocator.rs 96.57% <100.00%> (-0.02%) ⬇️
src/tree_store/page_store/page_manager.rs 95.12% <90.90%> (-0.17%) ⬇️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cberner cberner merged commit b2da02d into master Jul 6, 2026
8 checks passed
@cberner cberner deleted the claude/region-tracker-merge-bug-x9g3yp branch July 6, 2026 02:32
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.

1 participant