7.1 KiB
ADR-0054: Cluster Recovery Claim、Fencing 与有界 Supervisor
- 状态:Proposed
- 日期:2026-07-19
- 关联 RFC:QL-RFC-0001 D-06、D-36 至 D-42、D-52、D-53
上下文
ADR-0053 已能从 PostgreSQL 有界发现失去有效 ownership 的 Run/Attempt,并在 admission 前独立验证是否收敛,但候选没有多副本 ownership。若两个 cluster-control Pod 都读取同一个过期 Attempt 并直接处理,它们可能重复探测、互相覆盖结果,甚至重放任务。把 FOR UPDATE SKIP LOCKED 事务一直持有到外部证据探测结束也不可接受:慢 Worker、网络或 Artifact I/O 会占用连接和行锁,放大集群故障。
本决策增加恢复协调 authority,不增加任务执行 authority。ADR-0055 已补充 narrowly scoped 的 evidence processor 与 fenced lost transition;其他终态、重试、取消和生产 provider 仍由后续专用组件定义。
决策
1. 恢复 claim 使用独立控制记录
pg-0008-run-recovery-claims 新增 ql3.run_recovery_controls。每个 run|attempt 候选只有一行控制记录,保存候选快照、数据库 observation、available|claimed|retry|manual|resolved 状态、owner、token、单调 version、claim expiry、next claim time 和有界 failure count。
该表不复用 RunAttempt 的执行 lease。执行 lease 是任务执行 authority 和原始故障证据;恢复 claim 只是“哪个控制面副本可以裁决此候选”的短期 authority。覆盖执行 lease 会销毁取证边界并混淆 Worker 与 recovery owner。
2. Claim 事务必须短,processor 在事务外运行
一次 claim() 最多请求 128 项,并在一个短 READ COMMITTED transaction 内完成:
- 用 ADR-0053 source 在同一数据库连接读取固定 observation 的有界候选页;
- upsert 候选快照,但不覆盖已有 claim/retry/manual/resolved 控制状态;
- 只在本页候选内按稳定顺序选择可认领、retry 已到期或 claim 已过期的行;
- 使用
FOR UPDATE SKIP LOCKED更新 owner/token/version/expiry 后立即提交。
外部证据探测、Run/Attempt 重读和后续状态机处理必须在事务外执行。不得跨网络调用、Executor probe 或 Artifact I/O 持有数据库锁。
3. Settle 必须被完整 fence
settle() 是单条原子 UPDATE,必须同时匹配 (target kind, target id, state=claimed, owner, token, version),并由 PostgreSQL statement_timestamp() 证明 claim 尚未过期。任一条件不匹配返回 fenced,旧副本不得覆盖接管者结果。
允许的 disposition 只有:
resolved:本次证据处理已收敛,但只要底层候选事实仍存在,后续 discovery 可以再次认领;retry(delay):使用数据库时间写入有上限的下次认领时间;manual:永久退出自动 claim,等待后续受审人工恢复入口。
processor 异常不会被解释为 resolved;Supervisor 将其收敛为有界 retry。存储错误使用稳定、低敏且 retryable 的 ClusterControlRecoveryStoreError,不泄漏 SQL/连接信息。
4. Supervisor 单页、串行、无 timer、绝不执行任务
runtime-core 的 ClusterControlRecoverySupervisor 每次只处理一个 claim page,默认 16、硬上限 128;claim lease 默认 30 秒、硬上限 5 分钟;retry delay 硬上限 5 分钟。页内串行处理,不递归翻页,不创建 timer,不缓存全局 backlog,也不调用 Executor。
这些边界同时服务两类部署:小规格控制节点可选择更小的 page/cadence,集群副本依靠 PostgreSQL claim/fencing 横向并行。edge/standalone 不安装该 PostgreSQL 表、driver 或 supervisor,它们继续使用 Profile 专属 SQLite 生命周期。
5. Capability 与最小权限 lockstep
control-core capability 推进到 v7,并增加 cluster_recovery_claim: 1。reviewed migration、冻结 checksum、Drizzle schema、catalog contract 和 readiness 必须同步。runtime role 对控制表只有 SELECT/INSERT/UPDATE、没有 DELETE/DDL;cluster-admin 对该表零权限。
ClusterControlAssemblyInput 同时暴露只读 recovery source 和 recoveryClaims repository,避免把候选发现、协调 authority 和业务状态推进揉成一个万能 Repository。
6. 当前仍不开放生产执行
ADR-0055 已实现 provider contract、保守 evidence processor 与 Run/Attempt lost 原子事务,但仍没有默认 production evidence provider、retry/其他 terminal 状态事务、周期 lifecycle、指标告警或人工 recovery API。bootstrap 仍只接受 application 提供的 reconcile,并在之后独立验证 source 收敛。
因此不能把 claim 成功解释为任务可安全重跑,也不能据此默认注册 run.get、开放 mutation API 或宣称 cluster-control 已可部署。
被否决的替代方案
复用 RunAttempt execution lease
拒绝。它会覆盖原 Worker authority 和过期证据,使 recovery 与执行 ownership 无法独立审计。
在外部证据探测期间保持行锁
拒绝。慢依赖会形成长事务、连接耗尽和锁放大;Pod 崩溃只能依赖数据库断开,无法表达可观测 retry/manual 状态。
只使用进程内 mutex 或 leader election
拒绝。进程内锁不能协调多 Pod;单 leader 会把全部 backlog 和 failover 恢复压到一个节点,而且仍需要每项 fencing 防止旧 leader 晚到写入。
Claim 到期后直接重放任务
拒绝。claim expiry 只证明 recovery owner 失权,不证明旧任务副作用不存在。自动重试必须经过固定 Run 策略、Attempt N+1 和可信业务去重边界。
影响
正向
- 多副本对同一候选只有一个 claim winner,过期后可接管;
- 旧 owner 的迟到 settle 被数据库时间、token 和 version fence 拒绝;
- 外部探测不持有数据库事务或行锁;
- 单批数据库、内存和处理工作量均有硬上限;
- manual/retry/resolved 具备持久化、可审计控制状态;
- runtime/admin 权限边界可由 readiness 精确验证。
代价与未完成项
- 新增一张控制表、三个部分索引和每候选一次持久化控制写;
- resolved 只表示控制处理结果,底层候选未消失时会再次被发现;
- poison candidate 仍可能反复消耗页预算,需要后续告警、退避策略和人工恢复入口;
- 仍需 PostgreSQL 16/18 多 Pool、failover、大 backlog
EXPLAIN与资源基准。
验证
- runtime-core contract test 证明 Supervisor 单页串行、异常转 retry、manual/retry/fenced 均保持 unsafe,并拒绝越界配置与畸形页。
- PostgreSQL unit test 证明短事务顺序、rollback/release、完整 settle fence 与稳定低敏错误。
- PostgreSQL 13.3 真实集成测试证明两个 repository 并发只有一个 winner、有效 claim 不可偷取、到期接管后旧 settle 被 fence、retry 未到期/manual 不可认领、resolved 在底层事实仍存在时可再次认领。
- migration/manifest/Drizzle/schema/readiness test 证明 capability v7、表、索引、CHECK/FK 和 runtime/admin 权限 lockstep。
- Node 22/24、PostgreSQL 16/18 三角色 CI、edge import audit、cluster dependency audit 与 GitNexus detect-changes 继续作为合并门禁。