自动化与脚本
在 Convex 应用中调度定时后台任务时,用 cron…
需要在 Convex 中按固定间隔或 cron 表达式运行后台任务(清理、同步、报表、健康检查)时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
在 Convex 应用中调度定时后台任务特定
解决思路
- 实现前先查最新文档,不要假设:访问 `https://docs.convex.dev/scheduling/cron-jobs`、`https://docs.convex.dev/scheduling`、`https://docs.convex.dev/scheduling/scheduled-functions`,以及 `https://docs.convex.dev/llms.txt` 获取上下文。
- 在 `convex/crons.ts` 中从 `convex/server` 导入 `cronJobs`,从 `./_generated/api` 导入 `internal`,创建 `const crons = cronJobs()`,最后 `export default crons`。
- 用 `crons.interval` 做间隔调度:第一个参数任务名,第二个参数间隔对象,第三个参数 `internal` 函数,第四个参数参数对象;例如 `{ seconds: 30 }`、`{ minutes: 5 }`、`{ hours: 1 }`、`{ hours: 2 }`;最小间隔 30 秒。
- 用 `crons.cron` 做精确调度:传任务名、cron 表达式、`internal` 函数、参数对象;例如 `0 0 * * *` 每天 UTC 午夜,`0 9 * * *` 每天 9 AM UTC,`0 8 * * 1` 每周一 8 AM UTC,`0 0 1 * *` 每月 1 日午夜,`*/15 * * * *` 每 15 分钟。
- 记住 cron 五段格式:minute (0-59)、hour (0-23)、day of month (1-31)、month (1-12)、day of week (0-6, Sunday=0);常用:`* * * * *` 每分钟,`0 * * * *` 每小时,`0 0 * * *` 每天午夜,`0 0 * * 0` 每周日午夜,`0 0 1 * *` 每月第一天,`*/5 * * * *` 每 5 分钟,`0 9-17 * * 1-5` 周一到周五 9 AM-5 PM 每小时。
- Cron jobs 应调用 `internal` 函数以保证安全:定义 `internalMutation`/`internalQuery`,从 `./_generated/server` 和 `v` 导入;例如 `cleanupExpiredSessions` 查询 `sessions` 的 `by_lastActive` 索引,过滤 `lastActive` 小于一小时前,删除过期 session 并返回数量。
- 对待处理任务用 internalMutation:查询 `tasks` 的 `by_status` 索引中 `status` 为 `pending` 的任务,`take(100)`,逐个 `ctx.db.patch` 为 `processing` 和 `startedAt: Date.now()`,再用 `ctx.scheduler.runAfter(0, internal.tasks.processTask, { taskId: task._id })` 调度实际处理。
- 给 cron jobs 传静态参数:可让同一个 `internal.cleanup.cleanupByType` 被不同 cron 调用,参数如 `{ fileType: 'temp', maxAge: 3600000 }` 和 `{ fileType: 'cache', maxAge: 86400000 }`;函数内根据 `args.fileType` 和 `args.maxAge` 查 `files` 的 `by_type_and_created` 索引,删除 storage 和 db 记录并返回数量。
- 为生产 cron 加日志:记录 `startTime`、`processedCount`、`errorCount`;在 try 中逐项删除并计数;完成后向 `cronLogs` 插入 `jobName`、`startTime`、`endTime`、`duration`、`processedCount`、`errorCount`、`status`(`success` 或 `partial`);在 catch 中插入 `status: 'failed'` 和 `error`,并重新抛出错误。
- 大数据集用批处理:设 `BATCH_SIZE = 100`;用 `internalMutation` 接收可选 `cursor`;用 `.paginate({ numItems: BATCH_SIZE, cursor: args.cursor ?? null })` 分页;处理 `result.page`;如果 `!result.isDone`,用 `ctx.scheduler.runAfter(0, internal.tasks.processBatch, { cursor: result.continueCursor })` 调度下一批。
- 外部 API 调用用 action:在 `convex/sync.ts` 写 `use node` 的 `internalAction`;用 `fetch` 调 API,带 `Authorization: Bearer ${process.env.API_KEY}`;如果 `!response.ok` 抛出错误;解析 JSON 后通过 `ctx.runMutation(internal.sync.storeExternalData, { data, syncedAt: Date.now() })` 存储;再用 `crons.interval('sync external data', { minutes: 15 }, internal.sync.syncExternalData, {})` 调度。
- 为 cron 日志建 schema:定义 `cronLogs` 表字段 `jobName`、`startTime`、`endTime`、`duration`、`processedCount`、`errorCount`、`status`(`success`/`partial`/`failed`)、可选 `error`,并建 `by_job`、`by_status`、`by_startTime` 索引;定义 `sessions` 表字段 `userId`、`token`、`lastActive`、`expiresAt`,建 `by_user`、`by_lastActive`、`by_expiresAt` 索引;定义 `tasks` 表字段 `type`、`status`(`pending`/`processing`/`completed`/`failed`)、`data`、`createdAt`、可选 `startedAt`、可选 `completedAt`,建 `by_status`、`by_type_and_status` 索引。
- 用完整 cron 配置示例组织任务:`cleanup expired sessions` 每小时,`cleanup old logs` 每 24 小时并传 `{ maxAgeDays: 30 }`,`sync user data` 每 15 分钟,`daily analytics` 用 `0 1 * * *`,`weekly summary` 用 `0 9 * * 1`,`service health check` 每 5 分钟,最后 `export default crons`。
- 生产最佳实践:除非明确指示,绝不运行 `npx convex deploy`;除非明确指示,绝不运行任何 git 命令;只使用 `crons.interval` 或 `crons.cron`,不用已废弃 helper;cron jobs 始终调用 internal 函数;即使函数在同一文件也从 `_generated/api` 导入 `internal`;为生产 cron jobs 添加 logging 和 monitoring;对处理大数据的操作使用 batching;优雅处理错误防止整个 job 失败;使用有意义的 job names 以便 dashboard 可见;使用 cron expressions 时考虑 timezone(Convex 使用 UTC)。
- 避免常见陷阱:不要用 public functions;不要写长时间运行的 mutations,拆成 batches;不要缺少 error handling;不要忘记 timezone,所有 cron expressions 都是 UTC;不要用已废弃 helper 如 `crons.hourly`、`crons.daily`;不要不记录执行日志,否则生产问题难调试。
- 利用 Convex cron 失败自动重试;仍应在函数内捕获错误、记录日志,并在需要时重新抛出错误以触发失败处理。
适用边界 · 注意事项
- 不使用 Convex 或无法在 Convex 后端部署时。
- 需要低于 30 秒的调度间隔时(Convex interval 最小 30 秒)。
- 需要直接使用非 UTC 时区解析 cron 表达式时(Convex 使用 UTC,需自行换算)。