pg-boss 8.3.1 → 8.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pg-boss",
3
- "version": "8.3.1",
3
+ "version": "8.4.1",
4
4
  "description": "Queueing jobs in Node.js using PostgreSQL like a boss",
5
5
  "main": "./src/index.js",
6
6
  "engines": {
@@ -32,7 +32,8 @@
32
32
  "export-schema": "node ./scripts/construct.js",
33
33
  "export-migration": "node ./scripts/migrate.js",
34
34
  "export-rollback": "node ./scripts/rollback.js",
35
- "tsc": "tsc --noEmit types.d.ts"
35
+ "tsc": "tsc --noEmit types.d.ts",
36
+ "readme": "node ./test/readme.js"
36
37
  },
37
38
  "mocha": {
38
39
  "timeout": 10000,
package/src/attorney.js CHANGED
@@ -159,6 +159,7 @@ function getConfig (value) {
159
159
  applyDatabaseConfig(config)
160
160
  applyMaintenanceConfig(config)
161
161
  applyArchiveConfig(config)
162
+ applyArchiveFailedConfig(config)
162
163
  applyDeleteConfig(config)
163
164
  applyMonitoringConfig(config)
164
165
  applyUuidConfig(config)
@@ -195,6 +196,19 @@ function applyArchiveConfig (config) {
195
196
  }
196
197
  }
197
198
 
199
+ function applyArchiveFailedConfig (config) {
200
+ assert(!('archiveFailedAfterSeconds' in config) || config.archiveFailedAfterSeconds >= 1,
201
+ 'configuration assert: archiveFailedAfterSeconds must be at least every second and less than ')
202
+
203
+ config.archiveFailedSeconds = config.archiveFailedAfterSeconds || config.archiveSeconds
204
+ config.archiveFailedInterval = `${config.archiveFailedSeconds} seconds`
205
+
206
+ // Do not emit warning twice
207
+ if (config.archiveFailedSeconds < 60 && config.archiveSeconds >= 60) {
208
+ emitWarning(WARNINGS.CRON_DISABLED)
209
+ }
210
+ }
211
+
198
212
  function applyCompletionConfig (config, defaults) {
199
213
  assert(!('onComplete' in config) || config.onComplete === true || config.onComplete === false,
200
214
  'configuration assert: onComplete must be either true or false')
package/src/boss.js CHANGED
@@ -33,7 +33,7 @@ class Boss extends EventEmitter {
33
33
  this.events = events
34
34
 
35
35
  this.expireCommand = plans.locked(config.schema, plans.expire(config.schema))
36
- this.archiveCommand = plans.locked(config.schema, plans.archive(config.schema, config.archiveInterval))
36
+ this.archiveCommand = plans.locked(config.schema, plans.archive(config.schema, config.archiveInterval, config.archiveFailedInterval))
37
37
  this.purgeCommand = plans.locked(config.schema, plans.purge(config.schema, config.deleteAfter))
38
38
  this.getMaintenanceTimeCommand = plans.getMaintenanceTime(config.schema)
39
39
  this.setMaintenanceTimeCommand = plans.setMaintenanceTime(config.schema)
package/src/manager.js CHANGED
@@ -341,7 +341,7 @@ class Manager extends EventEmitter {
341
341
  }
342
342
 
343
343
  async sendOnce (name, data, options, key) {
344
- options = options || {}
344
+ options = options ? { ...options } : {}
345
345
 
346
346
  options.singletonKey = key || name
347
347
 
@@ -351,7 +351,7 @@ class Manager extends EventEmitter {
351
351
  }
352
352
 
353
353
  async sendSingleton (name, data, options) {
354
- options = options || {}
354
+ options = options ? { ...options } : {}
355
355
 
356
356
  options.singletonKey = SINGLETON_QUEUE_KEY
357
357
 
@@ -361,7 +361,7 @@ class Manager extends EventEmitter {
361
361
  }
362
362
 
363
363
  async sendAfter (name, data, options, after) {
364
- options = options || {}
364
+ options = options ? { ...options } : {}
365
365
  options.startAfter = after
366
366
 
367
367
  const result = Attorney.checkSendArgs([name, data, options], this.config)
@@ -370,7 +370,7 @@ class Manager extends EventEmitter {
370
370
  }
371
371
 
372
372
  async sendThrottled (name, data, options, seconds, key) {
373
- options = options || {}
373
+ options = options ? { ...options } : {}
374
374
  options.singletonSeconds = seconds
375
375
  options.singletonNextSlot = false
376
376
  options.singletonKey = key
@@ -381,7 +381,7 @@ class Manager extends EventEmitter {
381
381
  }
382
382
 
383
383
  async sendDebounced (name, data, options, seconds, key) {
384
- options = options || {}
384
+ options = options ? { ...options } : {}
385
385
  options.singletonSeconds = seconds
386
386
  options.singletonNextSlot = true
387
387
  options.singletonKey = key
package/src/plans.js CHANGED
@@ -601,7 +601,7 @@ function insertJobs (schema) {
601
601
  keepUntil,
602
602
  on_complete
603
603
  )
604
- SELECT
604
+ SELECT
605
605
  COALESCE(id, gen_random_uuid()) as id,
606
606
  name,
607
607
  data,
@@ -639,12 +639,16 @@ function purge (schema, interval) {
639
639
  `
640
640
  }
641
641
 
642
- function archive (schema, interval) {
642
+ function archive (schema, completedInterval, failedInterval = completedInterval) {
643
643
  return `
644
644
  WITH archived_rows AS (
645
645
  DELETE FROM ${schema}.job
646
- WHERE
647
- completedOn < (now() - interval '${interval}')
646
+ WHERE (
647
+ state <> '${states.failed}' AND completedOn < (now() - interval '${completedInterval}')
648
+ )
649
+ OR (
650
+ state = '${states.failed}' AND completedOn < (now() - interval '${failedInterval}')
651
+ )
648
652
  OR (
649
653
  state < '${states.active}' AND keepUntil < now()
650
654
  )
package/src/timekeeper.js CHANGED
@@ -45,7 +45,7 @@ class Timekeeper extends EventEmitter {
45
45
 
46
46
  async start () {
47
47
  // setting the archive config too low breaks the cron 60s debounce interval so don't even try
48
- if (this.config.archiveSeconds < 60) {
48
+ if (this.config.archiveSeconds < 60 || this.config.archiveFailedSeconds < 60) {
49
49
  return
50
50
  }
51
51
 
@@ -88,29 +88,47 @@ class Timekeeper extends EventEmitter {
88
88
  }
89
89
 
90
90
  async monitorCron () {
91
- const { secondsAgo } = await this.getCronTime()
91
+ try {
92
+ if (this.config.__test__force_cron_monitoring_error) {
93
+ throw new Error(this.config.__test__force_cron_monitoring_error)
94
+ }
92
95
 
93
- if (secondsAgo > 60) {
94
- await this.checkSchedulesAsync()
96
+ const { secondsAgo } = await this.getCronTime()
97
+
98
+ if (secondsAgo > 60) {
99
+ await this.checkSchedulesAsync()
100
+ }
101
+ } catch (err) {
102
+ this.emit(this.events.error, err)
95
103
  }
96
104
  }
97
105
 
98
106
  async cacheClockSkew () {
99
- const { rows } = await this.db.executeSql(this.getTimeCommand)
107
+ let skew = 0
108
+
109
+ try {
110
+ if (this.config.__test__force_clock_monitoring_error) {
111
+ throw new Error(this.config.__test__force_clock_monitoring_error)
112
+ }
100
113
 
101
- const local = Date.now()
114
+ const { rows } = await this.db.executeSql(this.getTimeCommand)
102
115
 
103
- const dbTime = parseFloat(rows[0].time)
116
+ const local = Date.now()
104
117
 
105
- const skew = dbTime - local
118
+ const dbTime = parseFloat(rows[0].time)
106
119
 
107
- const skewSeconds = Math.abs(skew) / 1000
120
+ skew = dbTime - local
108
121
 
109
- if (skewSeconds >= 60 || this.config.__test__force_clock_skew_warning) {
110
- Attorney.warnClockSkew(`Instance clock is ${skewSeconds}s ${skew > 0 ? 'slower' : 'faster'} than database.`)
111
- }
122
+ const skewSeconds = Math.abs(skew) / 1000
112
123
 
113
- this.clockSkew = skew
124
+ if (skewSeconds >= 60 || this.config.__test__force_clock_skew_warning) {
125
+ Attorney.warnClockSkew(`Instance clock is ${skewSeconds}s ${skew > 0 ? 'slower' : 'faster'} than database.`)
126
+ }
127
+ } catch (err) {
128
+ this.emit(this.events.error, err)
129
+ } finally {
130
+ this.clockSkew = skew
131
+ }
114
132
  }
115
133
 
116
134
  async checkSchedulesAsync () {
@@ -128,7 +146,7 @@ class Timekeeper extends EventEmitter {
128
146
 
129
147
  try {
130
148
  if (this.config.__test__throw_clock_monitoring) {
131
- throw new Error('clock monitoring error')
149
+ throw new Error(this.config.__test__throw_clock_monitoring)
132
150
  }
133
151
 
134
152
  const items = await this.getSchedules()
package/types.d.ts CHANGED
@@ -44,6 +44,7 @@ declare namespace PgBoss {
44
44
  maintenanceIntervalMinutes?: number;
45
45
 
46
46
  archiveCompletedAfterSeconds?: number;
47
+ archiveFailedAfterSeconds?: number;
47
48
  }
48
49
 
49
50
  type ConstructorOptions =