Search code examples
javascriptnode.jscronschedule

Schedule multiple cron jobs 24h apart on Node.js server


I currently have 3 tasks that need to be executed every 3 days, and I want them to be staggered with 24h in between so that only one job runs on a given day for the following schedule:

day1: job1

day2: job2

day3: job3

day4: job1

day5: job2

day6: job3 and so on

Here is my current (clumsy) approach using 24h timeouts to stagger the jobs

import cron from 'node-cron'

cron.schedule('0 0 12 */3 * *', () => {
  console.log('run job 1')
})
setTimeout(() => {  
    cron.schedule('0 0 12 */3 * *', () => {
        console.log('run job 2')
    })
    setTimeout(() => {  
        cron.schedule('0 0 12 */3 * *', () => {
            console.log('run job 3')
        }) 
    }, 24 * 3600 * 1000)
}, 24 * 3600 * 1000)

This seems very unstable and the timing gets screwed up every time the server gets interrupted or has to be restarted.

Is there a way to tell cron the timing between multiple jobs so that they are automatically spaced 24h apart, and so that cron knows which job is supposed to run on any given calendar day?


Solution

  • If I were to do something like this, my approach would be as follows:

    import cron from 'node-cron';
    
    let currentJob = 0;
    
    cron.schedule('0 0 12 * * *', () => {
      const jobs = [ job1, job2, job3 ];
      jobs[currentJob]();
      currentJob = (currentJob + 1) % 3;
    });
    

    This way, a new job is run each day.

    But this has an implication, whenever the server is restarted or interrupted, regardless of the last job that was run, the first task is run. This is an undesired behaviour if the order in which the tasks are carried out matters, and I suspect it does. You will then need to somehow persist the value currentJob so that it doesn't reset to zero. Something like this should suffice:

    import cron from 'node-cron';
    import fs from "fs";
    
    cron.schedule('0 0 12 * * *', () => {
      const jobs = [ job1, job2, job3 ];
      currentJob = getCurrentJobValue();
      jobs[currentJob]();
      setCurrentJobValue(currentJob + 1);
    });
    
    const FILE_NAME = "currentJob.json";
    
    function getCurrentJobValue() {
        if (!fs.existsSync(FILE_NAME)) {
            fs.writeFileSync(FILE_NAME, JSON.stringify({
                currentJob: 0
            }));
            return 0;
        }
        let data = fs.readFileSync(FILE_NAME);
        return JSON.parse(data).currentJob;
    }
    
    function setCurrentJobValue(value) {
        var data = fs.readFileSync(FILE_NAME);
        fs.writeFileSync(FILE_NAME, JSON.stringify({
            currentJob: value % 3
        }));
        return value % 3;
    }
    

    This is a more robust solution as it covers the case of the server restarting.