Error de 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"

Dos (o más) transacciones están cada una esperando un bloqueo que la otra sostiene. Ninguna puede avanzar nunca, así que PostgreSQL detecta el ciclo, elige una transacción como víctima y la aborta con este error. La otra transacción continúa con normalidad.

Qué significa este error

Los bloqueos a nivel de fila en PostgreSQL se sostienen hasta que la transacción hace commit o rollback. Si la transacción A bloquea la fila 1 y luego quiere la fila 2, mientras que la transacción B sostiene la fila 2 y quiere la fila 1, forman un ciclo: un deadlock. Cuando un backend ha estado esperando un bloqueo durante más de deadlock_timeout (por defecto: 1 segundo), PostgreSQL ejecuta una comprobación de deadlock; si encuentra un ciclo, aborta a uno de los participantes con el SQLSTATE 40P01, hermano de 40001.

Un matiz importante: la base de datos ya ha resuelto la situación para cuando ves el error. Nada está corrupto y la transacción superviviente completó su espera de bloqueo. Lo que queda es un problema de la aplicación — la transacción abortada se revirtió y su trabajo se perdió, así que tu aplicación debe estar preparada para reintentarla.

Causas comunes

Cómo diagnosticarlo

El error en sí lleva casi todo lo que necesitas:

Configura log_lock_waits = on para registrar también cualquier espera de bloqueo más larga que deadlock_timeout, lo que te muestra los casos que estuvieron a punto de colisionar, no solo las colisiones. Para investigar en vivo quién bloquea a quién:

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));

Cómo solucionarlo

🔍 Lectura relacionada: Niveles de aislamiento de transacciones — la sección "Reintentar correctamente" también aplica a los deadlocks: reintenta la transacción completa, no la sentencia fallida.