php 42 lines · 8 steps

Retrying database deadlocks in PHP

A transaction wrapper that automatically retries when the database reports a deadlock, backing off a little longer each time.

Explained by highlit
1public function transaction(callable $callback, int $maxAttempts = 3): mixed
2{
3 $attempt = 0;
4 
5 while (true) {
6 $attempt++;
7 $this->pdo->beginTransaction();
8 
9 try {
10 $result = $callback($this->pdo);
11 $this->pdo->commit();
12 
13 return $result;
14 } catch (PDOException $e) {
15 if ($this->pdo->inTransaction()) {
16 $this->pdo->rollBack();
17 }
18 
19 if (! $this->isDeadlock($e) || $attempt >= $maxAttempts) {
20 throw $e;
21 }
22 
23 $this->logger->warning('Deadlock detected, retrying transaction', [
24 'attempt' => $attempt,
25 'sqlstate' => $e->getCode(),
26 ]);
27 
28 usleep(random_int(50_000, 250_000) * $attempt);
29 }
30 }
31}
32 
33private function isDeadlock(PDOException $e): bool
34{
35 if ($e->getCode() !== '40001' && $e->getCode() !== '40P01') {
36 $driverCode = $e->errorInfo[1] ?? null;
37 
38 return in_array($driverCode, [1213, 1205], true);
39 }
40 
41 return true;
42}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Deadlocks are transient, so retrying the whole transaction is often the correct response rather than failing outright.
  2. 2Randomized, attempt-scaled backoff spreads out competing retries so they don't collide again immediately.
  3. 3Only retry errors you can specifically identify as safe — rethrow everything else so real failures still surface.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Retrying database deadlocks in PHP — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code