Aplicar DDL com segurança usando lock_timeout e novas tentativas (evitar filas de lock)
PostgreSQLSessõesComando
-- 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 $$;O que faz
Evita que um DDL que precisa de ACCESS EXCLUSIVE espere indefinidamente quando não consegue o lock e bloqueie toda consulta que se enfileira atrás dele (lock queue); faz novas tentativas em intervalos curtos.
Quando usar
Ao aplicar em produção, em tabelas movimentadas, mudanças rápidas mas que travam muito, como ALTER TABLE, CREATE/DROP TRIGGER, ATTACH PARTITION ou RENAME.
Atenção
Sem lock_timeout, um ALTER TABLE enfileirado atrás de um único SELECT longo também faz toda leitura/gravação seguinte esperar e pode esgotar o pool de conexões em minutos. lock_timeout limita apenas a espera pelo lock; não limita o tempo de execução de DDL que reescreve a tabela (ex.: mudar o tipo de uma coluna). O bloco DO é uma única transação e mantém um snapshot entre as tentativas; para cenários de retentativa longos prefira um loop externo em shell.
Relacionados
- Encerrar um backend (pg_terminate_backend)
- Cancelar uma consulta em execução (pg_cancel_backend)
- Mostrar consultas ativas e bloqueadoras
- Montar a árvore de bloqueios (encontrar o bloqueador raiz)
- Inspecionar em detalhe as esperas de lock por objeto e modo de lock (pg_locks)
- Encontrar transações longas e 'idle in transaction'