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 都能接受本次变更,并提前锁定所需资源,防止并发冲突。该阶段包含以下步骤:

  1. 资源锁定: 在 Control Plane 的分布式锁服务中,为本次变更涉及的资源键(Resource Key)创建租约(Lease)。
  2. 状态快照: 记录当前各 Plane 的资源状态版本号,用于后续冲突检测。
  3. 预写入: 将变更内容写入各 Plane 的预提交日志(Prepare Log),标记为 PENDING 状态。
  4. 健康检查: 向 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 的完整状态流转:

原子提交三阶段状态机 初始状态 Prepare 阶段 Commit 阶段 Rollback 阶段 最终状态 开始变更 全部成功 任一失败/超时 写入失败 提交成功 回滚完成 说明: • 绿色箭头表示正常流程:初始 → Prepare → Commit → 最终状态。 • 红色箭头表示异常流程:Prepare 或 Commit 失败时进入 Rollback,完成后到达最终状态。 • 每个阶段都有严格的超时和重试策略,确保系统在故障时不会卡死。

3.6 阶段间状态校验清单

在实际编码中,每个阶段切换前必须执行状态校验。以下清单列出了关键校验项:

  • Prepare → Commit 前置校验:
    • 所有 Plane 的 Prepare Log 状态均为 PENDING。
    • 分布式锁租约未过期(剩余时间 > 2 秒)。
    • 资源版本号与快照一致,无并发修改。
  • 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 将无法完全恢复现场。