Errore 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"

Due (o più) transazioni stanno ciascuna aspettando un lock che l'altra tiene. Nessuna potrà mai proseguire, così PostgreSQL rileva il ciclo, sceglie una transazione come vittima e la interrompe con questo errore. L'altra transazione prosegue normalmente.

Cosa significa questo errore

I lock a livello di riga in PostgreSQL sono tenuti fino al commit o al rollback della transazione. Se la transazione A blocca la riga 1 e poi vuole la riga 2, mentre la transazione B tiene la riga 2 e vuole la riga 1, si forma un ciclo: un deadlock. Quando un backend ha atteso su un lock per più tempo di deadlock_timeout (default: 1 secondo), PostgreSQL esegue un controllo dei deadlock; se trova un ciclo, interrompe uno dei partecipanti con lo SQLSTATE 40P01, parente del 40001.

Un inquadramento importante: nel momento in cui vedi l'errore, il database ha già risolto la situazione. Niente è corrotto e la transazione sopravvissuta ha completato la sua attesa sul lock. Quello che resta è un problema applicativo — la transazione interrotta è stata sottoposta a rollback e il suo lavoro è perso, quindi la tua applicazione deve essere pronta a riprovarla.

Cause comuni

Come diagnosticarlo

L'errore stesso contiene la maggior parte di ciò che serve:

Imposta log_lock_waits = on per loggare anche ogni attesa su un lock più lunga di deadlock_timeout, che ti mostra i quasi-incidenti, non solo le collisioni. Per l'analisi live di chi blocca chi:

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

Come risolverlo

🔍 Lettura correlata: Livelli di isolamento delle transazioni — la sezione "Retrying correctly" vale anche per i deadlock: riprova l'intera transazione, non lo statement fallito.