5.4 KiB
ADR-0230:Cluster Workflow 复用单一 Scheduler cadence
- 状态:Accepted
- 日期:2026-07-30
- 关联 RFC:QL-RFC-0001 D-18、D-19、D-71、D-104、D-118、D-207、D-212—D-214
- 关联 ADR:ADR-0105、ADR-0118、ADR-0125、ADR-0225—0229
背景
Cluster 已有完整的 PostgreSQL Workflow admission、frontier、generation-bound
Task Attempt admission、Remote Worker lease/activation/completion 和取消收敛
repository,也已有正式 cluster-control production process、唯一 cron scheduler
lifecycle 与独立的全局 cancellation lifecycle。
此前 frontier 和 Task Attempt admission 只由测试或外部 caller 直接驱动,生产 Scheduler cadence 没有调用它们。结果是合法 Workflow 可以停在 admitted 或 ready, 无法自动进入 Remote Worker dispatch。另建 Workflow package、timer、连接或进程会 扩大多副本资源成本,也会与 D-207 的 package 边界规则冲突。
决策
1. 在既有 Scheduler cadence 中组合,不新增 lifecycle
新增无 timer 的 ClusterWorkflowSchedulerCoordinator,由既有
ClusterSchedulerLifecycle 持有。每轮固定执行:
- 普通 cluster cron schedule;
- Workflow frontier;
- Workflow Task Attempt admission。
整体 Workflow cancellation 继续由既有单一全局 cancellation lifecycle 承载。它与 Scheduler 是不同的故障域,不为每个 Workflow 或 Step 创建 timer。
2. 所有扫描都必须有硬上限
frontier 与 Task Attempt admission 均固定为 32 条一页、每轮最多 4 页。单轮每阶段 最多观察 128 个候选,页内串行执行。repository 返回超过 page size 的结果或不前进的 continuation 时立即失败关闭。
同一 cadence 的重入请求复用当前 in-flight promise,不允许重叠扫描。任一阶段失败时 不伪造后续成功;durable 已提交事实由下一轮 exact replay 收敛。
3. 复用同一 Pool,但保持 repository 能力入口最小
组合根复用 readiness 后的同一个 runtime Pool,不创建额外 Pool 或长连接。Workflow
frontier 与 Task Attempt repository 仍只能从
@qinglong/cluster-postgres/plugin-package-workflow-frontier 和
@qinglong/cluster-postgres/plugin-package-workflow-task-attempt-admission 两个显式
子路径导入,不进入宽泛的 /runtime 聚合出口。
这使 production composition 能使用所需能力,同时保留入口级权限审计。常驻
ql3_worker_ingress 仍不获得 Run、Attempt、Lease mutation authority。
4. 启动与停止顺序不变
production bootstrap 仍按 readiness → recovery → application lifecycle → Scheduler/cancellation lifecycle → admission 开放。停止时先撤 admission,再 drain cancellation、Scheduler、application,最后关闭 Pool 和 listener。
Workflow coordinator 不拥有 listener、watcher、queue、per-Workflow state 或 stop hook,因此不会增加新的停止竞态。
不采用的方案
新建 cluster-workflow workspace package
拒绝。它没有独立部署、权限角色或重依赖边界,只是现有 control cadence 与两个显式 repository port 的组合。新包只会增加单文件 importer 和发布审计成本。
新建第二个 Workflow scheduler timer
拒绝。多副本下会增加唤醒、扫描与 shutdown 顺序;既有 Scheduler lifecycle 已提供
非重叠、诊断隔离、unref 与有界 drain。
把 repository 加入 cluster-postgres/runtime
拒绝。宽聚合出口会让不需要 Workflow admission authority 的 runtime importer 也能 取得这些实现。显式 subpath 能在不拆 package 的情况下保持能力隔离。
让 Worker ingress 直接推进 frontier 或创建 Attempt
拒绝。Worker ingress 只负责认证后的内部 transport,不应获得控制面 Run mutation 权限;frontier 与 admission 是 runtime-role 的数据库权威。
当前验证
- coordinator 行为测试覆盖两页 frontier/Task admission、created/existing 计数、 重入 coalescing、停滞 continuation 和非法上限,3/3 通过;
- production bootstrap 测试启动真实既有 250 ms Scheduler lifecycle,观察到 schedule → frontier → Task Attempt 三段扫描,并完成 connection release 与 stop-and-drain;
cluster-control全量 145 项为 143 pass、2 条外部服务条件 skip、0 fail;cluster-postgres全量 238 项为 237 pass、1 条真库条件 skip、0 fail;显式 repository subpath 隔离测试通过;- Linux arm64 Node 24.18.0
cluster-control-ci门在 512 MiB、2 CPU、256 PID、 零 swap、非 root、只读 root/workspace 下通过;模块加载 RSS 增量23867392bytes,memory/OOM 事件增量为零; QL3_HA_SKIP_IMAGE_PULL=true的 PostgreSQL 18.4 arm64 HA 门通过remote_apply、timeline 1→2、旧主 fencing、两个 fresh control replica、pg_rewind只读同步重入,以及 Workflow admission/frontier/Task Attempt/cancellation 的复制与晋升后重放;总gates.passed=true;- workspace 仍为 20 个 package,没有新增 migration、表、生产依赖、timer、 watcher、listener、Pool 或数据库连接。
尚未关闭
- Worker ingress production process/deployment 与内部 runtime capability port 已由 ADR-0231 关闭;
- Remote Worker expiry/retry 的生产 lifecycle 与部署启动装配;
- Cluster Secret provider、真实 Kubernetes 多 Pod 分区、operator/proxy、STONITH 与容量证据。