Erreur PostgreSQL : deadlock detected

ERROR:  deadlock detected
DETAIL:  Process 18461 waits for ShareLock on transaction 1064; blocked by process 18463.
Process 18463 waits for ShareLock on transaction 1063; blocked by process 18461.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (0,3) in relation "accounts"

Deux transactions (ou plus) attendent chacune un verrou détenu par l'autre. Aucune ne pourra jamais progresser, donc PostgreSQL détecte le cycle, choisit une transaction comme victime et l'annule avec cette erreur. L'autre transaction se poursuit normalement.

Ce que signifie cette erreur

Les verrous au niveau ligne dans PostgreSQL sont conservés jusqu'à ce que la transaction valide (commit) ou annule (rollback). Si la transaction A verrouille la ligne 1 puis veut la ligne 2, tandis que la transaction B détient la ligne 2 et veut la ligne 1, elles forment un cycle : un interblocage (deadlock). Lorsqu'un backend attend un verrou depuis plus longtemps que deadlock_timeout (par défaut : 1 seconde), PostgreSQL lance une vérification d'interblocage ; s'il trouve un cycle, il annule l'un des participants avec le SQLSTATE 40P01, cousin du 40001.

Un cadrage important : la base de données a déjà résolu la situation au moment où vous voyez l'erreur. Rien n'est corrompu et la transaction survivante a terminé son attente de verrou. Ce qui reste est un problème applicatif — la transaction annulée a subi un rollback et son travail est perdu, donc votre application doit être prête à la réessayer.

Causes fréquentes

Comment la diagnostiquer

L'erreur elle-même contient l'essentiel de ce dont vous avez besoin :

Activez log_lock_waits = on pour journaliser aussi toute attente de verrou plus longue que deadlock_timeout, ce qui vous montre les quasi-collisions, pas seulement les collisions. Pour une investigation en direct de qui bloque qui :

SELECT waiting.pid  AS waiting_pid,  waiting.query  AS waiting_query,
       blocking.pid AS blocking_pid, blocking.query AS blocking_query
FROM pg_stat_activity waiting
JOIN pg_stat_activity blocking
  ON blocking.pid = ANY (pg_blocking_pids(waiting.pid));

Comment la corriger

🔍 À lire aussi : Les niveaux d'isolation des transactions — la section « Réessayer correctement » s'applique aussi aux interblocages : réessayez toute la transaction, pas l'instruction en échec.