Single-file embedded DB forkbve
Turso (async write) paired with DuckDB (analytics read) over one SQLite-format file — parallel reader pool, streaming reads, batched writes, and a FromEmbedRow derive.
One file, two engines
Turso writes and DuckDB analytics over a single SQLite-format file. The derive macro ships in embeddb-derive.
- Write — Turso async, parameterized, transactions.
- Read — DuckDB pool, streaming, columnar analytics.
What it gives you
Features
Turso write path
Pure-Rust async SQLite rewrite for parameterized writes, transactions, and batched inserts.
DuckDB analytics pool
A pooled read-only DuckDB reader for parallel columnar analytics over the same file, built lazily on first use.
Streaming + batch
analytics_for_each streams rows without materializing; execute_batch runs many writes in one transaction.
Row mapping
EmbedValue type set, QueryResult with column names, and
Live-read freshness
DuckDB replays uncheckpointed WAL, so analytics reflect committed writes without a forced checkpoint.
Vector search
Optional vector feature stores embeddings as packed f32 blobs and ranks them by cosine similarity, adding no dependencies.
Questions
Frequently asked
What is the embeddb crate?
embeddb is a foundational Rust library providing a single-file embedded database. It pairs Turso (a pure-Rust async SQLite rewrite) as the write path with DuckDB as the analytics read path over one SQLite-format file, so a single file gives both transactional writes and columnar analytics.
What does embeddb provide?
Parameterized writes and transactions via Turso, a DuckDB reader pool for parallel analytics, streaming row callbacks, batched writes, migrations and config, a rich EmbedValue type set, QueryResult with column names, and a FromEmbedRow derive for mapping rows into structs.
Can embeddb store and search embeddings?
Yes, via the optional vector feature. Embeddings are stored as packed f32 blobs in the same single file, normalized on write so similarity search reduces to a dot product, and ranked by brute-force cosine similarity. The feature adds no dependencies and reads through Turso rather than DuckDB, so it works in distroless and egress-restricted images. Embedding model identity is part of each row, and every search filters on it, because vectors from different models do not share a comparable space.
Can embeddb be used without DuckDB?
Yes. DuckDB sits behind the analytics Cargo feature, which is on by default. Building with default-features = false and features = ["derive"] leaves a pure-Rust dependency tree with the full Turso write path, transactions, migrations and point reads, dropping only the analytics_* methods. This matters for game clients and iOS or Android cross-compiles, where duckdb's bundled feature would otherwise force a C++ build of the DuckDB engine.
How does concurrency and freshness work?
Turso owns all writes; DuckDB attaches the same file read-only for analytics. DuckDB replays uncheckpointed WAL frames, so analytics reflect committed writes even before a checkpoint, and concurrent reads stay consistent. The reader pool builds lazily on the first analytics call, so write-only use never loads the analytics engine.