Featured image of post Cloudflare Cron 定时任务实战

Cloudflare Cron 定时任务实战

使用场景

RuiToolAI 的图片生成记录有 7 天有效期,R2 文件会自动删除,但 DB 里的记录不会自动清理。如果不清理,DB 会越来越大,而且历史页面会显示大量"已过期"的空白卡片。

解决方案:用 Cloudflare Cron Triggers 每天定时清理过期记录。

配置 Cron Trigger

wrangler.jsonc 中添加:

1
2
3
4
5
{
  "triggers": {
    "crons": ["0 3 * * *"]
  }
}

Cron 表达式格式:分 时 日 月 周

表达式含义
0 3 * * *每天 UTC 凌晨 3 点
0 */6 * * *每 6 小时
0 0 * * 0每周日 UTC 0 点
*/5 * * * *每 5 分钟

实现 scheduled 处理器

worker-entrypoint.ts 中添加 scheduled 方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export default {
  ...worker,
  async scheduled(
    _event: ScheduledEvent,
    env: Env,
    ctx: ExecutionContext
  ): Promise<void> {
    ctx.waitUntil(cleanupExpiredImageRecords(env));
  },
};

注意:这里用 ctx.waitUntil 是正确的,因为 scheduled 处理器本身就是异步的,不像 HTTP 请求有 CPU 时间限制。

自动清理过期图片记录

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async function cleanupExpiredImageRecords(env: Env): Promise<void> {
  const { drizzle } = await import("drizzle-orm/d1");
  const schema = await import("./src/db/schema");
  const { generatedImageTable, IMAGE_GEN_STATUS } = schema;
  const { lt, eq, and } = await import("drizzle-orm");

  // 直接用 env.NEXT_TAG_CACHE_D1,不用 getDB()
  // 因为 scheduled 处理器里没有 cloudflare:workers 的全局 env
  const db = drizzle(env.NEXT_TAG_CACHE_D1, { schema });
  const now = new Date();

  const result = await db
    .delete(generatedImageTable)
    .where(
      and(
        eq(generatedImageTable.status, IMAGE_GEN_STATUS.COMPLETED),
        lt(generatedImageTable.expiresAt, now),
      ),
    )
    .returning({ id: generatedImageTable.id });

  console.log(`[cron] Cleaned up ${result.length} expired image records.`);
}

为什么不用 getDB()

getDB() 内部用了 cloudflare:workersenv,这个 env 是通过 Cloudflare 的全局上下文注入的,只在 HTTP 请求处理中可用。scheduled 处理器里需要直接用参数传入的 env

R2 生命周期规则配合

R2 的生命周期规则负责删除文件,Cron 负责清理 DB 记录,两者配合:

R2 设置(Cloudflare Dashboard):

1
2
3
4
对象生命周期规则
  规则名称:Delete user uploads after 7 days
  前缀:users/
  操作:7 天后删除对象

时间线:

1
2
3
Day 0:用户生成图片 → 存 R2 → DB 记录 expiresAt = Day 7
Day 7:R2 自动删除文件
Day 7 凌晨 3 点:Cron 清理 DB 中 expiresAt < now 的记录

这样 DB 和 R2 都保持干净。

本地调试

先 build 再用 wrangler dev:

1
2
3
4
5
6
7
8
9
# 方法一:部署到 Cloudflare 后在 Dashboard 手动触发
# Workers → 你的 Worker → Triggers → Cron Triggers → Run

# 方法二:本地 wrangler dev(需要先 build)
pnpm build
npx wrangler dev --port 8787

# 然后触发
curl "http://localhost:8787/__scheduled?cron=0+3+*+*+*"

推荐方法一,更简单,而且可以看到真实的 D1 数据。

总结

Cloudflare Cron Triggers 的使用要点:

  1. wrangler.jsonctriggers.crons 中配置 Cron 表达式
  2. worker-entrypoint.ts 中实现 scheduled 方法
  3. scheduled 处理器里用参数传入的 env,不用全局 env
  4. 配合 R2 生命周期规则,实现文件和 DB 记录的双重清理

参考资源

使用 Hugo 构建
主题 StackJimmy 设计