This page is PostgreSQL and MySQL only.
Locking patterns
Claim jobs, lock cursor pages, lock a joined relation, and handle lock timeouts.
These patterns build on row locks. Each one runs inside a transaction, because outside one the lock is released when the statement ends.
Claim a row, then write it
The most common pattern is combining locks with transactions for atomic read-then-write:
await client.transaction(async (tx) => {
const job = await tx.jobs.findFirst({
where: { status: 'pending' },
lock: {
mode: 'update',
noWait: true,
},
});
if (!job) return null;
await tx.jobs.update({
where: { id: job.id },
data: { status: 'processing' },
});
return job;
});Lock a cursor page
Locks propagate to cursor pagination, including the internal hasPrevious/hasNext probe queries:
const page = await client.users.cursor({
where: { active: true },
lock: {
mode: 'update',
skipLocked: true,
},
limit: 25,
after: previousCursor,
});Lock a row and its to-one relation
Locks work with a single include: { relation: true } on a to-one relation when where also filters that relation with is. The pair compiles to one inner join, so the lock covers both tables:
const posts = await client.posts.findMany({
where: { published: true, author: { is: { active: true } } },
include: { author: true },
lock: 'update',
});General relation loading (multiple include fields or relation select) is intentionally rejected when lock is present.
This avoids silently dropping the lock on the relation query:
// This throws LOCK_NOT_SUPPORTED
const posts = await client.posts.findMany({
include: { author: true, comments: true },
lock: 'update',
});Handle lock timeouts
Lock acquisition failures are normalized to BetterDrizzleError with code LOCK_TIMEOUT:
import { BetterDrizzleError, BetterDrizzleErrorCode } from 'better-drizzle';
try {
await client.transaction(async (tx) => {
return tx.users.findMany({
where: { id: 1 },
lock: {
mode: 'update',
noWait: true,
},
});
});
} catch (error) {
if (error instanceof BetterDrizzleError && error.code === BetterDrizzleErrorCode.LockTimeout) {
console.log('Row is locked by another transaction');
}
}