pg-boss 10.3.3 → 11.0.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/src/manager.js CHANGED
@@ -2,7 +2,7 @@ const assert = require('node:assert')
2
2
  const EventEmitter = require('node:events')
3
3
  const { randomUUID } = require('node:crypto')
4
4
  const { serializeError: stringify } = require('serialize-error')
5
- const { delay } = require('./tools')
5
+ const { delay, resolveWithinSeconds } = require('./tools')
6
6
  const Attorney = require('./attorney')
7
7
  const Worker = require('./worker')
8
8
  const plans = require('./plans')
@@ -17,61 +17,22 @@ const events = {
17
17
  wip: 'wip'
18
18
  }
19
19
 
20
- const resolveWithinSeconds = async (promise, seconds) => {
21
- const timeout = Math.max(1, seconds) * 1000
22
- const reject = delay(timeout, `handler execution exceeded ${timeout}ms`)
23
-
24
- let result
25
-
26
- try {
27
- result = await Promise.race([promise, reject])
28
- } finally {
29
- reject.abort()
30
- }
31
-
32
- return result
33
- }
34
-
35
20
  class Manager extends EventEmitter {
36
21
  constructor (db, config) {
37
22
  super()
38
23
 
39
24
  this.config = config
40
25
  this.db = db
41
-
42
- this.events = events
43
26
  this.wipTs = Date.now()
44
27
  this.workers = new Map()
28
+ this.queues = null
45
29
 
46
- this.nextJobCommand = plans.fetchNextJob(config.schema)
47
- this.insertJobCommand = plans.insertJob(config.schema)
48
- this.insertJobsCommand = plans.insertJobs(config.schema)
49
- this.completeJobsCommand = plans.completeJobs(config.schema)
50
- this.cancelJobsCommand = plans.cancelJobs(config.schema)
51
- this.resumeJobsCommand = plans.resumeJobs(config.schema)
52
- this.deleteJobsCommand = plans.deleteJobs(config.schema)
53
- this.retryJobsCommand = plans.retryJobs(config.schema)
54
- this.failJobsByIdCommand = plans.failJobsById(config.schema)
55
- this.getJobByIdCommand = plans.getJobById(config.schema)
56
- this.getArchivedJobByIdCommand = plans.getArchivedJobById(config.schema)
57
- this.subscribeCommand = plans.subscribe(config.schema)
58
- this.unsubscribeCommand = plans.unsubscribe(config.schema)
59
- this.getQueuesCommand = plans.getQueues(config.schema)
60
- this.getQueueByNameCommand = plans.getQueueByName(config.schema)
61
- this.getQueuesForEventCommand = plans.getQueuesForEvent(config.schema)
62
- this.createQueueCommand = plans.createQueue(config.schema)
63
- this.updateQueueCommand = plans.updateQueue(config.schema)
64
- this.purgeQueueCommand = plans.purgeQueue(config.schema)
65
- this.deleteQueueCommand = plans.deleteQueue(config.schema)
66
- this.clearStorageCommand = plans.clearStorage(config.schema)
67
-
68
- // exported api to index
30
+ this.events = events
69
31
  this.functions = [
70
32
  this.complete,
71
33
  this.cancel,
72
34
  this.resume,
73
35
  this.retry,
74
- this.deleteJob,
75
36
  this.fail,
76
37
  this.fetch,
77
38
  this.work,
@@ -88,22 +49,56 @@ class Manager extends EventEmitter {
88
49
  this.createQueue,
89
50
  this.updateQueue,
90
51
  this.deleteQueue,
91
- this.purgeQueue,
92
- this.getQueueSize,
52
+ this.getQueueStats,
93
53
  this.getQueue,
94
54
  this.getQueues,
95
- this.clearStorage,
55
+ this.deleteQueuedJobs,
56
+ this.deleteStoredJobs,
57
+ this.deleteAllJobs,
58
+ this.deleteJob,
96
59
  this.getJobById
97
60
  ]
98
61
  }
99
62
 
100
- start () {
63
+ async start () {
101
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
102
95
  }
103
96
 
104
97
  async stop () {
105
98
  this.stopped = true
106
99
 
100
+ clearInterval(this.queueCacheInterval)
101
+
107
102
  for (const worker of this.workers.values()) {
108
103
  if (!INTERNAL_QUEUES[worker.name]) {
109
104
  await this.offWork(worker.name)
@@ -121,7 +116,7 @@ class Manager extends EventEmitter {
121
116
  }
122
117
 
123
118
  async work (name, ...args) {
124
- const { options, callback } = Attorney.checkWorkArgs(name, args, this.config)
119
+ const { options, callback } = Attorney.checkWorkArgs(name, args)
125
120
  return await this.watch(name, options, callback)
126
121
  }
127
122
 
@@ -213,10 +208,10 @@ class Manager extends EventEmitter {
213
208
  const jobIds = jobs.map(job => job.id)
214
209
 
215
210
  try {
216
- const result = await resolveWithinSeconds(callback(jobs), maxExpiration)
217
- this.complete(name, jobIds, jobIds.length === 1 ? result : undefined)
211
+ const result = await resolveWithinSeconds(callback(jobs), maxExpiration, `handler execution exceeded ${maxExpiration}s`)
212
+ await this.complete(name, jobIds, jobIds.length === 1 ? result : undefined)
218
213
  } catch (err) {
219
- this.fail(name, jobIds, err)
214
+ await this.fail(name, jobIds, err)
220
215
  }
221
216
 
222
217
  this.emitWip(name)
@@ -276,27 +271,28 @@ class Manager extends EventEmitter {
276
271
  async subscribe (event, name) {
277
272
  assert(event, 'Missing required argument')
278
273
  assert(name, 'Missing required argument')
279
-
280
- return await this.db.executeSql(this.subscribeCommand, [event, name])
274
+ const sql = plans.subscribe(this.config.schema)
275
+ return await this.db.executeSql(sql, [event, name])
281
276
  }
282
277
 
283
278
  async unsubscribe (event, name) {
284
279
  assert(event, 'Missing required argument')
285
280
  assert(name, 'Missing required argument')
286
-
287
- return await this.db.executeSql(this.unsubscribeCommand, [event, name])
281
+ const sql = plans.unsubscribe(this.config.schema)
282
+ return await this.db.executeSql(sql, [event, name])
288
283
  }
289
284
 
290
285
  async publish (event, ...args) {
291
286
  assert(event, 'Missing required argument')
292
-
293
- const { rows } = await this.db.executeSql(this.getQueuesForEventCommand, [event])
287
+ const sql = plans.getQueuesForEvent(this.config.schema)
288
+ const { rows } = await this.db.executeSql(sql, [event])
294
289
 
295
290
  await Promise.allSettled(rows.map(({ name }) => this.send(name, ...args)))
296
291
  }
297
292
 
298
293
  async send (...args) {
299
- const { name, data, options } = Attorney.checkSendArgs(args, this.config)
294
+ const { name, data, options } = Attorney.checkSendArgs(args)
295
+
300
296
  return await this.createJob(name, data, options)
301
297
  }
302
298
 
@@ -304,7 +300,7 @@ class Manager extends EventEmitter {
304
300
  options = options ? { ...options } : {}
305
301
  options.startAfter = after
306
302
 
307
- const result = Attorney.checkSendArgs([name, data, options], this.config)
303
+ const result = Attorney.checkSendArgs([name, data, options])
308
304
 
309
305
  return await this.createJob(result.name, result.data, result.options)
310
306
  }
@@ -315,7 +311,7 @@ class Manager extends EventEmitter {
315
311
  options.singletonNextSlot = false
316
312
  options.singletonKey = key
317
313
 
318
- const result = Attorney.checkSendArgs([name, data, options], this.config)
314
+ const result = Attorney.checkSendArgs([name, data, options])
319
315
 
320
316
  return await this.createJob(result.name, result.data, result.options)
321
317
  }
@@ -326,12 +322,14 @@ class Manager extends EventEmitter {
326
322
  options.singletonNextSlot = true
327
323
  options.singletonKey = key
328
324
 
329
- const result = Attorney.checkSendArgs([name, data, options], this.config)
325
+ const result = Attorney.checkSendArgs([name, data, options])
330
326
 
331
327
  return await this.createJob(result.name, result.data, result.options)
332
328
  }
333
329
 
334
- async createJob (name, data, options, singletonOffset = 0) {
330
+ async createJob (name, data, options) {
331
+ const singletonOffset = 0
332
+
335
333
  const {
336
334
  id = null,
337
335
  db: wrapper,
@@ -339,78 +337,71 @@ class Manager extends EventEmitter {
339
337
  startAfter,
340
338
  singletonKey = null,
341
339
  singletonSeconds,
342
- deadLetter = null,
343
- expireIn,
344
- expireInDefault,
340
+ singletonNextSlot,
341
+ expireInSeconds,
342
+ deleteAfterSeconds,
345
343
  keepUntil,
346
- keepUntilDefault,
347
344
  retryLimit,
348
- retryLimitDefault,
349
345
  retryDelay,
350
- retryDelayDefault,
351
346
  retryBackoff,
352
- retryBackoffDefault
347
+ retryDelayMax
353
348
  } = options
354
349
 
355
- const values = [
356
- id, // 1
357
- name, // 2
358
- data, // 3
359
- priority, // 4
360
- startAfter, // 5
361
- singletonKey, // 6
362
- singletonSeconds, // 7
363
- singletonOffset, // 8
364
- deadLetter, // 9
365
- expireIn, // 10
366
- expireInDefault, // 11
367
- keepUntil, // 12
368
- keepUntilDefault, // 13
369
- retryLimit, // 14
370
- retryLimitDefault, // 15
371
- retryDelay, // 16
372
- retryDelayDefault, // 17
373
- retryBackoff, // 18
374
- retryBackoffDefault // 19
375
- ]
350
+ const job = {
351
+ id,
352
+ name,
353
+ data,
354
+ priority,
355
+ startAfter,
356
+ singletonKey,
357
+ singletonSeconds,
358
+ singletonOffset,
359
+ expireInSeconds,
360
+ deleteAfterSeconds,
361
+ keepUntil,
362
+ retryLimit,
363
+ retryDelay,
364
+ retryBackoff,
365
+ retryDelayMax
366
+ }
376
367
 
377
368
  const db = wrapper || this.db
378
- const { rows } = await db.executeSql(this.insertJobCommand, values)
379
369
 
380
- if (rows.length === 1) {
381
- return rows[0].id
382
- }
370
+ const { table } = await this.getQueueCache(name)
383
371
 
384
- if (!options.singletonNextSlot) {
385
- return null
372
+ const sql = plans.insertJobs(this.config.schema, { table, name, returnId: true })
373
+
374
+ const { rows: try1 } = await db.executeSql(sql, [JSON.stringify([job])])
375
+
376
+ if (try1.length === 1) {
377
+ return try1[0].id
386
378
  }
387
379
 
388
- // delay starting by the offset to honor throttling config
389
- options.startAfter = this.getDebounceStartAfter(singletonSeconds, this.timekeeper.clockSkew)
380
+ if (singletonNextSlot) {
381
+ // delay starting by the offset to honor throttling config
382
+ job.startAfter = this.getDebounceStartAfter(singletonSeconds, this.timekeeper.clockSkew)
383
+ job.singletonOffset = singletonSeconds
390
384
 
391
- // toggle off next slot config for round 2
392
- options.singletonNextSlot = false
385
+ const { rows: try2 } = await db.executeSql(sql, [JSON.stringify([job])])
393
386
 
394
- singletonOffset = singletonSeconds
387
+ if (try2.length === 1) {
388
+ return try2[0].id
389
+ }
390
+ }
395
391
 
396
- return await this.createJob(name, data, options, singletonOffset)
392
+ return null
397
393
  }
398
394
 
399
- async insert (jobs, options = {}) {
395
+ async insert (name, jobs, options = {}) {
400
396
  assert(Array.isArray(jobs), 'jobs argument should be an array')
401
397
 
402
- const db = options.db || this.db
398
+ const { table } = await this.getQueueCache(name)
403
399
 
404
- const params = [
405
- JSON.stringify(jobs), // 1
406
- this.config.expireIn, // 2
407
- this.config.keepUntil, // 3
408
- this.config.retryLimit, // 4
409
- this.config.retryDelay, // 5
410
- this.config.retryBackoff // 6
411
- ]
400
+ const db = this.assertDb(options)
412
401
 
413
- const { rows } = await db.executeSql(this.insertJobsCommand, params)
402
+ const sql = plans.insertJobs(this.config.schema, { table, name, returnId: false })
403
+
404
+ const { rows } = await db.executeSql(sql, [JSON.stringify(jobs)])
414
405
 
415
406
  return (rows.length) ? rows.map(i => i.id) : null
416
407
  }
@@ -434,13 +425,27 @@ class Manager extends EventEmitter {
434
425
 
435
426
  async fetch (name, options = {}) {
436
427
  Attorney.checkFetchArgs(name, options)
437
- const db = options.db || this.db
438
- const nextJobSql = this.nextJobCommand({ ...options })
428
+
429
+ const db = this.assertDb(options)
430
+
431
+ const { table, policy, singletonsActive } = await this.getQueueCache(name)
432
+
433
+ options = {
434
+ ...options,
435
+ schema: this.config.schema,
436
+ table,
437
+ name,
438
+ policy,
439
+ limit: options.batchSize,
440
+ ignoreSingletons: singletonsActive
441
+ }
442
+
443
+ const sql = plans.fetchNextJob(options)
439
444
 
440
445
  let result
441
446
 
442
447
  try {
443
- result = await db.executeSql(nextJobSql, [name, options.batchSize])
448
+ result = await db.executeSql(sql)
444
449
  } catch (err) {
445
450
  // errors from fetchquery should only be unique constraint violations
446
451
  }
@@ -480,41 +485,51 @@ class Manager extends EventEmitter {
480
485
 
481
486
  async complete (name, id, data, options = {}) {
482
487
  Attorney.assertQueueName(name)
483
- const db = options.db || this.db
488
+ const db = this.assertDb(options)
484
489
  const ids = this.mapCompletionIdArg(id, 'complete')
485
- const result = await db.executeSql(this.completeJobsCommand, [name, ids, this.mapCompletionDataArg(data)])
490
+ const { table } = await this.getQueueCache(name)
491
+ const sql = plans.completeJobs(this.config.schema, table)
492
+ const result = await db.executeSql(sql, [name, ids, this.mapCompletionDataArg(data)])
486
493
  return this.mapCommandResponse(ids, result)
487
494
  }
488
495
 
489
496
  async fail (name, id, data, options = {}) {
490
497
  Attorney.assertQueueName(name)
491
- const db = options.db || this.db
498
+ const db = this.assertDb(options)
492
499
  const ids = this.mapCompletionIdArg(id, 'fail')
493
- const result = await db.executeSql(this.failJobsByIdCommand, [name, ids, this.mapCompletionDataArg(data)])
500
+ const { table } = await this.getQueueCache(name)
501
+ const sql = plans.failJobsById(this.config.schema, table)
502
+ const result = await db.executeSql(sql, [name, ids, this.mapCompletionDataArg(data)])
494
503
  return this.mapCommandResponse(ids, result)
495
504
  }
496
505
 
497
506
  async cancel (name, id, options = {}) {
498
507
  Attorney.assertQueueName(name)
499
- const db = options.db || this.db
508
+ const db = this.assertDb(options)
500
509
  const ids = this.mapCompletionIdArg(id, 'cancel')
501
- const result = await db.executeSql(this.cancelJobsCommand, [name, ids])
510
+ const { table } = await this.getQueueCache(name)
511
+ const sql = plans.cancelJobs(this.config.schema, table)
512
+ const result = await db.executeSql(sql, [name, ids])
502
513
  return this.mapCommandResponse(ids, result)
503
514
  }
504
515
 
505
516
  async deleteJob (name, id, options = {}) {
506
517
  Attorney.assertQueueName(name)
507
- const db = options.db || this.db
518
+ const db = this.assertDb(options)
508
519
  const ids = this.mapCompletionIdArg(id, 'deleteJob')
509
- const result = await db.executeSql(this.deleteJobsCommand, [name, ids])
520
+ const { table } = await this.getQueueCache(name)
521
+ const sql = plans.deleteJobsById(this.config.schema, table)
522
+ const result = await db.executeSql(sql, [name, ids])
510
523
  return this.mapCommandResponse(ids, result)
511
524
  }
512
525
 
513
526
  async resume (name, id, options = {}) {
514
527
  Attorney.assertQueueName(name)
515
- const db = options.db || this.db
528
+ const db = this.assertDb(options)
516
529
  const ids = this.mapCompletionIdArg(id, 'resume')
517
- const result = await db.executeSql(this.resumeJobsCommand, [name, ids])
530
+ const { table } = await this.getQueueCache(name)
531
+ const sql = plans.resumeJobs(this.config.schema, table)
532
+ const result = await db.executeSql(sql, [name, ids])
518
533
  return this.mapCommandResponse(ids, result)
519
534
  }
520
535
 
@@ -522,7 +537,9 @@ class Manager extends EventEmitter {
522
537
  Attorney.assertQueueName(name)
523
538
  const db = options.db || this.db
524
539
  const ids = this.mapCompletionIdArg(id, 'retry')
525
- const result = await db.executeSql(this.retryJobsCommand, [name, ids])
540
+ const { table } = await this.getQueueCache(name)
541
+ const sql = plans.retryJobs(this.config.schema, table)
542
+ const result = await db.executeSql(sql, [name, ids])
526
543
  return this.mapCommandResponse(ids, result)
527
544
  }
528
545
 
@@ -531,76 +548,62 @@ class Manager extends EventEmitter {
531
548
 
532
549
  Attorney.assertQueueName(name)
533
550
 
534
- const { policy = QUEUE_POLICIES.standard } = options
551
+ options.policy = options.policy || QUEUE_POLICIES.standard
535
552
 
536
- assert(policy in QUEUE_POLICIES, `${policy} is not a valid queue policy`)
553
+ assert(options.policy in QUEUE_POLICIES, `${options.policy} is not a valid queue policy`)
537
554
 
538
- const {
539
- retryLimit,
540
- retryDelay,
541
- retryBackoff,
542
- expireInSeconds,
543
- retentionMinutes,
544
- deadLetter
545
- } = Attorney.checkQueueArgs(name, options)
555
+ Attorney.validateQueueArgs(options)
546
556
 
547
- if (deadLetter) {
548
- Attorney.assertQueueName(deadLetter)
557
+ if (options.deadLetter) {
558
+ Attorney.assertQueueName(options.deadLetter)
559
+ assert.notStrictEqual(name, options.deadLetter, 'deadLetter cannot be itself')
560
+ await this.getQueueCache(options.deadLetter)
549
561
  }
550
562
 
551
- // todo: pull in defaults from constructor config
552
- const data = {
553
- policy,
554
- retryLimit,
555
- retryDelay,
556
- retryBackoff,
557
- expireInSeconds,
558
- retentionMinutes,
559
- deadLetter
560
- }
561
-
562
- await this.db.executeSql(this.createQueueCommand, [name, data])
563
+ const sql = plans.createQueue(this.config.schema, name, options)
564
+ await this.db.executeSql(sql)
563
565
  }
564
566
 
565
- async getQueues () {
566
- const { rows } = await this.db.executeSql(this.getQueuesCommand)
567
+ async getQueues (names) {
568
+ if (names) {
569
+ names = Array.isArray(names) ? names : [names]
570
+ for (const name of names) {
571
+ Attorney.assertQueueName(name)
572
+ }
573
+ }
574
+
575
+ const sql = plans.getQueues(this.config.schema, names)
576
+ const { rows } = await this.db.executeSql(sql)
567
577
  return rows
568
578
  }
569
579
 
570
580
  async updateQueue (name, options = {}) {
571
581
  Attorney.assertQueueName(name)
572
582
 
573
- const { policy = QUEUE_POLICIES.standard } = options
583
+ assert(Object.keys(options).length > 0, 'no properties found to update')
574
584
 
575
- assert(policy in QUEUE_POLICIES, `${policy} is not a valid queue policy`)
585
+ if ('policy' in options) {
586
+ assert(options.policy in QUEUE_POLICIES, `${options.policy} is not a valid queue policy`)
587
+ }
576
588
 
577
- const {
578
- retryLimit,
579
- retryDelay,
580
- retryBackoff,
581
- expireInSeconds,
582
- retentionMinutes,
583
- deadLetter
584
- } = Attorney.checkQueueArgs(name, options)
589
+ Attorney.validateQueueArgs(options)
585
590
 
586
- const params = [
587
- name,
588
- policy,
589
- retryLimit,
590
- retryDelay,
591
- retryBackoff,
592
- expireInSeconds,
593
- retentionMinutes,
594
- deadLetter
595
- ]
591
+ const { deadLetter } = options
596
592
 
597
- await this.db.executeSql(this.updateQueueCommand, params)
593
+ if (deadLetter) {
594
+ Attorney.assertQueueName(deadLetter)
595
+ assert.notStrictEqual(name, deadLetter, 'deadLetter cannot be itself')
596
+ }
597
+
598
+ const sql = plans.updateQueue(this.config.schema, { deadLetter })
599
+ await this.db.executeSql(sql, [name, options])
598
600
  }
599
601
 
600
602
  async getQueue (name) {
601
603
  Attorney.assertQueueName(name)
602
604
 
603
- const { rows } = await this.db.executeSql(this.getQueueByNameCommand, [name])
605
+ const sql = plans.getQueues(this.config.schema, [name])
606
+ const { rows } = await this.db.executeSql(sql)
604
607
 
605
608
  return rows[0] || null
606
609
  }
@@ -608,48 +611,79 @@ class Manager extends EventEmitter {
608
611
  async deleteQueue (name) {
609
612
  Attorney.assertQueueName(name)
610
613
 
611
- const { rows } = await this.db.executeSql(this.getQueueByNameCommand, [name])
614
+ try {
615
+ await this.getQueueCache(name)
616
+ const sql = plans.deleteQueue(this.config.schema, name)
617
+ await this.db.executeSql(sql)
618
+ } catch {}
619
+ }
612
620
 
613
- if (rows.length === 1) {
614
- await this.db.executeSql(this.deleteQueueCommand, [name])
615
- }
621
+ async deleteQueuedJobs (name) {
622
+ Attorney.assertQueueName(name)
623
+ const { table } = await this.getQueueCache(name)
624
+ const sql = plans.deleteQueuedJobs(this.config.schema, table)
625
+ await this.db.executeSql(sql, [name])
616
626
  }
617
627
 
618
- async purgeQueue (name) {
628
+ async deleteStoredJobs (name) {
619
629
  Attorney.assertQueueName(name)
620
- await this.db.executeSql(this.purgeQueueCommand, [name])
630
+ const { table } = await this.getQueueCache(name)
631
+ const sql = plans.deleteStoredJobs(this.config.schema, table)
632
+ await this.db.executeSql(sql, [name])
621
633
  }
622
634
 
623
- async clearStorage () {
624
- await this.db.executeSql(this.clearStorageCommand)
635
+ async deleteAllJobs (name) {
636
+ Attorney.assertQueueName(name)
637
+ const { table, partition } = await this.getQueueCache(name)
638
+
639
+ if (partition) {
640
+ const sql = plans.deleteAllJobs(this.config.schema, table)
641
+ await this.db.executeSql(sql, [name])
642
+ } else {
643
+ const sql = plans.truncateTable(this.config.schema, table)
644
+ await this.db.executeSql(sql)
645
+ }
625
646
  }
626
647
 
627
- async getQueueSize (name, options) {
648
+ async getQueueStats (name) {
628
649
  Attorney.assertQueueName(name)
629
650
 
630
- const sql = plans.getQueueSize(this.config.schema, options)
651
+ const { table } = await this.getQueueCache(name)
652
+
653
+ const sql = plans.getQueueStats(this.config.schema, table, [name])
631
654
 
632
- const result = await this.db.executeSql(sql, [name])
655
+ const { rows } = await this.db.executeSql(sql)
633
656
 
634
- return result ? parseFloat(result.rows[0].count) : null
657
+ return rows.at(0) || null
635
658
  }
636
659
 
637
660
  async getJobById (name, id, options = {}) {
638
661
  Attorney.assertQueueName(name)
639
662
 
640
- const db = options.db || this.db
663
+ const db = this.assertDb(options)
641
664
 
642
- const result1 = await db.executeSql(this.getJobByIdCommand, [name, id])
665
+ const { table } = await this.getQueueCache(name)
666
+
667
+ const sql = plans.getJobById(this.config.schema, table)
668
+
669
+ const result1 = await db.executeSql(sql, [name, id])
643
670
 
644
671
  if (result1?.rows?.length === 1) {
645
672
  return result1.rows[0]
646
- } else if (options.includeArchive) {
647
- const result2 = await db.executeSql(this.getArchivedJobByIdCommand, [name, id])
648
- return result2?.rows[0] || null
649
673
  } else {
650
674
  return null
651
675
  }
652
676
  }
677
+
678
+ assertDb (options) {
679
+ if (options.db) {
680
+ return options.db
681
+ }
682
+
683
+ assert(this.db._pgbdb && this.db.opened, 'Database connection is not opened')
684
+
685
+ return this.db
686
+ }
653
687
  }
654
688
 
655
689
  module.exports = Manager