Skip to main content

Transactions

Transactions group multiple operations into a single atomic commit (WAL-backed).

db.transaction(fn)

transaction<T>(fn: (tx: DBTransactionContext) => Promise<T>): Promise<T>
  • Runs fn(tx), records operations, then commits them as one unit.
  • If the manager/db is in readonly mode, transactions are rejected.
  • Inside a transaction, tx.collection("<name>") is a transaction recorder:
    • call collection methods normally
    • don't rely on return values inside the transaction (the calls are recorded)

Example: transfer between accounts (single commit)

import { LioranManager } from "@liorandb/core";

const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("bank");

await db.transaction(async (tx) => {
const accounts = tx.collection<{ _id?: string; id: string; balance: number }>("accounts");

accounts.updateOne({ id: "a" }, { $inc: { balance: -50 } }, { upsert: true });
accounts.updateOne({ id: "b" }, { $inc: { balance: 50 } }, { upsert: true });

return true;
});

const accounts = db.collection("accounts");
console.log(await accounts.find({}, { projection: ["id", "balance"] }));
await manager.close();
Sandbox output (example)
[ { id: "a", balance: -50 }, { id: "b", balance: 50 } ]

When should you use transactions?

  • Updating multiple collections where you want "all-or-nothing"
  • Multi-step updates to a single collection where partial state would be harmful