EdgeStore's public API surface is intentionally small. The core type is Engine — everything else is configuration, traits, or return types.
For full rustdoc, see docs.rs/edgestore.
The single-writer KV engine. All mutations go through one Engine instance per database directory.
Engine::open(config: EdgestoreConfig) -> Result<Engine, EdgestoreError>
Open or create a database at the path specified in config. Acquires a file lock to enforce single-writer semantics.
engine.put(ns, key, val) -> Result<Lsn, EdgestoreError>
Write a key-value pair in the given namespace. Appends to WAL, then memtable. Returns the assigned LSN.
engine.put_with_ttl(ns, key, val, ttl_seconds) -> Result<Lsn, EdgestoreError>
Write with a time-to-live. Expired via deathtime-cohort compaction, not per-read checks.
engine.get(ns, key) -> Result<Option<Vec<u8>>, EdgestoreError>
Point lookup. Checks memtable first, then segments in LSN order (newest wins).
engine.delete(ns, key) -> Result<Lsn, EdgestoreError>
Write a delete tombstone. Visible to subsequent reads; removed at compaction.
engine.range(ns, start, end) -> Result<Vec<(Vec<u8>, Vec<u8>)>, EdgestoreError>
Range scan with exclusive end bound. Returns latest LSN values across memtable and all segments.
engine.prefix(ns, prefix) -> Result<Vec<(Vec<u8>, Vec<u8>)>, EdgestoreError>
Prefix scan. Efficient for time-series keys, hierarchical data, etc.
engine.flush() -> Result<(), EdgestoreError>
Force WAL fsync. Does not flush memtable to segments.
engine.flush_to_segments() -> Result<SegmentMeta, EdgestoreError>
Flush memtable to an immutable segment on disk. Creates .dat, .idx, .xf, and .meta files.
engine.compact_once() -> Result<CompactionStats, EdgestoreError>
Run one deathtime-cohort compaction cycle. Budget-bounded; stops after first partial cohort if write budget exhausted.
engine.snapshot() -> Result<Snapshot, EdgestoreError>
Capture a point-in-time view of current segments. RAII release on Drop.
engine.metrics() -> MetricsSnapshot
Return current engine metrics: segment count, memtable entries, WAL files, total bytes.
use edgestore::EdgestoreConfig;
let config = EdgestoreConfig::new("/tmp/mydb")
.with_wal_max_bytes(64 * 1024 * 1024) // 64 MB WAL rotation
.with_wal_max_seconds(60) // or 60 seconds
.with_segment_target_size(16 * 1024 * 1024) // 16 MB segments
.with_cohort_window_secs(3600) // 1 hour cohorts
.with_compression_wal(edgestore::Compression::Lz4)
.with_compression_segment(edgestore::Compression::Zstd { level: 1 });
engine.begin() -> Transaction
Start a new multi-record transaction batch.
tx.put(ns, key, val, ttl, timestamp) -> Result<(), EdgestoreError>
Add a put to the transaction. Accumulated in memory until commit.
engine.commit_transaction(tx) -> Result<Lsn, EdgestoreError>
Atomically commit all transaction records to WAL with a single fsync.
engine.rollback_transaction(tx)
Discard the transaction without writing anything.
Every read has a _with_stats twin that returns a QueryStats alongside the result. Bounded variants stop early when a ScanBudget is exceeded.
engine.get_with_stats(ns, key) -> Result<(Option<Vec<u8>>, QueryStats), EdgestoreError>
Point lookup with cost reporting. Returns zero stats on cache miss, 1 item + byte count on hit.
engine.range_with_stats(ns, start, end) -> Result<(Vec<(Vec<u8>, Vec<u8>)>, QueryStats), EdgestoreError>
Range scan that accumulates bytes_scanned and items_examined as it iterates.
engine.prefix_with_stats(ns, prefix) -> Result<(Vec<(Vec<u8>, Vec<u8>)>, QueryStats), EdgestoreError>
Prefix scan with cost accounting.
engine.range_budgeted(ns, start, end, budget: ScanBudget) -> Result<BudgetedScan, EdgestoreError>
Range scan that stops when max_items or max_bytes is reached. BudgetedScan.truncated is true when cut short; next_key is the continuation cursor.
engine.prefix_budgeted(ns, prefix, budget: ScanBudget) -> Result<BudgetedScan, EdgestoreError>
Prefix scan with early termination on budget exhaustion.
engine.vector_search_with_stats(ns, query, k, metric) -> Result<(Vec<VectorSearchResult>, QueryStats), EdgestoreError>
ANN / flat-scan search with byte and segment accounting.
engine.search_text_with_stats(ns, query, k) -> Result<(Vec<TextSearchResult>, QueryStats), EdgestoreError>
BM25 text search with cost reporting. Counts posting-list bytes read.
VectorEngine::vector_put(ns, key, dims, dtype, data) -> Result<(), EdgestoreError>
Store a typed vector. Uses a synthetic __vec__{ns} namespace underneath.
VectorEngine::vector_get(ns, key) -> Result<Option<VectorRecord>, EdgestoreError>
Retrieve a vector by key.
VectorEngine::vector_delete(ns, key) -> Result<(), EdgestoreError>
Remove a vector from the collection.
engine.vector_search(ns, query, k, metric) -> Result<Vec<VectorSearchResult>, EdgestoreError>
Search top-k nearest neighbors. Routes to HNSW if an index exists, otherwise flat SIMD scan.
engine.build_vector_index(ns, dims, dtype, metric) -> Result<(), EdgestoreError>
Build an HNSW approximate index for the collection. Persists to a sidecar file.
engine.preload_vector_index(ns) -> Result<bool, EdgestoreError>
Lazy-load an existing HNSW index from disk.
engine.strip_vector_index(segment_id: u64) -> Result<SegmentMeta, EdgestoreError> v1.5.0
Remove all __vec__ namespace records from the named segment, returning a new segment (with a new ID) that contains only KV data. The original segment is deleted. Idempotent when called on the returned segment ID.
Dtype
F32, F16, I8 — the vector element type. All distances computed in f32 (widening for f16/i8).
Metric
Cosine, L2 (Euclidean), DotProduct — three supported distance metrics.
TextEngine::index_text(ns, key, text) -> Result<(), EdgestoreError>
Tokenize and index a text document. Stores inverted index in a synthetic __text__{ns} namespace.
TextEngine::search_text(ns, query, k) -> Result<Vec<TextSearchResult>, EdgestoreError>
BM25-ranked search. Tokenizes the query, intersects posting lists, scores with BM25(k1=1.2, b=0.75).
TextEngine::delete_text(ns, key) -> Result<(), EdgestoreError>
Remove a document from the text index.
engine.search_text_with_snippets(ns, query, k) -> Result<Vec<SnippetResult>, EdgestoreError> v1.5.0
BM25 search that returns context windows around matched terms. Each SnippetResult contains a doc_id, score, and a list of Snippet{term, span, context_start, context_end}. Character offsets are exact — derived from the v3 tokenizer's char_start positions.
SearchOptions
Builder for facet filters, typo tolerance, and result limits. Chain methods: .with_facet(field, value), .with_typo_tolerance(true).
engine.export_manifest() -> Result<Vec<SegmentRef>, EdgestoreError>
Export the current live segment list with BLAKE3 hashes. Send this to a replica to compare.
engine.missing_segments(peer_segments) -> Vec<[u8; 32]>
Compare local segments against a peer's manifest. Returns BLAKE3 hashes of missing segments.
engine.import_segment(seg_ref, data) -> Result<ImportResult, EdgestoreError>
Import a segment from a peer. Applies LWW conflict resolution by wall-clock timestamp. Verifies BLAKE3 hash.
engine.range_merkle_root() -> Result<[u8; 32], EdgestoreError>
Compute the root hash of the range-level Merkle tree over all segments.
engine.compare_merkle(other_root) -> Result<bool, EdgestoreError>
Compare local Merkle root with a peer's. Returns true if identical.
TieredEngine::new(local: Engine, remote: Box<dyn RemoteStore>) -> TieredEngine
Wrap a local engine with a remote store. Reads fall through to remote on local miss. Writes always go to local only.
tiered.get(ns, key) -> Result<Option<Vec<u8>>, EdgestoreError>
Local-first read. On miss, downloads the matching archived segment ephemerally from S3 and serves from it.
tiered.archive_segments(metas: &[SegmentMeta]) -> Result<(), EdgestoreError>
Upload the named segments to the remote store. Does not delete local copies — caller decides when to prune.
tiered.local() -> &Engine / tiered.local_mut() -> &mut Engine
Direct access to the underlying local engine for operations that don't need remote fallback.
tiered.get_needs_archived_fetch(ns, key) -> bool
Returns true if answering this get would require fetching an archived segment. Used by AsyncTieredEngine to decide whether to escalate from read lock to write lock.
AsyncTieredEngine::flush_notify() -> Arc<tokio::sync::Notify>
Returns a Notify handle wired to the engine's flush callback. Race this against a polling interval to trigger archiving immediately after a segment is written.
TieredEngine::with_vector_stripping(self) -> TieredEngine v1.5.0
Builder method that wires strip_vector_index into the archive_segments path. When enabled, every segment uploaded to the remote store automatically has its __vec__ records removed from the local copy, reclaiming disk proportional to vector data size.
EdgestoreError
All errors: Io, KeyNotFound, Corruption, CompactionError, InvalidArgument, etc.
Snapshot / SnapshotRegistry
Point-in-time reads with RAII segment pinning. Registry tracks active snapshots for compaction exclusion.
CompactionStats
cohorts_collected, segments_removed, segments_written, bytes_written, live_records_relocated.
StorageBackend / DefaultStorageBackend / MemoryStorageBackend
Pluggable I/O trait. Default uses pread/pwrite. Memory backend for tests.
HnswIndex
Standalone HNSW graph. Can be serialized/deserialized for persistence. Used by Engine for vector_search routing.
RangeMerkleTree
Merkle tree over key ranges for anti-entropy probes and delta sync.
QueryStats v1.5.0
segments_scanned: u64, bytes_scanned: u64, items_examined: u64. Returned by all _with_stats read variants. Zero-valued on a miss that short-circuited before touching segments.
ScanBudget v1.5.0
max_items: usize, max_bytes: u64. Passed to range_budgeted / prefix_budgeted. The scan stops when either limit is reached, whichever comes first.
BudgetedScan v1.5.0
items: Vec<(Vec<u8>, Vec<u8>)>, truncated: bool, next_key: Vec<u8>. When truncated is true, pass next_key as the start of the next call to continue pagination.
SnippetResult / Snippet v1.5.0
SnippetResult{doc_id, score, snippets: Vec<Snippet>}. Each Snippet{term, span, context_start, context_end} holds the matched text and its character-accurate context window in the original document.
use edgestore_tokio::AsyncEngine;
let engine = AsyncEngine::open(EdgestoreConfig::new("/tmp/async_db")).await?;
engine.put(b"default", b"key", b"val").await?;
let val = engine.get(b"default", b"key").await?;
All Engine methods are wrapped with tokio::task::spawn_blocking. No async code in the core library — the wrapper is thin and optional.