Files
qinglong/docs/adr/ADR-0040-profile-activation-and-edge-import-isolation.md

8.3 KiB
Raw Permalink Blame History

ADR-0040Profile 激活顺序与 Edge 导入隔离

  • 状态:Proposed
  • 日期:2026-07-19
  • 关联:QL-RFC-0001、ADR-0036、ADR-0038、ADR-0039

上下文

独立 migration stream、schema readiness 和独立 cluster package 仍不能单独证明部署安全。两个装配错误会直接破坏 Profile 边界:

  1. cluster-control 在 readiness 前创建 Repository、启动恢复 timer 或注册 admission
  2. edge 虽然不执行 cluster 路径,但根 importer 或启动导入闭包仍安装、解析或加载 PostgreSQL/Drizzle bundle。

前者会让不兼容 schema 或过度授权 role 在 fail-closed 之前产生业务副作用;后者会让低性能路由设备承担无用的安装体积、native/供应链风险和启动内存。仅使用动态 import() 只能推迟模块求值,不能从 edge lockfile、镜像或安装树中移除依赖。

决策

1. Cluster-control 只有一个有序激活门

cluster-control 的启动顺序固定为:

  1. 明确启用且 Profile 精确为 cluster-control
  2. 执行 ADR-0039 的只读 readiness
  3. readiness 成功后才创建业务 Repository 和 runtime stack
  4. 先执行有界 startup recovery,并证明 safe=trueremaining=0failed=0
  5. 再启动 claim、recovery 等 lifecycle
  6. 最后安装 admission,使 API、scheduler 或其他 producer 可以提交新工作。

禁用时不得查询数据库、创建 stack、启动 timer 或安装 admission。错误 Profile 必须在数据库探测前拒绝。readiness 成功不是自动接管信号,只有完整顺序全部完成后状态才是 active。

2. 失败与停止采用反向撤销

stack 创建后的任一步失败都必须:

  1. 撤销已安装 admission
  2. 有界停止已创建的 lifecycle/stack
  3. 写低敏失败审计;
  4. 保留原始激活错误,不用 cleanup 或 audit 错误覆盖根因。

正常停止同样先关闭 admission,再停止 stack。停止必须幂等;重复调用共享同一个停止结果。installAdmission() 必须自身具备原子安装或失败回滚语义,不能抛错后留下不可撤销的半注册入口。

数据库资源由外层 lazy bootstrap 管理:只有已启用且 Profile 为 cluster-control 时才调用 openDatabase()。readiness、assembly 或 activation 任一步失败,都在保留原错误后关闭数据库;active shutdown 必须先完成 admission/stack stop,再关闭 Pool。Pool close 失败不得跳过 stack stop,重复 stop/close 共享同一 Promise。

3. Readiness evidence 只授权本次装配

Repository factory 接收本次 readiness evidence,用于绑定 contract name/version、PostgreSQL major 和 migration IDs。evidence 不是永久缓存,也不能跨数据库、凭据或进程复用。连接重建、role 变化或数据库 failover 后,实际 production adapter 必须按运维策略重新证明 readiness;不得用旧 evidence 绕过检查。

4. Edge 根 importer 不得声明 cluster 依赖

edge/standalone 共用的根 package.json 在以下区段都不得声明 pgpg-nativedrizzle-ormdrizzle-kit@types/pg 或 cluster package

  • dependencies
  • devDependencies
  • optionalDependencies
  • peerDependencies

PostgreSQL driver、Drizzle schema/tooling 和 cluster-control assembly 只属于 ADR-0038 定义的独立 workspace/package 与 profile-specific 产物。根包的 dynamic import、optional dependency 或 install-time feature detection 均不能替代该边界。

5. Edge CI 同时审计 manifest 与代表性导入闭包

CI 必须在 edge Profile 下导入代表性的:

  • Profile/resource policy
  • standalone/manual Primary 的轻量 bootstrap
  • headless Worker application runtime。

新增模块闭包不得包含 PostgreSQL migration 实现、pgpg-native、Drizzle 或 cluster package。该门禁用于尽早发现静态 value import、barrel re-export 和 bootstrap 回归;它不等同于最终产物证明。独立 importer 落地后,还必须对 edge package tarball/镜像的 lockfile、安装树、SBOM、压缩体积与冷启动 RSS 做闭环审计。

当前孵化状态

next 已实现默认不可达的 activateClusterControlRuntime()

  • 错误 Profile 在 readiness 前拒绝;
  • readiness 失败前不会调用业务 factory;
  • 成功路径固定 readiness → assembly → recovery → lifecycle → admission
  • unsafe/incomplete recovery 会停止 stack,且不会开放 admission
  • admission-first shutdown 和重复 stop 已有契约测试。

独立 @qinglong/cluster-controlbootstrapClusterControlRuntime() 已把 lazy database、ADR-0039 readiness、ADR-0041 Run Repository 与激活门串联:disabled/错误 Profile 零打开,readiness 通过后才创建真实 PostgresRunRepository 和业务 stack,失败自动关闭,正常关闭顺序固定为 admission → stack → Pool。真实 openDatabase()@qinglong/cluster-postgres 提供,组合根不读取 legacy back/**

同时已加入 audit:edge-imports:ql3

  • 检查根 manifest 的四类依赖区段;
  • 导入三个代表性 edge/standalone/worker 入口;
  • 检查新增 require.cache 闭包是否触达 cluster bundle
  • 纳入 Node 20/24、x64/arm64 的 QL3 CI 矩阵。

ADR-0045 已在独立 package 增加有界 HTTP application host、/livez、activation-backed /readyz 和 fail-closed /api/v3 admission,并把 disposer 升级为可异步 drain:停止先拒绝新请求、向在途 handler 传播 Abort,真实 handler 未结束时不能继续宣称排空。公开 config 入口先解析 Profile/enable,禁用时不读取 PostgreSQL Secretapplication/config 导入闭包不含 migration DDL 或 legacy 根。

当前仍没有认证/Policy-fenced 业务 router、完整 production recovery/lifecycle stack、独立 cluster 镜像/SBOM 或 failover 演练。现有 host 是 executable application boundary,但尚不是完整可部署控制面,不能据此开启匿名或生产 cluster-control API。

影响

正面影响:

  • schema/role 不兼容时不会提前构造可产生业务副作用的对象;
  • startup recovery 与新流量之间存在可测试的硬顺序;
  • edge 对 cluster 依赖的隔离从文档约束变为 CI 约束;
  • 正常停止、部分启动失败和重复停止具有同一 ownership 语义。

代价与风险:

  • 新 lifecycle 必须接入统一 stack,不能自行在模块加载时启动;
  • readiness catalog 扫描会增加一次启动成本,需要 timeout、节流与重新证明策略;
  • 代表性 CommonJS 导入审计无法发现所有 bundler、ESM tree 或容器打包错误;
  • 独立 workspace 落地后,CI 还需同时证明 cluster 产物包含依赖、edge 产物排除依赖。

未选择的方案

  1. Repository 创建后再做 readinessconstructor、pool hook 或 timer 已可能产生副作用,拒绝。
  2. recovery 与 admission 并行启动:新工作会扩大未收敛状态,拒绝。
  3. readiness 失败后回退 SQLite:形成双事实源并违反 Profile 边界,拒绝。
  4. 只依靠 dynamic import:不能降低安装体积或供应链暴露,拒绝。
  5. 只检查根 package.json:无法发现 barrel/value import 回归,拒绝。
  6. 只检查运行时 require cache:无法证明未加载包不存在于 edge 产物,拒绝。

验证要求

  • disabled 路径无数据库、factory、timer 和 admission 行为;
  • disabled/错误 Profile 不调用 openDatabase(),也不触发 driver dynamic import
  • cluster-control Profile 在数据库探测前失败;
  • readiness 失败保持原错误,且业务 factory 调用次数为零;
  • recovery unsafe、remaining 或 failed 非零时不得启动 lifecycle/admission
  • lifecycle 启动失败或 admission 安装失败会反向清理;
  • active stop 先同步切换 not-ready/拒绝新 admission,再异步等待在途 handler 结束,之后才停止 stackdrain timeout 显式失败且重复 stop 幂等;
  • readiness/assembly/activation 失败关闭数据库;active stop 在 stack 后关闭 Poolclose 失败不覆盖更早的激活/停止错误;
  • edge 根 manifest 出现任一禁止依赖时 CI 失败;
  • 代表性导入闭包触达 PostgreSQL/Drizzle/cluster 路径时 CI 失败;
  • 独立 package 落地后,edge tarball/镜像的 lockfile、SBOM 和安装树不包含 cluster bundle
  • PostgreSQL 16/18 role integration 通过后,才允许真实 cluster factory 接入激活门。