Convex Cron Jobs 实战指南:用 `cronJobs()` 定时清理数据(附示例应用源码解析)
2026/9/23 18:45:06 网站建设 项目流程

Convex Cron Jobs 实战指南:用cronJobs()定时清理数据(附示例应用源码解析)

【免费下载链接】convex-backendThe open-source reactive database for app developers项目地址: https://gitcode.com/gh_mirrors/co/convex-backend

本指南围绕开源仓库 convex-backend 中的npm-packages/demos/cron-jobs示例应用展开,讲解如何在 Convex 中使用内置的 cron 定时任务能力:每间隔一段时间自动执行 mutation 或 action,例如每分钟清空一次消息表。读完本文,你将掌握cronJobs()的完整用法、五种调度方式(interval/hourly/daily/weekly/monthly/cron字符串)的参数规则,并理解从客户端配置到服务端执行的完整链路。

示例应用概览:每 60 秒清空一次消息表

npm-packages/demos/cron-jobs是一个基于 Vite + React 的最小示例,它构建在仓库中的 Convex tutorial 教程示例 之上,只多了一样东西——cron 定时任务。它的行为非常直观:

  • 用户在聊天界面发送消息;
  • 消息写入messages表;
  • 一个每 1 分钟触发一次的 cron 任务将messages清空

前端界面上也明确写出了提示:"Messages will be cleared every minute"(消息每分钟会被清空),见 App.tsx。也就是说,这是一个"用完即清"的演示:既能验证定时任务的触发,又能避免演示数据无限堆积。

快速运行

示例应用使用 pnpm workspace 管理依赖(convexworkspace:*形式引入,见 package.json)。在npm-packages/demos/cron-jobs目录下执行:

npm install npm run dev

其中dev脚本实际执行的是:

convex dev --start 'vite --open'

(见 package.json)。这条命令会同时做两件事:

  1. 启动 Convex 本地开发后端,监听convex/目录下的函数文件,热重载部署;
  2. 启动 Vite 开发服务器并自动打开浏览器。

convex/crons.ts被部署后,cron 调度器即开始生效——按文档说明,interval 类任务从首次部署到 Convex 时开始计时(源码注释见 cron.ts)。

核心代码逐行拆解

1. 定义 cron 任务:convex/crons.ts

示例的定时任务定义在 crons.ts:

import { cronJobs } from "convex/server"; import { internal } from "./_generated/api"; const crons = cronJobs(); crons.interval( "clear messages table", { minutes: 1 }, internal.messages.clearAll, ); export default crons;

关键点:

  • cronJobs()convex/server导入,返回一个Crons实例(cron.ts);
  • 第一个参数"clear messages table"唯一标识符(cron identifier),源码规定必须是可打印 ASCII 字符(/^[ -~]*$/),且同一个文件中不允许重复注册,否则会抛出Cron identifier registered twice: ...(cron.ts);
  • 第二个参数{ minutes: 1 }是调度配置;
  • 第三个参数internal.messages.clearAll是要执行的函数引用——这里使用的是internal 函数(下文详述);
  • 文件必须export default crons,Convex 后端才会识别并注册这些定时任务。

2. 定时执行的函数:convex/messages.ts

被 cron 调用的clearAll定义在 messages.ts:

export const clearAll = internalMutation({ args: {}, handler: async (ctx) => { for (const message of await ctx.db.query("messages").collect()) { await ctx.db.delete("messages", message._id); } }, });

同文件还定义了聊天功能本身需要的两个函数:

export const list = query({ args: {}, handler: async (ctx) => { return await ctx.db.query("messages").collect(); }, }); export const send = mutation({ args: { body: v.string(), author: v.string(), }, handler: async (ctx, { body, author }) => { const message = { body, author }; await ctx.db.insert("messages", message); }, });

这里有一个值得注意的设计:cron 调用的clearAll用的是internalMutation而非普通mutation。internal 函数不能被客户端直接调用,只能由服务端(其他函数、scheduler、cron)调用,因此用于清理类任务更安全——用户无法通过 HTTP 端点绕过前端逻辑直接触发它。这也解释了为什么crons.ts中引用的是internal.messages.clearAll而不是api.messages.clearAll

3. 前端:src/App.tsx

App.tsx 使用convex/reactuseQuery/useMutation订阅api.messages.listapi.messages.send

const messages = useQuery(api.messages.list) || []; const sendMessage = useMutation(api.messages.send);

每当 cron 清空表后,useQuery会自动收到新的查询结果,消息列表随之变为空——不需要任何手动刷新。这也是观察 cron 是否生效的最直接方式:发送几条消息,等一分钟,列表被清空。

深入convex/servercronJobs()实现

cronJobs()的完整实现位于 npm-packages/convex/src/server/cron.ts。Crons类内部维护一个Record<string, CronJob>,每次调用schedule()都会做三件事:解析参数、校验标识符唯一性、把{ name, args, schedule }存入crons表(cron.ts)。

六种调度方式与参数校验

方法调度配置参数要求
crons.interval(id, schedule, fn){ seconds }/{ minutes }/{ hours }三选一必须且只能指定其中一个,值为正整数(cron.ts)
crons.hourly(id, schedule?, fn){ minuteUTC?: 0-59 },可整体省略省略minuteUTC时由后端在整点内随机分摊,避免所有任务挤在整点(cron.ts)
crons.daily(id, schedule, fn){ hourUTC: 0-23, minuteUTC?: 0-59 }hourUTC必填且为 UTC 小时(cron.ts)
crons.weekly(id, schedule, fn){ dayOfWeek: "monday"..."sunday", hourUTC, minuteUTC? }星期必须是英文小写全称(cron.ts)
crons.monthly(id, schedule, fn){ day: 1-31, hourUTC, minuteUTC? }注意:大于 28 的日期在部分月份不会触发,例如 30 号在二月不运行(cron.ts)
crons.cron(id, cronString, fn)标准 5 段 cron 表达式字段依次为:分钟(0-59)、小时(0-23)、日(1-31)、月(1-12)、星期(0-6,周日为 0),如"15 7 * * *"表示每天 7:15 UTC(cron.ts)

所有字段都在注册时进行严格校验,非法值会直接抛出Error。例如:

  • 间隔不是正整数 →Interval must be an integer greater than 0
  • 小时超出 0-23 →Hour of day must be an integer from 0 to 23
  • 星期名拼写错误 →Day of week must be a string like "monday".
更多调度示例
// 每 30 秒运行一次 crons.interval("cleanup", { seconds: 30 }, api.jobs.cleanup); // 每小时的 30 分运行(UTC) crons.hourly("reset scores", { minuteUTC: 30 }, api.scores.reset); // 每天 17:30 UTC 运行 crons.daily("digest", { hourUTC: 17, minuteUTC: 30 }, api.emails.sendDailyDigest); // 每周二 17:30 UTC 运行 crons.weekly( "weekly email", { dayOfWeek: "tuesday", hourUTC: 17, minuteUTC: 30 }, api.emails.send, ); // 每月 1 号 17:30 UTC 运行 crons.monthly( "bill customers", { day: 1, hourUTC: 17, minuteUTC: 30 }, api.billing.billCustomers, ); // 标准 cron 表达式:每天 7:15 UTC crons.cron("backup", "15 7 * * *", api.jobs.backup);

intervaldailyweeklymonthlycron都支持在函数引用后追加位置参数(...args),这些参数会被parseArgs解析并序列化为 JSON 传给目标函数(cron.ts)。

服务端如何执行 cron 任务

前端 SDK 的crons.ts只是"配置",真正把配置变成周期性执行的是 Rust 后端。

  • 定时任务的调度与执行逻辑位于 crates/application/src/cron_jobs/mod.rs,其中引用了model::cron_jobs模块的compute_next_tsstream_cron_jobs_to_run等核心函数,以及CronJobCronJobStateCronJobStatusCronNextRun等类型,负责计算每个任务的下一次执行时间戳并按序触发;
  • 触发时以InertIdentity(无用户身份的"系统身份")执行目标 UDF,并受SCHEDULED_JOB_EXECUTION_PARALLELISM(定时任务执行并行度)等 knobs 控制(mod.rs);
  • 任务执行结果会记录CronJobResultCronJobLogLines,便于在 Convex 仪表盘中查看每次运行的日志。

也就是说,客户端crons.ts里写的{ minutes: 1 }这类配置,最终会被后端持久化为带类型标签的调度计划({ type: "interval", minutes: 1 }),再由compute_next_ts依据当前时间计算出下一次执行时刻并排队执行。

排查与注意事项

  • 确认任务已注册crons.ts必须export default crons,且标识符不能重复,否则部署时报错;
  • 区分 internal 与 public:cron 可以调用 public 的mutation/action,也可以调用internalMutation/internalAction;示例选择 internal 是为了防止用户直接触发清理逻辑;
  • UTC 时区hourUTCminuteUTC以及 cron 表达式均以UTC为准,配置前需要把本地时区换算为 UTC,避免任务在错误的时间触发(cron.ts);
  • 每月大日期陷阱monthlyday若大于 28,并非每个月都会运行;
  • 首跳时间:interval 任务从部署时刻起算,因此间隔是相对部署时间的,而非对齐到整点。

小结

通过npm-packages/demos/cron-jobs这个极简示例,可以看到 Convex cron 的完整工作模式:convex/crons.ts集中声明调度计划 → SDK 校验并序列化 → Rust 后端cron_jobs模块按compute_next_ts计算的下一次时间触发 → 目标函数以系统身份执行并记录日志。从"每分钟清空消息表"的玩具示例出发,你完全可以把它扩展为数据清理、定时报表、邮件提醒、缓存刷新等生产级定时任务。

【免费下载链接】convex-backendThe open-source reactive database for app developers项目地址: https://gitcode.com/gh_mirrors/co/convex-backend

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询