cron-master
v1.0.1
Published
Better cron management for Node.js applications
Downloads
390
Maintainers
Readme
cron-master
cron-master provides a standardised way to manage your Node.js CronJobs created using the cron module.
Typically in projects we'll see instances of CronJob from the fantastic cron
module scattered throughout the codebase meaning they're hard to find and not
managed in a consistent manner. cron-master encourages the pattern of storing
all jobs in a single location, and ensures they all follow the same pattern. It
also adds events to your CronJob instances so you can add generic hooks for
logging, detecting errors, and preventing overlapping calls that can cause
unpredictable results.
For example, if you have a job that runs every 5 minutes, did you remember to ensure that next time it runs the previous run has completed!? cron-master removes the need to check for that case, it simply won't let the same job run again until the currently running call has completed.
Features
- Prevents a the same CronJob running more than once concurrently.
- Provides a structured way to create a manage jobs.
- Enables jobs to emit and receive events, also provides useful default events.
- Automatically computes time taken by each CronJob to complete.
- Provides an error and/or result from each job via an event and/or callback.
Install
Seemples!
npm install cron-master --save
API
module.getInstance()
Factory function that returns manager instances.
const cmaster = require('cron-master');
// This is our instance
const instance = cmaster.getInstance()
instance.loadJobs(absolutePath, callback)
Loads all jobs contained in the specified folder, and place them under the managment of cron-master. Each file in the folder must have module.exports set to an instance of CronMasterJob.
As of version 0.2.0 the jobs are not cached. This means if you call load jobs a second time with the same jobs, a new instance of the file containing the job will be loaded. This means the require.cache for the specifc cron files your loading is deleted to prevent conflicts
The callback should be of the format function(err, jobs). jobs will be an Array of the managed jobs that were loaded.
const path = require('path')
const cmaster = require('cron-master');
const manager = cmaster()
instance.loadJobs(path.join(__dirname, '../', 'my-jobs'), function (err, jobs) {
if (err) {
console.error('Failed to load jobs!');
} else {
console.log('Loaded %d jobs!', jobs.length);
}
});
instance.hasRunningJobs()
Returns a boolean indicating if any jobs are currently running.
instance.getJobs()
Get an Array containing all currently loaded jobs.
instance.getRunningJobs()
Get an Array containing all currently running jobs.
instance.startJobs(callback)
Starts all jobs that are being managed so that they will be run at the time specified by their cron tab. Internally this will call the cron module start function for each job.
instance.stopJobs(callback)
Stops all jobs being managed so they will no longer execute at the time specified by their cron tab. If any jobs are currently in the middle of a tick callback won't fire until they're all complete. Internally this will call the cron module stop function for each job. You can short circuit your job function to stop early by by using the STOP_REQUESTED event, examples below.
module.EVENTS
A map of the event names, helps avoid spelling errors etc. Events:
- TICK_STARTED - 'tick-started',
- START_REQUESTED - 'start-requested',
- TICK_COMPLETE - 'tick-complete',
- STOP_REQUESTED - 'stop-requested',
- STOPPED - 'stopped',
- TIME_WARNING - 'time-warning',
- OVERLAPPING_CALL - 'overlapping-call'
Usage examples are included below.
module.CronMasterJob
This is a replacement for cron.CronJob (the cron
module from npm) that you
usually use. It requires parameters to be passed in an Object.
The function you usually pass as the onTick parameter the cron module doesn't take a callback, but when using cron-master you must accept a callback into your cron onTick function as shown below.
Each job exposes the following functions for direct use if required:
instance.start([callback])
Starts the job so that it will run at the specified time(s).
instance.forceRun([callback])
Force the job to run immediately even if already running.
instance.run([callback])
Run the job if it is not currently in progress.
instance.stop([callback])
Stop the job so that it will not run at the specified time(s).
Here's a trivial example of creating a CronMasterJob:
var CronMasterJob = require('cron-master').CronMasterJob;
module.exports = new CronMasterJob({
// Optional. Used to determine when to trigger the 'time-warning'. Fires after
// the provided number of milliseconds (e.g 2 minutes in the case below) has
// passed if the job has not called the done callback
timeThreshold: 2 * 60 * 1000,
// Optional. Can be used to add useful meta data for a job
meta: {
name: 'Test Job'
},
// Just the usual params that you pass to the "cron" module!
cronParams: {
cronTime: '* * * * * *',
onTick: function (job, done) {
console.log('Running job!');
done(null, 'ok');
}
}
});
Examples
Basic Job
var CronMasterJob = require('cron-master').CronMasterJob;
module.exports = new CronMasterJob({
// The usual params that you pass to the "cron" module go here
cronParams: {
cronTime: '* * * * * *',
onTick: function (job, done) {
console.log('running job');
done(null, 'result');
}
}
});
Adding Events to Jobs
var path = require('path')
, cmaster = require('cron-master');
cmaster.loadJobs(path.join(__dirname, '../', 'my-jobs'), function (err, jobs) {
if (err) {
console.error('Failed to load jobs!');
} else {
jobs.forEach(function (job) {
// Using event map for name.
// Log output when the job is about to run.
job.on(cmaster.EVENTS.TICK_STARTED, function () {
console.log('Job tick starting!');
});
// Using String for event name.
// Log output when the job has complete.
job.on('tick-complete', function (err, res, time) {
console.log('Job tick complete in %d!', time);
if (err) {
console.error('Error running job %s: %s', job.meta.name, err);
} else {
console.log('Job complete. Result: %s', res);
}
});
job.on(events.TIME_WARNING, function () {
console.log('Job has %s exceeded expected run time!', job.meta.name);
});
job.on('overlapping-call', function () {
console.log(
'Job %s attempting to run before previous tick is complete!',
job.meta.name
);
});
});
}
});
Advanved Job with Short Circuit
The job below runs every 2 minutes. Interestingly however, it binds a one time event listener to see if the job was requested to stop. If so it will prevent further execution and simply skip the business logic.
const CronMasterJob = require('cron-master').CronMasterJob
const async = require('async')
const db = require('lib/db-wrapper')
/**
* Function to call for this cron job.
* @param {CronMaster} job Reference to the job itself, use for events.
* @param {Function} done Used to signal the job is finished.
*/
function cronFn (job, done) {
var stopped = false;
// Let's use the job events to allow this job to be stopped mid process!
job.once('stop-requested', stopListener);
function stopListener () {
stopped = true;
}
function letterProcessor (letter, next) {
if (!stopped) {
// Do nothing, just skip since a stop was requested
next();
} else {
db.insert(letter, next);
}
}
async.eachSeries(['a', 'b', 'c'], letterProcessor, function (err) {
// Remove event bindings to prevent memory leaks!
job.removeListener('stop-requested', stopListener);
// Pass the result to the CronMasterJob callback
done(err);
});
}
module.exports = new CronMasterJob({
// Optional. Used to determine when to trigger the 'time-warning'. Fires after
// the provided number of milliseconds (e.g 2 minutes in the case below) has
// passed if the job has not called the done callback
timeThreshold: (2 * 60 * 1000),
// Just the usual params that you pass to the "cron" module!
cronParams: {
cronTime: '00 */2 * * * *',
onTick: cronFn
}
});
Changelog
1.0.1
- Remove
EVENTS
from typings - Fix return type for
loadJobs
in typings - Fix
CronMasterJobOptions
to acceptmeta
andcronParams
1.0.0
- Changed public interface
- Added TypeScript typings
- Updated tests to use Jest
- Added coverage
- Added Synk
- Added StandardJS formatting
0.3.0
- Renamed the forceRun function to run to reflect it's true behaviour
- Added run function that will only run a job if it is not already running
0.2.0
- Delete require cache for each job loaded
- Add function hasRunningJobs()
- Add function getJobs()
- Add function getRunningJobs()
- Improve test cases
0.1.0
- Initial release