docs/components/read_flow/08_range_deletions.md
Files: db/range_del_aggregator.h, db/range_del_aggregator.cc, db/range_tombstone_fragmenter.h, db/range_tombstone_fragmenter.cc, table/table_cache.cc
Range deletions (DeleteRange()) create tombstones that cover a range of keys [start, end). These must be integrated into both point lookup and iterator paths. The implementation uses different mechanisms for each:
max_covering_tombstone_seq comparisonRangeDelAggregator with heap-based active tombstone trackingRangeDelAggregator (see RangeDelAggregator in db/range_del_aggregator.h) is abstract with two concrete implementations:
| Implementation | Use Case | Strategy |
|---|---|---|
ReadRangeDelAggregator | Point lookups and iterators | Single stripe covering [0, snapshot_seqno] |
CompactionRangeDelAggregator | Compaction | Multiple stripes partitioned by snapshot boundaries |
Before tombstones can be efficiently queried, overlapping tombstones are fragmented into non-overlapping segments by FragmentedRangeTombstoneList (see FragmentedRangeTombstoneList in db/range_tombstone_fragmenter.h):
Input (overlapping):
[a, e) @ seq=10[c, g) @ seq=15[f, z) @ seq=5Output (fragmented, non-overlapping):
| Fragment | Sequences |
|---|---|
[a, c) | 10 |
[c, e) | 10, 15 |
[e, f) | 15 |
[f, g) | 15, 5 |
[g, z) | 5 |
Benefits: No overlaps between fragments, efficient sequence number lookup within each fragment, and compact storage.
TruncatedRangeDelIterator (see TruncatedRangeDelIterator in db/range_del_aggregator.cc) ensures range tombstones do not leak beyond SST file boundaries:
[smallest, largest) of the source filelargest.sequence -= 1 in the general case to avoid affecting keys in the next file with the same user keykTypeRangeDeletion with kMaxSequenceNumber) or when largest.sequence == 0Key Invariant: Range tombstones are truncated at file boundaries, preventing deletion leakage across files.
Point lookups use a lightweight mechanism instead of the full RangeDelAggregator:
Step 1: In TableCache::Get(), for each SST file, compute the maximum covering tombstone sequence number via MaxCoveringTombstoneSeqnum(user_key) on the file's tombstone iterator.
Step 2: Track the highest max_covering_tombstone_seq across all files searched.
Step 3: In GetContext::SaveValue(), if max_covering_tombstone_seq > parsed_key.sequence, the point key is covered by a range tombstone and is treated as deleted.
This avoids building a full RangeDelAggregator for point lookups, using a single sequence number comparison instead.
Early termination: In Version::Get(), before each file search, if max_covering_tombstone_seq > 0, the search stops immediately -- any entry found at lower levels would have a lower sequence number and be covered by the tombstone. This check is at the top of the file search loop, before TableCache::Get() is called for the next file.
For iterators, tombstones are fully integrated into MergingIterator:
Step 1: Registration -- Each child point iterator is paired with a TruncatedRangeDelIterator via MergeIteratorBuilder::AddPointAndTombstoneIterator().
Step 2: Filtering -- MergingIterator::SkipNextDeleted() checks each candidate point key against active range tombstones before exposing it to DBIter.
Step 3: Cascading seeks -- If a point key is covered, the iterator seeks past the tombstone's end key. The new position might be covered by another tombstone from a different level, causing a cascade of seeks.
ForwardRangeDelIterator::ShouldDelete() (see ForwardRangeDelIterator in db/range_del_aggregator.cc) determines if a point key is covered by an active tombstone:
Data structures:
active_seqnums_: Multiset ordered by descending sequence number (highest on top)active_iters_: BinaryHeap ordered by end key (tracks when tombstones stop covering)inactive_iters_: BinaryHeap ordered by start key (tracks when tombstones start covering)Algorithm:
active_iters_ any tombstone whose end key <= current keyinactive_iters_ any tombstone whose start key <= current keyactive_seqnums_ is non-empty and its highest sequence > point key's sequence, the key is deletedComplexity: O(log K) amortized per query, where K is the number of range tombstones. Each tombstone is activated and expired at most once during a forward scan.
Key Invariant: A point key is deleted if and only if a range tombstone with higher sequence number covers it.