Rules: size and cost
Last updated 29 October 2026
Check these rules when you decide how your program commits: each keeps the bill small and every transaction within the limit.
The examples use the connection open() returns, a node:sqlite DatabaseSync, as Connect opens it.
Batch a step’s writes into one transaction
Every commit costs at least one write, however small, and waits its turn to reach Tonbo. Twenty rows committed one by one cost twenty writes; in one transaction they cost one (see the worked example).
db.exec("BEGIN");
try {
for (const event of step.events) insert.run(step.id, JSON.stringify(event));
db.exec("COMMIT"); // one commit, one write
} catch (error) {
if (db.isTransaction) db.exec("ROLLBACK");
throw error;
}for (const event of step.events) insert.run(step.id, JSON.stringify(event)); // a commit per rowWrite what changed, not the whole state
A commit costs one write per 10 KiB of the pages it changes, so rewriting a large value every step costs many writes where a new row costs one (see the worked example).
db.prepare("INSERT INTO messages(conversation, body) VALUES (?, ?)")
.run(id, JSON.stringify(message));conversation.messages.push(message);
db.prepare("UPDATE conversations SET body = ? WHERE id = ?")
.run(JSON.stringify(conversation), id); // the whole conversation, every turnKeep each transaction under 128 MiB
A transaction over 128 MiB of changed pages fails with too_large and commits nothing (see Limits). Load large data in batches.
for (let at = 0; at < rows.length; at += 1000) {
db.exec("BEGIN");
for (const row of rows.slice(at, at + 1000)) insert.run(row.id, row.body);
db.exec("COMMIT");
}db.exec("BEGIN");
for (const row of rows) insert.run(row.id, row.body); // millions of rows
db.exec("COMMIT"); // fails with too_large and commits nothing