Transactions
There are two types of transactions — atomic and usual. The difference is that atomic runs all queries in single batch, while usual transaction runs queries as they go.
So, if you want to execute query in usual transaction — Kikko will need to send n+2 queries to backend:
BEGIN[send, receive]
<your-query-1>[send, receive]
<your-query-2>[send,receive]
COMMIT[send, receive]
With atomic transaction only one array of queries will be send, which will reduce send/receive time and reduce total blocking time of queries queue.
Atomic transaction
Kikko will collect all the queries from the callback(even if callback is async) and execute all of them at once:
await db.runInAtomicTransaction(async (scope) => {
scope.addQuery(sql`DELETE FROM ${sql.table`comments`}`);
await new Promise((resolve) => {
setTimeout(() => resolve(), 2000);
});
scope.afterCommit(() => {
console.log("After commit!");
});
scope.afterRollback(() => {
console.log("After rollback!");
});
scope.addQuery(sql`DELETE FROM ${sql.table`notes`}`);
});
Usual Transaction
During transaction all other queries that run not in transaction will be queued. So to avoid timeout of those queries make sure that your transactions run as fast as possible. So we recommend to run all time-consuming work before transaction start.
await db.runInTransaction(async (db) => {
await db.runQuery(sql`DELETE FROM ${sql.table`comments`}`);
await db.runQuery(sql`DELETE FROM ${sql.table`notes`}`);
});
Transaction will rollback if any error will be thrown during.
Nesting
It also supports nesting:
await db.runInTransaction(async (db) => {
await db.runQuery(sql`DELETE FROM ${sql.table`comments`}`);
await db.runInTransaction(async (db) => {
await db.runQuery(sql`DELETE FROM ${sql.table`notes`}`);
});
});
Nested transaction will be merged to the parent transaction. It is also planned to add support
for SAVEPOINT
and RELEASE
commands, so nested transaction will run isolated.
After transaction callbacks
You could also register callback that will run once transaction will be finished or rollbacked:
await db.runInTransaction(async (db) => {
await db.runQuery(sql`DELETE FROM ${sql.table`notes`}`);
db.runAfterTransactionCommitted(() => {
console.log("All comments are deleted!");
});
db.runAfterTransactionRollbacked(() => {
console.log("Failed to delete comments");
});
});
Note that callbacks will run AFTER transaction COMMIT
or ROLLBACK
. It especially helpful when you need
to notify other components that they can read from DB inserted values.
Transaction modes
By default transaction will run in DEFERRED mode. If you need other modes use:
// DEFERRED
db.runInTransaction((db) => {}, { type: "deffered" });
// IMMEDIATE
db.runInTransaction((db) => {}, { type: "immediate" });
// EXCLUSIVE
db.runInTransaction((db) => {}, { type: "exclusive" });
For more information read https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions