Skip to content

config.db v3 — migration, rehearsal и rollback ​

Этот runbook не является разрешением на VPS mutation. Production cutover выполняется только после отдельного подтверждения оператора и только через scripts/deploy-vps.sh; ручной rsync/build/restart запрещён.

1. Offline rehearsal на production-shaped копии ​

  1. Остановить writer копии и сделать SQLite-consistent snapshot (VACUUM INTO либо .backup), не копировать живые -wal/-shm как обычные файлы.

  2. Записать до миграции: sha256sum, размер, owner/mode, schema version, PRAGMA integrity_check, список строк (id,label,type,enabled) и JSON validity. Копия обязана содержать current engine row, disabled invalid legacy row и большой strategy_audit.

  3. На отдельной рабочей копии запустить новый CLI read path. open() сначала создаёт <config.db>.pre-v3.bak, затем одной транзакцией создаёт v3 и snapshots. Прервать один пробный процесс до commit и повторить: исходная v2 DB должна остаться открываемой, backup — неизменным, повтор — идемпотентным.

  4. Проверить:

    sql
    PRAGMA foreign_keys;
    PRAGMA integrity_check;
    SELECT value FROM schema_meta WHERE key='schema_version';
    SELECT s.id,s.label,s.latest_config_version,s.published_config_version,
           v.version,v.validation_state,v.origin,v.content_sha256
    FROM strategies s JOIN strategy_config_versions v ON v.strategy_id=s.id
    ORDER BY s.id,v.version;

    Ожидается v3, latest=published=1, ровно один migration snapshot на старую строку, отсутствие реконструкции из audit и 64 lower-hex символа hash.

  5. Прогнать новый engine startup на копии с production selectors/ключами, но изолированными network/order endpoints. Именно engine-owned typed opener выполняет миграцию: выбранная валидная строка становится valid v1 с текущим contract fingerprint и сообщает stable label + version 1 + тот же hash; disabled invalid legacy остаётся legacy_unvalidated и не публикуется. Storage-only opener без typed validator обязан отказать.

  6. Создать draft v2, выполнить nonzero-order replay --config-version 2, затем replay без номера (published v1). В обоих отчётах проверить version/hash; published pointer после draft replay остаётся 1.

  7. Rollback drill: остановить новые процессы, отложить v3 DB и sidecars, восстановить точный .pre-v3.bak, сверить исходный SHA-256/integrity/schema и запустить соответствующий старый binary на копии. Новый v3 DB старому binary не передавать.

2. Production preflight — только drained и flat ​

  • Зафиксировать текущие image/binary digests и путь instance.
  • Остановить создание config versions. Перевести engine в drain, отменить открытые BUY, дождаться durable open_orders=0 и active_positions=0.
  • Сделать SQLite-consistent backup и сохранить SHA-256, owner/mode и PRAGMA integrity_check. Не продолжать при любом расхождении.
  • Убедиться, что rehearsal выше и все Rust/frontend gates зелёные.

3. Cutover и smoke ​

  1. Выполнить только утверждённую цель scripts/deploy-vps.sh, поставляющую совместимые engine/dashboard/CLI/SPA вместе.
  2. Проверить v3 SQL-инварианты из §1 и projection parity.
  3. Запустить version-pinned nonzero replay опубликованной версии; version/hash отчёта должны совпасть с DB.
  4. Выполнить контролируемый engine restart после drain/flat. В /api/local/seed проверить session.strategy, config_version и config_content_sha256; dashboard должен показать loaded=published без pending-restart.
  5. Проверить, что legacy PUT без CAS/mode получает 409, draft не меняет projection, enable/disable не создаёт config version.

4. Rollback — DB и binary являются одной парой ​

  1. Немедленно остановить новые engine и dashboard writers.
  2. Не открывать v3 старым бинарём. Отложить v3 DB и -wal/-shm с UTC marker.
  3. Восстановить exact pre-v3 backup, owner/mode и сверить сохранённый hash + integrity_check + schema v2.
  4. Вернуть matching old binaries только штатным deploy script, затем выполнить обычный smoke и проверить selectors/flat state.
  5. Сохранить отложенную v3 DB для форензики; не «чинить» её ручным UPDATE.