Files
qinglong/docs/adr/ADR-0013-remote-run-activation-and-start-failure.md
T

6.1 KiB
Raw Blame History

ADR-0013Remote Run 启动确认、Lease Fencing 与启动失败

  • 状态:Accepted
  • 日期:2026-07-22
  • 关联:QL-RFC-0001、ADR-0001、ADR-0003、ADR-0012、ADR-0109

上下文

Run candidate、Placement 和 claim 只能决定“哪个 Worker Session 暂时拥有某个 Attempt”,不能证明 ExecutionSpec 已被接受,更不能证明执行器已经建立可恢复所有权。若 claim 后直接把 Attempt 标记为 running,会留下三个无法区分的崩溃窗口:

  1. Worker 尚未收到 ExecutionSpec
  2. Worker 已收到但尚未调用 Executor
  3. Executor 已返回 handle,但控制面尚未持久化。

Remote Worker 还会同时更新 Worker Session Lease 和 Run Lease。启动 ACK 若不携带两层 fencing,旧 Session、旧 lease generation/token 或 renewal 前的旧 version 都可能迟到覆盖当前状态。

决策

1. claim、starting 和 running 是三个不同事实

Remote Run 启动固定为:

  1. Dispatcher 发现有界 candidate 并完成 Placement
  2. claim 事务提交 Run LeaseRun 进入 dispatchingAttempt 仍为 claimed
  3. Worker 接受匹配的 ExecutionSpec 后提交 acknowledgeStartingAttempt 进入 starting
  4. Worker 获得稳定 executor handle 后提交 acknowledgeRunningAttempt 与 Run 在同一事务进入 running
  5. 只有第 4 步提交后,控制面才能宣称执行器所有权已经持久化。

不得用 candidate 命中、消息 delivery ACK、内存中的 future 或 Worker heartbeat 代替上述状态事实。

2. 每个 ACK 都验证两层 Lease

acknowledgeStartingacknowledgeRunningfailStart 必须同时绑定:

  • Worker ID、Session ID、Worker generation
  • Run ID、Attempt ID
  • Run Lease generation、token 和 expected version
  • Attempt 的 executor type。

控制面在一个数据库事务中复验 Worker Session 状态/过期时间和 Run Lease 状态/过期时间。任一字段不匹配、lease 已过期、Worker 已 offline 或 version 已被 renewal 推进时 fail closed。Worker 必须从本地单 timer lifecycle 取得最新 lease version 后重试,不允许忽略 version mismatch。

3. ACK 必须幂等但不能掩盖元数据冲突

  • 重复 starting ACK 在同一 Lease authority 下返回 already_starting;若 Run 已正确进入 running,迟到的 starting ACK 返回 already_running
  • 重复 running ACK 只有在 executor handle、log artifact 和 Worker authority 完全一致时返回 already_running;不同 handle 必须拒绝。
  • ACK 不推进 Run Lease versionrenewal 是 version 的唯一非终态推进者。ACK 首次写入仍要求 expected version 精确匹配。
  • 原始 lease token 只存在于认证后的 Worker capability 和 RunDispatchLeases,不得进入 RunEvent、普通日志或 API 投影。

4. 启动失败是终结事务

Worker 已确认 starting 但 Executor 未建立所有权时,failStart 必须在同一事务中:

  • 将 Attempt 置为 failed,错误码为 EXECUTOR_START_FAILED
  • 将 Run 置为 failed
  • 追加 Attempt/Run 两条 Worker 归因事件;
  • 将 Run Lease 置为 completed 并推进 version。

若 Run 已存在 cancellation request,取消事实优先:普通取消进入 cancelledtimeout 取消进入 timed_out。因此 Run 状态机允许 dispatching → timed_out。事务任何一步失败都必须回滚 Attempt、Run、事件和 Lease。

5. SQLite 与 PostgreSQL 共享协议,生产装配仍受门禁

next 的 SQLite withLease、Activation Service 与 Worker 私有 inbox 继续证明单控制面 crash window。ADR-0109 已在 PostgreSQL runtime role 上实现等价的 starting/running/start-failure 原子事务,并把 callback sequence/token digest、offer、Session 与完整 Lease fence 一起固化;认证 Worker ingress 只调用注入 port,独立 ingress role 不获得 Run mutation 权限。

这不等于 Remote Worker 已生产可用。认证 ExecutionSpec/Artifact/completion transport、Worker inbox 网络装配、远端 completion/expiry/cancellation/retry 生命周期及多 Pod/failover 证据仍是后续门禁。不得通过共享 SQLite、delivery ACK 或消息系统 ACK 代替数据库 fencing。

影响

正面影响:

  • claim 后的三个启动崩溃窗口具有可区分状态;
  • 旧 Worker、旧 Lease 和旧 renewal version 无法提交启动事实;
  • executor handle 与 Run running 原子落库,后续 Reconciler 有稳定所有权依据;
  • 启动失败、取消和超时保留真实终态。

代价:

  • Worker transport 至少增加 starting、running 和 start-failure 三类幂等命令;
  • Worker 必须协调 Run Lease renewal 与启动 ACK 的最新 version
  • running 前的失联恢复需要按 claimed/starting 分别制定过期策略。

未选择的方案

  1. claim 后立即标记 running:无法证明 Worker 已接收任务或 Executor 已启动。
  2. Executor 启动后只发送一次 running 回调Worker 在 spawn 前后崩溃时可能留下重复执行窗口。
  3. ACK 不验证 lease versionrenewal、迟到请求和 Session replacement 无法形成统一 fencing。
  4. 启动失败只 release leaseAttempt 会残留 starting,候选与恢复路径失去事实依据。

验证要求

  • running ACK 未经过 starting 时必须拒绝;
  • starting/running ACK 相同元数据可幂等重放,冲突 handle 必须拒绝;
  • renewal 后的旧 expected version 必须被 fencing,最新 version 可继续;
  • lease expiry、Worker Session replacement 和错误 token 均不得改变 Run;
  • start failure 同事务提交 Attempt、Run、双 Event 和 lease completed
  • cancellation 与 timeout 在启动失败竞争中保持正确终态;
  • 第二条 running Event 或 lease completion 前注入失败时,所有写入整体回滚;
  • Node 22、Node 24 和未来 PostgreSQL contract suite 复用上述场景。

ADR-0109 已用 PostgreSQL 16.10 真库覆盖 starting/running 精确重放、普通启动失败、timeout 优先终态、digest-only capability 与独立角色权限;其余 renewal/ACK 并发和 failover 压力仍需远端矩阵继续验证。