Skip to main content

Transactions

Executing transactions is just as simple as executing queries. If you need to protect a block of code in a transaction, just wrap the code inside an async function and pass that function to query. When the function returns, the transaction will commit; if it throws, it will be rolled back.

It's allowed to nest transactions, in which case savepoints will be automatically used instead.

import { FIELDS, URI } from '@divine/uri'
import '@divine/uri-postgres-protocol'; // Activate optional PostgreSQL driver
...

const db = new URI('pg://localhost/my-db');

export async function storeUser(user: User) {
return await db.query(() => {
const [ current ] = await db.query`select * from users where id = ${user.id} for update`;

if (current) {
...
} else {
...
}
});
}

export async function insertOrder(incomingOrder: IncomingOrder) {
const { user, lines, ...order } = incomingOrder;

return await db.query(() => {
await storeUser(user);

const orderID = (await db.query`insert into orders ${q.values(order)}`)[FIELDS][0].rowKey;
await db.$`#order_lines`.append(lines.map((line) => ({ ...line, order: orderID })));

return orderID;
});
}

Notice how you don't have to pass connections around inside the transaction. Also notice the fact that storeUser will begin a transaction if called by its own, but will in this case instead start a savepoint, since it's being called when a transaction is already in progress.

Automatic Deadlock Handling

If the database driver detects that a transaction has been aborted because of a deadlock, it will automatically sleep for a while and then invoke the passed function again. See Deadlock handling in the API documentation for more information.

This, and other transaction parameters, may be configured on a per-transaction basis by providing query with a DBTransactionParams object before the callback function.