lance-format / lance-format/lance
bug: a failed memtable flush is skipped by WAL replay once a later generation commits
@hamersaw is already working on this.
Since Aug 5, 2026.
- Dominant language
- Rust
- Stars
- 7.1k
- Forks
- 852
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 272
Description
A failed L0 flush leaves its rows recoverable only in memory, and a subsequent successful flush revokes even that.
MemTableFlushHandler::flush_memtable failing is not fatal by design — TaskDispatcher::run logs the error and keeps draining, on the stated assumption that "the worst case for a transient flush failure is replay from the WAL on next open" (rust/lance/src/dataset/mem_wal/write.rs). That assumption does not hold once the next generation commits.
MemTableFlusher::update_manifest (rust/lance/src/dataset/mem_wal/memtable/flush.rs) unconditionally stamps the committing generation's coordinates:
replay_after_wal_entry_position: covered_wal_entry_position,
current_generation: generation + 1,
Neither checks for a gap. So with generation N failing and N+1 committing:
replay_after_wal_entry_positionadvances past N's WAL entries, so replay-on-reopen skips them.- N's rows are not in L0 — its flush failed.
- They survive only as the retained un-stamped
FrozenMemTableinWriterState::frozen_memtables, which dies with the process.
A crash after that point loses generation N. current_generation advancing past a generation that never reached L0 is the same defect seen from the read side: it is not a completion watermark.
The fix is contiguous advancement with sticky failure handling — refuse to commit a generation that is not the manifest's current_generation, so the shard stops advancing rather than silently stepping over a hole. That trades a wedged shard (recoverable by reopen + replay) for silent data loss, but it is a real change to failure-mode semantics and wants its own design pass: notably whether a failed flush should be retried, and whether the writer should poison instead of accumulating frozen memtables until backpressure stalls writes.
Found while addressing review on #8051, which no longer depends on the watermark — its seal fence now waits on per-generation flush outcomes directly. This issue is about the durability hole underneath, which predates that PR.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.