@tryghost/job-manager 0.8.23 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/job-manager.js +86 -3
- package/lib/jobs-repository.js +23 -0
- package/package.json +5 -5
package/lib/job-manager.js
CHANGED
|
@@ -7,6 +7,7 @@ const {UnhandledJobError, IncorrectUsageError} = require('@tryghost/errors');
|
|
|
7
7
|
const logging = require('@tryghost/logging');
|
|
8
8
|
const isCronExpression = require('./is-cron-expression');
|
|
9
9
|
const assembleBreeJob = require('./assemble-bree-job');
|
|
10
|
+
const JobsRepository = require('./jobs-repository');
|
|
10
11
|
|
|
11
12
|
const worker = async (task, callback) => {
|
|
12
13
|
try {
|
|
@@ -31,18 +32,73 @@ class JobManager {
|
|
|
31
32
|
* @param {Object} options
|
|
32
33
|
* @param {Function} [options.errorHandler] - custom job error handler
|
|
33
34
|
* @param {Function} [options.workerMessageHandler] - custom message handler coming from workers
|
|
35
|
+
* @param {Object} [options.JobModel] - a model which can persist job data in the storage
|
|
34
36
|
*/
|
|
35
|
-
constructor({errorHandler, workerMessageHandler}) {
|
|
37
|
+
constructor({errorHandler, workerMessageHandler, JobModel}) {
|
|
36
38
|
this.queue = fastq(this, worker, 1);
|
|
39
|
+
this._jobMessageHandler = this._jobMessageHandler.bind(this);
|
|
40
|
+
this._jobErrorHandler = this._jobErrorHandler.bind(this);
|
|
41
|
+
|
|
42
|
+
const combinedMessageHandler = workerMessageHandler
|
|
43
|
+
? ({name, message}) => {
|
|
44
|
+
workerMessageHandler({name, message});
|
|
45
|
+
this._jobMessageHandler({name, message});
|
|
46
|
+
}
|
|
47
|
+
: this._jobMessageHandler;
|
|
48
|
+
|
|
49
|
+
const combinedErrorHandler = errorHandler
|
|
50
|
+
? (error, workerMeta) => {
|
|
51
|
+
errorHandler(error, workerMeta);
|
|
52
|
+
this._jobErrorHandler(error, workerMeta);
|
|
53
|
+
}
|
|
54
|
+
: this._jobErrorHandler;
|
|
37
55
|
|
|
38
56
|
this.bree = new Bree({
|
|
39
57
|
root: false, // set this to `false` to prevent requiring a root directory of jobs
|
|
40
58
|
hasSeconds: true, // precision is needed to avoid task overlaps after immediate execution
|
|
41
59
|
outputWorkerMetadata: true,
|
|
42
60
|
logger: logging,
|
|
43
|
-
errorHandler:
|
|
44
|
-
workerMessageHandler:
|
|
61
|
+
errorHandler: combinedErrorHandler,
|
|
62
|
+
workerMessageHandler: combinedMessageHandler
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
this.bree.on('worker created', (name) => {
|
|
66
|
+
this._jobMessageHandler({name, message: 'started'});
|
|
45
67
|
});
|
|
68
|
+
|
|
69
|
+
this._jobsRepository = new JobsRepository({JobModel});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async _jobMessageHandler({name, message}) {
|
|
73
|
+
if (message === 'started') {
|
|
74
|
+
const job = await this._jobsRepository.read(name);
|
|
75
|
+
|
|
76
|
+
if (job) {
|
|
77
|
+
await this._jobsRepository.update(job.id, {
|
|
78
|
+
status: 'started',
|
|
79
|
+
started_at: new Date()
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
} else if (message === 'done') {
|
|
83
|
+
const job = await this._jobsRepository.read(name);
|
|
84
|
+
|
|
85
|
+
if (job) {
|
|
86
|
+
await this._jobsRepository.update(job.id, {
|
|
87
|
+
status: 'finished',
|
|
88
|
+
finished_at: new Date()
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async _jobErrorHandler(error, workerMeta) {
|
|
95
|
+
const job = await this._jobsRepository.read(workerMeta.name);
|
|
96
|
+
|
|
97
|
+
if (job) {
|
|
98
|
+
await this._jobsRepository.update(job.id, {
|
|
99
|
+
status: 'failed'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
46
102
|
}
|
|
47
103
|
|
|
48
104
|
/**
|
|
@@ -118,6 +174,33 @@ class JobManager {
|
|
|
118
174
|
}
|
|
119
175
|
}
|
|
120
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Adds a job that could ever be executed once.
|
|
179
|
+
*
|
|
180
|
+
* @param {Object} GhostJob - job options
|
|
181
|
+
* @prop {Function | String} GhostJob.job - function or path to a module defining a job
|
|
182
|
+
* @prop {String} [GhostJob.name] - unique job name, if not provided takes function name or job script filename
|
|
183
|
+
* @prop {String | Date} [GhostJob.at] - Date, cron or human readable schedule format. Manage will do immediate execution if not specified. Not supported for "inline" jobs
|
|
184
|
+
* @prop {Object} [GhostJob.data] - data to be passed into the job
|
|
185
|
+
* @prop {Boolean} [GhostJob.offloaded] - creates an "offloaded" job running in a worker thread by default. If set to "false" runs an "inline" job on the same event loop
|
|
186
|
+
*/
|
|
187
|
+
async addOneOffJob({name, job, data, offloaded = true}) {
|
|
188
|
+
const persistedJob = await this._jobsRepository.read(name);
|
|
189
|
+
|
|
190
|
+
if (persistedJob) {
|
|
191
|
+
throw new IncorrectUsageError({
|
|
192
|
+
message: `A "${name}" one off job has already been executed.`
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
await this._jobsRepository.add({
|
|
197
|
+
name,
|
|
198
|
+
status: 'queued'
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
this.addJob({name, job, data, offloaded});
|
|
202
|
+
}
|
|
203
|
+
|
|
121
204
|
/**
|
|
122
205
|
* Removes an "offloaded" job from scheduled jobs queue.
|
|
123
206
|
* It's NOT yet possible to remove "inline" jobs (will be possible when scheduling is added https://github.com/breejs/bree/issues/68).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class JobsRepository {
|
|
2
|
+
constructor({JobModel}) {
|
|
3
|
+
this._JobModel = JobModel;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async add(data) {
|
|
7
|
+
const job = await this._JobModel.add(data);
|
|
8
|
+
|
|
9
|
+
return job;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async read(name) {
|
|
13
|
+
const job = await this._JobModel.findOne({name});
|
|
14
|
+
|
|
15
|
+
return job;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async update(id, data) {
|
|
19
|
+
await this._JobModel.edit(data, {id});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = JobsRepository;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/job-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"repository": "https://github.com/TryGhost/Utils/tree/main/packages/job-manager",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@sinonjs/fake-timers": "9.1.2",
|
|
23
|
-
"c8": "7.
|
|
24
|
-
"date-fns": "2.
|
|
23
|
+
"c8": "7.12.0",
|
|
24
|
+
"date-fns": "2.29.1",
|
|
25
25
|
"delay": "5.0.0",
|
|
26
26
|
"mocha": "10.0.0",
|
|
27
27
|
"should": "13.2.3",
|
|
28
|
-
"sinon": "
|
|
28
|
+
"sinon": "14.0.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@breejs/later": "^4.0.2",
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"fastq": "^1.11.0",
|
|
36
36
|
"p-wait-for": "^3.2.0"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "2ce0a263975cf6e0f34233caf19404fd8ab5e7a5"
|
|
39
39
|
}
|