Files
mdm-ordinis/docs/ops/db-migrations.md
T
Zimin A.N. adc40aca22 docs(ops): operator runbook — kafka, outbox DLQ, db migrations + index
Карточки процедур для on-call. Self-contained: можно открыть в момент
инцидента без необходимости понимать всю архитектуру.

- README.md — index by symptom + быстрые гайды по 401, 500, admin-ui 502.
- kafka-restart.md — pause/unpause, replicas=0 fix, force-delete, restart
  consumer'ов после Kafka up.
- outbox-dlq.md — диагностика DLQ через UI/API/SQL, типичные причины
  (orphan topic, ACL, broken payload), replay через UPDATE dlq_at=NULL.
- db-migrations.md — Liquibase changelog, добавление, откат, recovery
  failed migration через DELETE databasechangelog.

Vault unseal был отдельным коммитом ранее.
2026-05-05 17:26:30 +03:00

126 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-<topic>.xml`.
- Номер строго следующий (последний +1).
- Author = `ordinis`.
- Idempotent: использовать `<preConditions onFail="MARK_RAN">` для добавлений.
2. Зарегистрировать в `master.xml` через `<include file="changes/00NN-*.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 требует `<rollback>` блок в changeset**. Без него
автоматический откат невозможен — только manual SQL.
### Если changeset имеет `<rollback>`
```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` |