7.4 KiB
ADR-0121:Production Worker Headless Execution Composition
- 状态:Accepted(执行平面具体装配已实现;Session heartbeat、证书/credential 产品流程、部署入口与 retention 仍为发布 Gate)
- 日期:2026-07-23
- 关联 RFC:QL-RFC-0001 D-05、D-06、D-14、D-85、D-108、D-111、D-114、D-115、D-116、D-120
- 关联 ADR:ADR-0061、ADR-0087、ADR-0110–ADR-0120
ADR-0122 当前增量:
@qinglong/worker-runtime/product已在不新增 package 的前提下接入 exact Session wire、共享 Agent、journal-derived capacity 与单 cadence shutdown;本 ADR 下方 98/98、294 sources 是 D-120 接受时快照。当前累计证据为 Worker 122/122、runtime-core 159/159、Cluster Control 129 pass/2 external skip、23 importer/303 TypeScript sources。credential material provider 已由 ADR-0123 实现, 远端 recovery 和设备证据仍未完成。
背景
ADR-0110 至 ADR-0118 已分别完成 Offer、单一 inbox、Activation、Secret、日志、POSIX Executor、 Artifact/completion 与 Lease control,但都只提供默认关闭的原语。继续由最终部署入口逐个手工实例化会 产生多个 HTTPS Agent、不同 storage root、遗漏 completion-first supervision 或错误 shutdown 顺序。
原 WorkerRemoteExecutionHeadlessLifecycle.stop() 还会直接 Abort Pull 并释放 journal owner。如果 Pull
已经取得 Offer,或本地进程仍在运行,部署进程可能在没有 durable drain 证明时退出;这不是可接受的
production shutdown。
决策
1. 不新增 package,只开放显式 production subpath
具体执行平面放在 @qinglong/worker-runtime/production。默认 package 根入口继续只暴露证书 store 与
续期原语,不加载 runtime-core remote execution、proper-lockfile、HTTPS client 或 POSIX Executor。
enabled=false 在读取 Profile、路径、credential、Session 或创建对象前返回。启用只接受 worker
Profile,并要求三个互不相同、互不嵌套、非文件系统根的绝对 storage authority:
- Offer/inbox journal;
- 本地日志 spool;
- completion receipt。
Edge 默认 4 条 startup/supervision page、64 条 journal、2 秒 cadence、4 MiB Attempt/32 MiB reserve; Node 默认 16/32 条 page、256 条 journal、500 ms cadence、64 MiB Attempt/256 MiB reserve。所有值仍受 1–64 page、1024 journal、100 ms–60 s cadence 等硬上限约束,不能由部署无限放大。
2. 一个 stack 装配全部现有具体能力
production factory 只使用现有 package 和 public subpath,装配:
- 单 owner
WorkerRemoteOfferFileJournal; - 单一
WorkerIngressHttpsClient与 keep-alive Agent; - Offer、Activation、Secret、Artifact upload、Completion 与 Lease-control adapters;
- Secret-before-Artifact materializer 与 Edge/Node 文件日志 policy;
- reviewed launcher 的 POSIX Executor、同一 receipt root 和
LocalProcessController; - completion-first control coordinator、Processor 与 headless lifecycle。
stack 不导入 cluster-control、PostgreSQL 或 legacy root,也不创建第二个 Agent、数据库连接、per-Run timer
或新 schema。credential provider 与 Session lifecycle 由更外层注入,因为证书签发、ql3w credential
恢复和 heartbeat 是独立部署 authority。
3. 返回 active 前必须完成有界 startup reconciliation
显式启动先取得 journal owner,再按配置 page 完整扫描历史 inbox。只有得到 reconciled 才创建一个
unref 全局 cadence;recovery_required 或超过由 journal hard cap 推导的最大 tick 数立即清理并拒绝
启动。启动扫描期间不 Pull 新任务。
定时 tick 继续使用 lifecycle 内部 coalescing;异常只进入低敏 diagnostic,不重叠创建另一轮。每轮先 completion/lease supervision,再在 Session available 时最多 Pull 并处理一个 Offer。
4. Draining 必须在释放 owner 前关闭 Pull 竞态
lifecycle 增加显式 beginDrain():
- 原子标记本地 draining;
- Abort 在途 Pull 并等待其结束;
- 替换请求 AbortController,但继续持有 journal owner;
- 后续 tick 只做既有执行监督,永不 Pull。
production stop 随后要求外层 Session.beginDrain() 已耐久关闭新工作,并复验当前 Session 不再
available。它在固定 1 秒–10 分钟总预算内重复 completion-first supervision 和最多 64 条一页的
journal 检查。只有全部记录进入 start_failure_acknowledged|completion_acknowledged,才按
cadence → lifecycle/owner → Offer transport → shared Agent 顺序关闭。
drain timeout 返回 drain_timed_out,保留 owner 并继续 draining cadence,允许稍后重试;durable
recovery_required 返回同名状态并保留 authority,禁止自动假装停止。Session drain 调用失败或不能
证明离开 available 也不得释放 owner。shutdown 等待 timer 保持 ref,防止进程在证明完成前自然退出;
只有常驻 cadence 使用 unref。
5. 本 ADR 不开放完整 Worker 发布
该入口完成的是 execution plane,而不是完整 Worker Profile。发布仍要求外层提供并验证:
- Worker Session register/heartbeat/drain/offline lifecycle;
- certificate enrollment/renewal 与
ql3wcredential provision/recovery; - deployment config、私有路径创建、告警和进程 shutdown budget;
- 本地 spool retention/delete 与对象存储 temporary lifecycle;
- 固定 Edge/Node x64/arm64、休眠、断网、断电和磁盘压力证据。
缺少任一项时部署入口继续默认关闭,不能把注入式测试 provider 当作产品配置。
被否决的替代方案
- 新建 worker-application package:没有新依赖或发布责任,违反 D-85。
- 每个 client 自建 Agent:重复 TLS credential authority、socket 与关闭状态。
- stop 直接释放 journal:运行进程和丢响应 Offer 会失去唯一监督 owner。
- Session drain 后仍允许 Pull:控制面和本地状态存在竞态,可能在 shutdown 接收新任务。
- drain timeout 后强制 close:把“预算耗尽”伪造成“副作用已停止”。
- 嵌套 journal/log/receipt root:扩大扫描、清理和路径 capability 的误伤范围。
- production 子入口从 package 根导出:让证书-only steady state eager-load 网络与文件锁依赖。
验收证据
- lifecycle 测试证明
beginDrainAbort Pull、draining tick 不再 Pull、最终 stop 前 owner 不释放。 - production 测试证明 disabled 零 option access、错误 Profile/重叠路径在文件系统前拒绝、空 execution plane 真实取得并释放 owner、Session drain 失败保持可重试。
- 独立进程证明 package 根不加载 journal/
proper-lockfile,只有/production加载执行图,且该入口 不加载任何ql3-cluster-*。 - Worker Runtime 98/98 通过,包含显式 enabled factory gate 与两个真实 TLS 1.3 mTLS 回环集成; 严格类型检查通过。
- workspace 仍为 23 个 importer、294 个 TypeScript source file,dependency/source audit
findings=[],没有新增依赖、schema、migration、数据库连接或 per-Run timer。 - 23 package clean build 与全量测试通过;六种本机制品再次通过真实 pack/offline install/import,
最大仍为 2,457,770 bytes、409 files、72 loaded modules,当前抽样 RSS delta 最大
12,566,528 bytes,且均不安装
@aws-sdk/*。