pg-boss 10.0.0-beta17 → 10.0.0-beta18
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/README.md +2 -2
- package/package.json +1 -2
- package/src/attorney.js +7 -8
- package/src/manager.js +42 -77
- package/src/plans.js +44 -21
- package/src/timekeeper.js +15 -24
- package/types.d.ts +16 -38
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ async function readme() {
|
|
|
19
19
|
|
|
20
20
|
console.log(`created job ${id} in queue ${queue}`)
|
|
21
21
|
|
|
22
|
-
await boss.work(queue, async job => {
|
|
22
|
+
await boss.work(queue, async ([ job ]) => {
|
|
23
23
|
console.log(`received job ${job.id} with data ${JSON.stringify(job.data)}`)
|
|
24
24
|
})
|
|
25
25
|
}
|
|
@@ -47,7 +47,7 @@ This will likely cater the most to teams already familiar with the simplicity of
|
|
|
47
47
|
|
|
48
48
|
## Installation
|
|
49
49
|
|
|
50
|
-
```
|
|
50
|
+
```bash
|
|
51
51
|
# npm
|
|
52
52
|
npm install pg-boss
|
|
53
53
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pg-boss",
|
|
3
|
-
"version": "10.0.0-
|
|
3
|
+
"version": "10.0.0-beta18",
|
|
4
4
|
"description": "Queueing jobs in Postgres from Node.js like a boss",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"cron-parser": "^4.0.0",
|
|
11
|
-
"p-map": "^4.0.0",
|
|
12
11
|
"pg": "^8.5.1",
|
|
13
12
|
"serialize-error": "^8.1.0"
|
|
14
13
|
},
|
package/src/attorney.js
CHANGED
|
@@ -124,24 +124,23 @@ function checkWorkArgs (name, args, defaults) {
|
|
|
124
124
|
|
|
125
125
|
applyPollingInterval(options, defaults)
|
|
126
126
|
|
|
127
|
-
assert(!('teamConcurrency' in options) ||
|
|
128
|
-
(Number.isInteger(options.teamConcurrency) && options.teamConcurrency >= 1 && options.teamConcurrency <= 1000),
|
|
129
|
-
'teamConcurrency must be an integer between 1 and 1000')
|
|
130
|
-
|
|
131
|
-
assert(!('teamSize' in options) || (Number.isInteger(options.teamSize) && options.teamSize >= 1), 'teamSize must be an integer > 0')
|
|
132
127
|
assert(!('batchSize' in options) || (Number.isInteger(options.batchSize) && options.batchSize >= 1), 'batchSize must be an integer > 0')
|
|
133
128
|
assert(!('includeMetadata' in options) || typeof options.includeMetadata === 'boolean', 'includeMetadata must be a boolean')
|
|
129
|
+
assert(!('priority' in options) || typeof options.priority === 'boolean', 'priority must be a boolean')
|
|
130
|
+
|
|
131
|
+
options.batchSize = options.batchSize || 1
|
|
134
132
|
|
|
135
133
|
return { options, callback }
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
function checkFetchArgs (name,
|
|
136
|
+
function checkFetchArgs (name, options) {
|
|
139
137
|
assert(name, 'missing queue name')
|
|
140
138
|
|
|
141
|
-
assert(!batchSize || (Number.isInteger(batchSize) && batchSize >= 1), 'batchSize must be an integer > 0')
|
|
139
|
+
assert(!('batchSize' in options) || (Number.isInteger(options.batchSize) && options.batchSize >= 1), 'batchSize must be an integer > 0')
|
|
142
140
|
assert(!('includeMetadata' in options) || typeof options.includeMetadata === 'boolean', 'includeMetadata must be a boolean')
|
|
141
|
+
assert(!('priority' in options) || typeof options.priority === 'boolean', 'priority must be a boolean')
|
|
143
142
|
|
|
144
|
-
|
|
143
|
+
options.batchSize = options.batchSize || 1
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
function getConfig (value) {
|
package/src/manager.js
CHANGED
|
@@ -2,7 +2,6 @@ const assert = require('assert')
|
|
|
2
2
|
const EventEmitter = require('events')
|
|
3
3
|
const { randomUUID } = require('crypto')
|
|
4
4
|
const { serializeError: stringify } = require('serialize-error')
|
|
5
|
-
const pMap = require('p-map')
|
|
6
5
|
const { delay } = require('./tools')
|
|
7
6
|
const Attorney = require('./attorney')
|
|
8
7
|
const Worker = require('./worker')
|
|
@@ -50,6 +49,7 @@ class Manager extends EventEmitter {
|
|
|
50
49
|
this.completeJobsCommand = plans.completeJobs(config.schema)
|
|
51
50
|
this.cancelJobsCommand = plans.cancelJobs(config.schema)
|
|
52
51
|
this.resumeJobsCommand = plans.resumeJobs(config.schema)
|
|
52
|
+
this.deleteJobsCommand = plans.deleteJobs(config.schema)
|
|
53
53
|
this.failJobsByIdCommand = plans.failJobsById(config.schema)
|
|
54
54
|
this.getJobByIdCommand = plans.getJobById(config.schema)
|
|
55
55
|
this.getArchivedJobByIdCommand = plans.getArchivedJobById(config.schema)
|
|
@@ -69,6 +69,7 @@ class Manager extends EventEmitter {
|
|
|
69
69
|
this.complete,
|
|
70
70
|
this.cancel,
|
|
71
71
|
this.resume,
|
|
72
|
+
this.delete,
|
|
72
73
|
this.fail,
|
|
73
74
|
this.fetch,
|
|
74
75
|
this.work,
|
|
@@ -187,71 +188,33 @@ class Manager extends EventEmitter {
|
|
|
187
188
|
const {
|
|
188
189
|
pollingInterval: interval = this.config.pollingInterval,
|
|
189
190
|
batchSize,
|
|
190
|
-
teamSize = 1,
|
|
191
|
-
teamConcurrency = 1,
|
|
192
|
-
teamRefill: refill = false,
|
|
193
191
|
includeMetadata = false,
|
|
194
192
|
priority = true
|
|
195
193
|
} = options
|
|
196
194
|
|
|
197
195
|
const id = randomUUID({ disableEntropyCache: true })
|
|
198
196
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
let refillTeamPromise
|
|
202
|
-
let resolveRefillTeam
|
|
203
|
-
|
|
204
|
-
// Setup a promise that onFetch can await for when at least one
|
|
205
|
-
// job is finished and so the team is ready to be topped up
|
|
206
|
-
const createTeamRefillPromise = () => {
|
|
207
|
-
refillTeamPromise = new Promise((resolve) => { resolveRefillTeam = resolve })
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
createTeamRefillPromise()
|
|
211
|
-
|
|
212
|
-
const onRefill = () => {
|
|
213
|
-
queueSize--
|
|
214
|
-
resolveRefillTeam()
|
|
215
|
-
createTeamRefillPromise()
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const fetch = () => this.fetch(name, batchSize || (teamSize - queueSize), { includeMetadata, priority })
|
|
197
|
+
const fetch = () => this.fetch(name, { batchSize, includeMetadata, priority })
|
|
219
198
|
|
|
220
199
|
const onFetch = async (jobs) => {
|
|
200
|
+
if (!jobs.length) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
|
|
221
204
|
if (this.config.__test__throw_worker) {
|
|
222
205
|
throw new Error('__test__throw_worker')
|
|
223
206
|
}
|
|
224
207
|
|
|
225
208
|
this.emitWip(name)
|
|
226
209
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
queueSize += jobs.length || 1
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const allTeamPromise = pMap(jobs, job =>
|
|
239
|
-
resolveWithinSeconds(callback(job), job.expireInSeconds)
|
|
240
|
-
.then(result => this.complete(name, job.id, result))
|
|
241
|
-
.catch(err => this.fail(name, job.id, err))
|
|
242
|
-
.then(() => refill ? onRefill() : null)
|
|
243
|
-
, { concurrency: teamConcurrency }
|
|
244
|
-
).catch(() => {}) // allow promises & non-promises to live together in harmony
|
|
245
|
-
|
|
246
|
-
if (refill) {
|
|
247
|
-
if (queueSize < teamSize) {
|
|
248
|
-
return
|
|
249
|
-
} else {
|
|
250
|
-
await refillTeamPromise
|
|
251
|
-
}
|
|
252
|
-
} else {
|
|
253
|
-
await allTeamPromise
|
|
254
|
-
}
|
|
210
|
+
const maxExpiration = jobs.reduce((acc, i) => Math.max(acc, i.expireInSeconds), 0)
|
|
211
|
+
const jobIds = jobs.map(job => job.id)
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
const result = await resolveWithinSeconds(callback(jobs), maxExpiration)
|
|
215
|
+
this.complete(name, jobIds, jobIds.length === 1 ? result : undefined)
|
|
216
|
+
} catch (err) {
|
|
217
|
+
this.fail(name, jobIds, err)
|
|
255
218
|
}
|
|
256
219
|
|
|
257
220
|
this.emitWip(name)
|
|
@@ -327,7 +290,7 @@ class Manager extends EventEmitter {
|
|
|
327
290
|
|
|
328
291
|
const { rows } = await this.db.executeSql(this.getQueuesForEventCommand, [event])
|
|
329
292
|
|
|
330
|
-
|
|
293
|
+
await Promise.allSettled(rows.map(({ name }) => this.send(name, ...args)))
|
|
331
294
|
}
|
|
332
295
|
|
|
333
296
|
async send (...args) {
|
|
@@ -465,25 +428,20 @@ class Manager extends EventEmitter {
|
|
|
465
428
|
return startAfter
|
|
466
429
|
}
|
|
467
430
|
|
|
468
|
-
async fetch (name,
|
|
469
|
-
|
|
431
|
+
async fetch (name, options = {}) {
|
|
432
|
+
Attorney.checkFetchArgs(name, options)
|
|
470
433
|
const db = options.db || this.db
|
|
471
434
|
const nextJobSql = this.nextJobCommand({ ...options })
|
|
472
|
-
const statementValues = [values.name, batchSize || 1]
|
|
473
435
|
|
|
474
436
|
let result
|
|
475
437
|
|
|
476
438
|
try {
|
|
477
|
-
result = await db.executeSql(nextJobSql,
|
|
439
|
+
result = await db.executeSql(nextJobSql, [name, options.batchSize])
|
|
478
440
|
} catch (err) {
|
|
479
441
|
// errors from fetchquery should only be unique constraint violations
|
|
480
442
|
}
|
|
481
443
|
|
|
482
|
-
|
|
483
|
-
return null
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
return result.rows.length === 1 && !batchSize ? result.rows[0] : result.rows
|
|
444
|
+
return result?.rows || []
|
|
487
445
|
}
|
|
488
446
|
|
|
489
447
|
mapCompletionIdArg (id, funcName) {
|
|
@@ -508,11 +466,11 @@ class Manager extends EventEmitter {
|
|
|
508
466
|
return stringify(result)
|
|
509
467
|
}
|
|
510
468
|
|
|
511
|
-
|
|
469
|
+
mapCommandResponse (ids, result) {
|
|
512
470
|
return {
|
|
513
471
|
jobs: ids,
|
|
514
472
|
requested: ids.length,
|
|
515
|
-
|
|
473
|
+
affected: result && result.rows ? parseInt(result.rows[0].count) : 0
|
|
516
474
|
}
|
|
517
475
|
}
|
|
518
476
|
|
|
@@ -521,7 +479,7 @@ class Manager extends EventEmitter {
|
|
|
521
479
|
const db = options.db || this.db
|
|
522
480
|
const ids = this.mapCompletionIdArg(id, 'complete')
|
|
523
481
|
const result = await db.executeSql(this.completeJobsCommand, [name, ids, this.mapCompletionDataArg(data)])
|
|
524
|
-
return this.
|
|
482
|
+
return this.mapCommandResponse(ids, result)
|
|
525
483
|
}
|
|
526
484
|
|
|
527
485
|
async fail (name, id, data, options = {}) {
|
|
@@ -529,7 +487,7 @@ class Manager extends EventEmitter {
|
|
|
529
487
|
const db = options.db || this.db
|
|
530
488
|
const ids = this.mapCompletionIdArg(id, 'fail')
|
|
531
489
|
const result = await db.executeSql(this.failJobsByIdCommand, [name, ids, this.mapCompletionDataArg(data)])
|
|
532
|
-
return this.
|
|
490
|
+
return this.mapCommandResponse(ids, result)
|
|
533
491
|
}
|
|
534
492
|
|
|
535
493
|
async cancel (name, id, options = {}) {
|
|
@@ -537,7 +495,15 @@ class Manager extends EventEmitter {
|
|
|
537
495
|
const db = options.db || this.db
|
|
538
496
|
const ids = this.mapCompletionIdArg(id, 'cancel')
|
|
539
497
|
const result = await db.executeSql(this.cancelJobsCommand, [name, ids])
|
|
540
|
-
return this.
|
|
498
|
+
return this.mapCommandResponse(ids, result)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async delete (name, id, options = {}) {
|
|
502
|
+
Attorney.assertQueueName(name)
|
|
503
|
+
const db = options.db || this.db
|
|
504
|
+
const ids = this.mapCompletionIdArg(id, 'delete')
|
|
505
|
+
const result = await db.executeSql(this.deleteJobsCommand, [name, ids])
|
|
506
|
+
return this.mapCommandResponse(ids, result)
|
|
541
507
|
}
|
|
542
508
|
|
|
543
509
|
async resume (name, id, options = {}) {
|
|
@@ -545,7 +511,7 @@ class Manager extends EventEmitter {
|
|
|
545
511
|
const db = options.db || this.db
|
|
546
512
|
const ids = this.mapCompletionIdArg(id, 'resume')
|
|
547
513
|
const result = await db.executeSql(this.resumeJobsCommand, [name, ids])
|
|
548
|
-
return this.
|
|
514
|
+
return this.mapCommandResponse(ids, result)
|
|
549
515
|
}
|
|
550
516
|
|
|
551
517
|
async createQueue (name, options = {}) {
|
|
@@ -570,6 +536,7 @@ class Manager extends EventEmitter {
|
|
|
570
536
|
Attorney.assertQueueName(deadLetter)
|
|
571
537
|
}
|
|
572
538
|
|
|
539
|
+
// todo: pull in defaults from constructor config
|
|
573
540
|
const data = {
|
|
574
541
|
policy,
|
|
575
542
|
retryLimit,
|
|
@@ -659,19 +626,17 @@ class Manager extends EventEmitter {
|
|
|
659
626
|
Attorney.assertQueueName(name)
|
|
660
627
|
|
|
661
628
|
const db = options.db || this.db
|
|
629
|
+
|
|
662
630
|
const result1 = await db.executeSql(this.getJobByIdCommand, [name, id])
|
|
663
631
|
|
|
664
|
-
if (result1
|
|
632
|
+
if (result1?.rows?.length === 1) {
|
|
665
633
|
return result1.rows[0]
|
|
634
|
+
} else if (options.includeArchive) {
|
|
635
|
+
const result2 = await db.executeSql(this.getArchivedJobByIdCommand, [name, id])
|
|
636
|
+
return result2?.rows[0] || null
|
|
637
|
+
} else {
|
|
638
|
+
return null
|
|
666
639
|
}
|
|
667
|
-
|
|
668
|
-
const result2 = await db.executeSql(this.getArchivedJobByIdCommand, [name, id])
|
|
669
|
-
|
|
670
|
-
if (result2 && result2.rows && result2.rows.length === 1) {
|
|
671
|
-
return result2.rows[0]
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
return null
|
|
675
640
|
}
|
|
676
641
|
}
|
|
677
642
|
|
package/src/plans.js
CHANGED
|
@@ -28,6 +28,7 @@ module.exports = {
|
|
|
28
28
|
completeJobs,
|
|
29
29
|
cancelJobs,
|
|
30
30
|
resumeJobs,
|
|
31
|
+
deleteJobs,
|
|
31
32
|
failJobsById,
|
|
32
33
|
failJobsByTimeout,
|
|
33
34
|
insertJob,
|
|
@@ -601,6 +602,19 @@ function resumeJobs (schema) {
|
|
|
601
602
|
state = '${JOB_STATES.created}'
|
|
602
603
|
WHERE name = $1
|
|
603
604
|
AND id IN (SELECT UNNEST($2::uuid[]))
|
|
605
|
+
AND state = '${JOB_STATES.cancelled}'
|
|
606
|
+
RETURNING 1
|
|
607
|
+
)
|
|
608
|
+
SELECT COUNT(*) from results
|
|
609
|
+
`
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function deleteJobs (schema) {
|
|
613
|
+
return `
|
|
614
|
+
with results as (
|
|
615
|
+
DELETE FROM ${schema}.job
|
|
616
|
+
WHERE name = $1
|
|
617
|
+
AND id IN (SELECT UNNEST($2::uuid[]))
|
|
604
618
|
RETURNING 1
|
|
605
619
|
)
|
|
606
620
|
SELECT COUNT(*) from results
|
|
@@ -678,7 +692,7 @@ function insertJob (schema) {
|
|
|
678
692
|
$17::int as retry_delay_default,
|
|
679
693
|
$18::bool as retry_backoff,
|
|
680
694
|
$19::bool as retry_backoff_default
|
|
681
|
-
) j
|
|
695
|
+
) j JOIN ${schema}.queue q ON j.name = q.name
|
|
682
696
|
ON CONFLICT DO NOTHING
|
|
683
697
|
RETURNING id
|
|
684
698
|
`
|
|
@@ -713,10 +727,10 @@ function insertJobs (schema) {
|
|
|
713
727
|
COALESCE(id, gen_random_uuid()) as id,
|
|
714
728
|
j.name,
|
|
715
729
|
data,
|
|
716
|
-
COALESCE(priority, 0),
|
|
717
|
-
|
|
718
|
-
"singletonKey",
|
|
719
|
-
COALESCE("deadLetter", q.dead_letter),
|
|
730
|
+
COALESCE(priority, 0) as priority,
|
|
731
|
+
j.start_after,
|
|
732
|
+
"singletonKey" as singleton_key,
|
|
733
|
+
COALESCE("deadLetter", q.dead_letter) as dead_letter,
|
|
720
734
|
CASE
|
|
721
735
|
WHEN "expireInSeconds" IS NOT NULL THEN "expireInSeconds" * interval '1s'
|
|
722
736
|
WHEN q.expire_seconds IS NOT NULL THEN q.expire_seconds * interval '1s'
|
|
@@ -725,7 +739,7 @@ function insertJobs (schema) {
|
|
|
725
739
|
END as expire_in,
|
|
726
740
|
CASE
|
|
727
741
|
WHEN "keepUntil" IS NOT NULL THEN "keepUntil"
|
|
728
|
-
ELSE COALESCE(
|
|
742
|
+
ELSE COALESCE(j.start_after, now()) + CAST(COALESCE((q.retention_minutes * 60)::text, defaults.keep_until, '14 days') as interval)
|
|
729
743
|
END as keep_until,
|
|
730
744
|
COALESCE("retryLimit", q.retry_limit, defaults.retry_limit, 2),
|
|
731
745
|
CASE
|
|
@@ -735,21 +749,29 @@ function insertJobs (schema) {
|
|
|
735
749
|
END as retry_delay,
|
|
736
750
|
COALESCE("retryBackoff", q.retry_backoff, defaults.retry_backoff, false) as retry_backoff,
|
|
737
751
|
q.policy
|
|
738
|
-
FROM
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
752
|
+
FROM (
|
|
753
|
+
SELECT *,
|
|
754
|
+
CASE
|
|
755
|
+
WHEN right("startAfter", 1) = 'Z' THEN CAST("startAfter" as timestamp with time zone)
|
|
756
|
+
ELSE now() + CAST(COALESCE("startAfter",'0') as interval)
|
|
757
|
+
END as start_after
|
|
758
|
+
FROM json_to_recordset($1) as x (
|
|
759
|
+
id uuid,
|
|
760
|
+
name text,
|
|
761
|
+
priority integer,
|
|
762
|
+
data jsonb,
|
|
763
|
+
"startAfter" text,
|
|
764
|
+
"retryLimit" integer,
|
|
765
|
+
"retryDelay" integer,
|
|
766
|
+
"retryBackoff" boolean,
|
|
767
|
+
"singletonKey" text,
|
|
768
|
+
"singletonOn" text,
|
|
769
|
+
"expireInSeconds" integer,
|
|
770
|
+
"keepUntil" timestamp with time zone,
|
|
771
|
+
"deadLetter" text
|
|
772
|
+
)
|
|
773
|
+
) j
|
|
774
|
+
JOIN ${schema}.queue q ON j.name = q.name,
|
|
753
775
|
defaults
|
|
754
776
|
ON CONFLICT DO NOTHING
|
|
755
777
|
`
|
|
@@ -776,6 +798,7 @@ function archive (schema, completedInterval, failedInterval = completedInterval)
|
|
|
776
798
|
INSERT INTO ${schema}.archive (${columns})
|
|
777
799
|
SELECT ${columns}
|
|
778
800
|
FROM archived_rows
|
|
801
|
+
ON CONFLICT DO NOTHING
|
|
779
802
|
`
|
|
780
803
|
}
|
|
781
804
|
|
package/src/timekeeper.js
CHANGED
|
@@ -2,13 +2,12 @@ const EventEmitter = require('events')
|
|
|
2
2
|
const plans = require('./plans')
|
|
3
3
|
const cronParser = require('cron-parser')
|
|
4
4
|
const Attorney = require('./attorney')
|
|
5
|
-
const pMap = require('p-map')
|
|
6
5
|
|
|
7
|
-
const
|
|
6
|
+
const QUEUES = {
|
|
8
7
|
SEND_IT: '__pgboss__send-it'
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
const
|
|
10
|
+
const EVENTS = {
|
|
12
11
|
error: 'error',
|
|
13
12
|
schedule: 'schedule'
|
|
14
13
|
}
|
|
@@ -24,7 +23,7 @@ class Timekeeper extends EventEmitter {
|
|
|
24
23
|
this.cronMonitorIntervalMs = config.cronMonitorIntervalSeconds * 1000
|
|
25
24
|
this.clockSkew = 0
|
|
26
25
|
|
|
27
|
-
this.events =
|
|
26
|
+
this.events = EVENTS
|
|
28
27
|
|
|
29
28
|
this.getTimeCommand = plans.getTime(config.schema)
|
|
30
29
|
this.getQueueCommand = plans.getQueueByName(config.schema)
|
|
@@ -53,16 +52,15 @@ class Timekeeper extends EventEmitter {
|
|
|
53
52
|
await this.cacheClockSkew()
|
|
54
53
|
|
|
55
54
|
try {
|
|
56
|
-
await this.manager.createQueue(
|
|
55
|
+
await this.manager.createQueue(QUEUES.SEND_IT)
|
|
57
56
|
} catch {}
|
|
58
57
|
|
|
59
58
|
const options = {
|
|
60
59
|
pollingIntervalSeconds: this.config.cronWorkerIntervalSeconds,
|
|
61
|
-
|
|
62
|
-
teamConcurrency: 5
|
|
60
|
+
batchSize: 50
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
await this.manager.work(
|
|
63
|
+
await this.manager.work(QUEUES.SEND_IT, options, (jobs) => this.manager.insert(jobs.map(i => i.data)))
|
|
66
64
|
|
|
67
65
|
setImmediate(() => this.onCron())
|
|
68
66
|
|
|
@@ -77,7 +75,7 @@ class Timekeeper extends EventEmitter {
|
|
|
77
75
|
|
|
78
76
|
this.stopped = true
|
|
79
77
|
|
|
80
|
-
await this.manager.offWork(
|
|
78
|
+
await this.manager.offWork(QUEUES.SEND_IT)
|
|
81
79
|
|
|
82
80
|
if (this.skewMonitorInterval) {
|
|
83
81
|
clearInterval(this.skewMonitorInterval)
|
|
@@ -141,12 +139,15 @@ class Timekeeper extends EventEmitter {
|
|
|
141
139
|
}
|
|
142
140
|
|
|
143
141
|
async cron () {
|
|
144
|
-
const
|
|
142
|
+
const schedules = await this.getSchedules()
|
|
145
143
|
|
|
146
|
-
const
|
|
144
|
+
const scheduled = schedules
|
|
145
|
+
.filter(i => this.shouldSendIt(i.cron, i.timezone))
|
|
146
|
+
.map(({ name, data, options }) =>
|
|
147
|
+
({ name: QUEUES.SEND_IT, data: { name, data, options }, options: { singletonKey: name, singletonSeconds: 60 } }))
|
|
147
148
|
|
|
148
|
-
if (
|
|
149
|
-
await
|
|
149
|
+
if (scheduled.length > 0 && !this.stopped) {
|
|
150
|
+
await this.manager.insert(scheduled)
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -162,16 +163,6 @@ class Timekeeper extends EventEmitter {
|
|
|
162
163
|
return prevDiff < 60
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
async send (job) {
|
|
166
|
-
await this.manager.send(queues.SEND_IT, job, { singletonKey: job.name, singletonSeconds: 60 })
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async onSendIt (job) {
|
|
170
|
-
if (this.stopped) return
|
|
171
|
-
const { name, data, options } = job.data
|
|
172
|
-
await this.manager.send(name, data, options)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
166
|
async getSchedules () {
|
|
176
167
|
const { rows } = await this.db.executeSql(this.getSchedulesCommand)
|
|
177
168
|
return rows
|
|
@@ -203,4 +194,4 @@ class Timekeeper extends EventEmitter {
|
|
|
203
194
|
}
|
|
204
195
|
|
|
205
196
|
module.exports = Timekeeper
|
|
206
|
-
module.exports.QUEUES =
|
|
197
|
+
module.exports.QUEUES = QUEUES
|
package/types.d.ts
CHANGED
|
@@ -114,7 +114,7 @@ declare namespace PgBoss {
|
|
|
114
114
|
|
|
115
115
|
type QueuePolicy = 'standard' | 'short' | 'singleton' | 'stately'
|
|
116
116
|
|
|
117
|
-
type Queue = RetryOptions & ExpirationOptions & RetentionOptions & { name: string, policy
|
|
117
|
+
type Queue = RetryOptions & ExpirationOptions & RetentionOptions & { name: string, policy?: QueuePolicy, deadLetter?: string }
|
|
118
118
|
|
|
119
119
|
type ScheduleOptions = SendOptions & { tz?: string }
|
|
120
120
|
|
|
@@ -122,41 +122,20 @@ declare namespace PgBoss {
|
|
|
122
122
|
pollingIntervalSeconds?: number;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
interface
|
|
125
|
+
interface JobFetchOptions {
|
|
126
126
|
includeMetadata?: boolean;
|
|
127
127
|
priority?: boolean;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
type JobFetchOptions = CommonJobFetchOptions & {
|
|
131
|
-
teamSize?: number;
|
|
132
|
-
teamConcurrency?: number;
|
|
133
|
-
teamRefill?: boolean;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
type BatchJobFetchOptions = CommonJobFetchOptions & {
|
|
137
|
-
batchSize: number;
|
|
128
|
+
batchSize?: number;
|
|
138
129
|
}
|
|
139
130
|
|
|
140
131
|
type WorkOptions = JobFetchOptions & JobPollingOptions
|
|
141
|
-
type
|
|
142
|
-
|
|
143
|
-
type FetchOptions = {
|
|
144
|
-
includeMetadata?: boolean;
|
|
145
|
-
} & ConnectionOptions;
|
|
132
|
+
type FetchOptions = JobFetchOptions & ConnectionOptions;
|
|
146
133
|
|
|
147
134
|
interface WorkHandler<ReqData> {
|
|
148
|
-
(job: PgBoss.Job<ReqData>): Promise<any>;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
interface BatchWorkHandler<ReqData> {
|
|
152
135
|
(job: PgBoss.Job<ReqData>[]): Promise<any>;
|
|
153
136
|
}
|
|
154
137
|
|
|
155
138
|
interface WorkWithMetadataHandler<ReqData> {
|
|
156
|
-
(job: PgBoss.JobWithMetadata<ReqData>): Promise<any>;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
interface BatchWorkWithMetadataHandler<ReqData> {
|
|
160
139
|
(job: PgBoss.JobWithMetadata<ReqData>[]): Promise<any>;
|
|
161
140
|
}
|
|
162
141
|
|
|
@@ -328,13 +307,14 @@ declare class PgBoss extends EventEmitter {
|
|
|
328
307
|
insert(jobs: PgBoss.JobInsert[]): Promise<void>;
|
|
329
308
|
insert(jobs: PgBoss.JobInsert[], options: PgBoss.InsertOptions): Promise<void>;
|
|
330
309
|
|
|
310
|
+
fetch<T>(name: string): Promise<PgBoss.Job<T>[]>;
|
|
311
|
+
fetch<T>(name: string, options: PgBoss.FetchOptions & { includeMetadata: true }): Promise<PgBoss.JobWithMetadata<T>[]>;
|
|
312
|
+
fetch<T>(name: string, options: PgBoss.FetchOptions): Promise<PgBoss.Job<T>[]>;
|
|
313
|
+
|
|
331
314
|
work<ReqData>(name: string, handler: PgBoss.WorkHandler<ReqData>): Promise<string>;
|
|
332
315
|
work<ReqData>(name: string, options: PgBoss.WorkOptions & { includeMetadata: true }, handler: PgBoss.WorkWithMetadataHandler<ReqData>): Promise<string>;
|
|
333
316
|
work<ReqData>(name: string, options: PgBoss.WorkOptions, handler: PgBoss.WorkHandler<ReqData>): Promise<string>;
|
|
334
317
|
|
|
335
|
-
work<ReqData>(name: string, options: PgBoss.BatchWorkOptions & { includeMetadata: true }, handler: PgBoss.BatchWorkWithMetadataHandler<ReqData>): Promise<string>;
|
|
336
|
-
work<ReqData>(name: string, options: PgBoss.BatchWorkOptions, handler: PgBoss.BatchWorkHandler<ReqData>): Promise<string>;
|
|
337
|
-
|
|
338
318
|
offWork(name: string): Promise<void>;
|
|
339
319
|
offWork(options: PgBoss.OffWorkOptions): Promise<void>;
|
|
340
320
|
|
|
@@ -342,14 +322,9 @@ declare class PgBoss extends EventEmitter {
|
|
|
342
322
|
|
|
343
323
|
subscribe(event: string, name: string): Promise<void>;
|
|
344
324
|
unsubscribe(event: string, name: string): Promise<void>;
|
|
345
|
-
publish(event: string): Promise<
|
|
346
|
-
publish(event: string, data: object): Promise<
|
|
347
|
-
publish(event: string, data: object, options: PgBoss.SendOptions): Promise<
|
|
348
|
-
|
|
349
|
-
fetch<T>(name: string): Promise<PgBoss.Job<T> | null>;
|
|
350
|
-
fetch<T>(name: string, batchSize: number): Promise<PgBoss.Job<T>[] | null>;
|
|
351
|
-
fetch<T>(name: string, batchSize: number, options: PgBoss.FetchOptions & { includeMetadata: true }): Promise<PgBoss.JobWithMetadata<T>[] | null>;
|
|
352
|
-
fetch<T>(name: string, batchSize: number, options: PgBoss.FetchOptions): Promise<PgBoss.Job<T>[] | null>;
|
|
325
|
+
publish(event: string): Promise<void>;
|
|
326
|
+
publish(event: string, data: object): Promise<void>;
|
|
327
|
+
publish(event: string, data: object, options: PgBoss.SendOptions): Promise<void>;
|
|
353
328
|
|
|
354
329
|
cancel(name: string, id: string, options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
355
330
|
cancel(name: string, ids: string[], options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
@@ -357,6 +332,9 @@ declare class PgBoss extends EventEmitter {
|
|
|
357
332
|
resume(name: string, id: string, options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
358
333
|
resume(name: string, ids: string[], options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
359
334
|
|
|
335
|
+
delete(name: string, id: string, options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
336
|
+
delete(name: string, ids: string[], options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
337
|
+
|
|
360
338
|
complete(name: string, id: string, options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
361
339
|
complete(name: string, id: string, data: object, options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
362
340
|
complete(name: string, ids: string[], options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
@@ -366,11 +344,11 @@ declare class PgBoss extends EventEmitter {
|
|
|
366
344
|
fail(name: string, ids: string[], options?: PgBoss.ConnectionOptions): Promise<void>;
|
|
367
345
|
|
|
368
346
|
getQueueSize(name: string, options?: object): Promise<number>;
|
|
369
|
-
getJobById(name: string, id: string, options?: PgBoss.ConnectionOptions): Promise<PgBoss.JobWithMetadata | null>;
|
|
347
|
+
getJobById(name: string, id: string, options?: PgBoss.ConnectionOptions & { includeArchive: bool }): Promise<PgBoss.JobWithMetadata | null>;
|
|
370
348
|
|
|
371
349
|
createQueue(name: string, options?: PgBoss.Queue): Promise<void>;
|
|
372
350
|
getQueue(name: string): Promise<PgBoss.Queue | null>;
|
|
373
|
-
getQueues(): Promise<
|
|
351
|
+
getQueues(): Promise<PgBoss.Queue[]>;
|
|
374
352
|
updateQueue(name: string, options?: PgBoss.Queue): Promise<void>;
|
|
375
353
|
deleteQueue(name: string): Promise<void>;
|
|
376
354
|
purgeQueue(name: string): Promise<void>;
|