@abtnode/queue 1.15.17 → 1.16.0-beta-8ee536d7

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 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
- const push = (job, jobId, persist = true) => {
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,11 +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 || (typeof options.id === 'function' ? options.id(job) : uuid.v4()) || uuid.v4();
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
+ }
83
145
 
84
146
  // eslint-disable-next-line no-shadow
85
- const clearJob = (id) => {
147
+ const clearJob = (id, cb) => {
86
148
  store.deleteJob(id, (e) => {
149
+ if (typeof cb === 'function') {
150
+ cb(e);
151
+ return;
152
+ }
153
+
87
154
  if (e) {
88
155
  logger.error('Failed to delete job from persistent store', { error: e, id });
89
156
  }
@@ -112,8 +179,9 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
112
179
 
113
180
  if (attrs.retryCount >= maxRetries) {
114
181
  logger.info('fail job', { id });
115
- emit('failed', { id, job, error: err });
116
- clearJob(id);
182
+ clearJob(id, () => {
183
+ emit('failed', { id, job, error: err });
184
+ });
117
185
  return;
118
186
  }
119
187
 
@@ -162,11 +230,22 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
162
230
 
163
231
  const cancel = async (id) =>
164
232
  new Promise((resolve, reject) => {
165
- store.updateJob(id, { cancelled: true }, (e, _job) => {
233
+ store.updateJob(id, { cancelled: true }, (e, doc) => {
166
234
  if (e) {
167
235
  return reject(e);
168
236
  }
169
- return resolve(_job);
237
+ return resolve(doc ? doc.job : null);
238
+ });
239
+ });
240
+
241
+ const getJob = async (id) =>
242
+ new Promise((resolve, reject) => {
243
+ store.getJob(id, (e, doc) => {
244
+ if (e) {
245
+ return reject(e);
246
+ }
247
+
248
+ return resolve(doc ? doc.job : null);
170
249
  });
171
250
  });
172
251
 
@@ -195,15 +274,38 @@ module.exports = function createQueue({ file, onJob, options = {} }) {
195
274
  });
196
275
  });
197
276
 
277
+ const loop = async () => {
278
+ if (enableScheduledJob === true) {
279
+ // eslint-disable-next-line no-constant-condition
280
+ while (true) {
281
+ /* eslint-disable no-await-in-loop */
282
+ const jobs = await store.getScheduledJobs();
283
+ jobs.forEach((x) => {
284
+ if (x.job && x._id) {
285
+ push(x.job, x._id, false);
286
+ } else {
287
+ logger.info('skip invalid job from db', { job: x });
288
+ }
289
+ });
290
+
291
+ await sleep(2000);
292
+ }
293
+ }
294
+ };
295
+
296
+ loop();
297
+
198
298
  return Object.assign(queueEvents, {
199
299
  store,
200
300
  push,
301
+ get: getJob,
201
302
  cancel,
202
303
  options: {
203
304
  concurrency,
204
305
  maxRetries,
205
306
  maxTimeout,
206
307
  retryDelay,
308
+ enableScheduledJob,
207
309
  },
208
310
  });
209
311
  };
package/lib/logger.js CHANGED
@@ -1,9 +1,9 @@
1
- /* eslint-disable no-console */
1
+ const debug = require('debug')(require('../package.json').name);
2
2
 
3
3
  const noop = () => {};
4
- const isTest = process.env.NODE_ENV === 'test';
4
+ const isTest = !!process.env.NODE_ENV && process.env.NODE_ENV.startsWith('test');
5
5
 
6
- module.exports = {
7
- info: isTest ? noop : (...args) => console.log(...args),
8
- error: isTest ? noop : (...args) => console.error(...args),
9
- };
6
+ module.exports = ['error', 'warn', 'info', 'debug', 'verbose', 'silly'].reduce((acc, fn) => {
7
+ acc[fn] = isTest ? noop : debug;
8
+ return acc;
9
+ }, {});
package/lib/store.js CHANGED
@@ -1,10 +1,7 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  /* eslint-disable func-names */
3
3
  /* eslint-disable consistent-return */
4
- const DataStore =
5
- process.env.NODE_ENV === 'test' || !process.env.NEDB_MULTI_PORT
6
- ? require('@nedb/core')
7
- : require('@nedb/multi')(Number(process.env.NEDB_MULTI_PORT));
4
+ const { DataStore } = require('@abtnode/db/lib/base');
8
5
 
9
6
  class JobStore {
10
7
  constructor(dbPath) {
@@ -17,9 +14,9 @@ class JobStore {
17
14
 
18
15
  isCancelled(id) {
19
16
  return new Promise((resolve, reject) => {
20
- this.db.findOne({ _id: id }, (e, attrs) => {
17
+ this.db.findOne({ _id: id }, (e, job) => {
21
18
  if (e) return reject(e);
22
- return resolve(!!attrs.cancelled);
19
+ return resolve(!!job && !!job.cancelled);
23
20
  });
24
21
  });
25
22
  }
@@ -30,7 +27,7 @@ class JobStore {
30
27
 
31
28
  getJobs(cb) {
32
29
  this.db
33
- .find({})
30
+ .cursor({ delay: { $exists: false } })
34
31
  .sort({ createdAt: 1 })
35
32
  .exec((err, result) => {
36
33
  if (err) {
@@ -40,6 +37,13 @@ class JobStore {
40
37
  });
41
38
  }
42
39
 
40
+ getScheduledJobs() {
41
+ return this.db
42
+ .cursor({ delay: { $exists: true }, willRunAt: { $lte: Date.now() } })
43
+ .sort({ createdAt: 1 })
44
+ .exec();
45
+ }
46
+
43
47
  updateJob(id, updates, cb) {
44
48
  this.db.findOne({ _id: id }, (err, exist) => {
45
49
  if (err) return cb(err);
@@ -58,7 +62,7 @@ class JobStore {
58
62
  },
59
63
  (e) => {
60
64
  if (e) {
61
- console.error('error updating job', err);
65
+ console.error('error updating job', e);
62
66
  return cb(e, null);
63
67
  }
64
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.15.17",
6
+ "version": "1.16.0-beta-8ee536d7",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,15 +19,14 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/util": "1.15.17",
23
- "@nedb/core": "^1.2.2",
24
- "@nedb/multi": "^1.2.2",
25
- "debug": "^4.3.2",
26
- "fastq": "^1.8.0",
22
+ "@abtnode/db": "1.16.0-beta-8ee536d7",
23
+ "@abtnode/util": "1.16.0-beta-8ee536d7",
24
+ "debug": "^4.3.4",
25
+ "fastq": "^1.13.0",
27
26
  "uuid": "7.0.3"
28
27
  },
29
- "gitHead": "22715c3ea74d0230f3413162a17f491614b6735a",
28
+ "gitHead": "57d0c45be311a5fbc1c0fffa2814b62c1a3ee34c",
30
29
  "devDependencies": {
31
- "jest": "^27.3.1"
30
+ "jest": "^27.5.1"
32
31
  }
33
32
  }