docs/components/write_flow/09_crash_recovery.md
Files: db/db_impl/db_impl_open.cc, db/wal_manager.h, db/log_reader.h, db/log_reader.cc
When RocksDB opens a database, it recovers un-flushed writes by replaying WAL records into fresh memtables. The recovery process relies on the WAL-before-memtable invariant: every write that reached the memtable was first persisted in the WAL.
Step 1 - Read MANIFEST: VersionSet::Recover() reads the MANIFEST file to reconstruct the LSM state (which SST files exist, their levels, and the last flushed sequence number).
Step 2 - Scan WAL directory: Find all .log files, sorted by log number. Filter out stale WALs based on the MANIFEST state. If track_and_verify_wals_in_manifest is enabled, WalSet provides additional integrity verification.
Step 3 - Replay each WAL (in log_number order): For each WAL file:
log::Reader::ReadRecord() reassembles logical records from 32 KB block fragmentsWriteBatchInternal::InsertInto(batch, memtable) replays each record into a fresh memtableenforce_write_buffer_manager_during_recovery is true (default: true) and WriteBufferManager::ShouldFlush() triggers, a mid-recovery flush may occur, writing recovered memtables to L0 SSTs during replayStep 4 - Flush or restore: The end-of-recovery behavior depends on several conditions:
avoid_flush_during_recovery is false (default): recovered memtables are flushed to SST files, establishing a clean stateavoid_flush_during_recovery is true and no mid-recovery flush occurred: memtables are kept unflushed and RestoreAliveLogFiles() preserves the WAL files for future recoveryStep 5 - Delete obsolete WALs: WAL files whose data has been fully flushed to SST are removed (or recycled). If memtables were not flushed (due to avoid_flush_during_recovery), WALs are preserved.
Key Invariant: WAL replay order matches write order because WAL files are processed in log_number order, which corresponds to the sequence number order of writes.
If memtable insertion fails during normal write (after the WAL write succeeds), logs_.back().SetAttemptTruncateSize() records the WAL file size before the failed write. On the next recovery attempt, the WAL is truncated to this size, discarding the record that could not be applied to the memtable.
This mechanism handles the edge case where a write is durably persisted in the WAL but cannot be applied to the memtable (e.g., due to memory allocation failure). Without truncation, recovery would repeatedly fail on the same record.
The WAL reader handles several types of corruption:
| Corruption Type | Detection | Behavior |
|---|---|---|
| CRC mismatch | crc32c verification on each physical record | Record dropped, error reported |
| Stale data in recycled file | Log number mismatch in recyclable header | Record ignored (expected for recycled files) |
| Fragment ordering error | Out-of-sequence kFirst/kMiddle/kLast | Error reported, partial record dropped |
| Truncated record | Incomplete header or payload at end of file | May indicate clean shutdown (partial write); behavior controlled by WALRecoveryMode |
WALRecoveryMode (see DBOptions in include/rocksdb/options.h) controls how aggressively the reader tolerates corruption:
| Mode | Behavior |
|---|---|
kPointInTimeRecovery (default) | Stop at the first corruption; all records before it are valid |
kTolerateCorruptedTailRecords | Tolerate incomplete records at the tail of the WAL (common after unclean shutdown) |
kAbsoluteConsistency | Report error on any corruption, even at the tail |
kSkipAnyCorruptedRecords | Skip corrupted records and continue; use with caution |
When track_and_verify_wals is enabled, each new WAL file records information about its predecessor via PredecessorWALInfoType records (or kRecyclePredecessorWALInfoType for recycled files). This creates a verifiable chain of WAL files:
This is separate from track_and_verify_wals_in_manifest, which tracks WAL metadata (synced sizes of closed WALs) in the MANIFEST file. See Write-Ahead Log for details on both mechanisms.
| Feature | Recovery Behavior |
|---|---|
| Multiple column families | WAL records may interleave writes to different CFs; recovery routes each to the correct memtable |
| User-defined timestamps | kUserDefinedTimestampSizeType meta-records in the WAL store per-CF timestamp sizes for correct decoding |
| WAL compression | kSetCompressionType meta-records activate streaming decompression during recovery |
| Atomic flush | Affects MANIFEST AtomicGroup semantics during recovery; does not provide a special atomic recovery flush path |