Verrous et deadlocks
Verrouiller, c'est coordonner ; trop verrouiller, c'est figer ; verrouiller en cercle, c'est le deadlock (deux transactions qui s'attendent mutuellement, pour toujours).
Prérequis : cours précédents (transactions, isolation).
Pourquoi
Verrouiller, c'est coordonner ; trop verrouiller, c'est figer ; verrouiller en cercle, c'est le deadlock (deux transactions qui s'attendent mutuellement, pour toujours). Postgres détecte et tue l'une — encore faut-il comprendre pourquoi.
Concepts
- Verrous ligne (
FOR UPDATE) : la ligne est à moi jusqu'au COMMIT. Portée minimale, durée minimale. - Verrous table (DDL :
ALTER,CREATE INDEXnon-concurrent) : attention aux migrations en journée ! - Deadlock : A tient 1 veut 2, B tient 2 veut 1 → Postgres tue l'une (erreur 40P01) après
deadlock_timeout. L'app doit réessayer (comme 40001). - Advisory locks (
pg_advisory_lock) : verrous applicatifs volontaires (leader d'un cron multi-instances, migrations concurrentes) sans table dédiée.
Exemple : l'ordre qui sauve
-- Toujours verrouiller dans le MÊME ordre (par id croissant) :
SELECT * FROM accounts WHERE id IN (1,2) ORDER BY id FOR UPDATE;
Deux transactions qui verrouillent dans le même ordre ne se bloquent jamais en cercle. Règle d'or, coût zéro.
Cas réel
Batch nocturne + API : deadlocks quotidiens à 3h. Cause : batch qui parcourt dans l'ordre physique, API dans l'ordre d'arrivée. ORDER BY id FOR UPDATE des deux côtés : zéro deadlock depuis.
Pièges
- Transaction ouverte « en attendant » (debug, breakpoint !) : verrous tenus pendant le café — tout le monde attend.
SELECT FOR UPDATEsur des milliers de lignes : verrouille un quartier. Filtrer d'abord, verrouiller ensuite.- Ignorer 40P01 (« ça passe au retry ») : le retry masque un ordre incohérent à corriger.
À retenir
Portée/durée minimales, même ordre partout, retry 40P01, advisory locks pour la coordination volontaire.