WAL, replay, лаг реплики и failover

О лекции

WAL — журнал изменений: локальная durability и канал для физической реплики. Асинхронная реплика отстаёт. Failover — это promote и то, как клиенты найдут новый primary, а не «перезапустили под».

PostgreSQL postgres собеседование

WAL (write-ahead log) — журнал изменений. Перед тем как считать транзакцию устойчивой, PostgreSQL пишет redo в этот журнал. Страницы данных могут ещё лежать грязными в shared buffers. После крэша их восстановят, проиграв WAL с последнего чекпоинта. Это буква D в ACID на одном узле: fsync (или эквивалент) журнала, не «все таблицы уже на диске».

Пример. Как дневник кассира: сначала записали операцию в журнал, потом можно править кассу. Журнал пропал — кассу по чекам не восстановите. Журнал цел — кассу соберёте заново.

Физическая реплика — непрерывный replay того же WAL. Байты страниц, та же major-версия. Logical replication декодирует изменения в другой формат: проще подписать гетерогенного потребителя или выборочные таблицы, другая модель слотов и конфликтов. «Реплика Postgres» почти всегда про physical streaming.

Чтение с реплики кажется бесплатным масштабированием SELECT. Цена — лаг и отсутствие read-your-writes: только что вставили на primary, SELECT со standby может не увидеть строку. Для кассы и после редиректа профиля это сюрприз. Для аналитики за вчера — норма.

Асинхронная реплика и лаг

По умолчанию реплика асинхронная: primary не ждёт, пока standby сбросит WAL на диск. RPO (сколько данных можно потерять) не ноль: можно потерять последние транзакции, если primary умер до передачи. Лаг бывает двух родов: replay не успевает (CPU, диск, тяжёлые запросы на реплике) и слот не читают — тогда WAL копится на primary, пока не кончится диск.

Синхронная реплика снижает RPO: commit ждёт подтверждения от standby. Цена — латентность записи и доступность. Если синхронный standby мёртв, primary может остановиться в зависимости от synchronous_commit и списка синхронных реплик. Это уже выбор PACELC, не «включим галочку надёжности».

SELECT application_name, state, replay_lag, write_lag
FROM pg_stat_replication;

SELECT slot_name, active, restart_lsn
FROM pg_replication_slots;

Лаг реплик и состояние слотов — первые две проверки, когда «реплика отстала» или на primary кончается диск.

Неактивный или отставший слот удерживает WAL с restart_lsn. Это тот же класс беды, что долгий xmin для vacuum: горизонт не едет. Logical слот плюс мёртвый консьюмер CDC за выходные — типичный инцидент по диску.

Failover

Failover — смена primary. Старый primary мёртв — replica promote (pg_ctl promote, Patroni, облачный operator). Клиенты должны найти новый адрес: DNS с коротким TTL, PgBouncer pause/reload, сервис discovery. Пока клиенты пишут в труп старого primary, вы получаете ошибки или, хуже, split-brain, если fencing не сработал и старый узел ожил.

После failover старый узел нельзя просто включить как primary: его таймлайн разошёлся. Нужен rewind или переинициализация с нового primary. Управляющие системы как раз про это, а не про «два docker compose up».

Типичная ошибка

Обещать, что read replica всегда consistent с primary. Async replica — eventual: лаг, RPO не ноль. Вторая ошибка — забыть про слот: «консьюмер CDC на паузе» выглядит безобидно, пока WAL не заполнит диск и primary не встанет.

На собеседовании

Свяжите Durability с fsync WAL, репликацию — с replay того же лога. Дальше RPO/RTO: async против sync. Failover — promote плюс клиенты и fencing, не только «реплика стала писать». Не обещайте read-your-writes со standby.