pg-boss 11.1.2 → 12.0.0-beta2
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/dist/index.cjs +2 -0
- package/dist/index.d.mts +270 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2350 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -18
- package/src/attorney.js +0 -258
- package/src/boss.js +0 -158
- package/src/contractor.js +0 -99
- package/src/db.js +0 -50
- package/src/index.js +0 -211
- package/src/manager.js +0 -697
- package/src/migrationStore.js +0 -155
- package/src/plans.js +0 -1017
- package/src/timekeeper.js +0 -203
- package/src/tools.js +0 -60
- package/src/worker.js +0 -99
- package/types.d.ts +0 -323
- package/version.json +0 -3
package/src/manager.js
DELETED
|
@@ -1,697 +0,0 @@
|
|
|
1
|
-
const assert = require('node:assert')
|
|
2
|
-
const EventEmitter = require('node:events')
|
|
3
|
-
const { randomUUID } = require('node:crypto')
|
|
4
|
-
const { serializeError: stringify } = require('serialize-error')
|
|
5
|
-
const { delay, resolveWithinSeconds } = require('./tools')
|
|
6
|
-
const Attorney = require('./attorney')
|
|
7
|
-
const Worker = require('./worker')
|
|
8
|
-
const plans = require('./plans')
|
|
9
|
-
|
|
10
|
-
const { QUEUES: TIMEKEEPER_QUEUES } = require('./timekeeper')
|
|
11
|
-
const { QUEUE_POLICIES } = plans
|
|
12
|
-
|
|
13
|
-
const INTERNAL_QUEUES = Object.values(TIMEKEEPER_QUEUES).reduce((acc, i) => ({ ...acc, [i]: i }), {})
|
|
14
|
-
|
|
15
|
-
const events = {
|
|
16
|
-
error: 'error',
|
|
17
|
-
wip: 'wip'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
class Manager extends EventEmitter {
|
|
21
|
-
constructor (db, config) {
|
|
22
|
-
super()
|
|
23
|
-
|
|
24
|
-
this.config = config
|
|
25
|
-
this.db = db
|
|
26
|
-
this.wipTs = Date.now()
|
|
27
|
-
this.workers = new Map()
|
|
28
|
-
this.queues = null
|
|
29
|
-
|
|
30
|
-
this.events = events
|
|
31
|
-
this.functions = [
|
|
32
|
-
this.complete,
|
|
33
|
-
this.cancel,
|
|
34
|
-
this.resume,
|
|
35
|
-
this.retry,
|
|
36
|
-
this.fail,
|
|
37
|
-
this.fetch,
|
|
38
|
-
this.work,
|
|
39
|
-
this.offWork,
|
|
40
|
-
this.notifyWorker,
|
|
41
|
-
this.publish,
|
|
42
|
-
this.subscribe,
|
|
43
|
-
this.unsubscribe,
|
|
44
|
-
this.insert,
|
|
45
|
-
this.send,
|
|
46
|
-
this.sendDebounced,
|
|
47
|
-
this.sendThrottled,
|
|
48
|
-
this.sendAfter,
|
|
49
|
-
this.createQueue,
|
|
50
|
-
this.updateQueue,
|
|
51
|
-
this.deleteQueue,
|
|
52
|
-
this.getQueueStats,
|
|
53
|
-
this.getQueue,
|
|
54
|
-
this.getQueues,
|
|
55
|
-
this.deleteQueuedJobs,
|
|
56
|
-
this.deleteStoredJobs,
|
|
57
|
-
this.deleteAllJobs,
|
|
58
|
-
this.deleteJob,
|
|
59
|
-
this.getJobById
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async start () {
|
|
64
|
-
this.stopped = false
|
|
65
|
-
this.queueCacheInterval = setInterval(() => this.onCacheQueues({ emit: true }), this.config.queueCacheIntervalSeconds * 1000)
|
|
66
|
-
await this.onCacheQueues()
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async onCacheQueues ({ emit = false } = {}) {
|
|
70
|
-
try {
|
|
71
|
-
assert(!this.config.__test__throw_queueCache, 'test error')
|
|
72
|
-
const queues = await this.getQueues()
|
|
73
|
-
this.queues = queues.reduce((acc, i) => { acc[i.name] = i; return acc }, {})
|
|
74
|
-
} catch (error) {
|
|
75
|
-
emit && this.emit(events.error, { ...error, message: error.message, stack: error.stack })
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async getQueueCache (name) {
|
|
80
|
-
let queue = this.queues[name]
|
|
81
|
-
|
|
82
|
-
if (queue) {
|
|
83
|
-
return queue
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
queue = await this.getQueue(name)
|
|
87
|
-
|
|
88
|
-
if (!queue) {
|
|
89
|
-
throw new Error(`Queue ${name} does not exist`)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
this.queues[name] = queue
|
|
93
|
-
|
|
94
|
-
return queue
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async stop () {
|
|
98
|
-
this.stopped = true
|
|
99
|
-
|
|
100
|
-
clearInterval(this.queueCacheInterval)
|
|
101
|
-
|
|
102
|
-
for (const worker of this.workers.values()) {
|
|
103
|
-
if (!INTERNAL_QUEUES[worker.name]) {
|
|
104
|
-
await this.offWork(worker.name)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async failWip () {
|
|
110
|
-
for (const worker of this.workers.values()) {
|
|
111
|
-
const jobIds = worker.jobs.map(j => j.id)
|
|
112
|
-
if (jobIds.length) {
|
|
113
|
-
await this.fail(worker.name, jobIds, 'pg-boss shut down while active')
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
async work (name, ...args) {
|
|
119
|
-
const { options, callback } = Attorney.checkWorkArgs(name, args)
|
|
120
|
-
return await this.watch(name, options, callback)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
addWorker (worker) {
|
|
124
|
-
this.workers.set(worker.id, worker)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
removeWorker (worker) {
|
|
128
|
-
this.workers.delete(worker.id)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
getWorkers () {
|
|
132
|
-
return Array.from(this.workers.values())
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
emitWip (name) {
|
|
136
|
-
if (!INTERNAL_QUEUES[name]) {
|
|
137
|
-
const now = Date.now()
|
|
138
|
-
|
|
139
|
-
if (now - this.wipTs > 2000) {
|
|
140
|
-
this.emit(events.wip, this.getWipData())
|
|
141
|
-
this.wipTs = now
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
getWipData (options = {}) {
|
|
147
|
-
const { includeInternal = false } = options
|
|
148
|
-
|
|
149
|
-
const data = this.getWorkers()
|
|
150
|
-
.map(({
|
|
151
|
-
id,
|
|
152
|
-
name,
|
|
153
|
-
options,
|
|
154
|
-
state,
|
|
155
|
-
jobs,
|
|
156
|
-
createdOn,
|
|
157
|
-
lastFetchedOn,
|
|
158
|
-
lastJobStartedOn,
|
|
159
|
-
lastJobEndedOn,
|
|
160
|
-
lastError,
|
|
161
|
-
lastErrorOn
|
|
162
|
-
}) => ({
|
|
163
|
-
id,
|
|
164
|
-
name,
|
|
165
|
-
options,
|
|
166
|
-
state,
|
|
167
|
-
count: jobs.length,
|
|
168
|
-
createdOn,
|
|
169
|
-
lastFetchedOn,
|
|
170
|
-
lastJobStartedOn,
|
|
171
|
-
lastJobEndedOn,
|
|
172
|
-
lastError,
|
|
173
|
-
lastErrorOn
|
|
174
|
-
}))
|
|
175
|
-
.filter(i => i.count > 0 && (!INTERNAL_QUEUES[i.name] || includeInternal))
|
|
176
|
-
|
|
177
|
-
return data
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async watch (name, options, callback) {
|
|
181
|
-
if (this.stopped) {
|
|
182
|
-
throw new Error('Workers are disabled. pg-boss is stopped')
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const {
|
|
186
|
-
pollingInterval: interval = this.config.pollingInterval,
|
|
187
|
-
batchSize,
|
|
188
|
-
includeMetadata = false,
|
|
189
|
-
priority = true
|
|
190
|
-
} = options
|
|
191
|
-
|
|
192
|
-
const id = randomUUID({ disableEntropyCache: true })
|
|
193
|
-
|
|
194
|
-
const fetch = () => this.fetch(name, { batchSize, includeMetadata, priority })
|
|
195
|
-
|
|
196
|
-
const onFetch = async (jobs) => {
|
|
197
|
-
if (!jobs.length) {
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (this.config.__test__throw_worker) {
|
|
202
|
-
throw new Error('__test__throw_worker')
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
this.emitWip(name)
|
|
206
|
-
|
|
207
|
-
const maxExpiration = jobs.reduce((acc, i) => Math.max(acc, i.expireInSeconds), 0)
|
|
208
|
-
const jobIds = jobs.map(job => job.id)
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const result = await resolveWithinSeconds(callback(jobs), maxExpiration, `handler execution exceeded ${maxExpiration}s`)
|
|
212
|
-
await this.complete(name, jobIds, jobIds.length === 1 ? result : undefined)
|
|
213
|
-
} catch (err) {
|
|
214
|
-
await this.fail(name, jobIds, err)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
this.emitWip(name)
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const onError = error => {
|
|
221
|
-
this.emit(events.error, { ...error, message: error.message, stack: error.stack, queue: name, worker: id })
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const worker = new Worker({ id, name, options, interval, fetch, onFetch, onError })
|
|
225
|
-
|
|
226
|
-
this.addWorker(worker)
|
|
227
|
-
|
|
228
|
-
worker.start()
|
|
229
|
-
|
|
230
|
-
return id
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
async offWork (value) {
|
|
234
|
-
assert(value, 'Missing required argument')
|
|
235
|
-
|
|
236
|
-
const query = (typeof value === 'string')
|
|
237
|
-
? { filter: i => i.name === value }
|
|
238
|
-
: (typeof value === 'object' && value.id)
|
|
239
|
-
? { filter: i => i.id === value.id }
|
|
240
|
-
: null
|
|
241
|
-
|
|
242
|
-
assert(query, 'Invalid argument. Expected string or object: { id }')
|
|
243
|
-
|
|
244
|
-
const workers = this.getWorkers().filter(i => query.filter(i) && !i.stopping && !i.stopped)
|
|
245
|
-
|
|
246
|
-
if (workers.length === 0) {
|
|
247
|
-
return
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
for (const worker of workers) {
|
|
251
|
-
worker.stop()
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
setImmediate(async () => {
|
|
255
|
-
while (!workers.every(w => w.stopped)) {
|
|
256
|
-
await delay(1000)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
for (const worker of workers) {
|
|
260
|
-
this.removeWorker(worker)
|
|
261
|
-
}
|
|
262
|
-
})
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
notifyWorker (workerId) {
|
|
266
|
-
if (this.workers.has(workerId)) {
|
|
267
|
-
this.workers.get(workerId).notify()
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
async subscribe (event, name) {
|
|
272
|
-
assert(event, 'Missing required argument')
|
|
273
|
-
assert(name, 'Missing required argument')
|
|
274
|
-
const sql = plans.subscribe(this.config.schema)
|
|
275
|
-
return await this.db.executeSql(sql, [event, name])
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async unsubscribe (event, name) {
|
|
279
|
-
assert(event, 'Missing required argument')
|
|
280
|
-
assert(name, 'Missing required argument')
|
|
281
|
-
const sql = plans.unsubscribe(this.config.schema)
|
|
282
|
-
return await this.db.executeSql(sql, [event, name])
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async publish (event, ...args) {
|
|
286
|
-
assert(event, 'Missing required argument')
|
|
287
|
-
const sql = plans.getQueuesForEvent(this.config.schema)
|
|
288
|
-
const { rows } = await this.db.executeSql(sql, [event])
|
|
289
|
-
|
|
290
|
-
await Promise.allSettled(rows.map(({ name }) => this.send(name, ...args)))
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async send (...args) {
|
|
294
|
-
const { name, data, options } = Attorney.checkSendArgs(args)
|
|
295
|
-
|
|
296
|
-
return await this.createJob(name, data, options)
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
async sendAfter (name, data, options, after) {
|
|
300
|
-
options = options ? { ...options } : {}
|
|
301
|
-
options.startAfter = after
|
|
302
|
-
|
|
303
|
-
const result = Attorney.checkSendArgs([name, data, options])
|
|
304
|
-
|
|
305
|
-
return await this.createJob(result.name, result.data, result.options)
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async sendThrottled (name, data, options, seconds, key) {
|
|
309
|
-
options = options ? { ...options } : {}
|
|
310
|
-
options.singletonSeconds = seconds
|
|
311
|
-
options.singletonNextSlot = false
|
|
312
|
-
options.singletonKey = key
|
|
313
|
-
|
|
314
|
-
const result = Attorney.checkSendArgs([name, data, options])
|
|
315
|
-
|
|
316
|
-
return await this.createJob(result.name, result.data, result.options)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
async sendDebounced (name, data, options, seconds, key) {
|
|
320
|
-
options = options ? { ...options } : {}
|
|
321
|
-
options.singletonSeconds = seconds
|
|
322
|
-
options.singletonNextSlot = true
|
|
323
|
-
options.singletonKey = key
|
|
324
|
-
|
|
325
|
-
const result = Attorney.checkSendArgs([name, data, options])
|
|
326
|
-
|
|
327
|
-
return await this.createJob(result.name, result.data, result.options)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async createJob (name, data, options) {
|
|
331
|
-
const singletonOffset = 0
|
|
332
|
-
|
|
333
|
-
const {
|
|
334
|
-
id = null,
|
|
335
|
-
db: wrapper,
|
|
336
|
-
priority,
|
|
337
|
-
startAfter,
|
|
338
|
-
singletonKey = null,
|
|
339
|
-
singletonSeconds,
|
|
340
|
-
singletonNextSlot,
|
|
341
|
-
expireInSeconds,
|
|
342
|
-
deleteAfterSeconds,
|
|
343
|
-
retentionSeconds,
|
|
344
|
-
keepUntil,
|
|
345
|
-
retryLimit,
|
|
346
|
-
retryDelay,
|
|
347
|
-
retryBackoff,
|
|
348
|
-
retryDelayMax
|
|
349
|
-
} = options
|
|
350
|
-
|
|
351
|
-
const job = {
|
|
352
|
-
id,
|
|
353
|
-
name,
|
|
354
|
-
data,
|
|
355
|
-
priority,
|
|
356
|
-
startAfter,
|
|
357
|
-
singletonKey,
|
|
358
|
-
singletonSeconds,
|
|
359
|
-
singletonOffset,
|
|
360
|
-
expireInSeconds,
|
|
361
|
-
deleteAfterSeconds,
|
|
362
|
-
retentionSeconds,
|
|
363
|
-
keepUntil,
|
|
364
|
-
retryLimit,
|
|
365
|
-
retryDelay,
|
|
366
|
-
retryBackoff,
|
|
367
|
-
retryDelayMax
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const db = wrapper || this.db
|
|
371
|
-
|
|
372
|
-
const { table } = await this.getQueueCache(name)
|
|
373
|
-
|
|
374
|
-
const sql = plans.insertJobs(this.config.schema, { table, name, returnId: true })
|
|
375
|
-
|
|
376
|
-
const { rows: try1 } = await db.executeSql(sql, [JSON.stringify([job])])
|
|
377
|
-
|
|
378
|
-
if (try1.length === 1) {
|
|
379
|
-
return try1[0].id
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (singletonNextSlot) {
|
|
383
|
-
// delay starting by the offset to honor throttling config
|
|
384
|
-
job.startAfter = this.getDebounceStartAfter(singletonSeconds, this.timekeeper.clockSkew)
|
|
385
|
-
job.singletonOffset = singletonSeconds
|
|
386
|
-
|
|
387
|
-
const { rows: try2 } = await db.executeSql(sql, [JSON.stringify([job])])
|
|
388
|
-
|
|
389
|
-
if (try2.length === 1) {
|
|
390
|
-
return try2[0].id
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return null
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
async insert (name, jobs, options = {}) {
|
|
398
|
-
assert(Array.isArray(jobs), 'jobs argument should be an array')
|
|
399
|
-
|
|
400
|
-
const { table } = await this.getQueueCache(name)
|
|
401
|
-
|
|
402
|
-
const db = this.assertDb(options)
|
|
403
|
-
|
|
404
|
-
const sql = plans.insertJobs(this.config.schema, { table, name, returnId: false })
|
|
405
|
-
|
|
406
|
-
const { rows } = await db.executeSql(sql, [JSON.stringify(jobs)])
|
|
407
|
-
|
|
408
|
-
return (rows.length) ? rows.map(i => i.id) : null
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
getDebounceStartAfter (singletonSeconds, clockOffset) {
|
|
412
|
-
const debounceInterval = singletonSeconds * 1000
|
|
413
|
-
|
|
414
|
-
const now = Date.now() + clockOffset
|
|
415
|
-
|
|
416
|
-
const slot = Math.floor(now / debounceInterval) * debounceInterval
|
|
417
|
-
|
|
418
|
-
// prevent startAfter=0 during debouncing
|
|
419
|
-
let startAfter = (singletonSeconds - Math.floor((now - slot) / 1000)) || 1
|
|
420
|
-
|
|
421
|
-
if (singletonSeconds > 1) {
|
|
422
|
-
startAfter++
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
return startAfter
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
async fetch (name, options = {}) {
|
|
429
|
-
Attorney.checkFetchArgs(name, options)
|
|
430
|
-
|
|
431
|
-
const db = this.assertDb(options)
|
|
432
|
-
|
|
433
|
-
const { table, policy, singletonsActive } = await this.getQueueCache(name)
|
|
434
|
-
|
|
435
|
-
options = {
|
|
436
|
-
...options,
|
|
437
|
-
schema: this.config.schema,
|
|
438
|
-
table,
|
|
439
|
-
name,
|
|
440
|
-
policy,
|
|
441
|
-
limit: options.batchSize,
|
|
442
|
-
ignoreSingletons: singletonsActive
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const sql = plans.fetchNextJob(options)
|
|
446
|
-
|
|
447
|
-
let result
|
|
448
|
-
|
|
449
|
-
try {
|
|
450
|
-
result = await db.executeSql(sql)
|
|
451
|
-
} catch (err) {
|
|
452
|
-
// errors from fetchquery should only be unique constraint violations
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return result?.rows || []
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
mapCompletionIdArg (id, funcName) {
|
|
459
|
-
const errorMessage = `${funcName}() requires an id`
|
|
460
|
-
|
|
461
|
-
assert(id, errorMessage)
|
|
462
|
-
|
|
463
|
-
const ids = Array.isArray(id) ? id : [id]
|
|
464
|
-
|
|
465
|
-
assert(ids.length, errorMessage)
|
|
466
|
-
|
|
467
|
-
return ids
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
mapCompletionDataArg (data) {
|
|
471
|
-
if (data === null || typeof data === 'undefined' || typeof data === 'function') { return null }
|
|
472
|
-
|
|
473
|
-
const result = (typeof data === 'object' && !Array.isArray(data))
|
|
474
|
-
? data
|
|
475
|
-
: { value: data }
|
|
476
|
-
|
|
477
|
-
return stringify(result)
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
mapCommandResponse (ids, result) {
|
|
481
|
-
return {
|
|
482
|
-
jobs: ids,
|
|
483
|
-
requested: ids.length,
|
|
484
|
-
affected: result && result.rows ? parseInt(result.rows[0].count) : 0
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
async complete (name, id, data, options = {}) {
|
|
489
|
-
Attorney.assertQueueName(name)
|
|
490
|
-
const db = this.assertDb(options)
|
|
491
|
-
const ids = this.mapCompletionIdArg(id, 'complete')
|
|
492
|
-
const { table } = await this.getQueueCache(name)
|
|
493
|
-
const sql = plans.completeJobs(this.config.schema, table)
|
|
494
|
-
const result = await db.executeSql(sql, [name, ids, this.mapCompletionDataArg(data)])
|
|
495
|
-
return this.mapCommandResponse(ids, result)
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
async fail (name, id, data, options = {}) {
|
|
499
|
-
Attorney.assertQueueName(name)
|
|
500
|
-
const db = this.assertDb(options)
|
|
501
|
-
const ids = this.mapCompletionIdArg(id, 'fail')
|
|
502
|
-
const { table } = await this.getQueueCache(name)
|
|
503
|
-
const sql = plans.failJobsById(this.config.schema, table)
|
|
504
|
-
const result = await db.executeSql(sql, [name, ids, this.mapCompletionDataArg(data)])
|
|
505
|
-
return this.mapCommandResponse(ids, result)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
async cancel (name, id, options = {}) {
|
|
509
|
-
Attorney.assertQueueName(name)
|
|
510
|
-
const db = this.assertDb(options)
|
|
511
|
-
const ids = this.mapCompletionIdArg(id, 'cancel')
|
|
512
|
-
const { table } = await this.getQueueCache(name)
|
|
513
|
-
const sql = plans.cancelJobs(this.config.schema, table)
|
|
514
|
-
const result = await db.executeSql(sql, [name, ids])
|
|
515
|
-
return this.mapCommandResponse(ids, result)
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
async deleteJob (name, id, options = {}) {
|
|
519
|
-
Attorney.assertQueueName(name)
|
|
520
|
-
const db = this.assertDb(options)
|
|
521
|
-
const ids = this.mapCompletionIdArg(id, 'deleteJob')
|
|
522
|
-
const { table } = await this.getQueueCache(name)
|
|
523
|
-
const sql = plans.deleteJobsById(this.config.schema, table)
|
|
524
|
-
const result = await db.executeSql(sql, [name, ids])
|
|
525
|
-
return this.mapCommandResponse(ids, result)
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
async resume (name, id, options = {}) {
|
|
529
|
-
Attorney.assertQueueName(name)
|
|
530
|
-
const db = this.assertDb(options)
|
|
531
|
-
const ids = this.mapCompletionIdArg(id, 'resume')
|
|
532
|
-
const { table } = await this.getQueueCache(name)
|
|
533
|
-
const sql = plans.resumeJobs(this.config.schema, table)
|
|
534
|
-
const result = await db.executeSql(sql, [name, ids])
|
|
535
|
-
return this.mapCommandResponse(ids, result)
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
async retry (name, id, options = {}) {
|
|
539
|
-
Attorney.assertQueueName(name)
|
|
540
|
-
const db = options.db || this.db
|
|
541
|
-
const ids = this.mapCompletionIdArg(id, 'retry')
|
|
542
|
-
const { table } = await this.getQueueCache(name)
|
|
543
|
-
const sql = plans.retryJobs(this.config.schema, table)
|
|
544
|
-
const result = await db.executeSql(sql, [name, ids])
|
|
545
|
-
return this.mapCommandResponse(ids, result)
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
async createQueue (name, options = {}) {
|
|
549
|
-
name = name || options.name
|
|
550
|
-
|
|
551
|
-
Attorney.assertQueueName(name)
|
|
552
|
-
|
|
553
|
-
options.policy = options.policy || QUEUE_POLICIES.standard
|
|
554
|
-
|
|
555
|
-
assert(options.policy in QUEUE_POLICIES, `${options.policy} is not a valid queue policy`)
|
|
556
|
-
|
|
557
|
-
Attorney.validateQueueArgs(options)
|
|
558
|
-
|
|
559
|
-
if (options.deadLetter) {
|
|
560
|
-
Attorney.assertQueueName(options.deadLetter)
|
|
561
|
-
assert.notStrictEqual(name, options.deadLetter, 'deadLetter cannot be itself')
|
|
562
|
-
await this.getQueueCache(options.deadLetter)
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const sql = plans.createQueue(this.config.schema, name, options)
|
|
566
|
-
await this.db.executeSql(sql)
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
async getQueues (names) {
|
|
570
|
-
if (names) {
|
|
571
|
-
names = Array.isArray(names) ? names : [names]
|
|
572
|
-
for (const name of names) {
|
|
573
|
-
Attorney.assertQueueName(name)
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
const sql = plans.getQueues(this.config.schema, names)
|
|
578
|
-
const { rows } = await this.db.executeSql(sql)
|
|
579
|
-
return rows
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async updateQueue (name, options = {}) {
|
|
583
|
-
Attorney.assertQueueName(name)
|
|
584
|
-
|
|
585
|
-
assert(Object.keys(options).length > 0, 'no properties found to update')
|
|
586
|
-
|
|
587
|
-
if ('policy' in options) {
|
|
588
|
-
throw new Error('queue policy cannot be changed after creation')
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if ('partition' in options) {
|
|
592
|
-
throw new Error('queue partitioning cannot be changed after creation')
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
Attorney.validateQueueArgs(options)
|
|
596
|
-
|
|
597
|
-
const { deadLetter } = options
|
|
598
|
-
|
|
599
|
-
if (deadLetter) {
|
|
600
|
-
Attorney.assertQueueName(deadLetter)
|
|
601
|
-
assert.notStrictEqual(name, deadLetter, 'deadLetter cannot be itself')
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
const sql = plans.updateQueue(this.config.schema, { deadLetter })
|
|
605
|
-
await this.db.executeSql(sql, [name, options])
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
async getQueue (name) {
|
|
609
|
-
Attorney.assertQueueName(name)
|
|
610
|
-
|
|
611
|
-
const sql = plans.getQueues(this.config.schema, [name])
|
|
612
|
-
const { rows } = await this.db.executeSql(sql)
|
|
613
|
-
|
|
614
|
-
return rows[0] || null
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
async deleteQueue (name) {
|
|
618
|
-
Attorney.assertQueueName(name)
|
|
619
|
-
|
|
620
|
-
try {
|
|
621
|
-
await this.getQueueCache(name)
|
|
622
|
-
const sql = plans.deleteQueue(this.config.schema, name)
|
|
623
|
-
await this.db.executeSql(sql)
|
|
624
|
-
} catch {}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
async deleteQueuedJobs (name) {
|
|
628
|
-
Attorney.assertQueueName(name)
|
|
629
|
-
const { table } = await this.getQueueCache(name)
|
|
630
|
-
const sql = plans.deleteQueuedJobs(this.config.schema, table)
|
|
631
|
-
await this.db.executeSql(sql, [name])
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
async deleteStoredJobs (name) {
|
|
635
|
-
Attorney.assertQueueName(name)
|
|
636
|
-
const { table } = await this.getQueueCache(name)
|
|
637
|
-
const sql = plans.deleteStoredJobs(this.config.schema, table)
|
|
638
|
-
await this.db.executeSql(sql, [name])
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
async deleteAllJobs (name) {
|
|
642
|
-
Attorney.assertQueueName(name)
|
|
643
|
-
const { table, partition } = await this.getQueueCache(name)
|
|
644
|
-
|
|
645
|
-
if (partition) {
|
|
646
|
-
const sql = plans.truncateTable(this.config.schema, table)
|
|
647
|
-
await this.db.executeSql(sql)
|
|
648
|
-
} else {
|
|
649
|
-
const sql = plans.deleteAllJobs(this.config.schema, table)
|
|
650
|
-
await this.db.executeSql(sql, [name])
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
async getQueueStats (name) {
|
|
655
|
-
Attorney.assertQueueName(name)
|
|
656
|
-
|
|
657
|
-
const queue = await this.getQueueCache(name)
|
|
658
|
-
|
|
659
|
-
const sql = plans.getQueueStats(this.config.schema, queue.table, [name])
|
|
660
|
-
|
|
661
|
-
const { rows } = await this.db.executeSql(sql)
|
|
662
|
-
|
|
663
|
-
return Object.assign(queue, rows.at(0) || {})
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
async getJobById (name, id, options = {}) {
|
|
667
|
-
Attorney.assertQueueName(name)
|
|
668
|
-
|
|
669
|
-
const db = this.assertDb(options)
|
|
670
|
-
|
|
671
|
-
const { table } = await this.getQueueCache(name)
|
|
672
|
-
|
|
673
|
-
const sql = plans.getJobById(this.config.schema, table)
|
|
674
|
-
|
|
675
|
-
const result1 = await db.executeSql(sql, [name, id])
|
|
676
|
-
|
|
677
|
-
if (result1?.rows?.length === 1) {
|
|
678
|
-
return result1.rows[0]
|
|
679
|
-
} else {
|
|
680
|
-
return null
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
assertDb (options) {
|
|
685
|
-
if (options.db) {
|
|
686
|
-
return options.db
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
if (this.db._pgbdb) {
|
|
690
|
-
assert(this.db.opened, 'Database connection is not opened')
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
return this.db
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
module.exports = Manager
|