本页内容
本页内容
开始使用
工人拥有的 cron
工人拥有的cron
分阶段重新设计。 将调度和保留决策放在一个 cron 工作线程中。提交运行请求,释放编写器,然后启动任务。目标约为 26,000 条生产线,低于 43,960:约 18,000 条生产线 (41%) 被移除,而不将相同的机器移至其他地方。保持平常的可靠性;接受在狭窄的崩溃窗口中错过的事件以及偶尔的发射赛车禁用。不要构建分布式调度。
当前设计和合同
源基线:7517981b5e670b88ebf61b4d1be18a194a46a043。物理 TypeScript 线,包括 comments/blanks,但不包括测试、模拟、夹具、harness和套件助手:src/cron 具有 262 生产文件/43,960 线 和 275 test/support 文件/79,391 行。其生产目录为顶级10,485,isolated-agent/ 9,196,service/ 16,272,和store/ 8,007 线。在职责共享文件的情况下,计数低于重叠;它们是所有权地图,而不是附加删除记录。引文使用此基线;除非另有标记,所有缩短的源路径均以 src/cron/ 开头。
写入器停顿是结构性的:store/run-admission.worker.ts:68 在第 76 行调用主机准备之前打开事务。工作者将消息发布到主机并在 Atomics.wait (src/infra/sqlite-worker-operation-admission.ts:670) 中进行阻止。主机准备和第二次提交检查在 service/runtime-mutation.ts:130 中运行。 #168291 修复为每次交易所设置一秒的截止日期(store/runtime-mutation.worker.ts:15、:38、:72);它限制了依赖关系而不是删除它。调度本身没有固有的主线程需求:网关依赖项作为 src/gateway/server-cron.ts:502 处的回调输入。
| 责任 | 当前所有者、证据和实物 LOC |
|---|---|
| 安排数学、身份、应有的决定、pacing/backoff | schedule.ts:26 (350), schedule-identity.ts:1 (137), service/jobs-scheduling.ts:339 (746), service/timer-runnable.ts:65 (204). |
| 计时器、容量、预订、追赶 | service/timer-scheduler.ts:119 (629)、service/timer-catchup.ts:177 (385)、service/scheduler-mutations.ts:156 (220)、store/startup-plan.worker.ts:29 (133); admission/reservation 模块如下。 |
| 定义,CRUD、config/state 读取 | normalize.ts:1 (518), service/ops-mutations.ts:1 (652), service/ops-read.ts:1 (333), service/jobs.ts:1 (722), service/store.ts:1 (534), store/row-codec.ts:1 (767). |
| 运行admission/capacity并激活 | service/run-admission.ts:1 (664), service/run-admission-mutation.ts:1 (434), service/run-admission-capacity.ts:1 (121), store/run-admission.worker.ts:62 (466), store/scheduler-reservation.worker.ts:93 (200). |
| 收据、授权、结算 | store/receipt-authority-owner.ts:101 和五个兄弟模块 (838); store/run-receipt-store.ts:51 (586)、store/run-receipt-settlement.ts:36 (225)、service/run-receipts.ts:1 (307); host/worker 突变方案 (489)。 |
| 结果、跳过的尝试、重新启动恢复 | service/timer-outcomes.ts:215 (745)、service/run-owner.ts:99 (119)、service/run-history.ts:1 (205);恢复系列包括 service/startup-run-repair.ts:57 和 store/run-recovery.kernel.ts:111 (1,050)。 |
| 手册 run/abandon 和生命周期 | service/ops-run.ts:243 (453)、service/ops-run-preparation.ts:239 (631)、service/ops-lifecycle.ts:268 (393);放弃是 store/scheduler-reservation.worker.ts:167 处的保留清理,而不是公共 cron 操作。 |
| 任务执行和session/model设置 | isolated-agent/run.ts:78 及其目录(53 个文件/9,196); command/script/trigger/heartbeat 适配器,包括 command-runner.ts:1、trigger-script.ts:1、heartbeat-monitor.ts:72(8 个文件/1,526)。 |
| 心跳、看门狗、timeout/cleanup | service/timer-execution.ts:157、service/timer-job-runner.ts:327、service/agent-watchdog.ts:51、timeout/interruption 帮助程序(7 个文件/1,648)。 |
| 通知、路由、交付 | 顶级交付助手和失败文本(10 个文件/1,176)、service/failure-alerts.ts:184 和通知系列(5 个文件/904);实际交付在isolated-agent/delivery-dispatch.ts:189,计入上面的isolated-agent。 |
| 权限、暂存、保留 | scheduled-tool-policy.ts:1 (258), store/runtime-authority-store.ts:57 (264); scratch-store.ts:1 (161), scratch-write.kernel.ts:1 (165); session-reaper.ts:1 (273), session-registry-maintenance.ts:1 (186). |
| Gateway/config 重新加载和事件源 | 外部 cron:src/gateway/server-cron*.ts(9 个文件/2,636),在 src/gateway/server-reload-hot.ts:343 处重建配置; exit/stream观察者家族(8个文件/2,418),包括src/gateway/cron-exit-watchers.ts:80、src/gateway/cron-stream-watchers.ts:50。 |
| RPC、CLI、工具 | 外部 cron:src/gateway/server-methods/cron*.ts(14 个文件/3,039;src/gateway/server-methods/cron.ts:212 处的操作); src/cli/cron-cli.ts 和目录 (15 / 3,036); src/agents/tools/cron-tool*.ts(9 / 2,723;src/agents/tools/cron-tool.ts:362 处的操作)。 |
保留可观察的行为
这些是现有测试锚点的验收标准,而不是保留每个测试的私有模拟或回调的要求。
| 契约 | 当前的实现和固定它的测试 |
|---|---|
| 每项工作赶上一次逾期发生的事件,最早的事件在前; agent/heartbeat 工作默认五立即、五秒溢出惊人、两分钟启动延迟。在重新启动时保留 deferred/paced 的最后期限,并避免出现预编辑失误。 | service/timer-catchup.ts:210, service/timer-execution-timeout.ts:36, service/timer-runnable.ts:90; service.startup-overflow-clobber.test.ts:110, service.restart-catchup-pacing.test.ts:168, :237. |
| 正常运行时不会出现同一事件重复执行; timezone/DST、最小重发间隔和配置的 retry/backoff 仍然有效。今天,它使用 markers/receipts,而不是确定性插槽 ID。 | service/timer-runnable.ts:77、:131、store/run-receipt-store.ts:332; cron-state-contracts.e2e.test.ts:205、schedule.test.ts:306、service/timer.regression.test.ts:159、:197。重试期限源自 service/timer-outcomes.ts:317、:394 的完成。 |
| 实际 rejected/ownerless/queued-but-not-launched 尝试记录跳过加上原因;一份失败的工作不会妨碍兄弟姐妹。不要制造安静触发评估或每个错过的滴答声的历史记录。 | service/run-owner.ts:99、service/ops-run.ts:309; service/run-admission.ownerless.test.ts:107、:153。 skipMissedJobs 仅推进重复性计划和日志(store/startup-plan.worker.ts:109;service.startup-overflow-clobber.test.ts:56); fire:false 很安静 (service/timer-trigger.ts:54)。 |
| 交付模式、recipient/thread 路由、suppression/unknown 结果、故障 threshold/cooldown、跳过的警报和所有者对话修复仍然存在。一次性删除取决于整个运行的成功完成,包括交付。 | delivery-plan.ts:40、service/failure-alerts.ts:184、:374、service/timer-outcomes.ts:233; cron-delivery-outcomes.e2e.test.ts:213、:411、:536、:750;外部 cron:src/gateway/server.cron.test.ts:1145。 |
| 保持超时 defaults/overrides、中止和有界清理、设置停止检测以及执行预算之外的容量等待。心跳安静hours/coalescing/busy规则和实际结果保留在心跳所有者手中。 | service/timeout-policy.ts:10, service/agent-watchdog.ts:86, service/timer-job-runner.ts:327; service/timer.timeout-watchdog.test.ts:288, :333, service/command-timeout.test.ts:95, service.heartbeat-payload.test.ts:107, :179. |
| 手动强制可以运行禁用的作业并保留其 natural/paced 插槽;到期模式尊重资格。公认的运行具有持久的会计。删除请求取消并可以报告清理待处理。 | service/ops-run-preparation.ts:239、service/ops-run.ts:113、:243; service/ops.regression.test.ts:329、:422、service/manual-ack-durability.test.ts:28;外部 cron:src/agents/tools/cron-tool.output-contract.test.ts:91。 |
| 坚持jobs/history,恢复已完成的结果,不再执行,记录中断,恢复以后的调度;保留公共 RPC 信封、精确运行查找、CLI 退出状态、过滤器和工具默认值。 | service/startup-run-repair.ts:121、service/ops-lifecycle.ts:268; service.restart-catchup.test.ts:168、:202;外部 cron:src/gateway/server.cron.test.ts:781、:823、src/cli/cron-cli.test.ts:267、src/agents/tools/cron-tool.output-contract.test.ts:161、src/gateway/server-methods/cron.runs.test.ts:574。 |
收据托管、撤销生成、随机数结算和同步 prepare/commit 回调是机械,而不是产品合约。一种有意的恢复放松需要是可见的:今天,当已知交付尚未开始时,可以重放中断的一次性(store/run-recovery.kernel.ts:152)。这并不能证明执行从未开始。新设计将不确定的活动运行记录为中断,而不是重放其有效负载。这是公认的崩溃损失权衡,而不是声称崩溃语义不变。
建议所有权和流量
Cron 工作者: 一位长期参与者拥有 job/runtime 状态、调度计算、计时器、队列、并发限制和终端记帐。使用现有的网关 SQLite 编写器基础设施来实现连接所有权、FIFO、初始完整性准入和缓存失效; cron 域决策和同步事务在工作端运行。没有第二个数据库所有者。现有的事务助手本身是同步的(src/state/openclaw-state-db.ts:485)。
Host: 验证 RPC/tool 请求,在 startup/change 上发布标准化配置快照 {version, config},并公开提交的 job/status 视图。版本仅订购快照;它不是撤销一代。作业 CRUD 和声明性协调通过 cron 工作线程。工作人员在命令之间安装快照,从不在事务期间提取主机策略。
Launcher/run 执行: 消耗已提交的请求,执行一项廉价的最终实时 Gateway/launch-eligibility 检查,然后分派到现有的 task/session 执行程序或运行工作程序。现有的任务、工具许可、心跳和传输所有者仍然执行自己的合同。他们没有获得 cron 接收权限。启动器处理效果;它永远不会决定时间表。
sequenceDiagram
participant H as Host
participant C as Cron worker
participant L as Launcher
participant R as Run executor
H->>C: Config snapshot or job command
C->>C: Due decision + request insert + next deadline; commit
C->>C: Capacity available: queued to active; commit
C->>L: Launch committed run ID and payload
L->>L: Live Gateway and eligible job?
L->>R: Start task
R->>R: Execute and finish primary delivery
R-->>C: Progress and combined terminal outcome
C->>C: Record outcome + history + next deadline; commit
C-->>H: Status and post-run notifications没有事务等待主机回复、任务、通知、模块导入或网络调用。容量等待使行排队并且写入器空闲。工作人员按 due/arrival 顺序选择请求,并具有稳定的作业顺序平局。
行和幂等性
无架构更改;没有请求 DDL。 重用 cron_jobs 进行定义和 state_json;重用 cron_run_receipts 作为运行请求分类帐。现有主键为 receipt_id,每个 store/job 有一张有效收据(src/state/openclaw-state-schema.sql:1426、:1453、:1475)。保留 task_runs 历史记录 (store/run-history.kernel.ts:58)、cron_job_scratch、权限来源及其保留。
“请求”是指现有收据 status='running' 加上 queuedAtMs; “有效”是指 runningAtMs 和 runningReceiptId。两者均已存在(store/run-admission.worker.ts:145、:203);请勿添加 queued SQL 状态。保留 decoding/diagnostics 所需的收据元数据,而不是作为权限令牌。
对于新计划的工作,运行 ID 是 [storeKey, jobId, scheduledSlotMs] 的带标记、防碰撞编码,使用持久的截止期限,而不是启动器唤醒的时间。 cron 可以解码这个定时键来重启;公众和历史读者认为 ID 是不透明的。插入它并在一笔交易中推进时间表;已请求重复密钥。 Backoff有意创造一个新的未来deadline/key;仅延迟现有请求会保留其 ID。手动力使用独特的请求ID并保留自然槽。流批次和退出事件通过同一队列使用不同的事件 ID。它们的瞬态 payload/force 上下文与启动请求保持一致;在崩溃中丢失它意味着记录跳过,而不是从当前配置重建事件。如今,这些详细信息是操作输入,而不是收据列 (store/runtime-worker.types.ts:139)。一旦该行成为终端,运行 ID 就会忽略重复的终端消息。
保留有界收据历史记录:目前,每个作业保留 64 个终端收据(store/run-receipt-store.ts:79、:246)。幂等性还取决于单调消耗预定的截止日期,而不是永远保留每个历史 ID。
常见故障,廉价处理
| 情况 | 建议的行为 |
|---|---|
| 请求提交后、分派前崩溃 | 使用可恢复密钥恢复定时排队请求;跳过暂时上下文丢失的请求。在发出启动之前提交排队至活动状态;永远不会自动重新发送活动请求。该提交和实际启动之间的崩溃可能会错过一次发生。 |
| 崩溃 during/after 执行 | 恢复已记录的终端任务结果;否则记录中断并继续将来的调度。不要重放不确定的有效负载或交付。尽最大努力记录结果;失败的通知无法撤消已提交的运行。 |
| 陈旧的网关 | 启动器在调度之前立即检查现有的实时网关所有者;如果退休的话,有理由跳过。关闭会停止新的调度并加入拥有的工作。没有新的围栏、租约或同级进程仲裁。 |
| Disable/remove/edit | 工作人员取消 disable/remove 上排队的请求或安排替换,并记录原因。有效负载编辑在出队时使用当前定义。活动任务保留其启动快照;完成记录其结果,同时保留任何较新的 schedule/enablement 编辑。接受一次发射比赛并禁用。 |
| 手动强制或消耗退出事件 | 将现有的例外保留为“启用”:显式强制可以运行禁用的作业;接受的 on-exit 有效负载在手表自动禁用 (store/run-admission.worker.ts:134) 后仍然有效。操作员禁用仍然会取消排队的工作。没有新用户选项。 |
| 飞行途中配置更改 | 在命令之间安装最新推送的快照。已经启动的任务保留其快照;稍后开始使用当前配置。删除旧的 cron-wide revoke/re-admit 循环。 |
| 时钟 jumps/sleep | 使用挂钟来确定到期时间和单调运行截止日期。在向前跳跃时,每个工作应用一次有界追赶;在向后跳跃时,保留持续的下一个截止日期而不是倒带它。保持有界定时器重新检查和重发保护,无需时钟修复协议。 |
运行执行器拥有其 AbortController、setup/execution 截止日期和有界清理。 Progress/heartbeat/result 消息异步到达;他们从不扩展数据库事务。保留现有的心跳唤醒适配器并从执行时间中排除通道等待。主机停顿可能会延迟调度或通知,但不能让 cron 事务在该主机上等待。主要交付保留在运行执行器的现有适配器中;其结果是一次性删除之前完成的一部分。运行后通知保留其 route/threshold 行为并异步报告,无需提交防护。不需要新的待交付状态机。
删除并合并
删除 store/receipt-authority-* (838 LOC)、host/worker 运行时突变握手和准备类型 (489) 以及 #168291 中的 cron 截止日期选择加入。将 admission/reservation 编排 (1,885)、收据 settlement/binding/观察管道 (717)、收据持久性助手 (933) 和恢复编排 (1,050) 替换为上面的队列和短工作人员本地转换。这些不重叠的组总共5,912条当前线;有用的存储和结果恢复得以保留,而不是这些层。
还将主机 timer/catch-up/manual-finalization 编排折叠到该工作线程中,并删除 store/dispatch.worker.ts:140、:175、:231、:244、store/save.worker.ts:26 和 store/load.worker.ts:20 中的 cron 直接握手。仅删除 runtime-mutation.worker.ts 会在其他地方留下相同的依赖关系。一起删除过时的注册、私有类型、模拟和协议测试;保留新主人的行为证据。不要删除其他子系统使用的通用SQLite准入机制。
大小预算:将 service/ + store/ 从 24,279 减少到大约 6,300 行,包括替换 worker/launcher 代码。保留其他 19,681 cron 行将目标置于 26,000 附近。这是重新设计预算,而不是经过验证的补丁大小。 Payload/delivery 广度是该估计的最大风险;不要通过将 cron 协调移至网关文件来隐藏增长。
保留权限来源: store/runtime-authority-store.ts:57和:97维护预定工具权限和恢复,与收据托管不同。保留该所有者和 cron_job_runtime_authorities (src/state/openclaw-state-schema.sql:1490)。不建议重新设计权限或删除表。
现有数据和阶段性 PR
切勿重写现有收据 ID 或历史连接。使用一种规范的行编解码器;新的定时密钥标签只是一个编码鉴别器,而不是权威。没有可恢复定时密钥的排队行(包括旧的随机 ID)将被跳过,其标记会自动清除。保留 request_run_id 进行确认运行查找。当追赶找到当前调度时隙的终端收据时,通过重新计算请求事务中的下一个截止时间来消耗该时隙;记录的跳过保留在历史记录中。人力无法消耗未来的自然槽。首先恢复最终的历史记录,然后在接受新工作之前恢复 queued/active 行。无需 Doctor 格式迁移:Doctor 已将运行标记恢复留给调度程序启动 (src/commands/doctor/cron/index.ts:52);其现有的遗留型移民仍然是唯一的正常化路径。使用匹配的二进制文件保留备份并测试回滚;没有运行时遗留解析器或新的 sidecar。
每个 PR 都切断其全部责任并删除其被取代的路径。每个都必须通过受影响的 owner/sibling 测试和 check:changed;更改了测试报告测试时间与 transform/setup 时间分开。列出的崩溃语义是有意接受的更改;上述所有其他可观察合约均保持绿色。
- 从 cron 事务中删除主机等待。 在写入之前推送 config/policy 输入;将需要当前行的决策移至现有的工作内核中。切断所有 cron prepare/commit 握手,包括 save/history/init 路径和截止日期选择。证明:worker提交后故意拖延host;不相关的状态编写器和更新切换仍必须完成。练习真正的突变入口点,而不是仅帮助程序的模拟;跟踪所有 cron 准入调用以证明事务中没有任何调用。
- 删除收据托管,然后削减调度并一起启动。 将此步骤拆分为两个有界 PR。 2a 删除了收据保管及其消费者,而主机仍然是唯一的调度程序。网关、任务、工具、权限和审批行所有者保留其现有检查;撤销可能会影响已检查的启动。收据存储、调度和结果恢复保持不变。 2b 启动工作参与者,停用主机 timers/reservations,使用幂等请求行,并使用 2a 中的免托管接口将 timer/manual/event 源连接到单个启动器。在队列边界拆分 2b:2b-i 为工作线程提供幂等接收请求、容量和激活,每个源都有一个主机启动器;主机仍然是 schedule/wake 制作人。 2b-ii 将计划计算、计时器、追赶和配置快照移动到长期工作线程中。每一步都会删除其取代的所有者。在 2b-i 中,排队标记保留自然截止日期,直到现有结果所有者提前。调度参与者通过现有的状态租约保留共享工作线程。它的定时器发送唤醒通知;有限承认的命令读取当前行并做出适当的决定,使写入器在唤醒之间保持可用。关闭会加入租约,而数据库停用会关闭相同的资源。 CRUD 准备和提交使用一个工作者突变实现; commit 将意图应用于当前行,保留干预的运行时更新。配置快照安装在命令之间。排队的收据会消耗资格,直到完成拥有重试、节奏和下一个自然截止日期。证明:重复槽、容量队列、disable/edit、手动强制、消耗退出事件、流批处理、追赶、重试和时钟跳跃流;在 request/active 边界崩溃并恢复复制的旧格式数据库。
- 折叠 result/recovery 管道并完成删除。 一个终端结果处理程序记录执行情况以及初次交付结果,更新 job/history,并发出运行后通知;退休随机数结算、国外收据监控和冗余生命周期包装器。证明:heartbeat/setup/execution 超时、清理、跳过历史、真实的 webhook/notification 结果和上面引用的 RPC/CLI/tool 合约;已发布更新程序×候选程序,具有隔离状态、重新启动和备份回滚。验证没有 cron commitGuard/revocation 路径保留,并分别报告净生产/测试 LOC。保留其他所有者的实时 effect/permission 支票。
设计通道证明仅是源头检查和文档验证。此通道中没有 production/test 更改、运行时实验、提交或 PR。
