PostgreSQL ERROR: deadlock detected — What It Means and How to Fix It

If you’ve ever seen the error "ERROR: deadlock detected" in PostgreSQL, chances are your application suddenly failed in production.

This error is confusing, hard to reproduce, and often misunderstood. In this article, we’ll explain what it really means and how to fix it properly.

What Is a Deadlock?

A deadlock happens when two (or more) transactions block each other forever.

Each transaction holds a lock and waits for another lock that will never be released.

Simple Example

Transaction A locks row 1 and waits for row 2  
Transaction B locks row 2 and waits for row 1

PostgreSQL detects this situation and aborts one transaction to prevent an infinite wait.

The Exact Error Message

ERROR: deadlock detected
DETAIL: Process 12345 waits for ShareLock on transaction 67890; blocked by process 54321.

This means PostgreSQL killed one transaction to resolve the deadlock.

Why Deadlocks Happen in PostgreSQL

1. Different Order of Updates

The most common cause is updating rows in different orders.

Example

-- Transaction A
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

-- Transaction B
UPDATE accounts SET balance = balance - 50 WHERE id = 2;
UPDATE accounts SET balance = balance + 50 WHERE id = 1;

Each transaction locks rows in a different order — classic deadlock.

2. Long Transactions

The longer a transaction stays open, the longer it holds locks.

This increases the chance of collisions with other transactions.

3. Missing Indexes

Without proper indexes, PostgreSQL may lock more rows than necessary.

This widens the lock scope and increases deadlock probability.

How PostgreSQL Detects Deadlocks

PostgreSQL periodically checks for circular lock dependencies.

When detected, it automatically cancels one transaction and returns the error.

How to Fix Deadlocks (Step by Step)

Step 1: Always Update Rows in the Same Order

Ensure all transactions update tables and rows in a consistent order.

Step 2: Keep Transactions Short

  • Avoid user interaction inside transactions
  • Commit as early as possible

Step 3: Add Missing Indexes

Indexes reduce the number of locked rows.

CREATE INDEX idx_accounts_id ON accounts(id);

Step 4: Retry Failed Transactions

Deadlocks are expected in high-concurrency systems.

The correct approach is retrying the transaction.

Example (Pseudo-code)

try {
  runTransaction();
} catch (DeadlockError) {
  retryTransaction();
}

How to Debug Deadlocks

Enable Deadlock Logging

PostgreSQL can log deadlock details:

Comments

Popular posts from this blog

How to Compare Strings in C#: Best Practices

Do You Really Need Advanced Algorithms to Be a Great Developer in 2025?

Is Python Becoming Obsolete? A Look at Its Limitations in the Modern Tech Stack