Skip to content
OpenCloseEsc
Tips
  • Laravel
  • database
  • concurrency

Let DB::transaction() retry a deadlock

The second argument of DB::transaction() is the number of attempts: a deadlock is retried that many times before the exception reaches you.

1 min read

Under load, two transactions that touch the same rows in a different order deadlock, and the database kills one of them. MySQL's answer is "retry it", and Laravel can do that for you.

DB::transaction(function () use ($order) {
    $order->lockForUpdate()->first()->decrementStock();
    Payment::create([...]);
}, attempts: 3);

On a deadlock error the closure runs again, up to three times, with a fresh transaction each time. Keep the closure free of side effects that must not repeat (an email, an HTTP call); queue those after the transaction with DB::afterCommit() or dispatch(...)->afterCommit().

Other exceptions are not retried, only the database's deadlock signal.