@abtnode/queue 1.8.19 → 1.8.21
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/index.js +95 -5
- package/lib/store.js +9 -2
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -3,12 +3,14 @@ const uuid = require('uuid');
|
|
|
3
3
|
const Queue = require('fastq');
|
|
4
4
|
const EventEmitter = require('events');
|
|
5
5
|
const tryWithTimeout = require('@abtnode/util/lib/try-with-timeout');
|
|
6
|
+
const sleep = require('@abtnode/util/lib/sleep');
|
|
6
7
|
const debug = require('debug')('core/queue');
|
|
7
8
|
|
|
8
9
|
const JobStore = require('./store');
|
|
9
10
|
const logger = require('./logger');
|
|
10
11
|
|
|
11
12
|
const CANCELLED = '__CANCELLED__';
|
|
13
|
+
const MIN_DELAY = 5;
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
*
|
|
@@ -21,6 +23,7 @@ const CANCELLED = '__CANCELLED__';
|
|
|
21
23
|
* @param {number} Options.options.maxRetries [param=1] number of max retries, default 1
|
|
22
24
|
* @param {number} Options.options.maxTimeout [param=86400000] max timeout, in ms, default 86400000ms(1d)
|
|
23
25
|
* @param {number} Options.options.retryDelay [param=0] retry delay, in ms, default 0ms
|
|
26
|
+
* @param {boolean} Options.options.enableScheduledJob [param=false] enable scheduled job or not, default is false
|
|
24
27
|
*/
|
|
25
28
|
module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
26
29
|
if (typeof onJob !== 'function') {
|
|
@@ -38,6 +41,7 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
38
41
|
const concurrency = Math.max(options.concurrency || defaults.concurrency, 1);
|
|
39
42
|
const maxTimeout = Math.max(options.maxTimeout || defaults.maxTimeout, 0);
|
|
40
43
|
const retryDelay = Math.max(options.retryDelay || defaults.retryDelay, 0);
|
|
44
|
+
const enableScheduledJob = typeof options.enableScheduledJob === 'boolean' ? options.enableScheduledJob : false;
|
|
41
45
|
|
|
42
46
|
if (typeof options.maxRetries === 'number' && options.maxRetries >= 0) {
|
|
43
47
|
maxRetries = options.maxRetries;
|
|
@@ -46,6 +50,9 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
46
50
|
const store = new JobStore(file);
|
|
47
51
|
const queueEvents = new EventEmitter();
|
|
48
52
|
|
|
53
|
+
const getJobId = (jobId, job) =>
|
|
54
|
+
jobId || (typeof options.id === 'function' ? options.id(job) : uuid.v4()) || uuid.v4();
|
|
55
|
+
|
|
49
56
|
const queue = Queue(async ({ job, id, persist }, cb) => {
|
|
50
57
|
if (persist) {
|
|
51
58
|
try {
|
|
@@ -68,7 +75,32 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
68
75
|
}
|
|
69
76
|
}, concurrency);
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Push job to the queue, the old way of calling `push(job, jobId, persist)` will be deprecated
|
|
80
|
+
* @param {object} args
|
|
81
|
+
* @param {object} args.job The data of the job
|
|
82
|
+
* @param {string} args.jobId Optional, custom jobId
|
|
83
|
+
* @param {boolean} args.persist [persist=true] Persisting the job to the database
|
|
84
|
+
* @param {number} args.delay Optional, default with no delay, unit is second, for example: 10 will run after 10s
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
87
|
+
const push = (...args) => {
|
|
88
|
+
let job;
|
|
89
|
+
let jobId;
|
|
90
|
+
let persist;
|
|
91
|
+
let delay;
|
|
92
|
+
|
|
93
|
+
if (
|
|
94
|
+
args.length === 1 &&
|
|
95
|
+
args[0] &&
|
|
96
|
+
typeof args[0] === 'object' &&
|
|
97
|
+
(args[0].job || args[0].jobId || args[0].persist)
|
|
98
|
+
) {
|
|
99
|
+
[{ job, jobId, persist = true, delay }] = args;
|
|
100
|
+
} else {
|
|
101
|
+
[job, jobId, persist = true] = args;
|
|
102
|
+
}
|
|
103
|
+
|
|
72
104
|
const jobEvents = new EventEmitter();
|
|
73
105
|
const emit = (e, data) => {
|
|
74
106
|
queueEvents.emit(e, data);
|
|
@@ -79,10 +111,46 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
79
111
|
throw new Error('Can not queue empty job');
|
|
80
112
|
}
|
|
81
113
|
|
|
82
|
-
const id = jobId
|
|
114
|
+
const id = getJobId(jobId, job);
|
|
115
|
+
|
|
116
|
+
if (delay) {
|
|
117
|
+
if (!enableScheduledJob) {
|
|
118
|
+
throw new Error('The queue must have options.enableScheduledJob set to true to run delay jobs');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 这里不是精确的 delay, 延迟的时间太短没有意义,所以这里限制了最小 delay
|
|
122
|
+
if (delay < MIN_DELAY) {
|
|
123
|
+
throw new Error(`minimum delay is ${MIN_DELAY}s`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
store.addJob(
|
|
127
|
+
id,
|
|
128
|
+
job,
|
|
129
|
+
(err) => {
|
|
130
|
+
if (err) {
|
|
131
|
+
logger.error('failed to add job', err);
|
|
132
|
+
emit('failed', { id, job, error: err });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
emit('queued', { id, job });
|
|
137
|
+
},
|
|
138
|
+
{ delay, willRunAt: Date.now() + delay * 1000 }
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
jobEvents.id = id;
|
|
142
|
+
|
|
143
|
+
return jobEvents;
|
|
144
|
+
}
|
|
145
|
+
|
|
83
146
|
// eslint-disable-next-line no-shadow
|
|
84
|
-
const clearJob = (id) => {
|
|
147
|
+
const clearJob = (id, cb) => {
|
|
85
148
|
store.deleteJob(id, (e) => {
|
|
149
|
+
if (typeof cb === 'function') {
|
|
150
|
+
cb(e);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
86
154
|
if (e) {
|
|
87
155
|
logger.error('Failed to delete job from persistent store', { error: e, id });
|
|
88
156
|
}
|
|
@@ -111,8 +179,9 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
111
179
|
|
|
112
180
|
if (attrs.retryCount >= maxRetries) {
|
|
113
181
|
logger.info('fail job', { id });
|
|
114
|
-
|
|
115
|
-
|
|
182
|
+
clearJob(id, () => {
|
|
183
|
+
emit('failed', { id, job, error: err });
|
|
184
|
+
});
|
|
116
185
|
return;
|
|
117
186
|
}
|
|
118
187
|
|
|
@@ -205,6 +274,26 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
205
274
|
});
|
|
206
275
|
});
|
|
207
276
|
|
|
277
|
+
const loop = async () => {
|
|
278
|
+
if (enableScheduledJob === true) {
|
|
279
|
+
while (true) {
|
|
280
|
+
/* eslint-disable no-await-in-loop */
|
|
281
|
+
const jobs = await store.getScheduledJobs();
|
|
282
|
+
jobs.forEach((x) => {
|
|
283
|
+
if (x.job && x._id) {
|
|
284
|
+
push(x.job, x._id, false);
|
|
285
|
+
} else {
|
|
286
|
+
logger.info('skip invalid job from db', { job: x });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await sleep(2000);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
loop();
|
|
296
|
+
|
|
208
297
|
return Object.assign(queueEvents, {
|
|
209
298
|
store,
|
|
210
299
|
push,
|
|
@@ -215,6 +304,7 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
|
|
|
215
304
|
maxRetries,
|
|
216
305
|
maxTimeout,
|
|
217
306
|
retryDelay,
|
|
307
|
+
enableScheduledJob,
|
|
218
308
|
},
|
|
219
309
|
});
|
|
220
310
|
};
|
package/lib/store.js
CHANGED
|
@@ -27,7 +27,7 @@ class JobStore {
|
|
|
27
27
|
|
|
28
28
|
getJobs(cb) {
|
|
29
29
|
this.db
|
|
30
|
-
.cursor({})
|
|
30
|
+
.cursor({ delay: { $exists: false } })
|
|
31
31
|
.sort({ createdAt: 1 })
|
|
32
32
|
.exec((err, result) => {
|
|
33
33
|
if (err) {
|
|
@@ -37,6 +37,13 @@ class JobStore {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
getScheduledJobs() {
|
|
41
|
+
return this.db
|
|
42
|
+
.cursor({ delay: { $exists: true }, willRunAt: { $lte: Date.now() } })
|
|
43
|
+
.sort({ createdAt: 1 })
|
|
44
|
+
.exec();
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
updateJob(id, updates, cb) {
|
|
41
48
|
this.db.findOne({ _id: id }, (err, exist) => {
|
|
42
49
|
if (err) return cb(err);
|
|
@@ -55,7 +62,7 @@ class JobStore {
|
|
|
55
62
|
},
|
|
56
63
|
(e) => {
|
|
57
64
|
if (e) {
|
|
58
|
-
console.error('error updating job',
|
|
65
|
+
console.error('error updating job', e);
|
|
59
66
|
return cb(e, null);
|
|
60
67
|
}
|
|
61
68
|
return cb(null, Object.assign(exist, update));
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.8.
|
|
6
|
+
"version": "1.8.21",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/db": "1.8.
|
|
23
|
-
"@abtnode/util": "1.8.
|
|
22
|
+
"@abtnode/db": "1.8.21",
|
|
23
|
+
"@abtnode/util": "1.8.21",
|
|
24
24
|
"debug": "^4.3.4",
|
|
25
25
|
"fastq": "^1.13.0",
|
|
26
26
|
"uuid": "7.0.3"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "f990cddd5ef18ece7ada5d7573b8e0652d74abdc",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"jest": "^27.5.1"
|
|
31
31
|
}
|