Cron Triggers
Cron Triggers là gì?
Scheduled Worker Execution
┌─────────────────────────────────────────────────────────────┐
│ CRON TRIGGERS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Schedule Worker │
│ │
│ "0 * * * *" ──────────────────▶ ┌──────────────┐ │
│ (every hour) │ scheduled() │ │
│ │ handler │ │
│ "0 0 * * *" ──────────────────▶ └──────────────┘ │
│ (daily midnight) │
│ │
│ Use cases: │
│ - Daily reports │
│ - Cleanup old data │
│ - Sync external data │
│ - Health checks │
│ - Scheduled notifications │
│ │
└─────────────────────────────────────────────────────────────┘
Configuration
wrangler.toml
name = "my-scheduled-worker"
main = "src/index.ts"
compatibility_date = "2024-01-15"
# Cron triggers
[triggers]
crons = [
"0 * * * *", # Every hour
"0 0 * * *", # Daily at midnight UTC
"*/15 * * * *", # Every 15 minutes
"0 9 * * 1", # Every Monday at 9 AM UTC
]
Cron Syntax
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *
Common Patterns
| Pattern |
Description |
* * * * * |
Every minute |
*/15 * * * * |
Every 15 minutes |
0 * * * * |
Every hour |
0 0 * * * |
Daily at midnight |
0 0 * * 0 |
Weekly on Sunday |
0 0 1 * * |
Monthly on 1st |
0 9 * * 1-5 |
Weekdays at 9 AM |
Handler Implementation
Basic Handler
interface Env {
MY_KV: KVNamespace;
// Other bindings
}
export default {
// HTTP handler (optional)
async fetch(request: Request, env: Env): Promise<Response> {
return new Response('This worker runs on schedule');
},
// Cron handler
async scheduled(
event: ScheduledEvent,
env: Env,
ctx: ExecutionContext
): Promise<void> {
console.log('Cron triggered at:', new Date(event.scheduledTime));
console.log('Cron pattern:', event.cron);
// Do scheduled work
await performScheduledTask(env);
},
};
async function performScheduledTask(env: Env) {
// Your task logic
console.log('Running scheduled task...');
}
Multiple Crons
async scheduled(
event: ScheduledEvent,
env: Env,
ctx: ExecutionContext
): Promise<void> {
switch (event.cron) {
case '0 * * * *':
// Hourly task
await hourlyCleanup(env);
break;
case '0 0 * * *':
// Daily task
await dailyReport(env);
break;
case '0 9 * * 1':
// Weekly Monday task
await weeklyDigest(env);
break;
default:
console.log('Unknown cron:', event.cron);
}
}
async function hourlyCleanup(env: Env) {
console.log('Running hourly cleanup');
// Delete old cache entries, etc.
}
async function dailyReport(env: Env) {
console.log('Generating daily report');
// Generate and send report
}
async function weeklyDigest(env: Env) {
console.log('Sending weekly digest');
// Compile and send digest
}
Common Patterns
Data Cleanup
async scheduled(event: ScheduledEvent, env: Env): Promise<void> {
if (event.cron === '0 0 * * *') {
// Daily cleanup at midnight
// Call Supabase to delete old data
const response = await fetch(`${env.SUPABASE_URL}/rest/v1/rpc/cleanup_old_data`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${env.SUPABASE_SERVICE_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
days_old: 30,
}),
});
const result = await response.json();
console.log('Cleanup result:', result);
}
}
External API Sync
async scheduled(event: ScheduledEvent, env: Env): Promise<void> {
if (event.cron === '*/15 * * * *') {
// Sync exchange rates every 15 minutes
const ratesResponse = await fetch('https://api.exchangerate.host/latest');
const rates = await ratesResponse.json();
// Store in KV
await env.MY_KV.put('exchange_rates', JSON.stringify(rates), {
expirationTtl: 900, // 15 minutes
});
console.log('Exchange rates updated');
}
}
Queue Trigger Pattern
async scheduled(event: ScheduledEvent, env: Env): Promise<void> {
if (event.cron === '0 9 * * *') {
// Daily at 9 AM: Queue email digests
// Get users who want daily digest
const response = await fetch(
`${env.SUPABASE_URL}/rest/v1/users?digest_frequency=eq.daily`,
{
headers: {
'Authorization': `Bearer ${env.SUPABASE_SERVICE_KEY}`,
},
}
);
const users = await response.json();
// Queue emails for batch processing
await env.EMAIL_QUEUE.sendBatch(
users.map((user: any) => ({
body: {
type: 'daily_digest',
userId: user.id,
email: user.email,
},
}))
);
console.log(`Queued ${users.length} digest emails`);
}
}
Testing Cron Locally
Trigger Manually
# Start dev server
wrangler dev
# In another terminal, trigger cron
curl "http://localhost:8787/__scheduled?cron=0+*+*+*+*"
# Or use wrangler
wrangler dev --test-scheduled
Logs
# View scheduled execution logs
wrangler tail --format pretty
Limits
Cron Limits
┌─────────────────────────────────────────────────────────────┐
│ CRON LIMITS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Free tier: │
│ - Cron triggers NOT available │
│ │
│ Paid tier: │
│ - Up to 5 crons per Worker │
│ - Minimum interval: 1 minute │
│ - Max execution: 30 seconds (can extend to 15 min) │
│ - Timezone: UTC only │
│ │
│ Note: For complex schedules, combine with Queue │
│ │
└─────────────────────────────────────────────────────────────┘
Best Practices
Idempotency
async scheduled(event: ScheduledEvent, env: Env): Promise<void> {
const runId = `${event.cron}-${event.scheduledTime}`;
// Check if already ran
const alreadyRan = await env.MY_KV.get(`cron:${runId}`);
if (alreadyRan) {
console.log('Already executed, skipping');
return;
}
// Mark as running
await env.MY_KV.put(`cron:${runId}`, 'running', { expirationTtl: 3600 });
try {
await doWork();
await env.MY_KV.put(`cron:${runId}`, 'completed', { expirationTtl: 3600 });
} catch (error) {
await env.MY_KV.put(`cron:${runId}`, 'failed', { expirationTtl: 3600 });
throw error;
}
}
Tổng kết
Configuration
[triggers]
crons = ["0 * * * *", "0 0 * * *"]
Handler
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
// event.cron - which cron triggered
// event.scheduledTime - timestamp
}
Best Practices
✅ Use specific cron patterns
✅ Handle multiple crons in one worker
✅ Make operations idempotent
✅ Use Queue for heavy work
✅ Log execution for monitoring
Q&A
- Scheduled tasks nào cần chạy?
- Frequency phù hợp?
- Timezone handling cần không?