6.1 KiB
ADR-0013:Remote 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,会留下三个无法区分的崩溃窗口:
- Worker 尚未收到 ExecutionSpec;
- Worker 已收到但尚未调用 Executor;
- Executor 已返回 handle,但控制面尚未持久化。
Remote Worker 还会同时更新 Worker Session Lease 和 Run Lease。启动 ACK 若不携带两层 fencing,旧 Session、旧 lease generation/token 或 renewal 前的旧 version 都可能迟到覆盖当前状态。
决策
1. claim、starting 和 running 是三个不同事实
Remote Run 启动固定为:
- Dispatcher 发现有界 candidate 并完成 Placement;
claim事务提交 Run Lease,Run 进入dispatching,Attempt 仍为claimed;- Worker 接受匹配的 ExecutionSpec 后提交
acknowledgeStarting,Attempt 进入starting; - Worker 获得稳定 executor handle 后提交
acknowledgeRunning,Attempt 与 Run 在同一事务进入running; - 只有第 4 步提交后,控制面才能宣称执行器所有权已经持久化。
不得用 candidate 命中、消息 delivery ACK、内存中的 future 或 Worker heartbeat 代替上述状态事实。
2. 每个 ACK 都验证两层 Lease
acknowledgeStarting、acknowledgeRunning 和 failStart 必须同时绑定:
- 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 version;renewal 是 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,取消事实优先:普通取消进入 cancelled,timeout 取消进入 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分别制定过期策略。
未选择的方案
- claim 后立即标记 running:无法证明 Worker 已接收任务或 Executor 已启动。
- Executor 启动后只发送一次 running 回调:Worker 在 spawn 前后崩溃时可能留下重复执行窗口。
- ACK 不验证 lease version:renewal、迟到请求和 Session replacement 无法形成统一 fencing。
- 启动失败只 release lease:Attempt 会残留 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 压力仍需远端矩阵继续验证。