docs/components/read_flow/03_superversion_and_snapshots.md
Files: db/column_family.h, db/column_family.cc, db/db_impl/db_impl.cc, db/snapshot_impl.h
SuperVersion (see SuperVersion in db/column_family.h) bundles the three data sources a reader needs into a single reference-counted object:
| Field | Type | Purpose |
|---|---|---|
mem | ReadOnlyMemTable* | Current mutable memtable |
imm | MemTableListVersion* | Snapshot of immutable memtable list |
current | Version* | SST file set (all levels) |
mutable_cf_options | MutableCFOptions | Column family options at time of creation |
version_number | uint64_t | Monotonically increasing version ID |
write_stall_condition | WriteStallCondition | Current write stall state |
full_history_ts_low | std::string | Oldest readable user-defined timestamp |
seqno_to_time_mapping | shared_ptr<const SeqnoToTimeMapping> | Sequence number to wall clock mapping |
SuperVersion uses an std::atomic<uint32_t> refs field:
Ref() increments with memory_order_relaxed (safe because the caller already has a valid reference)Unref() decrements with fetch_sub(1) and returns true if this was the last reference (previous_refs == 1)SuperVersion::Cleanup() runs immediately (under the DB mutex): it unrefs mem, imm, and current, and pushes obsolete memtables to a to_delete vector. The SuperVersion object itself is then either deleted immediately or deferred to a background purge thread (when background_purge_on_iterator_cleanup or avoid_unnecessary_blocking_io is set)Key Invariant: Every reader must acquire a SuperVersion reference before accessing any data and release it after the read completes. This prevents memtables and SST files from being deleted while in use.
A held SuperVersion reference pins mem, imm, and current (Version). This has three resource implications:
DB::GetSnapshot()) prevent CompactionIterator from dropping key versions visible to live snapshots (see earliest_snapshot_ in db/compaction/compaction_iterator.cc). Old Put/Delete pairs that would normally be collapsed are preserved, increasing SST file sizes.Monitoring snapshot age via rocksdb.oldest-snapshot-time DB property is important in production to detect these retention issues.
To avoid contention on the DB mutex for every read, SuperVersion pointers are cached in thread-local storage via ThreadLocalPtr (local_sv_).
Fast path (thread-local hit):
kSVInUse sentinelkSVObsolete), use it directly -- no mutex, no atomic refcountkSVInUse sentinel back with the SuperVersion pointerSlow path (cache miss or invalidated):
kSVObsolete (meaning InstallSuperVersion invalidated it), fall throughRef() on the current super_version_ to increment its refcountWhen the memtable or Version changes (e.g., after flush or compaction), InstallSuperVersion() is called:
Step 1: Create a new SuperVersion with updated mem/imm/current pointers (under DB mutex)
Step 2: Mark all thread-local copies as kSVObsolete via ResetThreadLocalSuperVersions()
Step 3: The old SuperVersion remains alive as long as any reader holds a reference to it
Step 4: When the last reader releases the old SuperVersion, Cleanup() runs under the DB mutex (unreffing mem/imm/current). Actual resource reclamation (deleting obsolete memtables, purging files) may happen on the calling thread or be deferred to background purge threads
Reads must see a consistent point-in-time view. The sequence number determines what is visible:
With explicit snapshot (ReadOptions::snapshot != nullptr):
DB::GetSnapshot() which captured last_sequence_ at that momentWithout explicit snapshot (implicit snapshot):
GetLastPublishedSequence() after SuperVersion acquisitionVisibility is enforced at multiple points in the read path:
| Location | Mechanism |
|---|---|
MemTable SaveValue() | ReadCallback::IsVisible(seq) or CheckCallback(seq) |
DBIter::IsVisible() | sequence <= sequence_ (snapshot seqno) check |
GetContext | Initialized with snapshot sequence, filters entries during SST reads |
For transactions using WriteUnpreparedTxn, the ReadCallback also checks whether the writing transaction is the reader's own transaction, allowing reads of uncommitted data within the same transaction.
The combination of SuperVersion refcounting and snapshot sequence numbers provides:
Key Invariant: Readers see a consistent point-in-time snapshot of mem + imm + Version without holding the DB mutex during the read.