Плагин Snapshot
Плагин snapshot обеспечивает почти мгновенный запуск узла путём сериализации и восстановления полного состояния блокчейна в виде JSON-файла. Вместо воспроизведения миллионов блоков из block log, узел загружает предварительно подготовленный снимок и начинает синхронизацию с высоты блока снимка через P2P.
Исходник: plugins/snapshot/snapshot.cpp
Зависимости
chain::pluginКонфигурация
Только CLI-опции
| Опция | По умолчанию | Описание |
|---|---|---|
--snapshot <path> | — | Загрузить состояние из файла снимка (DLT-режим). Пропускает импорт, если shared_memory.bin уже существует; переименовывает файл в .used после успешного импорта. |
--snapshot-auto-latest | false | Автоматически обнаружить последний снимок в snapshot-dir по номеру блока из имени файла. Игнорируется, если также указан --snapshot. |
--replay-from-snapshot | false | Восстановление после сбоя: импортировать снимок, затем воспроизвести блоки из скользящего DLT block log. Всегда очищает shared memory; НЕ переименовывает файл снимка в .used. Требует --snapshot или --snapshot-auto-latest. |
--auto-recover-from-snapshot | true | Автоматическое восстановление во время работы при повреждении shared memory — перезапуск не требуется. Требует plugin = snapshot и наличия снимков в snapshot-dir. Отключить через --no-auto-recover-from-snapshot. |
--create-snapshot <path> | — | Создать снимок по указанному пути, используя текущее состояние базы данных, затем выйти. Запускается до активации P2P или валидатора. |
--sync-snapshot-from-trusted-peer | false | Загрузить и применить снимок с доверенных пиров, когда состояние пустое. Требует trusted-snapshot-peer. Явно включается, чтобы предотвратить случайное стирание состояния. |
Опции файла конфигурации
| Опция | По умолчанию | Описание |
|---|---|---|
snapshot-at-block | 0 | Создать снимок при достижении указанного номера блока (0 = отключено). |
snapshot-every-n-blocks | 0 | Создавать снимок каждые N блоков (0 = отключено). Срабатывает только на живых блоках — пропускается во время начальной P2P-синхронизации. |
snapshot-dir | — | Каталог для автоматически создаваемых файлов снимков. Создаётся автоматически при отсутствии. |
snapshot-max-age-days | 90 | Удалять снимки старше N дней после создания нового (0 = отключено). |
allow-snapshot-serving | false | Включить обслуживание снимков по TCP для других узлов. |
allow-snapshot-serving-only-trusted | false | Ограничить обслуживание снимков только настроенными доверенными пирами. |
snapshot-serve-endpoint | 0.0.0.0:8092 | TCP-точка для слушателя обслуживания снимков. |
trusted-snapshot-peer | — | Конечная точка доверенного пира для P2P-синхронизации снимков (IP:port); может повторяться. |
Опция dlt-block-log-max-blocks (в разделе конфигурации плагина chain) управляет размером скользящего DLT block log и тесно связана с работой снимков — см. Скользящий DLT block log ниже.
Создание снимков
Метод 1: Единовременно (узел остановлен)
Остановите узел, затем перезапустите с --create-snapshot. Узел открывает существующую базу данных (воспроизводя из block log при необходимости), записывает снимок и выходит до активации P2P или валидатора:
vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshotМетод 2: На конкретном блоке (без простоя)
plugin = snapshot
snapshot-at-block = 5000000
snapshot-dir = /data/snapshotsКогда применяется блок 5 000 000, узел записывает /data/snapshots/snapshot-block-5000000.json без прерывания работы.
Метод 3: Периодически (рекомендуется)
plugin = snapshot
snapshot-every-n-blocks = 28800
snapshot-dir = /data/snapshots
snapshot-max-age-days = 90Файлы называются snapshot-block-<N>.json. Рекомендуемые интервалы:
| Интервал | Блоков | Время (при 3 с/блок) |
|---|---|---|
| Часто | 10 000 | ~8 ч |
| Ежедневно | 28 800 | ~24 ч |
| Еженедельно | 100 000 | ~3,5 суток |
Метод 4: Комбинирование at-block и периодического
Оба параметра можно задать вместе; snapshot-at-block срабатывает один раз, а snapshot-every-n-blocks — многократно.
Как работает создание снимка
Создание снимка асинхронно и разделено на две фазы для минимизации влияния:
- Фаза 1 (блокировка на чтение, ~1 с): Все 32 отслеживаемых типа объектов сериализуются в памяти. Обработка блоков ожидает во время этой фазы; операции чтения API и P2P выполняются параллельно.
- Фаза 2 (без блокировки, ~2 с): Сжатие, вычисление контрольной суммы SHA-256 и запись в файл. Нормальная работа узла возобновляется.
При сбое создания (например, диск переполнен) ошибка записывается в журнал, и узел продолжает работу.
Загрузка из снимка (DLT-режим)
Первый запуск
vizd --snapshot /path/to/snapshot.json --plugin snapshotПри первой загрузке:
- Плагин snapshot проверяет заголовок файла (версию формата, chain ID, контрольную сумму SHA-256).
- Shared memory очищается и переинициализируется из состояния снимка.
- Все 32 типа объектов импортируются.
- LIB повышается до
head_block_num, чтобы P2P-синхронизация начиналась с точки снимка. - Файл снимка переименовывается в
.usedдля предотвращения повторного импорта при перезапуске. - Плагин P2P запускается и синхронизируется вперёд от head снимка.
Безопасность перезапуска
Узел безопасно перезапускать с --snapshot всё ещё в командной строке (например, через VIZD_EXTRA_OPTS):
| Сценарий | Поведение |
|---|---|
| Первый запуск (нет shared memory, файл существует) | Импортирует снимок; переименовывает в .used |
| Перезапуск (shared memory существует) | Пропускает импорт — использует существующее состояние |
Перезапуск (shared memory очищена, файл уже .used) | Пропускает импорт — "snapshot file not found" |
| Принудительный повторный импорт | Используйте --resync-blockchain + предоставьте свежий файл снимка |
DLT-режим
После импорта снимка узел работает в DLT-режиме: основной block_log пуст. Отдельный скользящий DLT block log хранит последние блоки для P2P-обслуживания и локальных запросов блоков. Режим определяется автоматически при последующих перезапусках (пустой block_log + существующее состояние chainbase).
Формат файла снимка
Каждый снимок — один JSON-файл:
{
"header": {
"version": 1,
"chain_id": "...",
"snapshot_block_num": 12345678,
"snapshot_block_id": "...",
"snapshot_block_time": "2025-01-01T00:00:00",
"last_irreversible_block_num": 12345660,
"payload_checksum": "sha256...",
"object_counts": { "account": 50000, ... }
},
"state": {
"dynamic_global_property": [...],
"account": [...],
...
}
}Включённые типы объектов (32 всего)
Критические (11) — необходимы для консенсуса: dynamic_global_property, witness_schedule, hardfork_property, account, account_authority, validator, witness_vote, block_summary, content, content_vote, block_post_validation
Важные (15) — необходимы для полной работы: transaction, vesting_delegation, vesting_delegation_expiration, fix_vesting_delegation, withdraw_vesting_route, escrow, proposal, required_approval, committee_request, committee_vote, invite, award_shares_expire, paid_subscription, paid_subscribe, witness_penalty_expire
Опциональные (5) — метаданные и восстановление: content_type, account_metadata, master_authority_history, account_recovery_request, change_recovery_account_request
Скользящий DLT block log
В DLT-режиме основной block_log пуст. Скользящий DLT block log (dlt_block_log.log + dlt_block_log.index) хранит самые последние необратимые блоки.
Это обеспечивает:
- P2P-обслуживание блоков — пиры, запрашивающие последние блоки для разрешения форков и догона при синхронизации.
- Локальные запросы блоков — API-вызовы вроде
get_blockработают для хранимого диапазона.
Конфигурация
# Хранить последние 100 000 блоков (по умолчанию)
dlt-block-log-max-blocks = 100000
# Полностью отключить DLT block log
# dlt-block-log-max-blocks = 0Журнал использует скользящее окно: когда он превышает dlt-block-log-max-blocks, старые блоки обрезаются с начала. При перезапуске журнал сохраняется, и новые блоки добавляются с того места, где остановились.
Обнаружение устаревшего снимка
Если номер блока последнего снимка меньше начального блока DLT-журнала, скачивающие узлы не могут перекрыть разрыв (снимок на блоке N, DLT-журнал начинается с M > N, блоки N+1..M-1 отсутствуют). Плагин обнаруживает это при запуске и создаёт срочный свежий снимок на первом живом блоке после завершения синхронизации.
Восстановление после сбоя: --replay-from-snapshot
Когда shared_memory.bin повреждён и узел не может запуститься нормально, --replay-from-snapshot комбинирует импорт снимка с воспроизведением DLT block log:
# Указать снимок явно
vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot
# Или автоматически обнаружить последний снимок
vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshotШаги восстановления:
- Очищает shared memory (всегда — предполагается повреждение).
- Импортирует снимок.
- Воспроизводит блоки из
dlt_block_logпосле head снимка, восстанавливая до последнего доступного состояния. - Возобновляет P2P-синхронизацию от воспроизведённого head блока.
Сравнение: --snapshot и --replay-from-snapshot
| Аспект | --snapshot | --replay-from-snapshot |
|---|---|---|
| Назначение | Первоначальная настройка узла | Восстановление после повреждения |
| Проверка shared memory | Пропускает, если уже существует | Всегда очищает и повторно импортирует |
| Переименование файла снимка | Переименовывает в .used | НЕ переименовывает |
| Воспроизведение DLT block log | Нет | Да |
| Типичное использование | Первоначальная настройка | Восстановление после сбоя |
Пример восстановления
DLT-узел падает на блоке 79 274 318. Состояние:
snapshots/snapshot-block-79273800.vizjson ← на 518 блоков позади точки сбоя
blockchain/dlt_block_log.log ← содержит блоки 79174319..79274318
blockchain/shared_memory.bin ← ПОВРЕЖДЁНКоманда восстановления: vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot
Loading state from snapshot: snapshot-block-79273800.vizjson
Snapshot loaded at block 79273800, elapsed time 12.3 sec
Replaying dlt_block_log from block 79273801 to 79274318 (518 blocks)...
Done replaying, head_block=79274318, elapsed time: 7.3 secP2P-синхронизация заполняет разрыв до живого head цепочки.
Автоматическое восстановление во время работы: --auto-recover-from-snapshot
Включено по умолчанию. При обнаружении повреждения shared memory во время обработки или генерации блоков узел:
- Закрывает базу данных.
- Находит последний снимок в
snapshot-dir. - Очищает shared memory, импортирует снимок, воспроизводит
dlt_block_log. - Возобновляет P2P-синхронизацию — перезапуск не требуется.
Предварительные условия:
plugin = snapshotдолжен быть включён.- В
snapshot-dirдолжны существовать снимки. Используйтеsnapshot-every-n-blocksдля их автоматического создания. dlt_block_logдолжен покрывать блоки после снимка для минимальной потери данных.
При сбое восстановления (снимок не найден, ошибка импорта) узел записывает ошибку в журнал и чисто завершает работу.
Для отключения: --no-auto-recover-from-snapshot
P2P-синхронизация снимков
Узлы могут обслуживать и загружать снимки по TCP, обеспечивая полностью автоматизированную начальную настройку без ручной передачи файлов.
Конфигурация сервера
plugin = snapshot
allow-snapshot-serving = true
snapshot-serve-endpoint = 0.0.0.0:8092
snapshot-every-n-blocks = 28800
snapshot-dir = /data/snapshots
# Необязательно: ограничить только доверенными пирами
# allow-snapshot-serving-only-trusted = true
# trusted-snapshot-peer = 1.2.3.4:8092Конфигурация клиента
plugin = snapshot
trusted-snapshot-peer = seed1.viz.world:8092
trusted-snapshot-peer = seed2.viz.world:8092
trusted-snapshot-peer = seed3.viz.world:8092
sync-snapshot-from-trusted-peer = truesync-snapshot-from-trusted-peer по умолчанию false — должен быть явно включён, чтобы предотвратить случайное стирание состояния.
Как это работает
- Запрос: Подключается к каждому доверенному пиру, отправляет
snapshot_info_request, собирает метаданные (номер блока, контрольную сумму, размер). - Выбор: Выбирает пира с наибольшим номером блока.
- Загрузка: Загружается кусками по 1 МБ; прогресс записывается в консоль каждые 5%.
- Проверка: Контрольная сумма SHA-256 проверяется потоково (без полной загрузки файла в память).
- Импорт: Очищает состояние, загружает проверенный снимок, инициализирует харфорки.
Все операции происходят внутри chain::plugin_startup(), до активации P2P и валидатора. Узел полностью заблокирован до завершения импорта.
Безопасность
- Максимальный размер загрузки: 2 ГБ.
- Максимум 5 одновременных подключений на сервер, каждое с 60-секундным дедлайном подключения.
- Ограничение скорости по IP.
- Управляющие сообщения ограничены 64 КБ; только ответы с данными допускают до 64 МБ.
- TCP-сервер работает в выделенном потоке, независимо от основного цикла ввода/вывода.
Рекомендуемая производственная конфигурация
plugin = snapshot
# Ежедневные снимки (~24 ч при 3 с/блок)
snapshot-every-n-blocks = 28800
snapshot-dir = /data/viz-snapshots
# Хранить снимки 90 дней (по умолчанию)
snapshot-max-age-days = 90
# Скользящий DLT block log: последние 100 тыс. блоков (по умолчанию)
dlt-block-log-max-blocks = 100000Устранение неполадок
| Симптом | Проверка |
|---|---|
| Узел повторно импортирует снимок при каждом перезапуске | shared_memory.bin удаляется между перезапусками; или файл снимка так и не был переименован в .used |
Snapshot file not found при запуске | Файл уже переименован в .used при предыдущем успешном импорте; или неверный путь |
Chain ID mismatch при загрузке снимка | Снимок создан из другой цепочки; невозможно импортировать |
Checksum mismatch | Файл снимка повреждён или передан неполностью |
| Создание снимка никогда не срабатывает | snapshot-dir не задан, или узел всё ещё в P2P-синхронизации (периодические снимки пропускают режим синхронизации) |
| Предупреждение об устаревшем снимке при запуске | Последний снимок старше начала dlt_block_log; узел создаст свежий снимок после следующего живого блока |
| Автовосстановление срабатывает, но даёт сбой | Нет снимков в snapshot-dir; проверьте, настроен ли snapshot-every-n-blocks |
| Загрузка снимка по P2P не удаётся | Проверьте доступность trusted-snapshot-peer на порту 8092; проверьте allow-snapshot-serving = true на сервере |
См. также: Снимки, Плагин валидатора, Плагин chain, Обзор P2P.