Deadlocks and Downtime

aarvin_roshin2 pts0 comments

Deadlocks and downtime — PlanetScale📹 The future of AI infrastructure: optimize and shard your database with agents.Watch the talk

Navigation<br>Blog|Engineering<br>Table of contents «Close »Table of contents<br>How deadlocks occur<br>The damage deadlocks doBacked up transactions<br>Retry storms

Reducing deadlock likelihood in queriesKeep transactions short<br>How latency leads to deadlocks

Reducing deadlock frequency from your app<br>Observing errors<br>Protecting your database with Traffic Control<br>Conclusion<br>PlanetScale Postgres is the fastest way to run Postgres in the cloud. Plans start at just $5 per month.<br>Learn more

Get the RSS feed

Deadlocks and downtime<br>Simeon Griggs [@simeonGriggs] | July 8, 2026<br>Every second* of every day, your Postgres database could be hunting with intent to kill.<br>*or whatever your deadlock_timeout setting is.<br>Deadlocks happen when multiple transactions each hold a lock that the other needs, blocking both from proceeding. When a transaction has been waiting on a lock for longer than deadlock_timeout, Postgres runs its deadlock detector. If it finds a cycle, it cancels one transaction in the cycle — freeing the others to proceed.<br>Without this process, new transactions could continue to queue up behind the deadlock until the connection pool is full and the database is overwhelmed.<br>Your database stays healthy as long as deadlock scenarios are infrequent and small. But if your application is creating these events faster than your database can kill them, it won't be enough to keep up.<br>How deadlocks occur<br>There are several ways a Postgres transaction will acquire a lock on a row.<br>The simplest representation is two separate, concurrent transactions that attempt to update the same rows in a different order.<br>Transaction A is stuck waiting for row 2 to become available. Transaction B is stuck waiting for row 1.<br>The damage deadlocks do<br>A single deadlock won't bring down your database. A high frequency of deadlocked transactions can.<br>Backed up transactions<br>While transactions A and B wait for one another, transactions C, D, E, and so on may also be stuck if they need the same locks. The default deadlock_timeout of one second means transactions wait at least that long before the detector even runs.<br>Every second counts. If your database is receiving a high volume of queries per second (QPS), a large and problematic queue can form quickly.<br>Retry storms<br>This is made worse if your application resubmits the same query immediately and repeatedly with retry logic. Whenever your database kills transactions, your application immediately retries, recreating the deadlock scenario in an endless cycle.<br>Send, locked, killed, error, retry, locked, killed, error...<br>Reducing deadlock likelihood in queries<br>In the visual shown earlier, two transactions attempt updates to the same rows in the opposite order.<br>Transaction A updates row 1 and then row 2<br>Transaction B updates row 2 and then row 1<br>Where possible, process transactions in a consistent order. These two may still briefly block one another, but they should not deadlock as long as they make their updates in the same order.<br>Keep transactions short<br>The fewer rows a single transaction needs to lock, the better. Where possible, split transactions into smaller atoms of work and lock rows as late as possible within each one.<br>How latency leads to deadlocks<br>Deadlock errors are often a lagging indicator of an issue. The problem is sometimes first revealed as database latency.<br>Any transaction that was held up due to a deadlock and then allowed to proceed because the other transaction was killed would have a slower overall execution time than it should have.<br>Lower latency, fewer chances to deadlock.<br>See how the same transaction is fast until deadlocked. Canceling one of the deadlocked transactions allows the other to complete, but not as quickly as it should have.<br>Queries that run frequently but show abnormally high or inconsistent execution times relative to their size could signal a future problem.<br>Slow queries often result from missing indexes. Even a small query that must perform a full sequential scan (seq scan) every time increases latency.<br>Shorter transactions reduce the window for conflicts, making deadlocks less likely, though lock ordering still matters a ton.<br>Reducing deadlock frequency from your app<br>In the event of a deadlock, you will receive a response back from your database with the 40P01 error code (Postgres's SQLSTATE for "deadlock detected"). Your application should check for that and act accordingly.<br>Your application should have built-in retry logic with backoff (increasing wait times between attempts) and jitter (a random delay added to each retry).<br>You do not want your app to resubmit the same transaction immediately, nor at a consistent cadence.<br>With backoff, if the transaction fails more than once, each retry should wait longer than the last, giving existing, competing transactions time to complete.<br>Jitter refers to an additional amount of random time added to...

transactions deadlock transaction deadlocks database retry

Related Articles