Ревизию 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, также остались неизменными.