Architecture

EdgeStore is built around three principles: append-only writes, SSD-aware deathtime-cohort compaction, and layered purity (vector/text on top of KV, never inside it).

Crate Dependency

EdgeStore ships as a Cargo workspace. Each crate has a distinct purpose and audience.

edgestore core

KV + vector + text engine. Sync core. No async runtime dependency.

Engine WAL SegmentStore Compactor
edgestore-tokio async

Async wrapper. Every operation runs in spawn_blocking. Thin layer.

AsyncEngine
edgestore-repl network

REPLication transport — HTTP + S3. Not a REPL shell. Adds network and remote durability to the core engine.

HttpReplicationServer HttpReplicationClient AntiEntropyLoop S3RemoteStore
edgestore-cli admin

Administrative CLI tool. Create, put, get, stats, compact, export/import.

clap
edgestore ← used by all downstream crates

High-Level Architecture

Application
KV API
Vector API
Text API
Replication
Engine
Single-writer + group commit + snapshot RAII
WAL
LZ4 + CRC32C, rotated 64 MB / 60s
Memtable
BTreeMap, swappable trait
Segment Store
.dat (ZSTD) + .idx (sparse) + .xf (xor filter) + .meta (JSON)
Storage Backend
NVMe / SSD / S3 / FDP / Memory

Write Path

put(key, value, ttl?)
↓
append WAL record (LZ4, CRC32C)
↓
insert into Memtable (BTreeMap)
↓ flush triggered by size threshold
write sorted run → segment-{id}.dat (ZSTD)
build sparse index → segment-{id}.idx
build xor filter → segment-{id}.xf
write metadata → segment-{id}.meta
append to manifest → manifest file
↓
remove flushed keys from memtable

Read Path

get(namespace, key)
↓
search Memtable first
hit → return value
miss → continue
for each live segment (newest → oldest):
xor filter check → if absent, skip
sparse index seek → locate block
decompress block → scan for key
hit → return value with highest LSN
↓
not found → return None

Compaction Path

compact_once(write_budget)
↓
identify cohorts where max(death_time) < now
↓ fully expired cohorts first
for each selected cohort within budget:
read all segments in cohort
merge-sort by key, keep highest-LSN
write new segments with updated cohort buckets
recompute Merkle roots + BLAKE3 hashes
update manifest (append new, mark old obsolete)
delete obsolete files (if not pinned by snapshot)
↓
return CompactionStats

File Formats

WAL Record
txid
u64
lsn
u64
timestamp
i64
ttl
u32
namespace
bytes
key
bytes
operation
u8
value_hash
[u8;32]
value_bytes
bytes
Segment Files
dat
ZSTD data blocks (4 KiB aligned)
idx
Sparse offset index (key → block)
xf
Xor filter (~8 bits/key, 1% FPR)
meta
JSON metadata (bounds, hash, cohort)

Namespace Encoding

Single flat keyspace. No ColumnFamily complexity.

ns_len
2 bytes
namespace
variable
user_key
variable
Enables: prefix scans, range queries, time-series, secondary indexes — all within one sorted keyspace.

Concurrency Model

Single Writer
One active write tx at a time
Multiple Readers
Never block writers; snapshot manifest
Group Commit
Batch tx commits into one fsync
No MVCC in v1. WAL txid field reserved for future layering.

References