3. 原子提交生命周期:Prepare、Commit、Rollback 三阶段模型
KMS 原子提交的核心在于将一次跨多 Plane 的状态变更拆解为三个严格有序的阶段:Prepare(预提交)、Commit(提交) 和 Rollback(回滚)。这个三阶段模型保证了在任意节点发生故障时,系统都能恢复到一致的状态,避免出现“部分生效”的脏数据。
3.1 三阶段总览
下表对比了三个阶段的核心职责、执行条件和故障后果:
| 阶段 | 核心职责 | 执行条件 | 故障后果 |
|---|---|---|---|
| Prepare | 校验各 Plane 资源可用性,锁定资源,预写入变更日志 | 所有 Plane 返回 Ready 状态 | 资源锁定超时,触发 Rollback |
| Commit | 将预写入的变更持久化,释放锁定,更新最终状态 | Prepare 阶段全部成功 | 写入失败,进入 Rollback 恢复 |
| Rollback | 撤销 Prepare 阶段的锁定和预写入,恢复至初始状态 | Prepare 或 Commit 阶段任一失败 | 回滚失败导致资源残留,需人工介入 |
关键认知: Prepare 阶段是“可行性验证”,Commit 阶段是“生效执行”,Rollback 是“安全网”。三阶段缺一不可,跳过 Prepare 直接 Commit 是生产环境中最常见的故障诱因。
3.2 Prepare 阶段:锁定与预校验
Prepare 阶段的目标是确认所有参与 Plane 都能接受本次变更,并提前锁定所需资源,防止并发冲突。该阶段包含以下步骤:
- 资源锁定: 在 Control Plane 的分布式锁服务中,为本次变更涉及的资源键(Resource Key)创建租约(Lease)。
- 状态快照: 记录当前各 Plane 的资源状态版本号,用于后续冲突检测。
- 预写入: 将变更内容写入各 Plane 的预提交日志(Prepare Log),标记为
PENDING状态。 - 健康检查: 向 Data Plane 和 Config Plane 发送轻量级探针,确认目标节点或配置项可达。
实战要点: Prepare 阶段的超时时间通常设置为 5-10 秒。如果某个 Plane 在超时内未返回 ACK,Coordinator 必须立即启动 Rollback,不能无限等待。
3.3 Commit 阶段:持久化与生效
当所有 Plane 在 Prepare 阶段返回成功,Coordinator 进入 Commit 阶段。此阶段将预写入的变更正式生效:
- 日志提升: 将 Prepare Log 中的
PENDING状态提升为COMMITTED。 - 资源释放: 释放 Prepare 阶段获取的分布式锁租约。
- 状态更新: 更新各 Plane 的最终状态版本号,并通知订阅者(如监控系统)。
- 幂等保证: 如果 Commit 请求因网络抖动重试,目标 Plane 必须能识别重复请求并返回成功,避免重复生效。
风险提示: Commit 阶段一旦开始,原则上不允许中断。如果 Commit 过程中某个 Plane 写入失败,Coordinator 必须记录失败现场并触发 Rollback,但此时 Rollback 只能恢复到“部分提交”状态,需要人工校验数据一致性。
3.4 Rollback 阶段:故障恢复与补偿
Rollback 阶段在 Prepare 或 Commit 失败时触发,目标是撤销所有已执行的变更操作:
| 触发场景 | Rollback 动作 | 补偿策略 |
|---|---|---|
| Prepare 阶段任一 Plane 返回失败 | 释放所有已获取的锁,清除 Prepare Log | 直接回滚,无残留 |
| Prepare 阶段超时 | 强制释放超时锁,标记变更失败 | 需检查锁是否被其他进程误用 |
| Commit 阶段部分写入成功 | 对已写入的 Plane 执行逆向操作(如删除新增资源) | 需记录补偿日志,人工审计 |
最佳实践: Rollback 操作本身也必须是幂等的。建议在 Rollback 逻辑中加入“状态校验”步骤,确认当前状态与预期回滚目标一致后再执行撤销,避免重复回滚导致数据损坏。
3.5 三阶段状态机 SVG 图
下图展示了原子提交从 Prepare 到 Commit 或 Rollback 的完整状态流转:
3.6 阶段间状态校验清单
在实际编码中,每个阶段切换前必须执行状态校验。以下清单列出了关键校验项:
- Prepare → Commit 前置校验:
- 所有 Plane 的 Prepare Log 状态均为
PENDING。 - 分布式锁租约未过期(剩余时间 > 2 秒)。
- 资源版本号与快照一致,无并发修改。
- 所有 Plane 的 Prepare Log 状态均为
- Commit → 最终状态 前置校验:
- 所有 Plane 的 Commit 操作返回 HTTP 200 或等效成功码。
- 日志已持久化到磁盘(fsync 确认)。
- 锁租约已释放,无残留。
- Prepare → Rollback 触发条件:
- 任一 Plane 返回错误码(如 500、503)。
- Prepare 阶段整体超时(默认 8 秒)。
- Coordinator 自身崩溃重启后检测到未完成的 Prepare 记录。
代码示例: 以下伪代码展示了 Coordinator 在 Prepare 阶段的校验逻辑:
func (c *Coordinator) Prepare(ctx context.Context, req *ChangeRequest) error {
// 1. 锁定所有资源
for _, resource := range req.Resources {
lease, err := c.LockService.Acquire(ctx, resource.Key, 10*time.Second)
if err != nil {
c.Rollback(ctx, req) // 锁定失败,立即回滚
return err
}
c.leases = append(c.leases, lease)
}
// 2. 预写入所有 Plane
for _, plane := range c.Planes {
if err := plane.Prepare(ctx, req); err != nil {
c.Rollback(ctx, req) // 任一 Plane 失败,回滚
return err
}
}
// 3. 全部成功,进入 Commit
return c.Commit(ctx, req)
}
3.7 故障排查:三阶段中的常见异常
| 异常现象 | 可能原因 | 排查命令/方法 |
|---|---|---|
| Prepare 阶段卡住 | 分布式锁服务宕机或网络分区 | kubectl get lease -n kms-system 检查租约状态 |
| Commit 阶段返回 409 | 资源版本冲突,并发写入 | 查看 Plane 日志中的 resource_version 对比 |
| Rollback 执行失败 | 目标 Plane 已不可达或资源已被删除 | 手动执行补偿脚本,或从备份恢复 |
| 状态机死循环 | 重试策略未设置最大次数 | 检查 Coordinator 配置中的 max_retry 参数 |
生产警告: 不要在 Prepare 阶段执行任何非幂等操作(如创建唯一索引、发送通知)。Prepare 阶段的所有动作必须可逆,否则 Rollback 将无法完全恢复现场。