Skip to content

Плагин Snapshot

Плагин snapshot обеспечивает почти мгновенный запуск узла путём сериализации и восстановления полного состояния блокчейна в виде JSON-файла. Вместо воспроизведения миллионов блоков из block log, узел загружает предварительно подготовленный снимок и начинает синхронизацию с высоты блока снимка через P2P.

Исходник: plugins/snapshot/snapshot.cpp


Зависимости

chain::plugin

Конфигурация

Только CLI-опции

ОпцияПо умолчаниюОписание
--snapshot <path>Загрузить состояние из файла снимка (DLT-режим). Пропускает импорт, если shared_memory.bin уже существует; переименовывает файл в .used после успешного импорта.
--snapshot-auto-latestfalseАвтоматически обнаружить последний снимок в snapshot-dir по номеру блока из имени файла. Игнорируется, если также указан --snapshot.
--replay-from-snapshotfalseВосстановление после сбоя: импортировать снимок, затем воспроизвести блоки из скользящего DLT block log. Всегда очищает shared memory; НЕ переименовывает файл снимка в .used. Требует --snapshot или --snapshot-auto-latest.
--auto-recover-from-snapshottrueАвтоматическое восстановление во время работы при повреждении shared memory — перезапуск не требуется. Требует plugin = snapshot и наличия снимков в snapshot-dir. Отключить через --no-auto-recover-from-snapshot.
--create-snapshot <path>Создать снимок по указанному пути, используя текущее состояние базы данных, затем выйти. Запускается до активации P2P или валидатора.
--sync-snapshot-from-trusted-peerfalseЗагрузить и применить снимок с доверенных пиров, когда состояние пустое. Требует trusted-snapshot-peer. Явно включается, чтобы предотвратить случайное стирание состояния.

Опции файла конфигурации

ОпцияПо умолчаниюОписание
snapshot-at-block0Создать снимок при достижении указанного номера блока (0 = отключено).
snapshot-every-n-blocks0Создавать снимок каждые N блоков (0 = отключено). Срабатывает только на живых блоках — пропускается во время начальной P2P-синхронизации.
snapshot-dirКаталог для автоматически создаваемых файлов снимков. Создаётся автоматически при отсутствии.
snapshot-max-age-days90Удалять снимки старше N дней после создания нового (0 = отключено).
allow-snapshot-servingfalseВключить обслуживание снимков по TCP для других узлов.
allow-snapshot-serving-only-trustedfalseОграничить обслуживание снимков только настроенными доверенными пирами.
snapshot-serve-endpoint0.0.0.0:8092TCP-точка для слушателя обслуживания снимков.
trusted-snapshot-peerКонечная точка доверенного пира для P2P-синхронизации снимков (IP:port); может повторяться.

Опция dlt-block-log-max-blocks (в разделе конфигурации плагина chain) управляет размером скользящего DLT block log и тесно связана с работой снимков — см. Скользящий DLT block log ниже.


Создание снимков

Метод 1: Единовременно (узел остановлен)

Остановите узел, затем перезапустите с --create-snapshot. Узел открывает существующую базу данных (воспроизводя из block log при необходимости), записывает снимок и выходит до активации P2P или валидатора:

bash
vizd --create-snapshot /data/snapshots/viz-snapshot.json --plugin snapshot

Метод 2: На конкретном блоке (без простоя)

ini
plugin = snapshot
snapshot-at-block = 5000000
snapshot-dir = /data/snapshots

Когда применяется блок 5 000 000, узел записывает /data/snapshots/snapshot-block-5000000.json без прерывания работы.

Метод 3: Периодически (рекомендуется)

ini
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-режим)

Первый запуск

bash
vizd --snapshot /path/to/snapshot.json --plugin snapshot

При первой загрузке:

  1. Плагин snapshot проверяет заголовок файла (версию формата, chain ID, контрольную сумму SHA-256).
  2. Shared memory очищается и переинициализируется из состояния снимка.
  3. Все 32 типа объектов импортируются.
  4. LIB повышается до head_block_num, чтобы P2P-синхронизация начиналась с точки снимка.
  5. Файл снимка переименовывается в .used для предотвращения повторного импорта при перезапуске.
  6. Плагин 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-файл:

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 работают для хранимого диапазона.

Конфигурация

ini
# Хранить последние 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:

bash
# Указать снимок явно
vizd --replay-from-snapshot --snapshot /data/snapshots/snapshot-block-79273800.vizjson --plugin snapshot

# Или автоматически обнаружить последний снимок
vizd --replay-from-snapshot --snapshot-auto-latest --plugin snapshot

Шаги восстановления:

  1. Очищает shared memory (всегда — предполагается повреждение).
  2. Импортирует снимок.
  3. Воспроизводит блоки из dlt_block_log после head снимка, восстанавливая до последнего доступного состояния.
  4. Возобновляет 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 sec

P2P-синхронизация заполняет разрыв до живого head цепочки.


Автоматическое восстановление во время работы: --auto-recover-from-snapshot

Включено по умолчанию. При обнаружении повреждения shared memory во время обработки или генерации блоков узел:

  1. Закрывает базу данных.
  2. Находит последний снимок в snapshot-dir.
  3. Очищает shared memory, импортирует снимок, воспроизводит dlt_block_log.
  4. Возобновляет P2P-синхронизацию — перезапуск не требуется.

Предварительные условия:

  • plugin = snapshot должен быть включён.
  • В snapshot-dir должны существовать снимки. Используйте snapshot-every-n-blocks для их автоматического создания.
  • dlt_block_log должен покрывать блоки после снимка для минимальной потери данных.

При сбое восстановления (снимок не найден, ошибка импорта) узел записывает ошибку в журнал и чисто завершает работу.

Для отключения: --no-auto-recover-from-snapshot


P2P-синхронизация снимков

Узлы могут обслуживать и загружать снимки по TCP, обеспечивая полностью автоматизированную начальную настройку без ручной передачи файлов.

Конфигурация сервера

ini
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

Конфигурация клиента

ini
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 = true

sync-snapshot-from-trusted-peer по умолчанию false — должен быть явно включён, чтобы предотвратить случайное стирание состояния.

Как это работает

  1. Запрос: Подключается к каждому доверенному пиру, отправляет snapshot_info_request, собирает метаданные (номер блока, контрольную сумму, размер).
  2. Выбор: Выбирает пира с наибольшим номером блока.
  3. Загрузка: Загружается кусками по 1 МБ; прогресс записывается в консоль каждые 5%.
  4. Проверка: Контрольная сумма SHA-256 проверяется потоково (без полной загрузки файла в память).
  5. Импорт: Очищает состояние, загружает проверенный снимок, инициализирует харфорки.

Все операции происходят внутри chain::plugin_startup(), до активации P2P и валидатора. Узел полностью заблокирован до завершения импорта.

Безопасность

  • Максимальный размер загрузки: 2 ГБ.
  • Максимум 5 одновременных подключений на сервер, каждое с 60-секундным дедлайном подключения.
  • Ограничение скорости по IP.
  • Управляющие сообщения ограничены 64 КБ; только ответы с данными допускают до 64 МБ.
  • TCP-сервер работает в выделенном потоке, независимо от основного цикла ввода/вывода.

Рекомендуемая производственная конфигурация

ini
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.