# 数据库迁移

在同一个 Hosted Service 项目版本中声明 Schema 与 Data Migration，并在目标环境部署前执行。

数据库迁移属于后端应用部署，不属于 Bundle 安装。`db-service` 提供数据库 Instance、Logical Database、
Profile、Allocation 和连接配置解析；Hosted Service 项目版本发布首次从主线固定运行程序与迁移声明，
部署时在目标 Project Environment 上编排、执行和审计迁移。

以下组件不参与这条应用发布链路：Bundle、Module、App Runtime、`baijimu-agent`、Control Plane 和
`release-control`。

版本使用严格 SemVer，并与 `Cargo.toml` 的 `package.version` 完全一致。首次请求固定源码；失败、取消或请求中断后的重试沿用原版本来源，主线变化不会换源。修改源码必须发布新版本。SHA 只用于内部来源追溯，不再作为构建输入。

## 同源版本不变量

一次 `hosted_service_release` 可以包含：

- 一个运行程序及 `hosted-service.toml`；
- 零或一个 Schema Migration；
- 零或多个按清单身份排序的 Data Migration。

运行程序和迁移必须来自同一个 Project、同一个主线 commit 和同一个 `Cargo package.version`。平台不接受
“运行代码来自一个 commit、迁移来自另一个 commit”的组合，也不在部署阶段重新构建或接收额外 Artifact ID。

## 显式选择数据库资源

先在每个目标环境创建有名字的数据库绑定，例如 `orders` 和 `audit`。在项目的 `hosted-service.toml` 中，按
`migrationKey` 声明每个迁移使用的绑定，并将项目声明的 `contract_version` 更新为 `4.0.0`：

```toml
[service.database_bindings]
business-schema = "orders"
audit-backfill = "audit"
```

同一份项目声明可以部署到多个环境，各环境将同一个名字绑定到自己的实际数据库。项目没有迁移时声明空的
`database_bindings` 表。每个迁移必须有对应条目，未使用的条目也会在构建时被拒绝；同一 migrationKey 的多个
版本使用同一个绑定。

发布器将目标绑定名写入不可变 Release 清单。Hosted 在创建部署时校验该绑定属于目标环境、Provider 生效且
提供数据库能力，然后冻结迁移目标和环境资源绑定。执行时使用这份冻结记录，不会因为环境后来换绑而改用
另一个数据库。不存在默认的 `primary` 数据库选择。

## Schema Migration

Schema 迁移目录根部必须包含 `changelog.yaml` 和 `migration.json`。一个项目只能有一个满足该结构的目录。
`migration.json` 使用固定协议：

```json
{
  "protocolVersion": "1.0.0",
  "migrationKey": "business-schema",
  "migrationVersion": "2.1.0",
  "ownerComponentKey": "business-service"
}
```

`migrationVersion` 必须是规范的 SemVer 2.0.0；`changelog.yaml` 路径和迁移身份会进入不可变项目版本清单。

## Data Migration

Data Migration 放在 `data-migrations/` 下。每个迁移目录只能有一个 `manifest.json`，当前支持
migration-agent `1.0.0` 和 `2.0.0` 协议。清单使用与 Schema Migration 相同的四个身份字段，并声明
数据库方言、事务、前置检查和有界更新步骤。精确结构以当前协议 Schema 和发布校验错误为准。

发布器按 `migrationKey`、`migrationVersion` 和清单路径生成确定顺序，避免调用方通过命令参数改变执行顺序。

## 发布并随版本部署

迁移和运行程序使用同一条项目版本发布命令：

```bash
baijimu hosted-service project release <projectId> \
  --workspace-id <workspaceId> \
  --version <projectVersion> \
  --json

baijimu hosted-service project release-status <releaseOperationId> \
  --workspace-id <workspaceId> \
  --json
```

Release Operation 成功后按版本部署，不再传 `schemaMigrationArtifactId` 或 `dataMigrationArtifactId`：

```bash
baijimu hosted-service project deploy-version <projectId> \
  --workspace-id <workspaceId> \
  --environment-id <environmentId> \
  --version <projectVersion> \
  --json
```

平台按 Provider 返回的规范数据库资源身份加锁，按 Schema → Data 顺序执行，全部成功后才部署运行程序并切换 Endpoint。
一次部署引用多个数据库时，按固定顺序取得所有相关资源的锁，避免不同部署以相反顺序等待。
Data Migration 先 dry-run、冻结候选数，再在单个事务中 apply；同一不可变版本重试幂等，不同内容复用同一
迁移版本会失败。

凭据只在当前执行内存和受控子进程中存在，不会写入迁移 Operation、Attempt 或日志。不要把数据库 URL、
密码放入源码、Artifact、普通环境 JSON 或部署参数。

## 状态、失败与恢复

Deployment 记录中的 `databaseMigrations` 保存按顺序创建的 Migration Operation；每次执行形成只追加的
Attempt。部署请求被接受不代表迁移或服务已成功，必须持续查询 deployment，直到迁移和运行部署都进入
终态，并完成真实业务验证。

迁移失败时，新的运行程序和 Endpoint 不会切换，旧部署继续服务；但此前已成功提交的数据库变更不会自动
回滚。应用和迁移必须采用 expand/contract 兼容策略，并把失败恢复设计为新的向前迁移：

1. 先增加兼容结构或数据，使新旧程序都可运行。
2. 部署并验证新程序。
3. 确认旧程序和旧数据路径不再使用后，在后续版本中收缩。

不要把破坏性逆向 SQL、手工修改生产库或在应用启动时补跑迁移当作标准回滚机制。
