mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
118 lines
8.9 KiB
Markdown
118 lines
8.9 KiB
Markdown
# ADR-0056: Cluster Recovery Provider Registry 与 Bootstrap-Owned Convergence
|
||
|
||
- 状态:Proposed
|
||
- 日期:2026-07-19
|
||
- 关联 RFC:QL-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 provider,cluster 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 authority,bootstrap 只能事后 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-core,PostgreSQL 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;
|
||
- Kubernetes:cluster/namespace/Pod UID/container ID 或 Job UID/resourceVersion 的精确绑定;
|
||
- Container:daemon 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/admission,stop 会释放 registry。
|
||
4. PostgreSQL 16–18 三角色 CI 通过真实 bootstrap 证明未启动 aggregate 在 application/admission 前原子 lost,execution lease 保留、双事件有序且最终 verifier 为空。
|
||
5. Node 22/24 package test、edge import audit、cluster dependency audit、frozen lockfile 与 GitNexus detect-changes 继续作为合并门禁。
|