81 lines
8.1 KiB
Markdown
81 lines
8.1 KiB
Markdown
# Миграции PostgreSQL
|
||
|
||
Crank использует единственную append-only migration authority в `crank-registry`. `admin-api` и `mcp-server` выполняют только read-only preflight. DDL применяет one-shot binary `crank-migrate`, который Community Compose запускает до readiness сервисов.
|
||
|
||
## Безопасная последовательность оператора
|
||
|
||
Для source Compose замените `<compose>` на:
|
||
|
||
```bash
|
||
docker compose -f deploy/community/docker-compose.yml --env-file deploy/community/.env.example
|
||
```
|
||
|
||
Для image Compose используйте `docker compose -f deploy/community/docker-compose.images.yml --env-file .env`. Перед обновлением уже работающей установки выполните строго:
|
||
|
||
1. Read-only preflight: `<compose> run --rm migrate crank-migrate preflight`. Exit `0` означает current, exit `2` — ожидаемую `migration_required`; exit `1` запрещает mutation до устранения причины.
|
||
2. Создайте и проверьте согласованный backup PostgreSQL и artifact storage. Для первой пустой установки зафиксируйте, что восстанавливать нечего.
|
||
3. Проверьте immutable plan: `cargo run -p admin-api --bin crank-migrate -- plan --check` в source checkout либо `<compose> run --rm migrate crank-migrate plan` для образа.
|
||
4. Примените sequence: `<compose> run --rm migrate crank-migrate apply`.
|
||
5. Повторите preflight и убедитесь в `{"status":"current","version":3}`.
|
||
6. Только теперь запускайте long-running services: `<compose> up -d`.
|
||
|
||
Обычный `up` также содержит обязательный migration job, но при upgrade он не заменяет предварительные preflight и backup. Migrator делает до десяти bounded попыток подключения с секундной паузой и затем безопасно завершается ошибкой.
|
||
|
||
Команда читает только `CRANK_DATABASE_URL`/`POSTGRES_*`. Master key, session secret, bootstrap password, MCP credentials и другие service secrets не входят в её config projection.
|
||
|
||
## Фактический brownfield inventory
|
||
|
||
| Источник | Исторический владелец/lock | Известный контракт |
|
||
|---|---|---|
|
||
| `__crank_core_migrations` | `crank-registry`, session lock `0x4352414e4b` | columns `version, description, checksum, applied_at`; v1 checksum `crank-community-baseline-v1` |
|
||
| `__crank_mcp_migrations` | MCP session store, session lock `0x4352414e4b4d4350` | columns `version, checksum, applied_at`; v1 checksum `mcp-transport-sessions-v1` |
|
||
| `__crank_ext_migrations` | прежний `RegistryExtension`, без общего lock | legacy columns `extension_name, version, applied_at`; checksum отсутствовал |
|
||
| `__crank_migrations` | canonical `MigrationAuthority`, transaction lock `0x4352414e4b4d4947` | append-only sequence с version/name/checksum/phase/compatibility |
|
||
| `__crank_migration_legacy_audit` | canonical `MigrationAuthority` | только доказуемо сопоставленное legacy provenance |
|
||
|
||
Legacy extension row без зарегистрированного exact `(name, version, checksum)` несовместим. Community пока не публиковала extension migrations, поэтому authority не выдумывает им checksum и блокирует такие строки с `legacy_conflict`.
|
||
|
||
## Контракт sequence
|
||
|
||
Machine plan находится в [`schemas/migration-sequence.json`](schemas/migration-sequence.json) и проверяется командой:
|
||
|
||
```bash
|
||
cargo run -p admin-api --bin crank-migrate -- plan --check
|
||
```
|
||
|
||
- V1 — immutable brownfield baseline с историческим ledger token и отдельным exact-source SHA-256.
|
||
- V2 — единый exact-byte expand SQL artifact, создающий canonical ledgers и MCP session schema; его SHA-256 закреплён в executable descriptor.
|
||
- V3 — append-only expand для независимого nullable `invocation_logs.trace_id`, canonical-format constraint и partial request/trace indexes; исторические строки остаются `NULL` без fabricated backfill.
|
||
- Каждая версия имеет contiguous `i64` version, стабильное имя, lowercase SHA-256, owner, phase, explicit readable schema min/max и backfill policy.
|
||
- `migrate` требует bounded cursor/batch policy; `contract` дополнительно требует tracked compatibility evidence и закрытого окна.
|
||
- Добавление descriptor без executable implementation блокируется `invalid_contract` до DB I/O.
|
||
|
||
Legacy ledgers остаются readable. Down migration, destructive automatic rollback, ledger rewrite, `--force` и arbitrary SQL/path input отсутствуют. Полная N/N-1 upgrade/rollback qualification остаётся Story 7.2.
|
||
|
||
## Диагностика и восстановление
|
||
|
||
CLI/stderr возвращают bounded JSON: `code`, `stage`, nullable numeric `version`, `recovery`. Database URL, credentials, raw SQL/driver body, row values и host paths не выводятся.
|
||
|
||
| Code | Значение | Безопасное действие |
|
||
|---|---|---|
|
||
| `schema_missing` | Service startup увидел пустую schema | Запустить controlled migration; не разрешать runtime DDL |
|
||
| `migration_required` | Schema отстаёт; preflight CLI завершает работу с exit `2` | Проверить backup и выполнить controlled `apply` |
|
||
| `checksum_mismatch` | Artifact и ledger не совпали | Сначала проверить immutable application image/release manifest; восстанавливать БД только после доказанной ledger corruption |
|
||
| `partial_sequence` | Relation/ledger/structural fingerprint неполон | Не чинить вручную; сопоставить backup и matching artifact |
|
||
| `future_version` | База новее приложения | Установить matching application; не откатывать schema автоматически |
|
||
| `legacy_conflict` | Legacy provenance невозможно доказать | Сохранить backup и привлечь оператора |
|
||
| `lock_timeout` | Другой migration runner удерживает canonical lock | Дождаться завершения и повторить preflight |
|
||
| `apply_failed` | Transaction migration откатилась | Проверить matching artifact/backup, затем повторить preflight |
|
||
| `storage_unavailable` | PostgreSQL/transport недоступен | Проверить сеть/TLS/права; секреты в diagnostic не копировать |
|
||
| `config_invalid` | Database-only config невалиден | Исправить указанный config contract |
|
||
| `invalid_command` | Неизвестная CLI команда/аргумент | Использовать только `plan`, `preflight`, `apply` |
|
||
| `contract_drift` | Committed machine plan расходится с Rust authority | Перегенерировать только для новой append-only version и проверить diff |
|
||
| `invalid_contract` | Descriptor/implementation/window/evidence несовместимы | Исправить authoring contract до любого DB I/O |
|
||
|
||
## Правила разработчика
|
||
|
||
- DDL, SQL migration assets и canonical advisory lock разрешены только в `crank-registry::migrations`.
|
||
- Schema развивается `expand → bounded/resumable migrate → evidence-gated contract`.
|
||
- Опубликованные source bytes/checksums не редактируются: добавляется новая version.
|
||
- Для каждой version обязательны fresh/current/concurrent/corrupt/rollback/data-preservation tests и explicit Community scope scan.
|