fix(core): delete merged MemWAL generation dirs after WAL merge#152
Merged
Conversation
merge_own_shard appended each flushed generation into the base table and
drained it from the shard manifest, but never deleted the underlying
_mem_wal/{shard}/{gen}/ blob directory. Every merged generation therefore
left a zombie directory (data.lance + bloom_filter + manifest) on object
storage forever — a pure storage leak that grows without bound on any
long-running store. Data stayed correct (the drained manifest no longer
references the directory, so reads never see it), which is why this went
unnoticed: only _mem_wal/ disk usage was affected.
Delete each merged generation's directory after the manifest drain
(best-effort, warn-on-failure). Ordering is deliberate: the drain removes
the ids first so a reader can no longer resolve them, then the data is
deleted — crash-safe in either order.
Adds a serial (no concurrency, no fence) regression test asserting on-disk
generation dirs match the manifest's pending count after count-triggered
merges. Verified it fails without the fix (30 leaked dirs) and passes with it.
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
RolloutStore::merge_own_shardfolds each flushed MemWAL generation into the base table (Dataset::append) and drains it from the shard manifest (commit_update) — but never deletes the underlying_mem_wal/{shard}/{gen}/blob directory. Every merged generation leaves a zombie directory (data.lance+bloom_filter.bin+ manifest) on object storage forever.This is a pure storage leak:
_mem_wal/grows without bound — the number of orphaned directories equals the number of successfully merged generations, accumulating across the store's entire lifetime.Observed in production (
conntest_lianghongxu): blob had more_gen_directories than any shard manifest referenced. The surplus was exactly the store's historically merged generations — not a fence/concurrency artifact.Root cause is the merge drain, not fencing
An earlier hypothesis blamed a fence between
flush's data-write and manifest-commit. That was ruled out experimentally: a concurrent-fence repro produced 40 fences and 0 orphans (fences land beforewrite_data_file, leaving nothing behind). A fully serial, single-shard, zero-fence repro reproduced the leak deterministically (30 merges → 30 leaked dirs), pinning it on the merge path.Fix
Delete each merged generation's directory after the manifest drain, best-effort (a delete failure logs a warning but does not fail the already-successful merge). Ordering is deliberate and crash-safe: the drain removes the ids first (so no reader can resolve them), then the data is deleted.
Test plan
wal_merge_generation_cleanup.rs(no concurrency, no fence) asserting on-disk_gen_dirs == manifest pending generations after count-triggered mergescargo test -p lance-context-core— 130 passed + 2 regression testscargo fmt --all+cargo clippy -p lance-context-coreclean🤖 Generated with Claude Code