or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

event-system.mdindex.mdjob-creation.mdjob-processing.mdjob-state.mdqueue-management.mdqueue-monitoring.mdrepeatable-jobs.mdutils.md
tile.json

repeatable-jobs.mddocs/

Repeatable Jobs

Advanced job scheduling with cron syntax, repeatable patterns, and time-based execution control.

Capabilities

Repeatable Job Creation

Create jobs that execute on recurring schedules using cron expressions or intervals.

// Repeatable jobs are created using the repeat option in add() method
interface RepeatOptions {
  /** Cron expression for scheduling */
  cron?: string;
  /** Repeat every N milliseconds */
  every?: number;
  /** Timezone for cron expressions */
  tz?: string;
  /** End date to stop repetition */
  endDate?: Date | string | number;
  /** Maximum number of repetitions */
  limit?: number;
  /** Starting count for iteration tracking */
  count?: number;
  /** Start date for cron-based jobs */
  startDate?: Date | string | number;
}

interface CronRepeatOptions extends RepeatOptions {
  cron: string;
  startDate?: Date | string | number;
}

interface EveryRepeatOptions extends RepeatOptions {
  every: number;
}

Usage Examples:

const Queue = require('bull');
const taskQueue = new Queue('scheduled tasks');

// Cron-based repeatable job - daily at 9 AM
const dailyReport = await taskQueue.add('daily-report', 
  { reportType: 'sales' },
  {
    repeat: {
      cron: '0 9 * * *',  // 9 AM every day
      tz: 'America/New_York'
    }
  }
);

// Interval-based repeatable job - every 5 minutes
const healthCheck = await taskQueue.add('health-check',
  { service: 'api' },
  {
    repeat: {
      every: 5 * 60 * 1000  // 5 minutes in milliseconds
    }
  }
);

// Weekly job with end date
const weeklyCleanup = await taskQueue.add('cleanup',
  { type: 'temp-files' },
  {
    repeat: {
      cron: '0 2 * * 0',  // 2 AM every Sunday
      endDate: new Date('2024-12-31'),
      tz: 'UTC'
    }
  }
);

// Limited repetition - run 10 times
const limitedJob = await taskQueue.add('backup',
  { database: 'users' },
  {
    repeat: {
      cron: '0 0 * * *',  // Daily at midnight
      limit: 10           // Stop after 10 executions
    }
  }
);

Cron Expression Examples

Common cron patterns for scheduling repeatable jobs.

// Cron expression format: second minute hour day month dayOfWeek
// Note: Bull uses 6-field cron (includes seconds), but 5-field also supported

const cronExamples = {
  // Every minute
  everyMinute: '* * * * *',
  
  // Every 5 minutes
  every5Minutes: '*/5 * * * *',
  
  // Every hour at minute 30
  everyHourAt30: '30 * * * *',  
  
  // Daily at 8:30 AM
  dailyAt830AM: '30 8 * * *',
  
  // Every weekday at 9 AM
  weekdaysAt9AM: '0 9 * * 1-5',
  
  // Every Monday at 10 AM
  mondaysAt10AM: '0 10 * * 1',
  
  // First day of every month at midnight
  monthlyFirstDay: '0 0 1 * *',
  
  // Every 15 minutes during business hours (9 AM - 5 PM)
  businessHours: '*/15 9-17 * * 1-5',
  
  // Twice daily at 8 AM and 8 PM
  twiceDaily: '0 8,20 * * *'
};

// Use in repeatable jobs
await taskQueue.add('business-hours-check', { type: 'status' }, {
  repeat: { cron: cronExamples.businessHours }
});

Repeatable Job Management

Retrieve and manage existing repeatable jobs.

/**
 * Get information about all repeatable jobs
 * @param start - Start index (default: 0)
 * @param end - End index (default: -1 for all)
 * @param asc - Sort ascending by next execution time (default: false)
 * @returns Promise that resolves to array of repeatable job information
 */
getRepeatableJobs(start?: number, end?: number, asc?: boolean): Promise<JobInformation[]>;

interface JobInformation {
  /** Unique key for the repeatable job */
  key: string;
  /** Job name */
  name: string;
  /** Job ID if specified */
  id?: string;
  /** End date timestamp */
  endDate?: number;
  /** Timezone */
  tz?: string;
  /** Cron expression */
  cron: string;
  /** Interval in milliseconds */
  every: number;
  /** Next execution timestamp */
  next: number;
}

Usage Examples:

// Get all repeatable jobs
const repeatableJobs = await taskQueue.getRepeatableJobs();
console.log(`Found ${repeatableJobs.length} repeatable jobs`);

repeatableJobs.forEach(job => {
  const nextRun = new Date(job.next);
  console.log(`${job.name}: next run at ${nextRun.toISOString()}`);
  if (job.cron) {
    console.log(`  Cron: ${job.cron}`);
  }
  if (job.every) {
    console.log(`  Interval: ${job.every}ms`);
  }
});

// Get upcoming repeatable jobs (sorted by next execution)
const upcoming = await taskQueue.getRepeatableJobs(0, 10, true);
console.log('Next 10 jobs to run:');
upcoming.forEach(job => {
  const nextRun = new Date(job.next);
  const timeUntil = job.next - Date.now();
  console.log(`  ${job.name} in ${Math.round(timeUntil / 1000)}s`);
});

Removing Repeatable Jobs

Remove repeatable jobs by configuration or key.

/**
 * Remove a repeatable job by repeat configuration
 * @param repeat - Repeat options that match the original job
 * @returns Promise that resolves when repeatable job is removed
 */
removeRepeatable(repeat: (CronRepeatOptions | EveryRepeatOptions) & { jobId?: string | number }): Promise<void>;

/**
 * Remove a repeatable job by name and repeat configuration  
 * @param name - Job name
 * @param repeat - Repeat options that match the original job
 * @returns Promise that resolves when repeatable job is removed
 */
removeRepeatable(name: string, repeat: (CronRepeatOptions | EveryRepeatOptions) & { jobId?: string | number }): Promise<void>;

/**
 * Remove a repeatable job by its unique key
 * @param key - Repeatable job key from getRepeatableJobs()
 * @returns Promise that resolves when repeatable job is removed
 */
removeRepeatableByKey(key: string): Promise<void>;

Usage Examples:

// Remove repeatable job by matching original configuration
await taskQueue.removeRepeatable({
  cron: '0 9 * * *',
  tz: 'America/New_York'
});

// Remove named repeatable job
await taskQueue.removeRepeatable('daily-report', {
  cron: '0 9 * * *',
  tz: 'America/New_York'
});

// Remove by key (easier method)
const repeatableJobs = await taskQueue.getRepeatableJobs();
const reportJob = repeatableJobs.find(job => job.name === 'daily-report');
if (reportJob) {
  await taskQueue.removeRepeatableByKey(reportJob.key);
  console.log('Removed daily report job');
}

// Remove all repeatable jobs
const allRepeatable = await taskQueue.getRepeatableJobs();
for (const job of allRepeatable) {
  await taskQueue.removeRepeatableByKey(job.key);
}
console.log('Removed all repeatable jobs');

Advanced Repeatable Job Management

Get next instance and count repeatable jobs.

/**
 * Get the next job instance for a repeatable job
 * @param name - Job name
 * @param data - Job data
 * @param opts - Job options with repeat configuration
 * @returns Promise that resolves to next job instance
 */
nextRepeatableJob(name: string, data: any, opts: JobOptions): Promise<Job>;

/**
 * Get count of repeatable job definitions
 * @returns Promise that resolves to number of repeatable jobs
 */
getRepeatableCount(): Promise<number>;

Usage Examples:

// Get count of repeatable jobs
const repeatableCount = await taskQueue.getRepeatableCount();
console.log(`Total repeatable jobs defined: ${repeatableCount}`);

// Get next instance of a repeatable job (advanced usage)
const nextJob = await taskQueue.nextRepeatableJob('daily-report', 
  { reportType: 'sales' },
  { 
    repeat: { cron: '0 9 * * *' }
  }
);
console.log(`Next job will run at: ${new Date(nextJob.timestamp)}`);

Repeatable Job Processing

Process repeatable jobs like regular jobs.

// Repeatable jobs are processed by regular processors
taskQueue.process('daily-report', async (job) => {
  console.log(`Running daily report at ${new Date()}`);
  console.log('Job data:', job.data);
  
  // Generate report
  const report = await generateDailyReport(job.data.reportType);
  
  return {
    reportGenerated: true,
    reportId: report.id,
    timestamp: Date.now()
  };
});

taskQueue.process('health-check', async (job) => {
  console.log(`Health check for ${job.data.service}`);
  
  const status = await checkServiceHealth(job.data.service);
  
  if (!status.healthy) {
    // Alert on health issues
    await sendAlert(`Service ${job.data.service} is unhealthy`);
  }
  
  return status;
});

Timezone Handling

Work with timezones in repeatable jobs.

// Different timezone examples
const timezoneJobs = [
  // New York time
  {
    name: 'ny-market-open',
    data: { market: 'NYSE' },
    repeat: {
      cron: '30 9 * * 1-5',  // 9:30 AM weekdays
      tz: 'America/New_York'
    }
  },
  
  // London time
  {
    name: 'london-report',
    data: { region: 'Europe' },
    repeat: {
      cron: '0 17 * * 1-5',  // 5 PM weekdays
      tz: 'Europe/London'
    }
  },
  
  // Tokyo time
  {
    name: 'tokyo-backup',
    data: { system: 'trading' },
    repeat: {
      cron: '0 2 * * *',  // 2 AM daily
      tz: 'Asia/Tokyo'
    }
  },
  
  // UTC (default)
  {
    name: 'global-sync',
    data: { type: 'sync' },
    repeat: {
      cron: '0 0 * * *'  // Midnight UTC
    }
  }
];

// Add all timezone-aware jobs
for (const jobDef of timezoneJobs) {
  await taskQueue.add(jobDef.name, jobDef.data, { repeat: jobDef.repeat });
}

Complex Scheduling Examples

Advanced scheduling patterns and use cases.

// Business day only job (excluding weekends and holidays)
await taskQueue.add('business-day-task', { type: 'report' }, {
  repeat: {
    cron: '0 9 * * 1-5',  // Weekdays only
    tz: 'America/New_York'
  }
});

// Quarterly job (first Monday of quarter at 8 AM)
await taskQueue.add('quarterly-review', { quarter: 'Q1' }, {
  repeat: {
    cron: '0 8 1-7 1,4,7,10 1',  // First Monday of Jan, Apr, Jul, Oct
    tz: 'UTC'
  }
});

// End-of-month job (last day of month at 11 PM)  
await taskQueue.add('month-end-close', { type: 'accounting' }, {
  repeat: {
    cron: '0 23 28-31 * *',  // Run on days 28-31 of month
    tz: 'America/New_York'
  }
});

// High-frequency job with limit (every 30 seconds for 1 hour)
await taskQueue.add('burst-monitoring', { duration: '1hour' }, {
  repeat: {
    every: 30000,        // Every 30 seconds
    limit: 120,          // Stop after 2 hours (120 * 30sec = 1 hour)
    endDate: new Date(Date.now() + 60 * 60 * 1000)  // Also set end date
  }
});

// Seasonal job (summer months only)
await taskQueue.add('summer-maintenance', { season: 'summer' }, {
  repeat: {
    cron: '0 2 1 6-8 *',  // 1st of June, July, August at 2 AM
    tz: 'UTC'
  }
});

Monitoring Repeatable Jobs

Monitor repeatable job execution and health.

// Monitor repeatable job execution
taskQueue.on('completed', (job, result) => {
  if (job.opts.repeat) {
    console.log(`Repeatable job ${job.name} completed:`, result);
    
    // Log execution time for performance monitoring
    const executionTime = job.finishedOn - job.processedOn;
    console.log(`Execution time: ${executionTime}ms`);
  }
});

taskQueue.on('failed', (job, err) => {
  if (job.opts.repeat) {
    console.error(`Repeatable job ${job.name} failed:`, err.message);
    
    // Alert on repeatable job failures
    sendAlert(`Repeatable job ${job.name} failed: ${err.message}`);
  }
});

// Periodic health check for repeatable jobs
async function checkRepeatableJobHealth() {
  const repeatableJobs = await taskQueue.getRepeatableJobs();
  const now = Date.now();
  
  for (const job of repeatableJobs) {
    const timeSinceNext = now - job.next;
    
    // Alert if job is overdue by more than 5 minutes
    if (timeSinceNext > 5 * 60 * 1000) {
      console.warn(`Repeatable job ${job.name} is overdue by ${Math.round(timeSinceNext / 1000)}s`);
    }
  }
}

// Run health check every 10 minutes
setInterval(checkRepeatableJobHealth, 10 * 60 * 1000);