Prisma Interactive Transaction Retry
Use Prisma interactive transactions with serialization failure detection, bounded retry loops, and rollback on invariant violations.
Code
import { PrismaClient, Prisma } from '@prisma/client';
const prisma = new PrismaClient({ log: ['warn', 'error'] });
async function adjustBalance(userId: number, delta: number) {
for (let attempt = 1; attempt <= 4; attempt++) {
try {
return await prisma.$transaction(async (tx) => {
const wallet = await tx.wallet.findUniqueOrThrow({
where: { userId },
});
const next = wallet.balanceCents + delta;
if (next < 0) {
throw new Error('balance would go negative');
}
return tx.wallet.update({
where: { userId },
data: { balanceCents: next },
});
}, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
} catch (err) {
const conflict =
err instanceof Prisma.PrismaClientKnownRequestError
&& ['P2034'].includes(err.code);
if (conflict && attempt < 4) {
await new Promise((r) => setTimeout(r, 50 * 2 ** attempt));
continue;
}
throw err;
}
}
throw new Error('unreachable');
}
const wallet = await adjustBalance(1, -250);
console.log('balance now:', wallet.balanceCents);
await prisma.$disconnect();Report a problem with this exampleOpens a pre-filled issue on GitHub.