0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-25 02:31:59 -05:00

Added job status checks to job manager

refs ttps://github.com/TryGhost/Toolbox/issues/358

- One off jobs need a way to check for prior execution and await for their completion (in cases when it is reasonably short).
- Added `hasExecuted` and `awaitCompletion` methods to the job manager allowing to monitor one off job state
This commit is contained in:
Naz 2022-07-27 18:04:19 +01:00
parent b7853323ae
commit 6d5a5e90b1
2 changed files with 103 additions and 0 deletions

View file

@ -1,4 +1,6 @@
const path = require('path');
const util = require('util');
const setTimeoutPromise = util.promisify(setTimeout);
const fastq = require('fastq');
const later = require('@breejs/later');
const Bree = require('bree');
@ -229,6 +231,40 @@ class JobManager {
this.addJob({name, job, data, offloaded});
}
/**
* Checks if the one-off job has ever been successfully executed
* @param {String} name one-off job name
*/
async hasExecuted(name) {
// TODO: return false if the job has failed?
if (this._jobsRepository) {
const persistedJob = await this._jobsRepository.read(name);
return !!persistedJob;
} else {
return false;
}
}
/**
*
* @param {String} name one-off job name
* @returns
*/
async awaitCompletion(name) {
const persistedJob = await this._jobsRepository.read({
name
});
if (!persistedJob || !['finished', 'failed'].includes(persistedJob.get('status'))) {
// NOTE: can implement exponential backoff here if that's ever needed
await setTimeoutPromise(500);
return this.awaitCompletion(name);
}
return !!persistedJob;
}
/**
* Removes an "offloaded" job from scheduled jobs queue.
* It's NOT yet possible to remove "inline" jobs (will be possible when scheduling is added https://github.com/breejs/bree/issues/68).

View file

@ -502,6 +502,73 @@ describe('Job Manager', function () {
});
});
describe('Job execution progress', function () {
it('checks if job has ever been executed', async function () {
const spy = sinon.spy();
const JobModel = {
findOne: sinon.stub()
.withArgs('solovei')
.onCall(0)
.resolves(null)
.onCall(1)
.resolves(null)
.resolves({id: 'unique', name: 'solovei'}),
add: sinon.stub().resolves()
};
const jobManager = new JobManager({JobModel});
let executed = await jobManager.hasExecuted('solovei');
should.equal(executed, false);
await jobManager.addOneOffJob({
job: spy,
name: 'solovei'
});
assert.equal(JobModel.add.called, true);
executed = await jobManager.hasExecuted('solovei');
should.equal(executed, true);
});
it('can wait for job completion', async function () {
const spy = sinon.spy();
let status = 'queued';
const jobWithDelay = async () => {
await delay(80);
status = 'finished';
spy();
};
const JobModel = {
findOne: sinon.stub()
// first call when adding a job
.withArgs('solovei')
.onCall(0)
// first call when adding a job
.resolves(null)
.onCall(1)
.resolves(null)
.resolves({
id: 'unique',
get: () => status
}),
add: sinon.stub().resolves()
};
const jobManager = new JobManager({JobModel});
await jobManager.addOneOffJob({
job: jobWithDelay,
name: 'solovei',
offloaded: false
});
should.equal(spy.called, false);
await jobManager.awaitCompletion('solovei');
should.equal(spy.called, true);
});
});
describe('Remove a job', function () {
it('removes a scheduled job from the queue', async function () {
const jobManager = new JobManager({});