7.3 KiB
ADR-0232:Cluster runtime recovery 与 lost retry 复用单一 Scheduler cadence
- 状态:Accepted
- 日期:2026-07-30
- 关联 RFC:QL-RFC-0001 D-14、D-57、D-104、D-118、D-214、D-216
- 关联 ADR:ADR-0012—0014、ADR-0057—0061、ADR-0108—0121、 ADR-0227、ADR-0230、ADR-0231
背景
Cluster 已具备 Worker ingress、Run Lease、starting/running/completion、 startup recovery 和 admission-time retry policy,但 production runtime 仍存在两处 断点:
- startup recovery 只在进程启动时扫描,运行期间过期的 Remote Worker Attempt 不会进入证据恢复;
- PostgreSQL 已保存 lost retry policy,却没有 production consumer 把 lost Run 收敛为失败,或在安全策略允许时建立下一次 Attempt。
现有 ClusterWorkflowSchedulerCoordinator 已由一个不重叠 Scheduler cadence 驱动。
另建 recovery/retry timer、sidecar 或 package 会增加多副本扫描、连接、shutdown
顺序和空闲资源成本,也会让 Edge/Standalone 或小型路由设备承担不属于其 Profile
的常驻闭包。
还发现一个原子性缺口:Attempt 被恢复为 lost 时,如果对应
run_dispatch_leases 仍保持 leased,数据库会同时表达“执行 authority 已失效”
和“旧 authority 仍有效”。这会阻塞后续调度,并允许运维查询观察到矛盾事实。
决策
1. 连续 recovery 只扫描运行期过期 Attempt
新增 PostgreSQL runtime recovery source,使用数据库
statement_timestamp() 观察时间,只选择:
runtimeowner 的dispatching|running|lostRun;claimed|starting|runningAttempt;- lease 缺失或已过期;
- 排除尚未分配 Worker、没有 lease/offer/start 事实的 pristine remote
claimedAttempt。
source 复用 startup recovery 的 claim、evidence、resolution authority,不复制
状态机。页上限沿用 recovery contract 的 64 条硬上限,production 默认 16 条;
查询使用 limit + 1 判定 hasMore,不保存跨轮 cursor,也不扫描 startup 专属的
orphaned created Run。
starting|running 过期不等于远端进程已经停止。start barrier 之后仍必须取得
可信的 not-running evidence;未知或仍在运行时失败关闭并留待下一轮,不能仅凭
lease timeout 复制可能有外部副作用的执行。
2. lost 转换与 dispatch lease 释放原子提交
恢复事务继续先取得 Attempt advisory authority,再锁 Run/Attempt/lease。
当 leased Attempt 收敛为 lost 时,同一事务把精确 generation/version 的
run_dispatch_leases 从 leased 改为 released:
- lease version 加一;
- release reason 固定为
lease_expired; - Attempt 保存新的 lease version;
- Run、Attempt、Event 与 lease release 一起提交或一起回滚。
旧 Worker 的 renew、activation 或 completion 随后会因 generation/version/token fence 被拒绝。没有匹配 lease authority 时不伪造 release。
3. 只有 admission 时声明安全的 Run 才自动重试
runtime-core 增加 profile-neutral 的纯 lost retry transition builder;PostgreSQL
只负责候选排序、锁和原子持久化:
- 没有 policy、
retryOnLost=false或最大次数不允许重试:收敛为 failed; - safety 未声明:收敛为 failed;
- attempts 已耗尽:收敛为 failed;
- 只有
idempotent|deduplicated才能从 lost 进入retry_wait; - 到达数据库观察的
nextAttemptAtMs后,原子建立全新的、未租约 Attempt N+1, 并把 Run 重新置为 queued。
新 Attempt 不继承旧 offer、Worker、session、lease、callback 或开始事实。 Plugin Package Workflow 的聚合 Run 被排除,继续由 StepRun-aware recovery、 frontier 与 generation-bound Task admission 管理。
每个候选使用短事务,Attempt advisory lock 先于 Run row lock;页上限为 64, production 默认 16。任何数据库、锁、状态或持久化异常均失败关闭,不以跳过损坏 head candidate 的方式静默推进后续执行。
4. 固定单一 cadence 与执行顺序
production 顺序固定为:
- runtime recovery;
- lost retry;
- 普通 Trigger schedule;
- Workflow frontier;
- Workflow Task Attempt admission。
新的 coordinator 自身不拥有 timer、Pool、连接、cursor 或 per-Run 状态,只包裹
既有 Scheduler coordinator,并由现有 ClusterSchedulerLifecycle 驱动。一次 cycle
重入继续 coalescing;recovery/retry 失败时该轮调度不继续,下一 cadence 再试,
避免在 authority 状态未知时派发新工作。
该装配只存在于 Cluster production bootstrap。Edge/Standalone 保留各自已有的 Profile lifecycle 和页预算,不加载 PostgreSQL adapter,也不增加常驻唤醒。
5. 不扩大部署与依赖面
实现复用现有 runtime-core、cluster-postgres 和 cluster-control:
- 不新增 workspace package;
- 不新增 migration、表、索引或生产依赖;
- 不新增 timer、watcher、listener、queue、sidecar、Pool 或连接;
- Worker ingress 最小权限 Pool 不取得 Run/Attempt/Lease mutation authority;
- Cluster runtime 继续使用既有 runtime Pool 和数据库 fencing。
不采用的方案
为 recovery 与 retry 各建一个 timer
拒绝。多个 cadence 会形成恢复、重试与调度之间的竞态,增加多副本扫描和 shutdown 复杂度,也无法证明低资源环境中的固定空闲成本。
lease 到期后直接复制 starting/running Attempt
拒绝。lease 到期只证明 authority 失效,不证明远端进程停止。没有可信 evidence 就建立 Attempt N+1,可能让非幂等副作用并行发生。
默认重试所有 lost Run
拒绝。旧数据或未声明安全性的 Task 不能被推断为幂等。默认终结失败比隐式重复执行 更安全;自动恢复必须是 admission-time durable policy。
新建 recovery package 或 sidecar
拒绝。该能力与 Cluster runtime Pool、Scheduler 生命周期和数据库 authority 完全同部署、同版本;拆包不会形成独立发布或权限边界,只会增加 importer、镜像与 常驻进程成本。
当前验证
runtime-core全量 425/425;cluster-postgres全量 246 项:245 pass、1 条真实 PostgreSQL 条件 skip、 0 fail;cluster-control全量 153 项:151 pass、2 条外部服务条件 skip、0 fail;- 定向测试覆盖 runtime-only 有界候选、pristine claim 排除、数据库观察时间、 advisory-lock 顺序、lost lease 原子 release/version fence、安全/禁用/耗尽 policy、fresh Attempt 与 recovery→retry→scheduler 顺序;
- workspace 保持 20 个 QL3 package,没有新增 migration、表、生产依赖、timer、 listener、Pool、连接或 sidecar;
- 禁止 image pull 的 PostgreSQL 18.4 arm64 physical HA 门已通过
remote_apply、timeline 1→2、旧主 fencing、pg_rewind只读同步重入、 两个 fresh control replica 与总gates.passed=true。
尚未关闭
- Cluster Secret material 的正式 provider、rotation/retention 和产品入口;
- Worker Session heartbeat/drain/offline、credential renewal/recovery 与真实 Kubernetes 多 Pod Session replacement;
- recovery/retry 的指标、告警、损坏 head candidate 诊断/人工处置,以及真实 raw-wire 分区、operator/proxy/STONITH 和固定多架构容量证据。