Files
qinglong/docs/adr/ADR-0056-cluster-recovery-provider-registry-and-bootstrap-owned-convergence.md
T

118 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0056: Cluster Recovery Provider Registry 与 Bootstrap-Owned Convergence
- 状态:Proposed
- 日期:2026-07-19
- 关联 RFCQL-RFC-0001 D-05、D-06、D-25、D-37、D-39、D-42、D-52 至 D-55
## 上下文
ADR-0055 已规定只有 durable pre-start 事实或可信 `not_running` 证据才能推进 `lost`,但 processor 仍接受一个任意 evidence providercluster bootstrap 仍把 raw recovery source/claim/transition repository 交给 application,并相信 application 自己编排恢复。这个边界有三个问题:
1. application 可以使用 wildcard/default provider,把未知 Executor 当成某个已知实现;
2. provider 可以看到 recovery owner/token,或因 timeout 后被反复调用而积累无界悬挂探测;
3. application 持有 raw claim/mutation authoritybootstrap 只能事后 verifier,无法证明执行过受审的标准 recovery 链。
此外,LocalProcess、Remote Worker、Container 与 Kubernetes 的执行身份位于不同 authority。cluster-control Pod 上的 `/proc` 不能证明 Worker 节点上的进程状态,Worker offline、HTTP timeout 或 Kubernetes API 暂时不可用也不能证明执行已经停止。
## 决策
### 1. Provider registry 只做 exact executor-type 路由
runtime-core 提供有硬上限的 `ClusterControlRecoveryEvidenceRegistry`
- 最多注册 32 个 provider,不接受 wildcard、重复或不规范 executor type
- 每个 provider 必须声明至少一个 required identity 字段:`workerId``executorHandle``pid``leaseToken`
- Run ID、Attempt ID、Attempt 状态、executor type 与 callback sequence 始终属于 probe target
- 缺失 provider、缺失 required identity 或畸形 target 返回 `identity_unverifiable`,进入 manual,而不是尝试其他 provider
- provider 注册时绑定 `inspect` capability,后续修改注册对象不能改变已安装 authority;
- provider 只收到冻结的 probe target、相对 timeout 和 `AbortSignal`,不接收 recovery owner、claim token/version,也不取得 start/stop/retry/completion capability。
provider 返回值被重新规范化,只允许 `running | not_running | unknown(reason)`。异常和 timeout 映射为 `provider_unavailable`;不合法结果映射为 `conflicting_evidence`。错误文本、transport detail 和 provider 私有字段不进入 disposition、事件或 wire response。
### 2. Timeout 不得制造无界 abandoned probe
每个 executor type 同时最多一个 provider inspection。默认 timeout 为 5 秒,硬上限 30 秒,timer 使用 `unref`,不会阻止进程退出。
timeout 后 registry 向 processor 返回 `provider_unavailable` 并发送 abort signal,但在底层 Promise 真正结束前保留该 provider 的 in-flight slot。后续同类 probe 直接返回 unavailable,不再启动第二个外部调用。这样即使 provider 忽略 abort 或永久挂起,每个 provider 也只保留一个 abandoned operation,不会按 candidate、pass 或 timer tick 无界累积。
registry dispose 会取消 timer、abort active provider,并让等待中的调用 fail closed;它不等待不合作的外部 Promise。
### 3. Provider budget 必须小于 recovery claim lease
cluster recovery 配置必须在打开 PostgreSQL 前验证:owner ID、claim page、lease、retry delay、provider timeout 和 startup pass 全部有硬边界。provider timeout 必须至少为 fenced mutation/settle 留出 250 ms,不能配置为等于或超过 claim lease。
该 250 ms 只是本地资源预算,不是 fencing authority。Pod wall clock 和 provider deadline 都不能证明 claim 有效;probe 返回后仍必须由 PostgreSQL statement time 重新锁定 owner/token/version/expiry fence。
### 4. Core Run recovery 由 bootstrap 拥有
cluster-control bootstrap 在 readiness 后自行构造:
`PostgreSQL source → claim repository → provider registry → evidence processor → resolution repository → supervisor → startup coordinator → convergence verifier`
业务 application 不再收到 raw PostgreSQL Pool、recovery source、claim repository 或 transition repository,只能消费受审领域 port。标准启动顺序为:
1. 用只读 `limit=1` verifier 预检;没有遗留时不创建 claim、不写 recovery control
2. 有遗留时运行有限多轮 supervisor,默认最多 8 轮、硬上限 64 轮,每轮仍只有一页且页内串行;
3. 任一 retry/manual/fenced/store failure 立即保持 unsafe,不运行 application recovery
4. core Run recovery 安全后才运行 application 的其他受审 reconciliation
5. application 自报安全后再用独立数据库 verifier 复核,之后才能启动 lifecycle 和 admission。
startup coordinator 没有 timer、递归分页、全表 backlog 或并行 provider fan-out。pass budget 耗尽时返回最后一个 remaining lower bound,不能伪报收敛。
### 5. Profile 与执行位置必须匹配
registry/coordinator 位于无 driver 的 runtime-corePostgreSQL repository 与默认装配只存在于 cluster-control artifact。edge/standalone 不引入该路径,继续使用各自 SQLite recovery lifecycle。
不得把现有 Linux `/proc` LocalProcess inspector 直接注册到 cluster-control,因为它只能观察当前 Pod/主机,不能证明远端 Worker 的执行。生产 provider 必须按执行位置分别实现:
- Remote Worker:认证 transport、Worker session/generation、Run lease、offer/callback sequence 与 Worker durable receipt/identity
- Kubernetescluster/namespace/Pod UID/container ID 或 Job UID/resourceVersion 的精确绑定;
- Containerdaemon identity、container immutable ID 与宿主 authority
- LocalProcess:只允许在实际执行该进程且持有 durable boot ID/PID/start ticks 的同一节点 Profile 使用。
## 被否决的替代方案
### 未知 Executor 使用 default provider
拒绝。默认 provider 会把新增插件 Executor 静默纳入旧证据语义,形成权限与副作用旁路。未知类型必须 manual,直到注册专用 provider。
### Timeout 后释放 slot 并立即重试
拒绝。AbortSignal 不是强制取消;不合作 provider 仍在执行,按 pass 重试会造成连接、Promise 和远端请求无界堆积。
### 继续让 application 持有 claim/transition repository
拒绝。业务 stack 不需要恢复协调 authority;它会允许绕过 registry、标准 processor 或 pass budget,并迫使 bootstrap 仅依赖事后检查。
### cluster-control 直接读取本机 `/proc` 判断 Remote Worker
拒绝。PID namespace、boot identity 和进程位置不匹配;“本机没有该 PID”对远端执行没有任何负证据意义。
## 影响
### 正向
- 未知 Executor、缺失身份和 provider 漂移默认 fail closed
- provider 看不到 recovery claim capability,也无法通过 registry 调用执行副作用;
- 慢/失控 provider 的在途数量和 timer 数量有固定上限;
- normal startup 只做一次只读候选点查,不产生 recovery control 写放大;
- core Run recovery 不再依赖 application 自选实现,admission 前的标准链可由真实 PostgreSQL 集成测试证明;
- application assembly 的 authority 面缩小,edge/cluster 依赖方向不变。
### 代价与未完成项
- enabled cluster-control 现在必须显式提供 recovery owner/config
- executor provider 缺失时 active Attempt 会进入 manual 并阻断 admission,这是安全默认值;
- 仍需 Remote Worker/Kubernetes/Container 的真实认证 provider,当前 registry 不等于这些证据已存在;
- ADR-0057 已补齐 Remote Worker provider 所需的 PostgreSQL Session/Run Lease 精确 authority 与 Attempt fence 投影,但认证 attestation ingress/provider 仍未实现,session offline 或 lease expiry 仍不得当作 `not_running`
- application 其他领域仍使用一个通用 reconcile seam,后续应演进为受审 reconciler registry 与各自 verifier
- 仍需持续 lifecycle、指标/告警、manual recovery API、PostgreSQL lost-retry/cancellation 协调和大 backlog/failover 基准。
## 验证
1. runtime-core contract test 覆盖 exact routing、required identity、claim capability 隔离、注册后 mutation 隔离、异常/畸形输出、timeout、single-flight abandoned probe、dispose 和配置硬上限。
2. startup coordinator contract test 覆盖多页收敛、retry/manual 立即停止、pass budget exhaustion 与畸形 summary fail closed。
3. cluster-control test 证明无候选时不打开 repository connection,配置错误在 PostgreSQL 前拒绝,core recovery 不安全时不调用 application reconcile/admissionstop 会释放 registry。
4. PostgreSQL 1618 三角色 CI 通过真实 bootstrap 证明未启动 aggregate 在 application/admission 前原子 lostexecution lease 保留、双事件有序且最终 verifier 为空。
5. Node 22/24 package test、edge import audit、cluster dependency audit、frozen lockfile 与 GitNexus detect-changes 继续作为合并门禁。