Skip to main content

Manifest

The manifest defines which SSTables are live at each level. It is the commit point for all state changes.

Role in the System

Loading diagram...

Structure

{
"format_version": 1,
"manifest_id": "00000000000000000042",
"writer_epoch": 7,
"compactor_epoch": 3,
"namespace": "my-namespace",
"last_flushed_sequence": 458923,
"levels": {
"L0": [{ "id", "size_bytes", "entry_count", "min_key", "max_key",
"bloom_filter_offset", "bloom_filter_size",
"sequence_range", "record_id_count" }],
"L1": [], "L2": [], "L3": []
},
"tombstone_compaction_watermarks": { "L1": "...", "L2": "..." },
"previous_manifest_id": "00000000000000000041"
}

Manifest IDs are 20-digit zero-padded integers. The highest ID is always the current manifest — S3 ListObjectsV2 gives you the latest. Two writers computing the same next ID race: one wins, the other retries.

S3 path: s3://{bucket}/{hash % 128}/flushdb/{namespace}/manifests/{id}

CAS Update Protocol

Every flush, compaction, and GC operation updates the manifest atomically:

Loading diagram...

S3 conditional writes reject a PUT if the key already exists. No external coordination needed.

Concurrent Flush + Compaction

Loading diagram...

Epoch-Based Fencing

Problem: Node A begins flushing, its lease expires, Node B takes over, A's flush completes and tries to update the manifest.

Solution: Each manifest carries writer_epoch and compactor_epoch. On lease acquisition, the new owner bumps the epoch. The old node's writes are rejected:

Loading diagram...

Snapshots and Pruning

Every 100 versions, a self-contained snapshot manifest is written. On startup, read the latest snapshot and apply subsequent deltas. Versions older than the second-most-recent snapshot are eligible for deletion.

A pointer file (manifest.json) provides fast lookup of the current manifest ID. Falls back to ListObjectsV2 if stale.