Crise de XID wraparound / autovacuum no Postgres
Sintoma
'database is not accepting commands to avoid wraparound data loss'; o autovacuum não consegue acompanhar, o datfrozenxid está envelhecendo.
1. Entenda a urgência
Os IDs de transação do PostgreSQL são de 32 bits; quando se aproximam do esgotamento, o servidor PARA AS ESCRITAS para evitar perda de dados. É uma situação crítica e sensível ao tempo. A boa notícia: tem solução; exige disciplina, não pânico.
2. Meça o quão perto você está
Levante a idade do XID não congelado mais antigo de cada banco; a proximidade de autovacuum_freeze_max_age (padrão 200M) mostra o perigo.
PostgreSQL
SELECT datname, age(datfrozenxid) AS xid_age
FROM pg_database ORDER BY xid_age DESC;3. Encontre as tabelas mais antigas
O problema geralmente vem de algumas tabelas grandes/muito escritas; concentre-se nos valores de relfrozenxid mais antigos.
PostgreSQL
SELECT relname, age(relfrozenxid) AS xid_age
FROM pg_class WHERE relkind IN ('r','m','t')
ORDER BY xid_age DESC LIMIT 20;4. Remova o que está bloqueando o VACUUM
Transações longas abertas, replication slots abandonados e prepared transactions bloqueiam a limpeza (freeze). Encontre-os e resolva — caso contrário o VACUUM não avança, por mais tempo que rode.
PostgreSQL
SELECT pid, now()-xact_start AS age, state FROM pg_stat_activity WHERE xact_start IS NOT NULL ORDER BY age DESC;
SELECT slot_name, active FROM pg_replication_slots WHERE NOT active;
SELECT * FROM pg_prepared_xacts;5. Execute um FREEZE agressivo
Quando os bloqueadores sumirem, force o congelamento começando pelas tabelas mais antigas. Mesmo que o servidor tenha parado de atribuir novos IDs de transação, não passe para o modo single-user (postgres --single): a documentação atual do PostgreSQL diz que ele não é mais necessário e deve ser evitado. Conectar como superusuário no modo normal e executar VACUUM é suficiente; no PG14+, o mecanismo failsafe do VACUUM entra em ação conforme o wraparound se aproxima.
PostgreSQL
VACUUM (FREEZE, VERBOSE) oldest_table;
-- Entire database: vacuumdb --all --freeze --jobs=46. Definitivo: reforce e monitore o autovacuum
Aumente autovacuum_vacuum_cost_limit, reduza autovacuum_naptime e defina parâmetros de armazenamento específicos nas tabelas muito escritas. Configure um alerta para age(datfrozenxid); nunca mais passe por esta crise.