Files
qinglong/docs/adr/ADR-0122-single-cadence-worker-session-product-lifecycle.md
T

152 lines
11 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-0122:单 Cadence Worker Session 产品生命周期
- 状态:Proposedexact wire、共享 client、Session coordinator、capacity oracle、product/process composition、credential material provider 与单身份部署基线已实现;远端 credential recovery、真实 Cluster 全链、Linux/固定设备证据仍是发布 Gate)
- 日期:2026-07-23
- 关联 RFCQL-RFC-0001 D-05、D-06、D-14、D-85、D-108、D-120、D-121
- 关联 ADRADR-0012、ADR-0057ADR-0061、ADR-0108、ADR-0110、ADR-0112、ADR-0121、ADR-0123、ADR-0234
## 背景
本 ADR 提出时,Cluster Control 已在独立 mTLS/`ql3w` ingress 上提供 Worker Session 的
`register|heartbeat|transition` 路由,PostgreSQL repository 也已拥有 generation/version/lease
fencing,但 route 仍直接解析手写 JSONWorker Runtime 也没有 Session client。当前增量已用共享
runtime contract 和 Worker product composition 收敛这两处缺口;后续 Gate 仍不得回退到第二套协议。
`back/runtime/application/workerHeartbeatLifecycle.ts` 只用于早期单控制面孵化:它依赖 legacy
Worker domain,自建 heartbeat timer,并由另一个 `HeadlessWorkerRuntime` 拼接 execution drainer。
ADR-0121 已建立单 journal owner、单 mTLS Agent 和单 Profile cadence 的 production execution plane。
直接复用旧 lifecycle 会重新产生两套 Session shape、两个 timer、两个 client ownership,并可能在
Session offline 或 Agent close 后仍留下需要 completion/lease supervision 的本地进程。
## 决策
### 1. Session wire 必须 exact、versioned 且 path-bound
在现有 `@qinglong/runtime-core` 增加一个懒加载 Session transport subpath,不新增 package。三个
request/response schema 固定为:
- `qinglong/worker-session-register@v1`
- `qinglong/worker-session-heartbeat@v1`
- `qinglong/worker-session-transition@v1`
Worker/Session identity 只来自既有 pathbody 不重复 principal,也不携带 credential。register request
最多 20 KiB,以容纳 16 KiB canonical capability snapshotheartbeat/transition request 与所有 response
最多 4 KiB。未知字段、未知 schema、非 canonical capability、generation/version/status/lease 漂移均
fail closed。register 的 replacement fact 只在 response 中表达,HTTP status 保持统一且可重放的 200。
Cluster ingress 必须调用 contract parser/serializer,不再维护第二套 ad-hoc key list。现有认证和耐久
audit 仍先于 bodyPostgreSQL repository 仍是 generation、version 和数据库时钟的唯一 authority。
### 2. Session 与 execution 必须共享一个 HTTPS client/Agent
Session adapter 留在 `@qinglong/worker-runtime`,并消费 ADR-0121 的同一个
`WorkerIngressHttpsClient`。不得为 register/heartbeat/transition 创建第二个 Agent、第二套 credential
provider 或独立 socket budget。client path allowlist 扩展必须精确到三个 Session operation,不放宽
Artifact stream 或通用 URL。
credential provider 仍在每个请求前惰性加载 certificate、private key、trust 与 `ql3w` token;本 ADR
不允许把这些材料复制进 Session journal。ADR-0123 已实现 active certificate + 原子 token 文件的具体
读取/轮换消费与 Buffer disposeSession transport 已把 401/403/409 分为 credential rejected/session fenced
使 Pull 立即暂停并允许同一 Session 在 token 修复后 heartbeat 恢复。远端 credential issue/revoke/recovery、
delivery acknowledgement 和管理面失败编排仍是后续 Gate,缺失时 product bootstrap 保持默认关闭。
### 3. Session coordinator 不拥有 timer
新增 caller-driven Session coordinator,只拥有以下显式操作:
1. `register()`:生成新 UUIDv7 Session,提交 canonical capability 与初始 capacity
2. `tick(now)`:仅在到达 heartbeat due time 时提交一次 heartbeat,禁止重叠;
3. `beginDrain()`:持久化 `draining + availableSlots=0`
4. `disconnect()`:只在 execution plane 已证明 settled 后持久化 `offline`
它不得调用 `setTimeout/setInterval`。ADR-0121 的唯一 Profile cadence 同时驱动 Session due check 和
execution tickEdge/Node 可使用不同 cadence,但 heartbeat 频率独立受 5 秒–5 分钟硬边界约束。临时
transport failure 只能在现有 lease 内由后续 cadence 重试;fence、credential rejection 或本地已观察
lease expiry 必须立即停止 Pull,不能自动用新 Session 接管旧 Session 下仍活动的 execution。
### 4. Capacity 必须来自单一 execution oracle
部署不得注入与 journal 无关的任意 available-slots 数字。production composition 必须从同一 owner
journal、当前 in-flight Pull reservation 与最大并发预算派生 `availableSlots`,范围始终为
`0..maxConcurrentRuns`。startup reconciliation 未完成、recovery required、draining 或 Session
unavailable 时必须为 0。
能力快照使用 runtime-core 的 canonical Remote Worker capabilitiescapability JSON/hash、Profile、
架构、Runtime 和 Executor 不能由 HTTP response 临时改写。Edge 默认并发与 page 更小,Node 可提高
有界预算;两者都不增加 per-Run timer 或 socket。
### 5. 启停顺序由一个 product composition root 拥有
启动顺序固定为:
1. disabled/Profile/config gate,且 disabled 在读取路径和 credential 前返回;
2. 创建单 credential provider 与单 HTTPS client
3. 取得 journal owner并完成全部有界 startup reconciliation
4. 只有无 recovery fact 才注册 Session,并发布由 execution oracle 计算的 capacity
5. 启动唯一 `unref` Profile cadence,之后才允许 Pull。
关闭顺序固定为:
1. execution `beginDrain()` 先 Abort Pull,但保留 journal owner与 shared Agent
2. Session `beginDrain()` 在控制面耐久写入 0 capacity
3. 唯一 cadence 继续 heartbeat 与 completion-first supervision
4. 所有 inbox record settled 后才 transition `offline`
5. 最后释放 journal owner、关闭 transport 与 shared Agent。
drain timeout、Session fence、heartbeat lease expiry、offline 失败或 recovery fact 都不得强制 close。
进程仍有可能产生 completion 时,不能为了满足 shutdown 时限丢弃唯一 owner。等待 shutdown proof 的
timer 必须保持 ref;只有常驻 cadence unref。
## 被否决的替代方案
1. **直接复用 `back/**` heartbeat lifecycle**:引入 legacy domain、第二 timer 和第二 shutdown owner。
2. **Session 与 execution 各建一个 HTTPS client**:重复证书、Agent、socket 与 credential rotation 状态。
3. **每 500 ms 都发 heartbeat**Node cadence 会把副本和路由设备的空闲成本放大。
4. **先 register available 再做 startup recovery**:控制面会把未证明安全的 Worker 当成可调度候选。
5. **drain timeout 后 offline/释放 owner**:把预算耗尽伪装成副作用已经停止。
6. **允许部署直接上报 availableSlots**:容量与本地 durable execution authority 可漂移。
7. **为 Session lifecycle 新建 workspace package**:没有独立部署、依赖或权限责任,违反 D-85。
## 验收 Gate
1. runtime exact contract 覆盖三个 schema、byte cap、unknown field、authority drift 与 canonical capability。
2. Cluster ingress 继续证明 auth/audit-before-body,并以 PostgreSQL 16 最小权限角色验证完整 fence/replay。
3. Worker 真实 TLS 1.3 测试在同一 Agent 连续执行 register、heartbeat、Offer、completion 与 transition。
4. startup recovery 前零 registerrecovery required 时零 Session/零 Pull。
5. 单 cadence、heartbeat due coalescing、in-flight 不重叠和本地 lease expiry fail closed 有确定性测试。
6. shutdown 证明 drain capacity 0 → execution settled → offline → owner/Agent release;所有失败路径保留 authority。
7. Edge/Node 固定设备资源证据证明没有新增 package、数据库、per-Run timer 或第二常驻 socket。
## 当前实现证据
1. `@qinglong/runtime-core/worker-session-transport` 已提供三个 exact v1 schema、20 KiB/4 KiB
request cap、4 KiB response cap、canonical capability 与 authority drift 校验;Cluster ingress 已复用
parser/serializer,并继续在 body 前完成认证与耐久 audit。
2. `@qinglong/worker-runtime/session-transport``/session-lifecycle` 已在同一
`WorkerIngressHttpsClient` 上实现 register/heartbeat/transitioncoordinator 无 timer、请求串行、按 due
heartbeat,并在本地 lease expiry 后停止暴露可执行 Session。
3. `WorkerExecutionCapacityOracle` 只读取 owner journal 与 durable pending claimreconciling、draining、
recovery 和 offline 一律发布 0,不接受部署直接注入 slots。
4. `@qinglong/worker-runtime/product` 在 startup reconciliation 后注册 Session,复用 execution 的唯一
cadence;真实 TLS 1.3 测试证明 register → draining → offline 共用一个 socket/Agent,且历史未结算
journal 在零 register 时失败关闭。
5. 确定性顺序测试证明 Session tick 与 execution tick 不重叠,shutdown 按 execution drain → Session
drain → heartbeat/supervision → offline → owner/transport release 收敛,并覆盖 offline 后 owner release 首次
失败的幂等重试。当前 Worker 129/129D-218 本切片未改动共享 contract 或 Cluster
ingress,后者继续由 D-216/D-217 的完整回归与 PostgreSQL HA 门覆盖。
6. ADR-0234 已增加 `ql3-worker` production binary、disabled-before-secret process config、
direct-file identity bootstrap、持久 certificate store、信号先行和 incomplete-drain 持续持权;
独立 Worker 镜像闭包为 3 个 workspace package/24 个 runtime externalKubernetes 固定
单身份单副本、Recreate、PVC 和 projected authority 私有物化。产品入口仍必须由明确
Worker Profile 配置激活,不能由其他部署默认开启。
7. macOS arm64/Node 24.18 真实进程+mTLS资源门测得 edge active/peak
`67,616,768/71,090,176` bytes、node `67,911,680/71,286,784` bytes,均为一个
TLS 1.3 socket并完成 register/drain/offline64 MiB 被明确否决,edge 暂按至少
96 MiB 规划。尚未完成 Gate:真实 Cluster ingress/PostgreSQL 全链、
credential issue/revoke/recovery、Linux image/cgroup 与固定设备断网/休眠/断电证据。
因此本 ADR 与 D-121 仍保持 Proposed。
8. 历史 23 package dependency/source audit 为 `findings=[]`Edge import audit 为 `compatible=true`;六种本机
制品均通过 pack/offline install/import,最大 standalone-application 为 2,474,124 bytes、411 files、
72 loaded modules,且制品闭包不包含 `@qinglong/worker-runtime`。runtime-core 新 contract 使本机制品比
D-120 快照增加两个文件,作为“不新增细碎 package”的显式可见成本保留。