Aplicar DDL de forma segura con lock_timeout y reintentos (evitar colas de bloqueo)
PostgreSQLSesionesComando
-- Single attempt: give up if the lock is not acquired within 3 s instead of queueing and blocking traffic
SET lock_timeout = '3s';
ALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;
RESET lock_timeout;
-- Automatic retry: up to 5 attempts, 3 s apart
DO $$
DECLARE
attempt int := 0;
BEGIN
LOOP
attempt := attempt + 1;
BEGIN
SET LOCAL lock_timeout = '2s';
ALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;
RAISE NOTICE 'OK, attempt %', attempt;
EXIT;
EXCEPTION WHEN lock_not_available THEN
RAISE NOTICE 'lock not acquired, attempt %', attempt;
IF attempt >= 5 THEN RAISE; END IF;
PERFORM pg_sleep(3);
END;
END LOOP;
END $$;Qué hace
Evita que un DDL que necesita ACCESS EXCLUSIVE espere indefinidamente cuando no obtiene el bloqueo, y que bloquee todas las consultas que se encolan detrás de él (lock queue); reintenta a intervalos cortos.
Cuándo usarlo
Al aplicar en producción, sobre tablas con mucha actividad, cambios rápidos pero con bloqueos fuertes como ALTER TABLE, CREATE/DROP TRIGGER, ATTACH PARTITION o RENAME.
Precaución
Sin lock_timeout, un ALTER TABLE encolado detrás de un solo SELECT largo también hace esperar a todas las lecturas/escrituras posteriores y puede agotar el connection pool en minutos. lock_timeout solo limita la espera del bloqueo; no limita el tiempo de ejecución de un DDL que reescribe la tabla (p. ej., cambiar el tipo de una columna). El bloque DO es una sola transacción y mantiene un snapshot entre intentos; para escenarios de reintentos largos es preferible un bucle externo en shell.
Relacionado
- Terminar un backend (pg_terminate_backend)
- Cancelar una consulta en ejecución (pg_cancel_backend)
- Mostrar consultas activas y bloqueantes
- Construir el árbol de bloqueos (encontrar el bloqueador raíz)
- Inspeccionar en detalle las esperas de bloqueo por objeto y modo de bloqueo (pg_locks)
- Encontrar transacciones de larga duración e 'idle in transaction'