Skip to content

fix(rollout): seal the memtable in cleanup_own_shard before merging - #189

Merged
beinan merged 1 commit into
mainfrom
fix/rollout-flush-interval-zero
Jul 25, 2026
Merged

fix(rollout): seal the memtable in cleanup_own_shard before merging#189
beinan merged 1 commit into
mainfrom
fix/rollout-flush-interval-zero

Conversation

@beinan

@beinan beinan commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes #184.

Problem

ROLLOUT_FLUSH_INTERVAL_SECS's documentation says 0 disables periodic flush and rows "then only become visible when the cleanup/merge path flushes them". That fallback did not exist.

cleanup_own_shard delegates to merge_own_shard_if_ready(1), which reads the shard manifest and short-circuits:

let pending = manifest.flushed_generations.len();
if pending == 0 || pending < threshold.max(1) {
    return Ok(0);
}

With no periodic flush, nothing seals the active memtable, so flushed_generations is always empty, so this returns 0 and never reaches merge_own_shard — which contains the only other close() (and therefore the only other seal) on that path.

Net effect with ROLLOUT_FLUSH_INTERVAL_SECS=0: add persists the WAL entry, so no data loss, but the rows are invisible to every reader indefinitely — until the process restarts and replays the WAL.

Verification

Confirmed with a throwaway probe before writing the fix:

before_close_visible=0
observe_row_count=0 pending_gens=0
cleanup_merged=0          <-- cleanup could not rescue it
after_cleanup_visible=0   <-- still invisible
after_close_visible=1     <-- only close() sealed it

Change

  1. cleanup_own_shard flushes before the threshold check (rollout_store.rs). One line, plus a comment explaining why the ordering matters. Cleanup becomes a genuine standalone fallback, as the config already claimed.

  2. Startup warning when both intervals are 0 (main.rs). Cleanup-alone is now sufficient, but flush=0 and cleanup=0 still leaves nothing to seal. That is a legitimate (if unusual) configuration, so it warns rather than refusing to boot.

  3. Config doc corrected to describe what each combination actually does.

Testing

New cleanup_own_shard_seals_before_merging asserts a single cleanup pass seals, merges, and exposes the row, with the count trigger explicitly disabled so cleanup is the only path that can help.

I verified it is a real guard by reverting just the self.flush().await? line:

FAILED: assertion `left == right` failed: cleanup must seal then merge the generation

Full runs: lance-context-core --lib 161 passed / lance-context-server 49 passed.

Note on scope

ShardWriter::close() does seal the active memtable (lance-7.0.0 write.rs:1911-1921), and RolloutStore's Drop spawns a detached close — so LRU eviction is not affected by this bug. That was the concern in #185, which I will close separately with these findings.

🤖 Generated with Claude Code

`ROLLOUT_FLUSH_INTERVAL_SECS=0` is documented as falling back to the
cleanup/merge path for visibility, but that fallback could not work.
`cleanup_own_shard` delegates to `merge_own_shard_if_ready(1)`, which reads
the shard manifest and returns early when `flushed_generations` is empty.
With no periodic flush nothing ever seals the active memtable, so that list
stayed empty forever: the merge was never reached, and rows that `add` had
durably persisted stayed invisible until a process restart replayed the WAL.

Flush before the threshold check so cleanup is a genuine standalone
fallback. Confirmed by a probe: with a row added and no flush,
`cleanup_own_shard` returned 0 and the row stayed invisible; it now returns
1 and the row is readable.

Also warn at startup when both the flush and cleanup intervals are 0, since
that combination still leaves nothing to seal, and correct the config doc
which promised the fallback unconditionally.

The new test fails on the parent commit (`reclaimed` 0 != 1) and passes here.

Closes #184

Co-Authored-By: Claude <noreply@anthropic.com>
@beinan
beinan merged commit 0359c9e into main Jul 25, 2026
9 checks passed
@beinan
beinan deleted the fix/rollout-flush-interval-zero branch July 25, 2026 07:13
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(rollout): rollout_flush_interval_secs=0 may leave rows durably written but permanently invisible

1 participant