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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Deadlocks are transient, so retrying the whole transaction is often the correct response rather than failing outright.
- 2Randomized, attempt-scaled backoff spreads out competing retries so they don't collide again immediately.
- 3Only retry errors you can specifically identify as safe — rethrow everything else so real failures still surface.
Related explainers
php
<?php class NameParser {
Parsing a full name into components in PHP
string-parsing
arrays
normalization
Intermediate
8 steps
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
php
<?php namespace App\Services;
Building a cached daily leaderboard in Laravel
caching
aggregation
eager-loading
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/retrying-database-deadlocks-in-php-explained-php-1d0e/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.