Files
qinglong/docs/adr/ADR-0448-bounded-legacy-shadow-startup-reconciliation.md

6.7 KiB
Raw Permalink Blame History

ADR-0448:有界 Legacy Shadow 启动恢复

  • 状态:Accepted
  • 日期:2026-08-18
  • 关联 RFCQL-RFC-0001 D-02、D-356、PR-4
  • 关联 ADRADR-0001、ADR-0002、ADR-0445、ADR-0446、ADR-0447
  • AmendsADR-0002 的 Shadow 启动与恢复边界
  • Amended byADR-0449

上下文

Legacy Shadow 已能旁路观察同一 ChildProcess、关联跨 worker callback/stop,并为 system crond 提供 response-loss-safe execution ID;但 HTTP worker 重启后,原 worker 的内存注册表和 ChildProcess listener 都已消失。数据库可能留下 queued/dispatching/running 的 legacy-owned Run,而 Legacy initData 又会在 HTTP 启动期间先把旧 RunningInstance 从 running 归一为 stopped。

因此不能照搬 Primary ReconcilerLegacy Attempt 没有可复验的 durable Executor handlePID 可能重用,system crond 还可能独立于 HTTP worker 继续运行。仅用 PID 存活检查会伪造 owner 连续性;把所有非终态 Run 一律 lost,又会提前终结仍可通过稳定 callback 收敛的 system crond。

部署范围同时覆盖低性能路由设备、standalone 和 cluster 节点。启动恢复不得引入常驻 watcher、第二个 SQLite authority 或对 cluster profile 的本机 数据库误装配。

决策

  1. 只有 QL3_SHADOW_ORIGINS 至少显式启用一个已审 origin 时,HTTP worker 才加载恢复 Source、Run Repository、Writer 和 Reconciler。默认 off 不导入重型 adapter、不查询数据库,也不创建 timer/listener。
  2. 恢复发生在 Legacy initData 完成 RunningInstance 状态归一之后、manual Primary activation 与 HTTP listen 之前。这样启动期间没有新的 HTTP manual admission 与恢复扫描竞争,Primary 也不会先于 Shadow 遗留事实审计激活。
  3. 新的只读 Source 使用 (created_at_ms, run_id) keyset 分页,只扫描 enabled origin、legacy owner 且状态为 queued/dispatching/running 的 Run。单页硬上限 64;每个 Cron 的 RunningInstance 证据硬上限 8,超限、重复身份或冲突一律 ambiguous,不猜测更新。
  4. Source 只把 RunningInstance 的 log path 转换为现有 36 字符 opaque log artifact ID;原始路径、command、用户名、Run ID 和 Cron ID 不进入 audit message 或指标 label。
  5. 状态裁决如下:
    • 唯一 PID/log/唯一实例证据已经 finished/error:复用 Shadow Writer 补齐 succeeded/failed
    • 唯一 stopped 且带 finished time:补齐 cancelledreason 为 reconcile
    • queued + claimed 且没有 spawn 证据:以 reconciler actor 收敛为 cancelled,并记录 acceptance abandoned
    • dispatching/running 的 Node worker-owned origin 在重启后没有终态证据:Attempt/Run 收敛为 lost
    • scheduled_system 没有终态证据:保持 pending,等待稳定 execution ID callback,不能因 HTTP worker 重启提前终结外部 crond
    • 多 Attempt、证据截断、身份冲突或非法状态:只计 ambiguous/failed,不覆盖 Legacy UI 状态。
  6. lost/abandoned 的 Attempt 与 Run 使用两个既有原子命令事务推进。若 Attempt 已提交而 Run 响应丢失或进程退出,下次启动会从唯一 terminal Attempt 修复 active Run;不产生第二个 Attempt,也不重放外部副作用。
  7. Reconciler 每次 HTTP 启动只运行一次:edge 为 8 × 1 pagestandalone 为 32 × 4 pages。页预算耗尽返回稳定 resume cursor 和 remaining=true,不在进程内排队、不自动循环;后续差异报表和正式 Primary gate 必须把 remaining/ambiguous/failed 视为未闭合证据。
  8. cluster-controlworker profile 拒绝本机 Legacy Shadow 恢复装配。它们未来必须使用 PostgreSQL/shared authority 的独立 Reconciler,不能复用 Legacy SQLite。
  9. Source、写入、配置或 audit sink 失败均保持 Shadow fail-open,只输出低敏 error type/有界 summary,不阻止 2.x HTTP 服务启动。

资源与部署影响

  • 不新增 package、生产依赖、schema、migration、表、索引、进程、线程、端口、timer、watcher 或 Kubernetes 对象。
  • edge 每次启动最多扫描 8 个 Run,每个带 Cron identity 的候选最多读取 8 条 RunningInstancestandalone 最多扫描 128 个 Run。
  • 查询使用现有 Runs/RunAttempts 索引与 keyset,不用 OFFSET、不全表加载 Attempt,也不把原始日志路径带出 adapter。
  • 默认 off 和 cluster/worker profile 都是零 Repository、零恢复查询、零写入;只有显式 Shadow 的本机 profile 承担一次性启动成本。

被拒绝的替代方案

复用 Primary Startup Reconciler

拒绝。Primary 依赖 durable Executor handle、receipt journal 和 runtime ownerLegacy Shadow 不具备这些证据,复用会把 PID 猜测伪装成精确身份。

启动后定时全表扫描

拒绝。它会给路由设备增加常驻 timer、重复数据库唤醒和不可控历史扫描,也与当前一次性启动门边界不符。

所有旧 active Run 一律 lost

拒绝。system crond 独立于 HTTP worker,并可用稳定 execution ID 在重启后补发终态 callback;提前 lost 会丢弃更强事实。

在 Source 中复用原始 log path 作为跨层 identity

拒绝。原始路径可能暴露任务结构或用户信息;adapter 内必须先转换为现有 opaque artifact ID。

在 cluster profile 打开同一 SQLite 恢复器

拒绝。多节点会形成多个本机 authority,既无法看到共享事实,也可能产生冲突终态。

验证

  • 真实 SQLite 覆盖唯一 RunningInstance 成功终态、startup-reset lost、spawn 前 abandoned、显式 stopped、system-crond pending、重复身份拒绝、 terminal Attempt response-loss 修复和稳定 keyset 分页。
  • Bootstrap 合同覆盖默认关闭零 execute、cluster/worker 拒绝、edge/standalone 独立预算、低敏失败开放,以及 Legacy normalization → Shadow recovery → Primary activation → HTTP listen 顺序。
  • 聚焦测试 15/15Legacy/Shadow 扩展 63/63Legacy 身份专项 8/8,两者串行组合 71/71;完整 backend 1,434 pass / 0 fail / 2 conditional skip18 个 QL3 package 均完成 clean build/testbuild:back 通过。
  • 14/14 static audit 与 14/14 artifact audit 通过。edge/standalone 的 base、adopted、application、application-api、AI、 application+AI、MCP 产物分别为 2,589,998 / 2,590,0762,809,293 / 2,809,4163,632,877 / 3,632,9973,800,430 / 3,800,5743,069,251 / 3,069,3414,493,151 / 4,493,2837,315,930 / 7,316,038 bytes,与前一阶段一致。
  • 本切片不改 schema/migration、数据库连接拓扑、容器或 Kubernetes 资源,未重跑物理 PostgreSQL HA/K3s 门;对象存储 backup/WAL/restore/PITR 与 cert-manager mTLS 轮换继续保留为发布最终化现场证据门。