emdb 0.7.0

A lightweight, high-performance embedded database for Rust.
Documentation

Status

v0.7. New v0.7 storage engine (opt-in via EmdbBuilder::prefer_v4(true)): packed slotted leaves with sharded (hash → Rid) keymap, layered page + value caches, group-commit WAL with crash-atomic batch markers, lock-free bloom filters, named namespaces with isolated keymaps + leaf chains, a foreground compactor backed by a real free list, and cross-platform Direct I/O. v0.6 remains the default backend; in-place v3 → v4 migration on first open. ~7× faster than v0.6 on bulk inserts; ahead of sled on both axes in the comparative bench. The API is still pre-1.0 and may change before 1.0.

Track progress and roadmap: https://github.com/jamesgober/emdb-rs

Installation

[dependencies]
emdb = "0.7"

Quick Start

use emdb::Emdb;

let db = Emdb::open_in_memory();
db.insert("name", "emdb")?;
assert_eq!(db.get("name")?, Some(b"emdb".to_vec()));
# Ok::<(), emdb::Error>(())

Persistence

use emdb::{Emdb, FlushPolicy};

let path = std::env::temp_dir().join("app.emdb");

{
    let db = Emdb::builder()
        .path(path.clone())
        .flush_policy(FlushPolicy::EveryN(64))
        .build()?;

    db.insert("user:1", "james")?;
    db.flush()?;
}

let reopened = Emdb::open(&path)?;
assert_eq!(reopened.get("user:1")?, Some(b"james".to_vec()));
# let _cleanup = std::fs::remove_file(path);
# Ok::<(), emdb::Error>(())

Manual compaction:

use emdb::Emdb;

let path = std::env::temp_dir().join("compact.emdb");
let db = Emdb::open(&path)?;
db.insert("k", "v")?;
db.compact()?;
db.flush()?;
# let _cleanup = std::fs::remove_file(path);
# Ok::<(), emdb::Error>(())

Transactions

Commit path:

use emdb::Emdb;

let db = Emdb::open_in_memory();
db.transaction(|tx| {
    tx.insert("user:1", "james")?;
    tx.insert("user:2", "alex")?;
    Ok(())
})?;

assert_eq!(db.get("user:1")?, Some(b"james".to_vec()));
assert_eq!(db.get("user:2")?, Some(b"alex".to_vec()));
# Ok::<(), emdb::Error>(())

Rollback path:

use emdb::{Emdb, Error};

let db = Emdb::open_in_memory();
let failed = db.transaction::<_, ()>(|tx| {
    tx.insert("temp", "value")?;
    Err(Error::TransactionAborted("rollback"))
});

assert!(failed.is_err());
assert_eq!(db.get("temp")?, None);
# Ok::<(), emdb::Error>(())

Crash Safety

Transactions are written as BatchBegin ... BatchEnd records. If a crash occurs before BatchEnd, the entire batch is discarded during replay. If a crash occurs after BatchEnd, the entire batch is applied.

Features

  • ttl (default): per-record expiration and default TTL support.
  • nested: dotted-prefix group operations and Focus handles.
  • persistence (core): append-only file log, replay-on-open, flush policy, and compaction.
  • transactions (core): closure-based atomic batches with read-your-writes and crash-safe replay.

Concurrency

Emdb is Send + Sync and cheap to clone. Internally it uses a 32-shard lock-striped primary index so reads on different keys never block each other, plus a serialized backend mutex for the WAL and page file. In-memory databases bypass the backend mutex entirely.

use std::sync::Arc;
use std::thread;

use emdb::Emdb;

let db = Arc::new(Emdb::open_in_memory());
db.insert("counter", "0")?;

let mut workers = Vec::new();
for i in 0_u32..4 {
    let db = Arc::clone(&db);
    workers.push(thread::spawn(move || {
        let _ = db.insert(format!("k{i}"), format!("v{i}"));
    }));
}

for worker in workers {
    let _ = worker.join();
}

assert!(db.len()? >= 4);
# Ok::<(), emdb::Error>(())

TTL Example

# #[cfg(feature = "ttl")]
# {
use std::time::Duration;

use emdb::{Emdb, Ttl};

let db = Emdb::builder()
    .default_ttl(Duration::from_secs(30))
    .build()?;
db.insert_with_ttl("session", "token", Ttl::Default)?;
assert!(db.ttl("session")?.is_some());
# }
# Ok::<(), emdb::Error>(())

Nested Example

# #[cfg(feature = "nested")]
# {
use emdb::Emdb;

let db = Emdb::open_in_memory();
let product = db.focus("product");
product.set("name", "phone")?;
product.set("price", "799")?;

assert_eq!(product.get("name")?, Some(b"phone".to_vec()));
assert_eq!(db.group("product")?.count(), 2);
# }
# Ok::<(), emdb::Error>(())

Goals

  • Embedded-first — runs in-process; no separate server, no network.
  • High performance — zero-copy reads, allocation-free hot paths, cache-friendly layout.
  • Safe — strict clippy profile, no unwrap in library code, all unsafe documented.
  • Small footprint — minimal dependency graph, fast compile times.
  • Portable — Linux, macOS, Windows (x86_64 and ARM64).

Benchmarking

emdb ships with Criterion benchmarks, including an optional comparative suite.

Quick start:

cargo bench --bench comparative

Compare with embedded DBs (sled + redb):

cargo bench --bench comparative --features bench-compare

Compare with RocksDB (optional):

cargo bench --bench comparative --features bench-rocksdb

Compare with Redis (optional):

$env:EMDB_REDIS_URL = "redis://127.0.0.1/"
cargo bench --bench comparative --features bench-redis

For full benchmark workflow, tuning, and reporting format, see docs/BENCH.md. The latest recorded baseline metrics are also tracked there.

Non-Goals

  • Client-server operation (use a dedicated DBMS for that).
  • A full SQL dialect at this stage.
  • Distributed replication at this stage.

Related Projects

emdb is the Rust implementation. Implementations in other languages (Go, C, and others) are planned and will live under their own repositories.

License

Licensed under the Apache License, Version 2.0.

Copyright © 2026 James Gober.