# Runbook: Liquibase миграции БД **Источник правды:** `ordinis-migrations/src/main/resources/db/changelog/` - `master.xml` — список включённых changelog'ов в порядке применения. - `changes/00NN-*.xml` — конкретные изменения (CREATE TABLE, ADD COLUMN и т.д.). Миграции применяются **автоматически** при старте `ordinis-app` через `spring.liquibase` (Spring Boot starter). Если миграция падает — pod не стартует, остальные pods тоже не поднимутся (read-api/projection-writer имеют startup-зависимость от схемы БД). ## 1. Просмотр applied миграций ```bash export KUBECONFIG=/tmp/kubeconfig-161 PG_PASS=$(kubectl get secret -n ordinis-staging ordinis-postgres-app \ -o jsonpath='{.data.password}' | base64 -d) kubectl exec -n ordinis-staging ordinis-postgres-1 -c postgres -- \ env PGPASSWORD="$PG_PASS" psql -h localhost -U ordinis_app -d ordinis -c \ "SELECT id, author, dateexecuted, exectype FROM databasechangelog ORDER BY orderexecuted;" ``` Колонка `exectype`: - `EXECUTED` — миграция применена успешно - `FAILED` — упала (pod не должен стартовать в этом случае) - `MARK_RAN` — пропущена через preCondition - `RERAN` — была применена повторно (manual) ## 2. Добавление новой миграции 1. Создать `ordinis-migrations/src/main/resources/db/changelog/changes/00NN-.xml`. - Номер строго следующий (последний +1). - Author = `ordinis`. - Idempotent: использовать `` для добавлений. 2. Зарегистрировать в `master.xml` через ``. 3. Тестировать локально на dev-Postgres: ```bash docker run -d --rm --name ord-pg -e POSTGRES_PASSWORD=dev \ -p 5432:5432 postgres:18 mvn -pl ordinis-migrations -am liquibase:update \ -Dliquibase.url=jdbc:postgresql://localhost:5432/postgres \ -Dliquibase.username=postgres -Dliquibase.password=dev ``` 4. Commit + push. CI пересобирает app image, deploy применяет новые миграции при следующем старте pod'а. ## 3. Откат миграции ⚠️ **Liquibase rollback требует `` блок в changeset**. Без него автоматический откат невозможен — только manual SQL. ### Если changeset имеет `` ```bash # Через kubectl exec на pod где есть mvn — обычно нет, проще локально: mvn -pl ordinis-migrations -am liquibase:rollback \ -Dliquibase.url=jdbc:postgresql://192.168.100.161:5432/ordinis \ -Dliquibase.username=ordinis_app \ -Dliquibase.password=$PG_PASS \ -Dliquibase.rollbackCount=1 ``` ### Manual SQL (если rollback не задан) 1. Прочитать changeset XML — посмотреть какие изменения сделаны. 2. Написать обратные SQL (DROP TABLE, ALTER TABLE DROP COLUMN и т.д.). 3. Удалить запись из `databasechangelog`: ```sql DELETE FROM databasechangelog WHERE id = '00NN-1-some-id' AND author = 'ordinis'; ``` 4. После этого Liquibase при следующем старте либо переприменит миграцию (если она всё ещё в master.xml), либо считает её не-применённой. ## 4. Failed migration recovery Сценарий: миграция упала, pod в CrashLoopBackOff. ### A) Миграция частично применилась Liquibase помечает changeset как FAILED в `databasechangelog`. Чтобы повторить: ```sql DELETE FROM databasechangelog WHERE id = '00NN-1-broken' AND exectype = 'FAILED'; ``` После этого pod рестарт → Liquibase снова попытается. Если миграция всё ещё broken, исправить XML (или применённый частично state в БД), пересобрать image, redeploy. ### B) Миграция полностью применилась но с ошибкой в данных Например column добавлена с NOT NULL без default. Manual SQL: ```sql ALTER TABLE my_table ALTER COLUMN broken_col DROP NOT NULL; -- либо UPDATE my_table SET broken_col = 'default' WHERE broken_col IS NULL; ``` И исправить XML на `defaultValueComputed=...` для следующих deployments. ## 5. Текущий список миграций (на 2026-05-05) | ID | Назначение | |---|---| | 0001-extensions.xml | PostGIS, btree_gist (для tstzrange exclusion) | | 0002-dictionary-definitions.xml | Таблица `dictionary_definitions` | | 0003-dictionary-records.xml | Таблица `dictionary_records` + tstzrange exclusion | | 0004-audit-log.xml | Таблица `audit_log` | | 0005-outbox-events.xml | Таблица `outbox_events` + partial index | | 0006-idempotency-keys.xml | Таблица `idempotency_keys` | | 0007-add-version-columns.xml | JPA optimistic lock columns | | 0008-fix-scope-check-uppercase.xml | Чек на scope в верхнем регистре | | 0009-fix-records-scope-check.xml | Чек consistency dictionary↔record scope | | 0010-envers-tables.xml | Hibernate Envers (revinfo, *_aud) | | 0011-outbox-dlq.xml | DLQ column + partial indexes | ## 6. Эскалация | Проблема | Действие | |---|---| | Migration на staging упала, pod не стартует | DELETE FAILED row + fix XML + redeploy. См. §4A. | | Migration on prod упала | **СТОП**. Не делать manual fix без apply tech lead. Возможно нужен rollback всего deployment. | | `databasechangelog` lock застрял (другая миграция параллельно) | `DELETE FROM databasechangelog_lock WHERE id=1; INSERT INTO databasechangelog_lock VALUES (1, false, NULL, NULL);` | | Postgres недоступен | См. CNPG cluster status: `kubectl get cluster -n ordinis-staging` |