Scheduled tasks are essential for any production application. Whether you need to send daily reports, clean up old data, sync external APIs, or run periodic health checks, flashQ's cron jobs have you covered.
Understanding Cron Syntax
flashQ uses a 6-field cron expression that includes seconds:
βββββββββββββ second (0-59)
β βββββββββββ minute (0-59)
β β βββββββββ hour (0-23)
β β β βββββββ day of month (1-31)
β β β β βββββ month (1-12)
β β β β β βββ day of week (0-6, Sunday=0)
β β β β β β
* * * * * *
Common Patterns
| Expression | Description |
|---|---|
0 * * * * * | Every minute |
0 0 * * * * | Every hour |
0 0 9 * * * | Daily at 9 AM |
0 30 9 * * 1-5 | Weekdays at 9:30 AM |
0 0 0 * * 0 | Weekly on Sunday midnight |
0 0 0 1 * * | Monthly on the 1st |
0 */15 * * * * | Every 15 minutes |
0 0 */2 * * * | Every 2 hours |
Creating Cron Jobs
import { Queue } from 'flashq';
const queue = new Queue('scheduled-tasks');
// Daily report at 9 AM
await queue.addCron('daily-report', {
queue: 'scheduled-tasks',
schedule: '0 0 9 * * *',
data: {
type: 'daily-report',
recipients: ['team@company.com'],
},
});
// Cleanup every Sunday at midnight
await queue.addCron('weekly-cleanup', {
queue: 'scheduled-tasks',
schedule: '0 0 0 * * 0',
data: {
type: 'cleanup',
olderThanDays: 30,
},
});
// Health check every 5 minutes
await queue.addCron('health-check', {
queue: 'scheduled-tasks',
schedule: '0 */5 * * * *',
data: {
type: 'health-check',
endpoints: ['/api/health', '/api/db'],
},
});
Managing Cron Jobs
// List all cron jobs
const crons = await queue.listCrons();
console.log(crons);
// [
// { name: 'daily-report', schedule: '0 0 9 * * *', ... },
// { name: 'weekly-cleanup', schedule: '0 0 0 * * 0', ... },
// ]
// Delete a cron job
await queue.deleteCron('health-check');
// Update a cron job (delete + recreate)
await queue.deleteCron('daily-report');
await queue.addCron('daily-report', {
queue: 'scheduled-tasks',
schedule: '0 0 8 * * *', // Changed to 8 AM
data: { type: 'daily-report' },
});
Real-World Use Cases
1. Daily Analytics Report
await queue.addCron('analytics-report', {
queue: 'reports',
schedule: '0 0 6 * * *', // 6 AM daily
data: {
type: 'analytics',
metrics: ['users', 'revenue', 'conversions'],
format: 'pdf',
},
});
// Worker
new Worker('reports', async (job) => {
const { metrics, format } = job.data;
// Generate report
const report = await generateAnalyticsReport(metrics);
const pdf = await convertToPDF(report);
// Send via email
await sendEmail({
to: 'executives@company.com',
subject: `Daily Analytics - ${new Date().toDateString()}`,
attachments: [pdf],
});
});
2. Database Cleanup
await queue.addCron('db-cleanup', {
queue: 'maintenance',
schedule: '0 0 3 * * *', // 3 AM daily (low traffic)
data: {
tasks: [
{ table: 'sessions', olderThan: '7 days' },
{ table: 'logs', olderThan: '30 days' },
{ table: 'temp_files', olderThan: '1 day' },
],
},
});
3. API Sync
await queue.addCron('sync-stripe', {
queue: 'integrations',
schedule: '0 */30 * * * *', // Every 30 minutes
data: {
provider: 'stripe',
syncTypes: ['subscriptions', 'invoices'],
},
});
4. Cache Warming
await queue.addCron('warm-cache', {
queue: 'cache',
schedule: '0 55 * * * *', // 5 min before each hour
data: {
endpoints: [
'/api/products',
'/api/categories',
'/api/homepage',
],
},
});
π‘ Timezone Handling
Cron jobs run in the server's timezone (typically UTC). For user-facing schedules, store the user's timezone and convert when creating the cron expression.
Best Practices
1. Idempotent Jobs
Design cron jobs to be idempotentβrunning them twice shouldn't cause issues:
// Good: Check before processing
async function processDaily() {
const lastRun = await db.settings.get('lastDailyRun');
const today = new Date().toDateString();
if (lastRun === today) {
console.log('Already ran today, skipping');
return;
}
// Process...
await db.settings.set('lastDailyRun', today);
}
2. Overlap Prevention
Prevent jobs from overlapping if they take longer than expected:
new Worker('maintenance', async (job) => {
const lockKey = `lock:${job.name}`;
// Try to acquire lock
const acquired = await redis.set(lockKey, '1', 'NX', 'EX', 3600);
if (!acquired) {
console.log('Job already running, skipping');
return;
}
try {
// Process job...
} finally {
await redis.del(lockKey);
}
});
3. Error Alerting
worker.on('failed', async (job, error) => {
if (job.name.startsWith('cron:')) {
await sendSlackAlert({
channel: '#ops-alerts',
message: `Cron job failed: ${job.name}\nError: ${error.message}`,
});
}
});
Conclusion
flashQ cron jobs provide a reliable way to schedule recurring tasks. Key takeaways:
- Use 6-field cron syntax (includes seconds)
- Design idempotent jobs
- Implement overlap prevention for long-running tasks
- Set up alerting for failures