Added errorHandler option to customize job error logic

closes https://github.com/TryGhost/Ghost-Utils/issues/118

- Custom error handling is needed to be able to override default bree
error handling logic.
- bree bump to 4.1.0 also fixed logging errors (object Object fix in
tests)
- The handler function receives two parameters. First contains an error
that has been thrown by the job. Second, job and worker metadata
This commit is contained in:
Naz 2020-12-07 16:50:41 +13:00
parent 0aff1a305e
commit c13d8a2fa6
3 changed files with 38 additions and 17 deletions

View File

@ -26,14 +26,20 @@ const handler = (error, result) => {
};
class JobManager {
constructor(logging) {
/**
* @param {Object} options
* @param {Object} [options.logging] - custom logging handler, defaults to console
* @param {Function} [options.errorHandler] - custom job error handler
*/
constructor({logging, errorHandler}) {
this.queue = fastq(this, worker, 1);
this.bree = new Bree({
root: false, // set this to `false` to prevent requiring a root directory of jobs
hasSeconds: true, // precission is needed to avoid task ovelaps after immidiate execution
outputWorkerMetadata: true,
logger: logging
logger: logging,
errorHandler: errorHandler
});
this.logging = logging;

View File

@ -28,7 +28,7 @@
},
"dependencies": {
"@breejs/later": "4.0.2",
"bree": "4.0.0",
"bree": "4.1.0",
"cron-validate": "1.4.1",
"fastq": "1.9.0",
"p-wait-for": "3.1.0"

View File

@ -20,7 +20,7 @@ describe('Job Manager', function () {
});
it('public interface', function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
should.exist(jobManager.addJob);
should.exist(jobManager.scheduleJob);
@ -29,7 +29,7 @@ describe('Job Manager', function () {
describe('Add a Job', function () {
it('adds a job to a queue', async function () {
const spy = sinon.spy();
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
jobManager.addJob(spy, 'test data');
should(jobManager.queue.idle()).be.false();
@ -44,7 +44,7 @@ describe('Job Manager', function () {
it('handles failed job gracefully', async function () {
const spy = sinon.stub().throws();
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
jobManager.addJob(spy, 'test data');
should(jobManager.queue.idle()).be.false();
@ -61,7 +61,7 @@ describe('Job Manager', function () {
describe('Schedule a Job', function () {
it('fails to schedule for invalid scheduling expression', function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
try {
jobManager.scheduleJob('invalid expression', 'jobName', {});
@ -71,7 +71,7 @@ describe('Job Manager', function () {
});
it('fails to schedule for no job name', function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
try {
jobManager.scheduleJob('invalid expression', () => {}, {});
@ -81,7 +81,7 @@ describe('Job Manager', function () {
});
it('schedules a job using date format', async function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
const timeInTenSeconds = new Date(Date.now() + 10);
const jobPath = path.resolve(__dirname, './jobs/simple.js');
@ -115,7 +115,7 @@ describe('Job Manager', function () {
});
it('schedules a job to run immediately', async function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
const clock = FakeTimers.install({now: Date.now()});
const jobPath = path.resolve(__dirname, './jobs/simple.js');
@ -144,7 +144,7 @@ describe('Job Manager', function () {
});
it('fails to schedule a job with the same name to run immediately one after another', async function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
const clock = FakeTimers.install({now: Date.now()});
const jobPath = path.resolve(__dirname, './jobs/simple.js');
@ -169,19 +169,34 @@ describe('Job Manager', function () {
should(jobManager.bree.workers['job-now']).type('undefined');
// note: job name resolves to [Object object], test can be made more precise
// once that is fixed in Bree.
(() => {
jobManager.scheduleJob(undefined, jobPath, undefined, 'job-now');
}).should.throw('Job #1 has a duplicate job name of [object Object]');
}).should.throw('Job #1 has a duplicate job name of job-now');
clock.uninstall();
});
it('uses custom error handler when job fails', async function (){
let job = function namedJob() {
throw new Error('job error');
};
const spyHandler = sinon.spy();
const jobManager = new JobManager({logging, errorHandler: spyHandler});
jobManager.scheduleJob(undefined, job, undefined, 'will-fail');
// give time to execute the job
await delay(100);
should(spyHandler.called).be.true();
should(spyHandler.args[0][0].message).equal('job error');
should(spyHandler.args[1][1].name).equal('will-fail');
});
});
describe('Remove a Job', function () {
it('removes a scheduled job from the queue', async function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
const timeInTenSeconds = new Date(Date.now() + 10);
const jobPath = path.resolve(__dirname, './jobs/simple.js');
@ -197,7 +212,7 @@ describe('Job Manager', function () {
describe('Shutdown', function () {
it('gracefully shuts down a synchronous jobs', async function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
jobManager.addJob(require('./jobs/timed-job'), 200);
@ -209,7 +224,7 @@ describe('Job Manager', function () {
});
it('gracefully shuts down an interval job', async function () {
const jobManager = new JobManager(logging);
const jobManager = new JobManager({logging});
jobManager.scheduleJob('every 5 seconds', path.resolve(__dirname, './jobs/graceful.js'));