API Overview

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.

Engine Config KV API Cost Accounting Vector Text Replication Tiered Types

Engine

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.

Configuration

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 });

Transaction API

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.

Cost Accounting & Bounded Scans v1.5.0

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.

Vector API

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.

Vector Types

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.

Text Search API

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).

Replication API

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.

Tiered Storage API edgestore-tier

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.

Key Types

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.

Async Wrapper (edgestore-tokio)

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.