Function: compact in log-structured-merge-tree/lsm.py

Date: 2026-05-28

Time: 19:02

LSMTree.compact — Full Compaction of SSTables

Purpose

compact performs full compaction — it merges every SSTable on disk into a single new SSTable, deduplicating keys (newest value wins) and permanently removing tombstones (deleted keys). This reclaims disk space and reduces the number of files that get and range_scan must search.

In the DDIA model, this is a size-tiered compaction strategy at its simplest: all SSTables collapse into one. It's triggered automatically by flush when len(self.sstables) >= self.compactionthreshold, or can be called directly.

Contract

Preconditions:

Postconditions:

Invariant preserved: SSTables remain sorted by sequence number (trivially — there's only one).

Short-circuit: If fewer than 2 SSTables exist, the method returns immediately — nothing to merge.

Parameters

None beyond self. The method operates entirely on self.sstables and self.dir.

Return Value

None. All effects are side effects.

Algorithm

1. Guard: Exit early if there are 0 or 1 SSTables — compaction requires at least 2.

2. Collect: Iterate every SSTable via scan_all(), gathering (key, seq, value) triples. The seq comes from the SSTable object, not from individual entries — every entry in a given SSTable shares the same sequence number. This is a simplification: it means if SSTable 3 has keys a, b, c, all three get seq=3.

3. Sort: Sort the collected entries by (key, -seq). This groups entries by key, with the highest sequence number (newest) first within each group.

4. Deduplicate: Walk the sorted list, keeping only the first occurrence of each key (the newest version). If that newest version is a TOMBSTONE (b""), skip it entirely — the key is dead.

5. Write: Create a new SSTable with a fresh sequence number. The merged list is already sorted by key (the sort in step 3 guarantees this), which is exactly what SSTable.write expects.

6. Swap and clean up: Replace self._sstables with a list containing only the new SSTable. Delete every old SSTable file from disk.

Side Effects

Error Handling

There is no error handling. If any step fails:

Usage Patterns


# Automatically triggered inside _flush:
if len(self._sstables) >= self._compaction_threshold:
    self.compact()

# Or called directly by application code:
tree.compact()

Callers should not assume compaction is fast — it reads and rewrites the entire dataset. The default compaction_threshold of 4 means this runs every 4th flush.

Dependencies

Assumptions Not Enforced by Types

1. scan_all() yields entries in sorted key order — the sort in compact doesn't depend on this (it re-sorts everything), but SSTable.write requires sorted input, which is guaranteed by the (key, -seq) sort producing key-ascending order after dedup.

2. Sequence numbers are unique per SSTable — if two SSTables shared a seq, the sort would be nondeterministic for entries with the same key.

3. No concurrent access — another thread calling get during the swap between self.sstables = [newsst] and the old file deletions would be fine, but a concurrent flush appending to self.sstables could lose the appended SSTable.

4. All values in merged are non-empty bytes — the tombstone check ensures b"" is never written, but there's no validation that other values are well-formed.

5. Disk has space for the new SSTable before the old ones are deleted — worst case, compaction temporarily doubles disk usage.

Topics to Explore

Beliefs