Claude Code 下的 Cron Jobs:分布式锁、历史跟踪和故障警报
发布: (2026年3月11日 GMT+8 12:56)
4 分钟阅读
原文: Dev.to
Source: Dev.to
Claude Code 可以生成完整且安全的 cron 基础设施,防止重复运行,跟踪执行历史,并发送失败警报。
Source: …
CLAUDE.md 用于 Cron 作业规则
Cron 作业设计规则
防止重复(必需)
- 防止同一作业的并发运行(分布式锁)
- 使用 Redis NX+EX 实现锁(TTL = 最大作业时长的 2 倍)
- 如果前一次作业仍在运行则跳过(不报错——直接跳过)
错误处理
- 失败时发送 Slack/PagerDuty 警报
- 连续 N 次失败后升级警报
- 在数据库中记录作业执行历史
调度器
- 使用 node-cron 或 BullMQ 可重复作业
- 通过环境变量使 cron 调度可配置
- 始终显式指定时区(避免默认使用 UTC)
生成 Cron 基础设施
// src/jobs/jobRunner.ts
import cron from 'node-cron';
import { createClient } from 'redis';
const redis = createClient({ url: process.env.REDIS_URL });
export async function runWithLock(
jobName: string,
timeoutMs: number,
handler: () => Promise<any>
): Promise<any> {
const lockKey = `cron:lock:${jobName}`;
const lockTtl = Math.ceil((timeoutMs * 2) / 1000);
// Acquire distributed lock
const acquired = await redis.set(lockKey, '1', {
NX: true, // Only set if not exists
EX: lockTtl,
});
if (!acquired) {
logger.info({ jobName }, 'Job already running, skipping');
return;
}
const startedAt = new Date();
try {
await handler();
await recordJobRun(jobName, 'success', startedAt);
} catch (err) {
await recordJobRun(jobName, 'failed', startedAt, String(err));
await notifySlack(`❌ Cron job failed: ${jobName}\n${err}`);
throw err;
} finally {
await redis.del(lockKey);
}
}
// src/jobs/definitions.ts
const TIMEZONE = process.env.CRON_TIMEZONE ?? 'Asia/Tokyo';
export function startCronJobs(): void {
// Daily at 3 AM: cleanup expired sessions
cron.schedule(
process.env.CLEANUP_SCHEDULE ?? '0 3 * * *',
() => runWithLock('cleanup-sessions', 30 * 60 * 1000, cleanupExpiredSessions),
{ timezone: TIMEZONE }
);
// Every hour: subscription expiry check
cron.schedule(
'0 * * * *',
() => runWithLock('subscription-check', 10 * 60 * 1000, checkSubscriptionExpiry),
{ timezone: TIMEZONE }
);
// Every minute: send notifications
cron.schedule(
'* * * * *',
() => runWithLock('send-notifications', 50 * 1000, sendPendingNotifications),
{ timezone: TIMEZONE }
);
logger.info('Cron jobs started');
}
BullMQ 可重复作业的替代方案
// src/jobs/bullmqCron.ts
export async function registerRepeatableJobs(): Promise<void> {
await cronQueue.add(
'cleanup-sessions',
{},
{
repeat: { pattern: '0 3 * * *', tz: 'Asia/Tokyo' },
jobId: 'cleanup-sessions', // Prevent duplicate registration
}
);
await cronQueue.add(
'subscription-check',
{},
{
repeat: { pattern: '0 * * * *', tz: 'Asia/Tokyo' },
jobId: 'subscription-check',
}
);
}
const worker = new Worker(
'cron-jobs',
async (job) => {
switch (job.name) {
case 'cleanup-sessions':
return cleanupExpiredSessions();
case 'subscription-check':
return checkSubscriptionExpiry();
}
},
{ connection: redisConnection, concurrency: 1 } // Serialize execution
);
工作历史的 Prisma 模式
model CronJobRun {
id String @id @default(cuid())
jobName String
status String // 'success' | 'failed'
startedAt DateTime
finishedAt DateTime
error String?
@@index([jobName, startedAt])
}
摘要
- CLAUDE.md – 定义所需的分布式锁、错误处理和显式时区。
- Redis NX+EX 锁 – 防止在多台服务器上双重运行。
- 数据库执行历史 – 便于事件调查。
- BullMQ 可重复 – 简化大规模调度器管理。
- 代码审查包(¥980)包括
/code‑review,用于检测缺失的锁、时区错误和缺少的失败警报。