pg-boss 9.0.3 → 10.0.0-beta10

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
@@ -1,21 +1,18 @@
1
1
  const assert = require('assert')
2
2
  const EventEmitter = require('events')
3
- const delay = require('delay')
4
- const uuid = require('uuid')
3
+ const { randomUUID } = require('crypto')
5
4
  const debounce = require('lodash.debounce')
6
5
  const { serializeError: stringify } = require('serialize-error')
6
+ const pMap = require('p-map')
7
+ const { delay } = require('./tools')
7
8
  const Attorney = require('./attorney')
8
9
  const Worker = require('./worker')
9
- const Db = require('./db')
10
- const pMap = require('p-map')
10
+ const plans = require('./plans')
11
11
 
12
- const { QUEUES: BOSS_QUEUES } = require('./boss')
13
12
  const { QUEUES: TIMEKEEPER_QUEUES } = require('./timekeeper')
13
+ const { QUEUE_POLICIES } = plans
14
14
 
15
- const INTERNAL_QUEUES = Object.values(BOSS_QUEUES).concat(Object.values(TIMEKEEPER_QUEUES)).reduce((acc, i) => ({ ...acc, [i]: i }), {})
16
-
17
- const plans = require('./plans')
18
- const { COMPLETION_JOB_PREFIX, SINGLETON_QUEUE_KEY } = plans
15
+ const INTERNAL_QUEUES = Object.values(TIMEKEEPER_QUEUES).reduce((acc, i) => ({ ...acc, [i]: i }), {})
19
16
 
20
17
  const WIP_EVENT_INTERVAL = 2000
21
18
  const WIP_DEBOUNCE_OPTIONS = { leading: true, trailing: true, maxWait: WIP_EVENT_INTERVAL }
@@ -27,16 +24,14 @@ const events = {
27
24
 
28
25
  const resolveWithinSeconds = async (promise, seconds) => {
29
26
  const timeout = Math.max(1, seconds) * 1000
30
- const reject = delay.reject(timeout, { value: new Error(`handler execution exceeded ${timeout}ms`) })
27
+ const reject = delay(timeout, `handler execution exceeded ${timeout}ms`)
31
28
 
32
29
  let result
33
30
 
34
31
  try {
35
32
  result = await Promise.race([promise, reject])
36
33
  } finally {
37
- try {
38
- reject.clear()
39
- } catch {}
34
+ reject.abort()
40
35
  }
41
36
 
42
37
  return result
@@ -58,7 +53,7 @@ class Manager extends EventEmitter {
58
53
  this.completeJobsCommand = plans.completeJobs(config.schema)
59
54
  this.cancelJobsCommand = plans.cancelJobs(config.schema)
60
55
  this.resumeJobsCommand = plans.resumeJobs(config.schema)
61
- this.failJobsCommand = plans.failJobs(config.schema)
56
+ this.failJobsByIdCommand = plans.failJobsById(config.schema)
62
57
  this.getJobByIdCommand = plans.getJobById(config.schema)
63
58
  this.getArchivedJobByIdCommand = plans.getArchivedJobById(config.schema)
64
59
  this.subscribeCommand = plans.subscribe(config.schema)
@@ -72,12 +67,9 @@ class Manager extends EventEmitter {
72
67
  this.resume,
73
68
  this.fail,
74
69
  this.fetch,
75
- this.fetchCompleted,
76
70
  this.work,
77
71
  this.offWork,
78
72
  this.notifyWorker,
79
- this.onComplete,
80
- this.offComplete,
81
73
  this.publish,
82
74
  this.subscribe,
83
75
  this.unsubscribe,
@@ -85,13 +77,14 @@ class Manager extends EventEmitter {
85
77
  this.send,
86
78
  this.sendDebounced,
87
79
  this.sendThrottled,
88
- this.sendOnce,
89
80
  this.sendAfter,
90
- this.sendSingleton,
81
+ this.createQueue,
82
+ this.updateQueue,
83
+ this.getQueue,
91
84
  this.deleteQueue,
92
- this.deleteAllQueues,
93
- this.clearStorage,
85
+ this.purgeQueue,
94
86
  this.getQueueSize,
87
+ this.clearStorage,
95
88
  this.getJobById
96
89
  ]
97
90
 
@@ -99,27 +92,31 @@ class Manager extends EventEmitter {
99
92
  }
100
93
 
101
94
  start () {
102
- this.stopping = false
95
+ this.stopped = false
103
96
  }
104
97
 
105
98
  async stop () {
106
- this.stopping = true
99
+ this.stopped = true
107
100
 
108
- for (const sub of this.workers.values()) {
109
- if (!INTERNAL_QUEUES[sub.name]) {
110
- await this.offWork(sub.name)
101
+ for (const worker of this.workers.values()) {
102
+ if (!INTERNAL_QUEUES[worker.name]) {
103
+ await this.offWork(worker.name)
111
104
  }
112
105
  }
113
106
  }
114
107
 
115
- async work (name, ...args) {
116
- const { options, callback } = Attorney.checkWorkArgs(name, args, this.config)
117
- return await this.watch(name, options, callback)
108
+ async failWip () {
109
+ for (const worker of this.workers.values()) {
110
+ const jobIds = worker.jobs.map(j => j.id)
111
+ if (jobIds.length) {
112
+ await this.fail(worker.name, jobIds, 'pg-boss shut down while active')
113
+ }
114
+ }
118
115
  }
119
116
 
120
- async onComplete (name, ...args) {
117
+ async work (name, ...args) {
121
118
  const { options, callback } = Attorney.checkWorkArgs(name, args, this.config)
122
- return await this.watch(COMPLETION_JOB_PREFIX + name, options, callback)
119
+ return await this.watch(name, options, callback)
123
120
  }
124
121
 
125
122
  addWorker (worker) {
@@ -175,21 +172,21 @@ class Manager extends EventEmitter {
175
172
  }
176
173
 
177
174
  async watch (name, options, callback) {
178
- if (this.stopping) {
179
- throw new Error('Workers are disabled. pg-boss is stopping.')
175
+ if (this.stopped) {
176
+ throw new Error('Workers are disabled. pg-boss is stopped')
180
177
  }
181
178
 
182
179
  const {
183
- newJobCheckInterval: interval = this.config.newJobCheckInterval,
180
+ pollingInterval: interval = this.config.pollingInterval,
184
181
  batchSize,
185
182
  teamSize = 1,
186
183
  teamConcurrency = 1,
187
184
  teamRefill: refill = false,
188
185
  includeMetadata = false,
189
- enforceSingletonQueueActiveLimit = false
186
+ priority = true
190
187
  } = options
191
188
 
192
- const id = uuid.v4()
189
+ const id = randomUUID({ disableEntropyCache: true })
193
190
 
194
191
  let queueSize = 0
195
192
 
@@ -210,7 +207,7 @@ class Manager extends EventEmitter {
210
207
  createTeamRefillPromise()
211
208
  }
212
209
 
213
- const fetch = () => this.fetch(name, batchSize || (teamSize - queueSize), { includeMetadata, enforceSingletonQueueActiveLimit })
210
+ const fetch = () => this.fetch(name, batchSize || (teamSize - queueSize), { includeMetadata, priority })
214
211
 
215
212
  const onFetch = async (jobs) => {
216
213
  if (this.config.__test__throw_worker) {
@@ -220,20 +217,20 @@ class Manager extends EventEmitter {
220
217
  this.emitWip(name)
221
218
 
222
219
  if (batchSize) {
223
- const maxExpiration = jobs.reduce((acc, i) => Math.max(acc, i.expire_in_seconds), 0)
220
+ const maxExpiration = jobs.reduce((acc, i) => Math.max(acc, i.expireInSeconds), 0)
224
221
 
225
222
  await resolveWithinSeconds(Promise.all([callback(jobs)]), maxExpiration)
226
- .then(() => this.complete(jobs.map(job => job.id)))
227
- .catch(err => this.fail(jobs.map(job => job.id), err))
223
+ .then(() => this.complete(name, jobs.map(job => job.id)))
224
+ .catch(err => this.fail(name, jobs.map(job => job.id), err))
228
225
  } else {
229
226
  if (refill) {
230
227
  queueSize += jobs.length || 1
231
228
  }
232
229
 
233
230
  const allTeamPromise = pMap(jobs, job =>
234
- resolveWithinSeconds(callback(job), job.expire_in_seconds)
235
- .then(result => this.complete(job.id, result))
236
- .catch(err => this.fail(job.id, err))
231
+ resolveWithinSeconds(callback(job), job.expireInSeconds)
232
+ .then(result => this.complete(name, job.id, result))
233
+ .catch(err => this.fail(name, job.id, err))
237
234
  .then(() => refill ? onRefill() : null)
238
235
  , { concurrency: teamConcurrency }
239
236
  ).catch(() => {}) // allow promises & non-promises to live together in harmony
@@ -320,21 +317,9 @@ class Manager extends EventEmitter {
320
317
  async publish (event, ...args) {
321
318
  assert(event, 'Missing required argument')
322
319
 
323
- const result = await this.db.executeSql(this.getQueuesForEventCommand, [event])
324
-
325
- if (!result || result.rowCount === 0) {
326
- return []
327
- }
328
-
329
- return await Promise.all(result.rows.map(({ name }) => this.send(name, ...args)))
330
- }
331
-
332
- async offComplete (value) {
333
- if (typeof value === 'string') {
334
- value = COMPLETION_JOB_PREFIX + value
335
- }
320
+ const { rows } = await this.db.executeSql(this.getQueuesForEventCommand, [event])
336
321
 
337
- return await this.offWork(value)
322
+ return await Promise.all(rows.map(({ name }) => this.send(name, ...args)))
338
323
  }
339
324
 
340
325
  async send (...args) {
@@ -342,26 +327,6 @@ class Manager extends EventEmitter {
342
327
  return await this.createJob(name, data, options)
343
328
  }
344
329
 
345
- async sendOnce (name, data, options, key) {
346
- options = options ? { ...options } : {}
347
-
348
- options.singletonKey = key || name
349
-
350
- const result = Attorney.checkSendArgs([name, data, options], this.config)
351
-
352
- return await this.createJob(result.name, result.data, result.options)
353
- }
354
-
355
- async sendSingleton (name, data, options) {
356
- options = options ? { ...options } : {}
357
-
358
- options.singletonKey = SINGLETON_QUEUE_KEY
359
-
360
- const result = Attorney.checkSendArgs([name, data, options], this.config)
361
-
362
- return await this.createJob(result.name, result.data, result.options)
363
- }
364
-
365
330
  async sendAfter (name, data, options, after) {
366
331
  options = options ? { ...options } : {}
367
332
  options.startAfter = after
@@ -395,42 +360,52 @@ class Manager extends EventEmitter {
395
360
 
396
361
  async createJob (name, data, options, singletonOffset = 0) {
397
362
  const {
363
+ id = null,
398
364
  db: wrapper,
399
- expireIn,
400
365
  priority,
401
366
  startAfter,
402
- keepUntil,
403
367
  singletonKey = null,
404
368
  singletonSeconds,
405
- retryBackoff,
369
+ deadLetter = null,
370
+ expireIn,
371
+ expireInDefault,
372
+ keepUntil,
373
+ keepUntilDefault,
406
374
  retryLimit,
375
+ retryLimitDefault,
407
376
  retryDelay,
408
- onComplete
377
+ retryDelayDefault,
378
+ retryBackoff,
379
+ retryBackoffDefault
409
380
  } = options
410
381
 
411
- const id = uuid[this.config.uuid]()
412
-
413
382
  const values = [
414
383
  id, // 1
415
384
  name, // 2
416
- priority, // 3
417
- retryLimit, // 4
385
+ data, // 3
386
+ priority, // 4
418
387
  startAfter, // 5
419
- expireIn, // 6
420
- data, // 7
421
- singletonKey, // 8
422
- singletonSeconds, // 9
423
- singletonOffset, // 10
424
- retryDelay, // 11
425
- retryBackoff, // 12
426
- keepUntil, // 13
427
- onComplete // 14
388
+ singletonKey, // 6
389
+ singletonSeconds, // 7
390
+ singletonOffset, // 8
391
+ deadLetter, // 9
392
+ expireIn, // 10
393
+ expireInDefault, // 11
394
+ keepUntil, // 12
395
+ keepUntilDefault, // 13
396
+ retryLimit, // 14
397
+ retryLimitDefault, // 15
398
+ retryDelay, // 16
399
+ retryDelayDefault, // 17
400
+ retryBackoff, // 18
401
+ retryBackoffDefault // 19
428
402
  ]
403
+
429
404
  const db = wrapper || this.db
430
- const result = await db.executeSql(this.insertJobCommand, values)
405
+ const { rows } = await db.executeSql(this.insertJobCommand, values)
431
406
 
432
- if (result && result.rowCount === 1) {
433
- return result.rows[0].id
407
+ if (rows.length === 1) {
408
+ return rows[0].id
434
409
  }
435
410
 
436
411
  if (!options.singletonNextSlot) {
@@ -449,11 +424,20 @@ class Manager extends EventEmitter {
449
424
  }
450
425
 
451
426
  async insert (jobs, options = {}) {
452
- const { db: wrapper } = options
453
- const db = wrapper || this.db
454
- const checkedJobs = Attorney.checkInsertArgs(jobs)
455
- const data = JSON.stringify(checkedJobs)
456
- return await db.executeSql(this.insertJobsCommand, [data])
427
+ assert(Array.isArray(jobs), 'jobs argument should be an array')
428
+
429
+ const db = options.db || this.db
430
+
431
+ const params = [
432
+ JSON.stringify(jobs), // 1
433
+ this.config.expireIn, // 2
434
+ this.config.keepUntil, // 3
435
+ this.config.retryLimit, // 4
436
+ this.config.retryDelay, // 5
437
+ this.config.retryBackoff // 6
438
+ ]
439
+
440
+ return await db.executeSql(this.insertJobsCommand, params)
457
441
  }
458
442
 
459
443
  getDebounceStartAfter (singletonSeconds, clockOffset) {
@@ -476,25 +460,15 @@ class Manager extends EventEmitter {
476
460
  async fetch (name, batchSize, options = {}) {
477
461
  const values = Attorney.checkFetchArgs(name, batchSize, options)
478
462
  const db = options.db || this.db
479
- const preparedStatement = this.nextJobCommand(options.includeMetadata || false, options.enforceSingletonQueueActiveLimit || false)
463
+ const nextJobSql = this.nextJobCommand({ ...options })
480
464
  const statementValues = [values.name, batchSize || 1]
481
465
 
482
466
  let result
483
- if (options.enforceSingletonQueueActiveLimit && !options.db) {
484
- // Prepare/format now and send multi-statement transaction
485
- const fetchQuery = preparedStatement
486
- .replace('$1', Db.quotePostgresStr(statementValues[0]))
487
- .replace('$2', statementValues[1].toString())
488
- // eslint-disable-next-line no-unused-vars
489
- const [_begin, _setLocal, fetchResult, _commit] = await db.executeSql([
490
- 'BEGIN',
491
- 'SET LOCAL jit = OFF', // JIT can slow things down significantly
492
- fetchQuery,
493
- 'COMMIT'
494
- ].join(';\n'))
495
- result = fetchResult
496
- } else {
497
- result = await db.executeSql(preparedStatement, statementValues)
467
+
468
+ try {
469
+ result = await db.executeSql(nextJobSql, statementValues)
470
+ } catch (err) {
471
+ // errors from fetchquery should only be unique constraint violations
498
472
  }
499
473
 
500
474
  if (!result || result.rows.length === 0) {
@@ -504,10 +478,6 @@ class Manager extends EventEmitter {
504
478
  return result.rows.length === 1 && !batchSize ? result.rows[0] : result.rows
505
479
  }
506
480
 
507
- async fetchCompleted (name, batchSize, options = {}) {
508
- return await this.fetch(COMPLETION_JOB_PREFIX + name, batchSize, options)
509
- }
510
-
511
481
  mapCompletionIdArg (id, funcName) {
512
482
  const errorMessage = `${funcName}() requires an id`
513
483
 
@@ -538,45 +508,155 @@ class Manager extends EventEmitter {
538
508
  }
539
509
  }
540
510
 
541
- async complete (id, data, options = {}) {
511
+ async complete (name, id, data, options = {}) {
512
+ assert(name, 'Missing queue name argument')
542
513
  const db = options.db || this.db
543
514
  const ids = this.mapCompletionIdArg(id, 'complete')
544
- const result = await db.executeSql(this.completeJobsCommand, [ids, this.mapCompletionDataArg(data)])
515
+ const result = await db.executeSql(this.completeJobsCommand, [name, ids, this.mapCompletionDataArg(data)])
545
516
  return this.mapCompletionResponse(ids, result)
546
517
  }
547
518
 
548
- async fail (id, data, options = {}) {
519
+ async fail (name, id, data, options = {}) {
520
+ assert(name, 'Missing queue name argument')
549
521
  const db = options.db || this.db
550
522
  const ids = this.mapCompletionIdArg(id, 'fail')
551
- const result = await db.executeSql(this.failJobsCommand, [ids, this.mapCompletionDataArg(data)])
523
+ const result = await db.executeSql(this.failJobsByIdCommand, [name, ids, this.mapCompletionDataArg(data)])
552
524
  return this.mapCompletionResponse(ids, result)
553
525
  }
554
526
 
555
- async cancel (id, options = {}) {
527
+ async cancel (name, id, options = {}) {
528
+ assert(name, 'Missing queue name argument')
556
529
  const db = options.db || this.db
557
530
  const ids = this.mapCompletionIdArg(id, 'cancel')
558
- const result = await db.executeSql(this.cancelJobsCommand, [ids])
531
+ const result = await db.executeSql(this.cancelJobsCommand, [name, ids])
559
532
  return this.mapCompletionResponse(ids, result)
560
533
  }
561
534
 
562
- async resume (id, options = {}) {
535
+ async resume (name, id, options = {}) {
536
+ assert(name, 'Missing queue name argument')
563
537
  const db = options.db || this.db
564
538
  const ids = this.mapCompletionIdArg(id, 'resume')
565
- const result = await db.executeSql(this.resumeJobsCommand, [ids])
539
+ const result = await db.executeSql(this.resumeJobsCommand, [name, ids])
566
540
  return this.mapCompletionResponse(ids, result)
567
541
  }
568
542
 
569
- async deleteQueue (queue, options) {
570
- assert(queue, 'Missing queue name argument')
571
- const sql = plans.deleteQueue(this.config.schema, options)
572
- const result = await this.db.executeSql(sql, [queue])
573
- return result ? result.rowCount : null
543
+ async createQueue (name, options = {}) {
544
+ assert(name, 'Missing queue name argument')
545
+
546
+ Attorney.assertQueueName(name)
547
+
548
+ const { policy = QUEUE_POLICIES.standard } = options
549
+
550
+ assert(policy in QUEUE_POLICIES, `${policy} is not a valid queue policy`)
551
+
552
+ const {
553
+ retryLimit,
554
+ retryDelay,
555
+ retryBackoff,
556
+ expireInSeconds,
557
+ retentionMinutes,
558
+ deadLetter
559
+ } = Attorney.checkQueueArgs(name, options)
560
+
561
+ const paritionSql = plans.createPartition(this.config.schema, name)
562
+
563
+ await this.db.executeSql(paritionSql)
564
+
565
+ const sql = plans.createQueue(this.config.schema, name)
566
+
567
+ const params = [
568
+ name,
569
+ policy,
570
+ retryLimit,
571
+ retryDelay,
572
+ retryBackoff,
573
+ expireInSeconds,
574
+ retentionMinutes,
575
+ deadLetter
576
+ ]
577
+
578
+ await this.db.executeSql(sql, params)
579
+ }
580
+
581
+ async updateQueue (name, options = {}) {
582
+ assert(name, 'Missing queue name argument')
583
+
584
+ const {
585
+ retryLimit,
586
+ retryDelay,
587
+ retryBackoff,
588
+ expireInSeconds,
589
+ retentionMinutes,
590
+ deadLetter
591
+ } = Attorney.checkQueueArgs(name, options)
592
+
593
+ const sql = plans.updateQueue(this.config.schema)
594
+
595
+ const params = [
596
+ name,
597
+ retryLimit,
598
+ retryDelay,
599
+ retryBackoff,
600
+ expireInSeconds,
601
+ retentionMinutes,
602
+ deadLetter
603
+ ]
604
+
605
+ await this.db.executeSql(sql, params)
574
606
  }
575
607
 
576
- async deleteAllQueues (options) {
577
- const sql = plans.deleteAllQueues(this.config.schema, options)
578
- const result = await this.db.executeSql(sql)
579
- return result ? result.rowCount : null
608
+ async getQueue (name) {
609
+ assert(name, 'Missing queue name argument')
610
+
611
+ const sql = plans.getQueueByName(this.config.schema)
612
+ const result = await this.db.executeSql(sql, [name])
613
+
614
+ if (result.rows.length === 0) {
615
+ return null
616
+ }
617
+
618
+ const {
619
+ policy,
620
+ retry_limit: retryLimit,
621
+ retry_delay: retryDelay,
622
+ retry_backoff: retryBackoff,
623
+ expire_seconds: expireInSeconds,
624
+ retention_minutes: retentionMinutes,
625
+ dead_letter: deadLetter
626
+ } = result.rows[0]
627
+
628
+ return {
629
+ name,
630
+ policy,
631
+ retryLimit,
632
+ retryDelay,
633
+ retryBackoff,
634
+ expireInSeconds,
635
+ retentionMinutes,
636
+ deadLetter
637
+ }
638
+ }
639
+
640
+ async deleteQueue (name) {
641
+ assert(name, 'Missing queue name argument')
642
+
643
+ const queueSql = plans.getQueueByName(this.config.schema)
644
+ const { rows } = await this.db.executeSql(queueSql, [name])
645
+
646
+ if (rows.length) {
647
+ Attorney.assertQueueName(name)
648
+ const sql = plans.dropPartition(this.config.schema, name)
649
+ await this.db.executeSql(sql)
650
+ }
651
+
652
+ const sql = plans.deleteQueueRecords(this.config.schema)
653
+ await this.db.executeSql(sql, [name])
654
+ }
655
+
656
+ async purgeQueue (queue) {
657
+ assert(queue, 'Missing queue name argument')
658
+ const sql = plans.purgeQueue(this.config.schema)
659
+ await this.db.executeSql(sql, [queue])
580
660
  }
581
661
 
582
662
  async clearStorage () {
@@ -594,15 +674,15 @@ class Manager extends EventEmitter {
594
674
  return result ? parseFloat(result.rows[0].count) : null
595
675
  }
596
676
 
597
- async getJobById (id, options = {}) {
677
+ async getJobById (queue, id, options = {}) {
598
678
  const db = options.db || this.db
599
- const result1 = await db.executeSql(this.getJobByIdCommand, [id])
679
+ const result1 = await db.executeSql(this.getJobByIdCommand, [queue, id])
600
680
 
601
681
  if (result1 && result1.rows && result1.rows.length === 1) {
602
682
  return result1.rows[0]
603
683
  }
604
684
 
605
- const result2 = await db.executeSql(this.getArchivedJobByIdCommand, [id])
685
+ const result2 = await db.executeSql(this.getArchivedJobByIdCommand, [queue, id])
606
686
 
607
687
  if (result2 && result2.rows && result2.rows.length === 1) {
608
688
  return result2.rows[0]
@@ -64,110 +64,5 @@ function migrate (value, version, migrations) {
64
64
 
65
65
  function getAll (schema) {
66
66
  return [
67
- {
68
- release: '7.4.0',
69
- version: 20,
70
- previous: 19,
71
- install: [
72
- `DROP INDEX ${schema}.job_singletonKey`,
73
- `DROP INDEX ${schema}.job_singleton_queue`,
74
- `CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < 'completed' AND singletonOn IS NULL AND NOT singletonKey LIKE '\\_\\_pgboss\\_\\_singleton\\_queue%'`,
75
- `CREATE UNIQUE INDEX job_singleton_queue ON ${schema}.job (name, singletonKey) WHERE state < 'active' AND singletonOn IS NULL AND singletonKey LIKE '\\_\\_pgboss\\_\\_singleton\\_queue%'`
76
- ],
77
- uninstall: [
78
- `DROP INDEX ${schema}.job_singletonKey`,
79
- `DROP INDEX ${schema}.job_singleton_queue`,
80
- `CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < 'completed' AND singletonOn IS NULL AND NOT singletonKey = '__pgboss__singleton_queue'`,
81
- `CREATE UNIQUE INDEX job_singleton_queue ON ${schema}.job (name, singletonKey) WHERE state < 'active' AND singletonOn IS NULL AND singletonKey = '__pgboss__singleton_queue'`
82
- ]
83
- },
84
- {
85
- release: '7.0.0',
86
- version: 19,
87
- previous: 18,
88
- install: [
89
- `CREATE TABLE ${schema}.subscription (
90
- event text not null,
91
- name text not null,
92
- created_on timestamp with time zone not null default now(),
93
- updated_on timestamp with time zone not null default now(),
94
- PRIMARY KEY(event, name)
95
- )`
96
- ],
97
- uninstall: [
98
- `DROP TABLE ${schema}.subscription`
99
- ]
100
- },
101
- {
102
- release: '6.1.1',
103
- version: 18,
104
- previous: 17,
105
- install: [
106
- `ALTER TABLE ${schema}.job ALTER COLUMN on_complete SET DEFAULT false`
107
- ]
108
- },
109
- {
110
- release: '6.0.0',
111
- version: 17,
112
- previous: 16,
113
- install: [
114
- `DROP INDEX ${schema}.job_singletonKey`,
115
- `CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < 'completed' AND singletonOn IS NULL AND NOT singletonKey = '__pgboss__singleton_queue'`,
116
- `CREATE UNIQUE INDEX job_singleton_queue ON ${schema}.job (name, singletonKey) WHERE state < 'active' AND singletonOn IS NULL AND singletonKey = '__pgboss__singleton_queue'`,
117
- `CREATE INDEX IF NOT EXISTS job_fetch ON ${schema}.job (name text_pattern_ops, startAfter) WHERE state < 'active'`,
118
- `ALTER TABLE ${schema}.job ADD output jsonb`,
119
- `ALTER TABLE ${schema}.archive ADD output jsonb`,
120
- `ALTER TABLE ${schema}.job ALTER COLUMN on_complete SET DEFAULT false`,
121
- `ALTER TABLE ${schema}.job ALTER COLUMN keepuntil SET DEFAULT now() + interval '14 days'`
122
- ],
123
- uninstall: [
124
- `DROP INDEX ${schema}.job_fetch`,
125
- `DROP INDEX ${schema}.job_singleton_queue`,
126
- `DROP INDEX ${schema}.job_singletonKey`,
127
- `CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < 'completed' AND singletonOn IS NULL`,
128
- `ALTER TABLE ${schema}.job DROP COLUMN output`,
129
- `ALTER TABLE ${schema}.archive DROP COLUMN output`,
130
- `ALTER TABLE ${schema}.job ALTER COLUMN on_complete SET DEFAULT true`,
131
- `ALTER TABLE ${schema}.job ALTER COLUMN keepuntil SET DEFAULT now() + interval '30 days'`
132
- ]
133
- },
134
- {
135
- release: '5.2.0',
136
- version: 16,
137
- previous: 15,
138
- install: [
139
- `ALTER TABLE ${schema}.job ADD on_complete boolean`,
140
- `UPDATE ${schema}.job SET on_complete = true`,
141
- `ALTER TABLE ${schema}.job ALTER COLUMN on_complete SET DEFAULT true`,
142
- `ALTER TABLE ${schema}.job ALTER COLUMN on_complete SET NOT NULL`,
143
- `ALTER TABLE ${schema}.archive ADD on_complete boolean`
144
- ],
145
- uninstall: [
146
- `ALTER TABLE ${schema}.job DROP COLUMN on_complete`,
147
- `ALTER TABLE ${schema}.archive DROP COLUMN on_complete`
148
- ]
149
- },
150
- {
151
- release: '5.0.6',
152
- version: 15,
153
- previous: 14,
154
- install: [
155
- `ALTER TABLE ${schema}.version ADD cron_on timestamp with time zone`
156
- ],
157
- uninstall: [
158
- `ALTER TABLE ${schema}.version DROP COLUMN cron_on`
159
- ]
160
- },
161
- {
162
- release: '5.0.0',
163
- version: 14,
164
- previous: 13,
165
- install: [
166
- `ALTER TABLE ${schema}.version ADD maintained_on timestamp with time zone`
167
- ],
168
- uninstall: [
169
- `ALTER TABLE ${schema}.version DROP COLUMN maintained_on`
170
- ]
171
- }
172
67
  ]
173
68
  }