Восстановление SQLite после миграции конфигурируемых изделий

4 мин. чтения

Статус Актуально
Последняя проверка 2026-10-07 (соответствует Git на момент импорта; содержание повторно не проверялось)
Источник Git
Исходный файл docs/SQLITE_MIGRATION_RECOVERY.md — открыть на GitHub
Язык статьи перевод с английского оригинала (файл перевода: translations/ru/docs/SQLITE_MIGRATION_RECOVERY.md)
Git-репозиторий git@github.com:advertech/signage-estimator.git (сервер: /root/signage-estimator)
Git-коммит d22587c1f7e3add9efd54c7ffc0be0a01b296fa3 (master)
Файл последний раз изменён 6638cf601e75 от 2026-09-27
Последний импорт 2026-10-07T09:14:19+02:00
Примечание Источник истины — Git. Меняйте файл в репозитории и перезапускайте /root/wiki-sign-expert/import_git_docs.py; правки, сделанные здесь, будут перезаписаны следующим импортом.

Ревизию b8e4f2a6c1d3 теперь можно повторно запустить после прерванного
обновления SQLite на 7c3d9e1f2a4b. Она не предполагает, что миграция либо
полностью выполнилась, либо не сделала ничего.

Что означает зафиксированное состояние базы данных #

Указанная исходная таблица по-прежнему является эталонной: в product_types
есть только id, name и rules. _alembic_tmp_product_types — выходная
таблица пакетного режима Alembic. В проверенной копии она имела ожидаемую
расширенную схему и ноль строк. Новые таблицы технологий/конфигураций
существовали с совместимыми схемами и без строк.

Исходная ревизия сначала пересобирала product_types пакетным режимом, затем
пересобирала operations и materials, заполняла стабильные коды и создавала
семь таблиц технологий/конфигураций. SQLite может оставить после себя DDL, если
процесс завершится до того, как Alembic обновит alembic_version; create_all()
моделей приложения также может заранее создать будущие таблицы до проставления
ревизии. Поэтому само наличие таблиц не доказывает, какие операторы миграции
были выполнены. Исправление проверяет схему и содержимое таблиц, а не
угадывает по их наличию.

Исправленная миграция:

  • использует добавление колонок вместо пакетной пересборки SQLite;
  • пропускает только уже существующие колонки совместимых типов;
  • создаёт необходимые уникальные ограничения/индексы только при их отсутствии и
    отклоняет конфликтующие индексы или дублирующиеся коды;
  • сохраняет существующие таблицы технологий/конфигураций и их строки после
    проверки обязательных колонок, типов, ключей и связей;
  • распознаёт только известные выходные таблицы _alembic_tmp_product_types,
    _alembic_tmp_operations или _alembic_tmp_materials;
  • удаляет известную временную таблицу, только если её схема совпадает с
    ожидаемой промежуточной схемой и каждая промежуточная строка дублирует
    исходную legacy-строку только со значениями по умолчанию миграции. Пустую
    промежуточную таблицу удалять безопасно. Неожиданные данные или данные не по
    умолчанию прерывают миграцию с ошибкой и остаются нетронутыми;
  • прерывается, если остаётся любая нераспознанная таблица _alembic_tmp_%.

Строка версии остаётся на 7c3d9e1f2a4b, пока Alembic не завершит ревизию.
Не проставляйте ревизию в базе вручную (stamp).

Одноразовое восстановление на production #

Сначала разверните код миграции, содержащий изменения для восстановления. Не
запускайте миграцию из старой версии приложения на частично обновлённой базе.
Затем выполните эту процедуру обслуживания на production-хосте. Она не трогает
Caddy и содержимое базы данных; остановите backend, чтобы избежать
одновременной записи.

cd /root/signage-estimator
systemctl stop signage-estimator-backend.service

# Перед продолжением сделайте и проверьте свежий бэкап SQLite
# по документированной процедуре из deploy/DEPLOYMENT.md.

set -a
. ./.env.production
set +a

.venv/bin/alembic -c backend/alembic.ini current
.venv/bin/alembic -c backend/alembic.ini upgrade b8e4f2a6c1d3
.venv/bin/alembic -c backend/alembic.ini current

.venv/bin/python - <<'PY'
import os, sqlite3
from sqlalchemy.engine import make_url
path=make_url(os.environ['DATABASE_URL']).database
db=sqlite3.connect(f'file:{path}?mode=ro',uri=True)
assert db.execute('select version_num from alembic_version').fetchone()[0]=='b8e4f2a6c1d3'
assert db.execute('pragma integrity_check').fetchone()[0]=='ok'
assert not db.execute('pragma foreign_key_check').fetchall()
assert not db.execute("select name from sqlite_master where type='table' and name like '_alembic_tmp_%'").fetchall()
for table,required in {
    'product_types':{'code','description','active','sort_order','created_at','updated_at'},
    'materials':{'code','price_unit'},
    'operations':{'code','unit','setup_time_min','notes'},
}.items():
    actual={row[1] for row in db.execute(f'pragma table_info("{table}")')}
    assert required <= actual, (table, required-actual)
print('revision, integrity, foreign keys, temporary tables, and required columns verified')
db.close()
PY

systemctl start signage-estimator-backend.service
systemctl status signage-estimator-backend.service --no-pager
curl -fsS http://127.0.0.1:8000/api/health

Если alembic upgrade b8e4f2a6c1d3 прерывается с сообщением о
несовместимости, не удаляйте названную таблицу и не проставляйте ревизию.
Оставьте сервис остановленным, сохраните бэкап и временную таблицу и изучите
точную схему/данные таблицы из сообщения об ошибке, прежде чем планировать
ручное восстановление.

Выполненная проверка #

Восстановление было проверено на бэкапе SQLite, соответствующем
зафиксированному состоянию, на чистой базе от base до b8e4f2a6c1d3 и на
обычной базе 7c3d9e1f2a4b без более поздних таблиц. Отдельная копия в
прерванном состоянии, содержащая корректные строки технологий, версий,
компонентов, операций, параметров, правил и конфигураций расчётов, сохранила эти
записи после обновления. Количество расчётов/ревизий/деталей/раскладок/
пользователей/записей аудита/каталога и отпечаток снимков ревизий на копии,
близкой к production, также остались неизменными.

Обновлено 07.10.2026