服务生命周期

启动流程(service/ops.ts::start)

  1. 加载存储:读取 jobs.json + jobs-state.json,合并状态
  2. 处理中断运行:检查 runningAtMs 字段,标记为失败(“cron: job interrupted by gateway restart”),增加 consecutiveErrors
  3. 追赶错过任务:识别 nextRunAtMs < nowMs 的任务,按优先级排序,限制并发(默认 5 个),错开执行(默认 5s 间隔)
  4. 启动定时器:计算下一个到期任务,设置定时器

定时器循环(service/timer.ts::armTimer)

armTimer()
  → 找到最近到期的任务
  → setTimeout(min(nextRunAtMs - now, 60s))
  → 触发时:executeJobCoreWithTimeout()
  → 完成后:重新 armTimer()

最大定时器延迟 60s,防止长时间睡眠导致任务延迟。


执行引擎(service/timer.ts)

executeJobCoreWithTimeout

带三层超时的任务执行:

阶段超时说明
Setup60s(CRON_AGENT_SETUP_WATCHDOG_MS)Agent 初始化
Pre-execution60s(CRON_AGENT_PRE_EXECUTION_WATCHDOG_MS)执行前准备
Execution5m(DEFAULT_JOB_TIMEOUT_MS)实际执行
Cleanup guard20s(CRON_TIMEOUT_CLEANUP_GUARD_MS)超时后清理

支持 AbortSignal 中断。

applyJobResult

执行完成后:

  1. 更新 lastRunStatus、lastRunAtMs、lastDurationMs
  2. 更新诊断信息(lastDiagnostics)
  3. 处理错误退避(更新 nextRunAtMs)
  4. 触发失败告警(满足阈值时)
  5. 处理交付(announce/webhook)
  6. 清理会话(deleteAfterRun)

任务管理(service/jobs.ts)

关键函数

createJob(input)                    // 创建任务,生成 ID 和初始状态
applyJobPatch(job, patch)           // 应用补丁更新
computeJobNextRunAtMs(job, nowMs)   // 计算下次运行时间(含错开)
isJobDue(job, nowMs)                // 检查是否到期
isJobEnabled(job)                   // 检查是否启用
recomputeNextRuns(state)            // 重新计算所有任务的下次运行时间

任务到期判断

isJobDue(job, nowMs):
  job.enabled
  && job.state.nextRunAtMs !== undefined
  && job.state.nextRunAtMs <= nowMs
  && job.state.runningAtMs === undefined  // 未在运行

并发控制(service/locked.ts)

使用 locked() 函数保护关键操作,防止并发修改存储:

// 所有写操作都通过 locked() 串行化
await locked(state, async () => {
  // 修改 state.jobs
  // 保存到磁盘
})

读操作使用 skipRecompute 标志避免副作用。


持久化存储(service/store.ts)

存储架构

文件内容写入频率
jobs.json任务配置(不含运行时状态)任务增删改时
jobs-state.json运行时状态每次执行后

分离存储的优势:

  • 减少写入冲突(配置和状态独立更新)
  • 配置文件更稳定,便于备份和版本控制
  • 状态文件高频写入不影响配置

原子写入

write(tmpFile)
→ rename(tmpFile, targetFile)  // 原子操作

防止写入中途崩溃导致文件损坏。

迁移逻辑

检测 jobs.json 中的内联状态(旧格式),自动迁移到 jobs-state.json:

  1. 读取 jobs.json,检测是否有内联 state 字段
  2. 提取状态到 jobs-state.json
  3. 后续只使用分离存储

缓存优化

序列化缓存:避免重复 JSON.stringify,仅在数据变化时重新序列化。


会话清理(session-reaper.ts)

清理策略

  • 保留期:24 小时(DEFAULT_RETENTION_MS)
  • 最小扫描间隔:5 分钟(MIN_SWEEP_INTERVAL_MS)
  • 清理目标:cron:<jobId>:run:<uuid> 格式的临时会话
  • 保留:基础会话 cron:<jobId>

清理流程

  1. 加载会话存储
  2. 识别过期的 cron 运行会话(createdAt < now - 24h)
  3. 删除会话记录
  4. 归档和清理转录文件

诊断与可观测性

CronRunDiagnostics

type CronRunDiagnostics = {
  entries: Array<{
    source: string
    severity: "info" | "warn" | "error"
    message: string
  }>
  summary?: string
}

限制:最多 10 条(MAX_DIAGNOSTIC_ENTRIES),每条最多 1000 字符,摘要最多 2000 字符。

active-jobs.ts

追踪当前正在运行的任务,用于:

  • 防止重复执行
  • 提供运行状态查询
  • 超时检测

相关页面