Erreur PostgreSQL : canceling statement due to lock timeout
ERROR: canceling statement due to lock timeout
L'instruction a attendu un verrou plus longtemps que ne l'autorise lock_timeout et a renoncé. Contrairement à la plupart des erreurs de cette liste, celle-ci signifie généralement qu'un mécanisme de sécurité a fonctionné : échouer vite sur un verrou vaut presque toujours mieux que de faire la queue derrière — surtout pour le DDL.
Ce que signifie cette erreur
Pourquoi échouer vite compte concerne la file d'attente, pas celui qui attend : un ALTER TABLE a besoin d'un verrou ACCESS EXCLUSIVE. Pendant qu'il attend derrière une requête de longue durée, toute nouvelle requête sur cette table — y compris de simples SELECT — fait la queue derrière lui. Une migration coincée peut geler une table de production en quelques secondes. lock_timeout transforme ce scénario en un échec rapide et réessayable.
Ne le confondez pas avec statement_timeout : celui-ci borne la durée d'exécution totale ; lock_timeout borne une seule attente de verrou et a son propre SQLSTATE (55P03).
Causes fréquentes
- DDL derrière une longue transaction — un rapport, une session
idle in transactioncoincée, un job batch. - Verrous de ligne détenus longtemps par des transactions qui font un travail lent entre BEGIN et COMMIT.
- Files d'attente de verrous : vous n'attendiez pas le détenteur d'origine mais celui qui attend le verrou exclusif devant vous.
Comment la diagnostiquer
-- Qui bloque qui, maintenant :
SELECT waiting.pid, waiting.query AS waiting_query,
blocking.pid AS blocking_pid, blocking.query AS blocking_query,
now() - blocking.xact_start AS blocking_xact_age
FROM pg_stat_activity waiting
JOIN pg_stat_activity blocking
ON blocking.pid = ANY (pg_blocking_pids(waiting.pid));
Activez aussi log_lock_waits = on : les attentes plus longues que deadlock_timeout sont journalisées avec les deux côtés, vous donnant un historique de la contention, pas seulement la vue en direct.
Comment la corriger
- Pour les migrations, conservez le motif et ajoutez des réessais :
Timeout court, boucle de réessai, fenêtre hors des heures de pointe : le standard pour un DDL sans drame.SET lock_timeout = '3s'; ALTER TABLE orders ADD COLUMN note text; -- en cas d'échec : réessayez avec backoff - Traitez le bloqueur : corrigez le job qui garde des transactions ouvertes ; définissez
idle_in_transaction_session_timeout; en dernier recours délibéré,pg_terminate_backend(pid). - Préférez les variantes non bloquantes lorsqu'elles existent :
CREATE INDEX CONCURRENTLY, des remplissages par lots au lieu d'un seul UPDATE gigantesque.
