Background Processing
Background Jobs
Production-grade background job scheduling with cron expressions, intervals, automatic retries, circuit breakers, and distributed system support. Zero dependencies required.
Overview
Schedule jobs with cron expressions or intervals. Automatic retries, circuit breakers, and distributed system support included.
1import { createApp } from '@morojs/moro';
2
3const app = await createApp({
4 jobs: {
5 enabled: true, // Enable job scheduler
6 },
7});
8
9// Simple cron job - runs daily at 2 AM
10app.job('cleanup-old-data', '0 2 * * *', async () => {
11 await database.cleanupOldRecords();
12});
13
14// Interval-based job - runs every 5 minutes
15app.job('health-check', '5m', async (ctx) => {
16 console.log(`Health check ${ctx.executionId}`);
17 await checkSystemHealth();
18});Without proper job scheduling, you're manually managing cron tasks, handling failures, and dealing with distributed systems. With MoroJS, you get all of that automatically. Traditional job scheduling requires external services and complex setup. We handle that automatically.
Without Job System
- External cron services required
- Manual retry logic
- No circuit breakers
- Complex distributed coordination
With MoroJS
- Built-in job scheduler
- Automatic retries with backoff
- Per-job circuit breakers
- Leader election for clusters
Schedule jobs with cron expressions or intervals. That's it.
1// Cron expressions
2app.job('backup', '0 3 * * *', backupDatabase); // Daily at 3 AM
3app.job('report', '0 9 * * 1', weeklyReport); // Mondays at 9 AM
4
5// Interval strings
6app.job('health-check', '5m', healthCheck); // Every 5 minutes
7app.job('cache-cleanup', '1h', cleanCache); // Every hour
8
9// Cron macros
10app.job('hourly-task', '@hourly', task);
11app.job('daily-task', '@daily', task);Production-Ready
Automatic retries, circuit breakers, timeouts, and memory leak detection.
Distributed
Leader election, Kubernetes awareness, and cluster mode support.
Zero Dependencies
Built-in scheduler. No external services required.
How It Works
MoroJS includes a production-grade job scheduler that supports cron expressions, interval-based scheduling, automatic retries with exponential backoff, circuit breakers, and distributed system coordination. Jobs can be scheduled with simple cron expressions or interval strings, and the system handles execution, retries, and failure recovery automatically.
Quick Start
1import { createApp } from '@morojs/moro';
2
3const app = await createApp({
4 jobs: {
5 enabled: true, // Enable job scheduler
6 },
7});
8
9// Simple cron job - runs daily at 2 AM
10app.job('cleanup-old-data', '0 2 * * *', async () => {
11 await database.cleanupOldRecords();
12});
13
14// Interval-based job - runs every 5 minutes
15app.job('health-check', '5m', async (ctx) => {
16 console.log(`Health check ${ctx.executionId}`);
17 await checkSystemHealth();
18});
19
20// Advanced configuration
21app.job('generate-report', '@daily', async (ctx) => {
22 const report = await generateDailyReport();
23 return report;
24}, {
25 timeout: 60000, // 1 minute timeout
26 maxRetries: 3,
27 retryBackoff: 'exponential',
28 priority: 10, // Higher priority
29 onError: (ctx, error) => {
30 console.error(`Report generation failed`, error);
31 }
32});
33
34app.listen(3000);1// Standard cron (minute hour day month weekday)
2app.job('backup', '0 3 * * *', backupDatabase); // Daily at 3 AM
3app.job('report', '0 9 * * 1', weeklyReport); // Mondays at 9 AM
4
5// Cron macros
6app.job('hourly-task', '@hourly', task);
7app.job('daily-task', '@daily', task);
8app.job('weekly-task', '@weekly', task);
9app.job('monthly-task', '@monthly', task);
10
11// Interval strings
12app.job('health-check', '5m', healthCheck); // Every 5 minutes
13app.job('cache-cleanup', '1h', cleanCache); // Every hour
14app.job('quick-task', '30s', quickTask); // Every 30 seconds
15app.job('nightly-job', '1d', nightlyJob); // Every dayAdvanced Features
1// Register a job
2const jobId = app.job('my-job', '*/5 * * * *', handler, {
3 name: 'My Custom Job Name',
4 enabled: true,
5 priority: 5,
6 maxConcurrent: 1,
7 timeout: 60000,
8 maxRetries: 3,
9 metadata: { owner: 'team-a' },
10});
11
12// Enable/Disable jobs
13app.setJobEnabled(jobId, false); // Disable job
14app.setJobEnabled(jobId, true); // Enable job
15
16// Manually trigger a job
17await app.triggerJob(jobId, { reason: 'manual-trigger' });1// Jobs with higher priority execute first
2app.job('critical-task', '*/1 * * * *', criticalTask, { priority: 10 });
3app.job('normal-task', '*/1 * * * *', normalTask, { priority: 5 });
4app.job('low-priority', '*/1 * * * *', lowPriorityTask, { priority: 1 });
5
6// Global concurrency limit (in config)
7const app = await createApp({
8 jobs: { maxConcurrentJobs: 10 },
9});
10
11// Per-job concurrency limit
12app.job('scraper', '*/5 * * * *', scrape, {
13 maxConcurrent: 3, // Allow up to 3 concurrent executions
14});1// Automatically stops retrying failing jobs
2app.job('flaky-api', '*/1 * * * *', callFlakyAPI, {
3 enableCircuitBreaker: true,
4 maxRetries: 5,
5});
6
7// Circuit breaker opens after 5 consecutive failures
8// Jobs won't execute until circuit breaker resets (60s default)1const app = await createApp({
2 jobs: {
3 enabled: true,
4 leaderElection: {
5 enabled: true,
6 strategy: 'file', // 'file' | 'redis' | 'none'
7 lockPath: '/tmp/moro-jobs-leader.lock',
8 lockTimeout: 30000,
9 heartbeatInterval: 10000,
10 },
11 },
12});1// Get job metrics
2const metrics = app.getJobMetrics(jobId);
3console.log(metrics);
4// {
5// successRate: 98.5,
6// failureRate: 1.5,
7// averageDuration: 1234,
8// totalExecutions: 200,
9// recentFailures: 0
10// }
11
12// Check job health
13const health = app.getJobHealth(jobId);
14console.log(health);
15// {
16// jobId: 'job_...',
17// name: 'cleanup-old-data',
18// status: 'healthy',
19// enabled: true,
20// lastExecution: Date,
21// consecutiveFailures: 0,
22// nextRun: Date
23// }
24
25// Get scheduler statistics
26const stats = app.getJobStats();
27console.log(stats);
28// {
29// totalJobs: 5,
30// enabledJobs: 4,
31// runningJobs: 2,
32// queuedJobs: 0,
33// isLeader: true,
34// isStarted: true
35// }
36
37// Subscribe to job events
38app.on('job:start', ({ jobId, executionId }) => {
39 console.log(`Job ${jobId} started: ${executionId}`);
40});
41
42app.on('job:complete', ({ jobId, result, duration }) => {
43 console.log(`Job ${jobId} completed in ${duration}ms`);
44});
45
46app.on('job:error', ({ jobId, error }) => {
47 console.error(`Job ${jobId} failed:`, error);
48});
49
50app.on('circuit-breaker:open', ({ jobId }) => {
51 console.error(`Circuit breaker opened for job ${jobId}`);
52});Production-Ready Features
Production-Ready Resilience
- Automatic retries with exponential backoff
- Per-job circuit breakers
- Timeout enforcement
- Memory leak detection
- Graceful shutdown
Distributed Systems
- Leader election (file or Redis)
- Kubernetes awareness
- Cluster mode support
- Crash recovery
- Full observability & metrics