# Миграции схемы данных EventHub ## Когда применяются После `infra_mnesia:init_tables()` и `wait_for_tables()` приложение вызывает `migration_engine:ensure_applied/0`: 1. Узел, захвативший **глобальную блокировку** в Mnesia (`schema_migration`, ключ `__migration_lock__`), применяет все pending-миграции. 2. Остальные узлы (join кластера) **ждут**, пока pending-список станет пустым, и не стартуют HTTP до синхронизации. Базовые таблицы создаёт `infra_mnesia` при старте. Миграции — только для **инкрементальных** изменений (индексы, трансформация данных, новые поля). ## Создание новой миграции 1. Создайте файл в `src/migrations/` с именем `YYYYMMDDHHMMSS_описание.erl`. 2. Реализуйте `up/0` и `down/0`. 3. Добавьте модуль в список `?ALL_MIGRATIONS` в `src/infra/migration_engine.erl` (в конец, по возрастанию версии). 4. При следующем старте миграция применится автоматически (на узле с lock). ## Ручной запуск ```erlang migration_engine:apply_pending(). migration_engine:status(). migration_engine:rollback("20260501120000_base_schema"). ``` ## Плавающее обновление (rolling update) 1. Переведите узел в режим обслуживания. 2. Выполните бэкап: `mnesia:backup("backup_node.bak")`. 3. Обновите код приложения (git pull / rsync). 4. Перезапустите узел — `ensure_applied/0` применит только новые миграции (один узел с lock). 5. Убедитесь: `migration_engine:status()` — `pending => []` на всех узлах. 6. Верните узел в работу. 7. Повторите для остальных узлов. ## Восстановление после сбоя Если узел упал во время миграции, lock в `schema_migration` может остаться. Он считается устаревшим через **5 минут** (`?LOCK_STALE_SECONDS`) — следующий стартующий узел перехватит lock и продолжит. При ошибке в `up/0` приложение не стартует (`{error, {migration_failed, ...}}`). Откатите код или исправьте миграцию, при необходимости восстановите из `mnesia:backup/1`.