Problem
Today transactions are expressed manually with raw SQL:
await c.db.execute("BEGIN");
try {
// ...queries...
await c.db.execute("COMMIT");
} catch (error) {
await c.db.execute("ROLLBACK");
throw error;
}
c.db is a shared actor-local database handle, so other c.db.execute(...) calls can interleave while a transaction is open (for example from concurrent actions). This makes transaction safety easy to get wrong.
Proposal
Add a first-class transaction API:
await c.db.transaction(async (tx) => {
await tx.execute("INSERT INTO todos (title) VALUES (?)", title);
await tx.execute(
"INSERT INTO comments (todo_id, body) VALUES (last_insert_rowid(), ?)",
body,
);
});
Expected behavior
transaction acquires exclusive access for the callback duration.
- Other
c.db operations are queued until transaction commit/rollback.
- Automatically
COMMIT on success.
- Automatically
ROLLBACK on throw.
tx only exposes transaction-safe operations (e.g. tx.execute).
Why this helps
- Prevents interleaving bugs with
BEGIN/COMMIT.
- Makes docs/examples safer and simpler.
- Aligns with ergonomics from other SQLite libraries.
Optional follow-ups
- Decide nested transaction behavior (
SAVEPOINT or disallow nesting).
- Add typed return support from callback (
transaction<T>(...) => Promise<T>).
Problem
Today transactions are expressed manually with raw SQL:
c.dbis a shared actor-local database handle, so otherc.db.execute(...)calls can interleave while a transaction is open (for example from concurrent actions). This makes transaction safety easy to get wrong.Proposal
Add a first-class transaction API:
Expected behavior
transactionacquires exclusive access for the callback duration.c.dboperations are queued until transaction commit/rollback.COMMITon success.ROLLBACKon throw.txonly exposes transaction-safe operations (e.g.tx.execute).Why this helps
BEGIN/COMMIT.Optional follow-ups
SAVEPOINTor disallow nesting).transaction<T>(...) => Promise<T>).