Open-source project
stoolap/stoolap avatar
stoolap/stoolap

Stoolap: an embedded SQL database with MVCC, time travel and vectors

A Modern Embedded SQL Database written in Rust

1,249 stars52 forksRustApache-2.0

At a glance

What is it?
Stoolap is a pure Rust embedded SQL database with no external server, built around ACID transactions with MVCC, a cost-based optimizer, hot and cold columnar storage modelled on Apache Iceberg and Delta Lake, and native vector columns with HNSW search. It is at version 0.4, and its own feature table is both its argument and its weakest point.
Who is it for?
Stoolap fits a Rust service that wants a single file to store its state, needs concurrent readers and writers with real isolation, and wants time-travel queries over its own history. It does not fit a team that needs a PostgreSQL wire protocol server, since the PG server binary is still commented out in the Cargo manifest, and it does not fit a project unwilling to track a 0.x release.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • stoolap/stoolap1,249 ★ · +5
Star history
Repository2026-10-212026-11-07
stoolap/stoolap1.2k1.2k

5 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Time travel is in the query language, not in a backup tool

Stoolap is described as a feature-rich embedded SQL database built in pure Rust, targeting low-latency transactional workloads and real-time analytical queries, with modern SQL features and no external server process.

The feature that distinguishes it is time travel, and it is part of the SQL surface rather than a separate mechanism. After a transaction commits, you can query the state of a table as it was at a moment:

sql
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

SELECT * FROM accounts AS OF TIMESTAMP '2024-01-15 10:30:00';
SELECT * FROM inventory AS OF TRANSACTION 1234;

There are two forms, one by wall clock time and one by transaction identifier, and the second is the one you would use to reconstruct what a specific operation saw.

Underneath the query language sit the usual foundations, which the README lists as ACID plus MVCC for concurrent reads and writes with transaction isolation, a cost-based optimizer with statistics-aware planning and adaptive execution, a rich SQL surface with joins, subqueries, CTEs, window functions and advanced aggregations, and several index types including B-tree, Hash, Bitmap, multi-column and HNSW for vectors. The last one is unusual for a database whose main pitch is transactions, and it is covered separately below.

Everything runs in process, in the same address space as the application, with no server to operate and no network protocol to speak.

Hot memory, cold columnar files, and an Iceberg-shaped layout

The storage architecture is the part that shows the project's influences most clearly. Stoolap uses a hot and cold volume storage architecture inspired by Apache Iceberg and Delta Lake.

The hot buffer is an in-memory MVCC store with a write ahead log for active writes. The cold volumes are immutable columnar files with zone maps, bloom filters, dictionary encoding and LZ4 compression. An adaptive compaction background thread merges cold volumes with a size-aware, fully dynamic merge strategy. Crash recovery uses atomic manifest writes, fsync before rename, and WAL replay from a checkpoint LSN. And cold scans materialise only the columns referenced by filters and projections, which is the column pruning that makes a columnar format worth having in the first place.

The consequence for a user is that a Stoolap database is a directory of files with a manifest, not a single file, and the way to open one is a URI. The README shows the shape of that URI:

code
file://./mydb?sync_mode=normal&compression=on&checkpoint_interval=60

And there is a table of DSN parameters behind it, with sync_mode defaulting to normal and three documented behaviours, none meaning no fsync at all: none, where data is durable at checkpoint, normal, where fsync happens every second, and full, where fsync happens on every write.

That last row is the setting an application has an opinion about. A throughput-oriented workload that can lose the last second of writes takes sync_mode=normal. A ledger cannot.

The use of Iceberg and Delta Lake vocabulary is not cosmetic, either. Zone maps, bloom filters and immutable volumes are the same primitives those table formats use, and the reason to use them in an embedded database is the same: a scan should not have to read a column it was not asked for, and a compaction should not have to rewrite the whole file.

Vectors are a column type, and embeddings are a feature flag

The vector support is not an extension and not a separate index format bolted on. Stoolap supports native vectors through a VECTOR(N) column type, with approximate nearest neighbour search through HNSW, the same index structure that the feature list names among its index types.

The example shows a table with an embedding column, an HNSW index with explicit parameters, and a query that orders by cosine distance:

sql
CREATE TABLE embeddings (
    id INTEGER PRIMARY KEY,
    content TEXT,
    embedding VECTOR(384)
);

CREATE INDEX idx_emb ON embeddings(embedding)
USING HNSW WITH (metric = 'cosine', m = 32, ef_construction = 400, ef_search = 128);

SELECT id, content,
       VEC_DISTANCE_COSINE(embedding, '[0.1, 0.2, ...]') AS dist
FROM embeddings
ORDER BY dist
LIMIT 10;

The m, ef_construction and ef_search parameters are the usual HNSW build and search knobs, and they are written into the index definition rather than left as session settings.

The semantic search feature is a separate thing and is enabled with a Cargo feature:

toml
[dependencies]
stoolap = { version = "0.4", features = ["semantic"] }

With it on, the database can compute an embedding itself, as in SELECT EMBED('How to reset my password'), which is a considerably heavier dependency than a vector column and is the reason it is opt-in.

What makes this interesting for a database that otherwise pitches itself at transactional work is that the vector path and the row path share one storage engine, one MVCC implementation and one query planner. A system that keeps embeddings in a separate store has to reconcile two consistency models; here a row and its embedding are updated in the same transaction.

The Rust API and a CLI that talks to the same engine

The Rust API is the primary interface, and the example in the README is a complete program: open a database, create a table, insert a row, and read it back.

rust
use stoolap::Database;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Database::open_in_memory()?;

    db.execute(
        "CREATE TABLE users (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            email TEXT UNIQUE,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )",
        (),
    )?;

    for row in db.query("SELECT id, name, email FROM users WHERE id = $1", (1,))? {
        let row = row?;
        println!(
            "id={} name={} email={}",
            row.get::<i64>(0)?,
            row.get::<String>(1)?,
            row.get::<String>(2)?
        );
    }

    Ok(())
}

Two details are worth noticing. Parameters are passed as tuples against positional placeholders, $1 and $2 and $3, so the API does not build SQL by string concatenation. And the row accessor is typed, so a column that changes type in the schema becomes a compile-time or runtime error rather than a silently wrong value.

The CLI is the same engine behind a binary, and the README lists three ways to use it: the bare invocation for an interactive REPL, the -e flag to execute a single query such as SELECT version(), and the --db flag to open a persistent database, for example --db "file://./mydb".

For a web-based management interface there is Stoolap Studio, a separate repository with a SQL editor, schema browser, interactive data grid, vector search and backup and restore, which you run with npm install and npm run dev after cloning it.

The feature table is the argument, and it is also the weak point

The README includes a comparison table across Stoolap, SQLite, DuckDB and PostgreSQL, with rows for time-travel queries, MVCC transactions, hot and cold columnar storage, a cost-based optimizer, adaptive query execution, semantic query caching, parallel query execution, native vector and HNSW search, and pure Rust.

In that table Stoolap is the only column with a mark on every row. That is worth being careful about, because a comparison table in a project's own README is an argument, not a measurement. The footnote about PostgreSQL is a good sign, since it concedes the asterisk case, and the point about PostgreSQL typically needing extensions for temporal query workflows is the kind of qualification that suggests the table was written by someone who knows the subject.

Still, a reader should take the table as a list of design intentions. Hot and cold columnar storage in an embedded engine is a research position more than a proven production property, and the same is true of semantic query caching and adaptive query execution. The place to look for evidence is BENCHMARKS.md, which the README links from the header, along with a compare directory in the repository containing a benchmark suite. The Cargo manifest also explains the shape of that comparison: the SQLite and DuckDB comparisons are their own crate, built only on request, so that --all-features here never compiles either database.

That last detail is the most telling line in the manifest. The project can compare itself to the alternatives, but it deliberately keeps the comparison out of the default build, which tells you the comparison is opt-in and therefore not something the maintainers expect every user to run.

Version 0.4, an optional CLI, and a PostgreSQL server that does not exist yet

The release history is regular. Version 0.4.0 came on 2026-04-01, 0.4.1 on 2026-09-06 and 0.4.2 on 2026-09-23, and the last push to the repository was on 2026-10-15. The licence is Apache-2.0, and the package metadata describes it as a high-performance embedded SQL database with MVCC, time-travel queries and full ACID compliance.

The Cargo manifest shows the shape of the crate and of the roadmap. The library builds as both a cdylib and an rlib, so it can be linked into a Rust binary or used as a C shared library, and the include directory and a benchmark_ffi.c example in the repository point at a foreign function interface being available. The CLI is a separate binary behind a required cli feature, with clap, rustyline, comfy-table and dirs as optional dependencies, and the same pattern applies to other features such as rayon for parallel execution.

The most telling lines are the commented-out ones. There is a pgserver.rs binary block in the manifest with a note that the PG server will be enabled once implemented, and it carries the pg-server feature flag. So the PostgreSQL wire protocol server, which would make Stoolap substitutable for a client that speaks to Postgres, is a plan rather than a feature.

The build requirement is rust-version 1.88 with edition 2021, and the repository is a full project layout with benches, compare, docs, examples, fuzz, include, a build.rs, a Cargo.lock, a codecov configuration, a SECURITY.md, a CONTRIBUTING.md and a BENCHMARKS.md. The presence of a fuzz directory and a benchmarks directory is the better signal here than any claim in the README.

Editorial conclusion

Stoolap fits a Rust service that wants a single file to store its state, needs concurrent readers and writers with real isolation, and wants time-travel queries over its own history. It does not fit a team that needs a PostgreSQL wire protocol server, since the PG server binary is still commented out in the Cargo manifest, and it does not fit a project unwilling to track a 0.x release. Before you depend on it, check the rust-version of 1.88 in Cargo.toml against your toolchain, and read BENCHMARKS.md in the repository, since the README links to it as the place where the performance claims are kept.

Frequently asked questions

What is Stoolap?

It is a feature-rich embedded SQL database built in pure Rust, targeting low-latency transactional workloads and real-time analytical queries, with modern SQL features and no external server process. The crate is published on crates.io as stoolap and is licensed under Apache 2.0.

How do I use Stoolap from Rust?

Add the dependency, stoolap = "0.4", then open a database with Database::open_in_memory() or a file URI, and use db.execute for statements and db.query for rows. Parameters are passed as tuples against positional placeholders such as $1, and row values are read with typed accessors like row.get::<i64>(0).

What is time-travel querying in Stoolap?

You can query a table as it was at a past moment, either with a wall clock timestamp, as in SELECT * FROM accounts AS OF TIMESTAMP '2024-01-15 10:30:00', or by transaction identifier, as in SELECT * FROM inventory AS OF TRANSACTION 1234. The project describes this as AS OF time-travel queries.

How does Stoolap store data on disk?

It uses a hot and cold volume storage architecture inspired by Apache Iceberg and Delta Lake, with an in-memory MVCC hot buffer and write ahead log for active writes, immutable columnar cold volumes with zone maps, bloom filters, dictionary encoding and LZ4 compression, an adaptive compaction background thread, and crash recovery through atomic manifest writes, fsync before rename and WAL replay from a checkpoint LSN.

Does Stoolap support vector search and embeddings?

Yes. Native vectors use the VECTOR(N) column type with approximate nearest neighbour search through HNSW, and there is a separate semantic feature that lets the database compute embeddings itself, enabled with the semantic Cargo feature and used through the EMBED function.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. stoolap/stoolap on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/stoolap-stoolap.svg)](https://hysenlabs.com/projects/stoolap-stoolap)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/stoolap-stoolap.svg)](https://hysenlabs.com/projects/stoolap-stoolap)

Was this useful?