Резервные копии и окно восстановления¶
История цели — это цепочка: полные резервные копии по расписанию, а между ними непрерывный поток изменений (WAL в Postgres, binlog в MySQL). Можно восстановить любую секунду, покрытую полной резервной копией и изменениями после неё. Этот промежуток и есть окно восстановления (point-in-time window).
gantt
dateFormat YYYY-MM-DD HH:mm
axisFormat %a %d
section Full backups
daily full :milestone, 2026-10-01 02:07, 0d
daily full :milestone, 2026-10-02 02:07, 0d
daily full :milestone, 2026-10-03 02:07, 0d
daily full :milestone, 2026-10-04 02:07, 0d
section Change stream
WAL / binlog segments :active, 2026-10-01 02:07, 2026-10-04 18:00
Полные резервные копии¶
| Движок | Режим | Инструмент | Восстановление |
|---|---|---|---|
| Postgres 15–17 | physical (по умолчанию) |
pg_basebackup -Ft -X fetch по протоколу репликации |
базовая копия + применение WAL до секунды |
| Postgres 15–17 | logical |
pg_dump -Fc, один объект на базу данных; исключение таблиц и выборочный экспорт |
pg_restore, плюс применение WAL через физическую цепочку, если она есть |
| MySQL 8.4 | logical |
mysqldump --single-transaction --source-data=2 (процедуры, события, триггеры) |
загрузка + применение binlog до секунды |
Каждая резервная копия записывает опись: базы данных, таблицы, столбцы, индексы, оценки числа строк и размеры, а
также версию миграции, если есть таблица миграций. Именно их сравнивают консоль и tenax diff.
Поток изменений¶
У каждой цели с восстановлением на момент времени есть один streamer:
- Postgres: собственный приёмник WAL в Tenax на физическом слоте репликации (ADR 0005). Слот гарантирует, что WAL не теряется, пока streamer перезапускается.
- MySQL:
mysqlbinlog --read-from-remote-server --raw --stop-neverс позиции binlog последней полной копии.
Сегменты загружаются по мере завершения, а сегмент, который ещё пишется, загружается каждые partialInterval
(по умолчанию 60 с), так что последняя минута тоже восстанавливаема. Каждый сегмент содержит сводку: коммиты,
вставки, обновления и удаления по таблицам, DDL и, по желанию, маскированные предпросмотры строк.
Если поток прерывается (слот потерян, binlog удалён), Tenax помечает цепочку как разорванную, отправляет оповещение
(TenaxStreamChainBroken), начинает новую цепочку и сразу делает полную резервную копию (причина chain-restart).
Старое окно остаётся восстанавливаемым до точки разрыва.
Хранение¶
Полные резервные копии хранятся по уровням; сегменты — лишь настолько далеко назад, насколько нужно окну; сборка мусора удаляет всё остальное, но никогда — закреплённое.
| Пресет | Окно | Ежедневные | Еженедельные | Ежемесячные | Ежегодные |
|---|---|---|---|---|---|
prod |
7 дней | 14 | 8 | 12 | 5 |
dev |
3 дня | 7 | 4 | 3 | 0 |
critical |
14 дней | 30 | 12 | 24 | 7 |
Первая зафиксированная полная копия дня становится ежедневной копией этого дня; первая за неделю ISO — еженедельной;
и так далее. Одна резервная копия может относиться к нескольким уровням; ничего не копируется. Закрепления
(tenax pin <id> --reason …) удерживают резервную копию независимо от уровней. GC записывает свой план в бакет до
удаления, удаляет манифесты раньше данных и через сутки убирает осиротевшие и незавершённые загрузки.
Формат хранения¶
<store prefix>/<org>/<project>/<namespace>/<target>/
full/<backup-id>/manifest.json # written last: the commit marker
full/<backup-id>/data/<database>.zst.age # dumps or the base backup tar
full/<backup-id>/inventory.json.zst.age # also readable with the catalog key
full/<backup-id>/tags.json # tiers, pins, verification results
stream/…/<segment>.zst.age # WAL / binlog segments and their summaries
stream/position.json # the streamer's confirmed position
- Сжатие: zstd с уровнем из политики, один раз, до шифрования; включено по умолчанию
(ADR 0013).
compression.algorithm: noneхранит данные как есть. - Шифрование: каждый объект шифруется age на клиенте для получателей хранилища. Бакет никогда не видит открытый текст.
- Целостность: размер и SHA-256 каждого объекта записаны в манифесте и проверяются при каждом чтении.
- Фиксация: резервная копия существует только после записи
manifest.json. Сбой до этого оставляет сирот, которых удаляет GC, и никогда — копию, выглядящую полной.
Управление нагрузкой¶
Резервное копирование никогда не конкурирует с продакшеном вслепую: слоты на хост, на цель и глобальные, ограничение
полосы, приоритет, случайный сдвиг расписаний и защита от нагрузки, которая откладывает полную резервную копию,
пока у сервера слишком много активных подключений, слишком большая задержка репликации или долгие транзакции. Всё
это задаётся в BackupPolicy.