jed

Transactions

jed has a single writer: at most one write transaction at a time, with readers never blocked except during the brief commit. A transaction’s changes apply all-or-nothing.

The update helper runs a read-write transaction that commits on success and rolls back if your code signals an error — the safest default; there’s a read-only view helper too. Both are available in every language (Rust, Go, and TypeScript), on the Database handle (where each call mints a fresh session) or on a session you’ve minted. For finer control, the explicit begin / commit / rollback form lives on a session — mint one with db.session(...) and drive the block across calls.

use jed::Database;

fn main() -> jed::Result<()> {
    let mut db = Database::open("bank.jed")?;

    // update() runs a read-write transaction: it mints a session, runs the closure, commits on
    // success, and rolls back if the closure returns an error — so the two writes are atomic. view()
    // is the read-only sibling. (For an explicit block spanning calls, mint a Session and drive
    // begin/commit/rollback on it: `let mut s = db.session(SessionOptions::default());`.)
    db.update(|tx| {
        tx.execute("UPDATE account SET balance = balance - 100 WHERE id = 1", &[])?;
        tx.execute("UPDATE account SET balance = balance + 100 WHERE id = 2", &[])?;
        Ok(())
    })?;

    Ok(())
}

Isolation

Readers see the last committed state and run without blocking against an in-flight writer; the only exclusive moment is the commit itself. This is not MVCC — there is exactly one committed version plus the current writer’s pending changes. It keeps the model simple and the read path nearly lock-free.

jed — an embeddable, strictly-typed SQL database.