HOME/ARTICLES/Atomic Concurrency & Database Isolation with Prisma ORM on PostgreSQL
DatabaseAugust 28, 20263 MIN READ (528 WORDS)

Atomic Concurrency & Database Isolation with Prisma ORM on PostgreSQL

Preventing race conditions, deadlocks, and write skews in multi-user concurrent applications using interactive transactions, optimistic locking, and advisory locks.

RH
MD. Rakibul HasanFull Stack Developer & Systems Engineer
Atomic Concurrency & Database Isolation with Prisma ORM on PostgreSQL

The Challenge of Concurrent Database Mutations

In high-concurrency web applications, such as e-commerce inventories, flash sales, seat reservations, or financial ledgers, multiple users often attempt to read and modify the same database records simultaneously.

Without appropriate isolation guarantees, applications suffer from:

  • Dirty Reads: Reading uncommitted mutations from another transaction.
  • Non-Repeatable Reads: Re-reading a row within the same transaction and finding changed data.
  • Phantom Reads: Queries returning newly inserted rows that match a search range.
  • Write Skew & Lost Updates: Overwriting concurrent mutations without awareness.
✦ ✦ ✦

Understanding PostgreSQL Isolation Levels

PostgreSQL supports three distinct transaction isolation levels:

Isolation LevelDirty ReadNon-Repeatable ReadPhantom ReadSerialization Anomaly
Read Committed (Default)PreventedAllowedAllowedAllowed
Repeatable ReadPreventedPreventedPreventedAllowed
SerializablePreventedPreventedPreventedPrevented
✦ ✦ ✦

Implementing Optimistic Concurrency with Version Numbers

Optimistic concurrency control is ideal for read-heavy systems where collisions are rare but catastrophic if unhandled. Each row maintains an incrementing version integer.

typescript
1import { PrismaClient } from "@prisma/client";
2
3const prisma = new PrismaClient();
4
5export async function transferFundsSafely(
6 accountId: string,
7 amount: number,
8 expectedVersion: number
9) {
10 // Update only if version matches the state we read
11 const result = await prisma.account.updateMany({
12 where: {
13 id: accountId,
14 version: expectedVersion,
15 balance: { gte: amount },
16 },
17 data: {
18 balance: { decrement: amount },
19 version: { increment: 1 },
20 },
21 });
22
23 if (result.count === 0) {
24 throw new Error("CONCURRENCY_CONFLICT: Account state was modified by another transaction.");
25 }
26
27 return { success: true };
28}
PRO TIP
Optimistic locking avoids expensive database lock contention and deadlocks. If a conflict occurs, simply catch the exception and retry the operation with exponential backoff.
✦ ✦ ✦

Interactive Transactions with Strict Isolation in Prisma

Prisma ORM provides first-class support for interactive transactions with custom isolation levels:

typescript
1import { Prisma } from "@prisma/client";
2
3export async function executeAtomicTransfer(fromId: string, toId: string, amount: number) {
4 return await prisma.$transaction(
5 async (tx) => {
6 // 1. Deduct from sender
7 const sender = await tx.wallet.update({
8 where: { id: fromId },
9 data: { balance: { decrement: amount } },
10 });
11
12 if (sender.balance < 0) {
13 throw new Error("INSUFFICIENT_FUNDS");
14 }
15
16 // 2. Credit to receiver
17 const receiver = await tx.wallet.update({
18 where: { id: toId },
19 data: { balance: { increment: amount } },
20 });
21
22 // 3. Create audit transaction log
23 const auditLog = await tx.auditLog.create({
24 data: {
25 fromWalletId: fromId,
26 toWalletId: toId,
27 amount,
28 status: "COMPLETED",
29 },
30 });
31
32 return { sender, receiver, auditLog };
33 },
34 {
35 isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
36 maxWait: 5000, // 5s timeout waiting for a connection
37 timeout: 10000, // 10s transaction run limit
38 }
39 );
40}
WARNING
Serializable isolation eliminates all concurrency anomalies, but transactions may fail with serialization errors (SQLSTATE 40001). You MUST wrap calls in a retry loop when using Serializable mode.
✦ ✦ ✦

Conclusion

Designing for concurrency from Day 1 ensures that your application scales predictably without silent data corruption. Combine optimistic locking for user-facing mutations and interactive serializable transactions for critical ledger operations.

RH

MD. Rakibul Hasan

AUTHOR // ENGINEER

Full Stack Developer specializing in scalable backend systems, Next.js, TypeScript, PostgreSQL, and distributed architectures.

Have questions or architecture ideas to discuss?
RECOMMENDED READING

Related Engineering Writeups

VIEW ALL