Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run Cron Job in Node.js application that uses cluster module?

I'm using node-cron module for scheduling tasks in Node.js application. I also want run the application in several processes using core cluster module.

Running application in several processes ends up in scheduled tasks execution in each process (e.g. if task was to send an email the email would be sent multiple times).

What are the best practices/possible ways of running cron job along with cluster module? Should I create some separate process which will handle only cron job and do not accept any requests. If yes, how can I do that in a right way?

like image 998
epidemiya30 Avatar asked Jun 15 '15 11:06

epidemiya30


People also ask

What is Cluster module in node js?

Definition and Usage. The cluster module provides a way of creating child processes that runs simultaneously and share the same server port. Node.

How does cluster work Nodejs?

A Look at ClusteringThe Node. js Cluster module enables the creation of child processes (workers) that run simultaneously and share the same server port. Each spawned child has its own event loop, memory, and V8 instance. The child processes use IPC (Inter-process communication) to communicate with the parent Node.

How does Nodejs scheduler work?

Node Schedule is a flexible cron-like and not-cron-like job scheduler for Node. js. It allows you to schedule jobs (arbitrary functions) for execution at specific dates, with optional recurrence rules. It only uses a single timer at any given time (rather than reevaluating upcoming jobs every second/minute).


2 Answers

If are using PM2, You can use an environment variable provided by PM2 itself called NODE_APP_INSTANCE which requires PM2 2.5 or greater.

NODE_APP_INSTANCE environment variable can be used to determine difference between process, for example you may want to run a cronjob only on one process, you can just do this

if(process.env.NODE_APP_INSTANCE == 0) { //schedule your cron job here since this part will be executed for only one cluster } ,

Since two processes can never have the same number.

More Info on PM2 official doc here.

like image 133
Rahul Kumar Avatar answered Sep 19 '22 13:09

Rahul Kumar


After some research I ended up with "Distributed locks using Redis" solution. There is node module for that: node-redis-warlock.

Hope this answer will be useful for someone else.

UPDATE. Minimal sample code:

var Warlock = require('node-redis-warlock'),     redis = require('redis');  // Establish a redis client redis = redis.createClient();  // and pass it to warlock var warlock = new Warlock(redis);  function executeOnce (key, callback) {     warlock.lock(key, 20000, function(err, unlock){         if (err) {             // Something went wrong and we weren't able to set a lock             return;         }          if (typeof unlock === 'function') {             setTimeout(function() {                 callback(unlock);             }, 1000);         }     }); }  // Executes call back only once executeOnce('every-three-hours-lock', function(unlock) {     // Do here any stuff that should be done only once...                 unlock();           }); 

UPDATE 2. More detailed example:

const CronJob = require('cron').CronJob; const Warlock = require('node-redis-warlock'); const redis = require('redis').createClient(); const warlock = new Warlock(redis); const async = require('async');  function executeOnce (key, callback) {     warlock.lock(key, 20000, function(err, unlock) {         if (err) {             // Something went wrong and we weren't able to set a lock             return;         }          if (typeof unlock === 'function') {             setTimeout(function() {                 callback(unlock);             }, 1000);         }     }); }  function everyMinuteJobTasks (unlock) {     async.parallel([         sendEmailNotifications,         updateSomething,         // etc...     ],     (err) => {         if (err) {             logger.error(err);         }          unlock();     }); }  let everyMinuteJob = new CronJob({     cronTime: '*/1 * * * *',     onTick: function () {         executeOnce('every-minute-lock', everyMinuteJobTasks);     },     start: true,     runOnInit: true });  /* Actual tasks */ let sendEmailNotifications = function(done) {     // Do stuff here     // Call done() when finished or call done(err) if error occurred }  let updateSomething = function(done) {     // Do stuff here     // Call done() when finished or call done(err) if error occurred }  // etc... 
like image 43
epidemiya30 Avatar answered Sep 20 '22 13:09

epidemiya30