Postgres XID wraparound / кризис autovacuum
Симптом
'database is not accepting commands to avoid wraparound data loss'; autovacuum не успевает, datfrozenxid стареет.
1. Оцените срочность
Идентификаторы транзакций PostgreSQL 32-битные; по мере их исчерпания сервер ОСТАНАВЛИВАЕТ ЗАПИСЬ, чтобы предотвратить потерю данных. Это критическая ситуация, ограниченная по времени. Хорошая новость: она решаема; нужна дисциплина, а не паника.
2. Измерьте, насколько близок предел
Получите возраст самого старого незамороженного XID для каждой базы данных; близость к autovacuum_freeze_max_age (по умолчанию 200M) показывает степень опасности.
PostgreSQL
SELECT datname, age(datfrozenxid) AS xid_age
FROM pg_database ORDER BY xid_age DESC;3. Найдите самые старые таблицы
Проблема обычно в нескольких больших/часто изменяемых таблицах; сосредоточьтесь на самых старых значениях relfrozenxid.
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. Устраните то, что блокирует VACUUM
Длительные открытые транзакции, заброшенные слоты репликации и prepared transactions блокируют очистку (freeze). Найдите и устраните их — иначе VACUUM не продвинется, сколько бы он ни работал.
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. Выполните агрессивный FREEZE
После устранения блокирующих факторов принудительно выполните заморозку, начиная с самых старых таблиц. Даже если сервер перестал выдавать новые идентификаторы транзакций, не переходите в однопользовательский режим (postgres --single): в актуальной документации PostgreSQL сказано, что он больше не нужен и его следует избегать. Достаточно подключиться суперпользователем в обычном режиме и выполнить VACUUM; в PG14+ при приближении wraparound срабатывает механизм failsafe у VACUUM.
PostgreSQL
VACUUM (FREEZE, VERBOSE) oldest_table;
-- Entire database: vacuumdb --all --freeze --jobs=46. Навсегда: усильте и отслеживайте autovacuum
Увеличьте autovacuum_vacuum_cost_limit, уменьшите autovacuum_naptime и задайте параметры хранения на уровне таблиц для интенсивно изменяемых таблиц. Настройте оповещение по age(datfrozenxid); больше никогда не попадайте в этот кризис.