pg-boss 7.2.0 → 7.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pg-boss",
3
- "version": "7.2.0",
3
+ "version": "7.4.0",
4
4
  "description": "Queueing jobs in Node.js using PostgreSQL like a boss",
5
5
  "main": "./src/index.js",
6
6
  "engines": {
package/src/attorney.js CHANGED
@@ -1,9 +1,10 @@
1
1
  const assert = require('assert')
2
- const { DEFAULT_SCHEMA } = require('./plans')
2
+ const { DEFAULT_SCHEMA, SINGLETON_QUEUE_KEY } = require('./plans')
3
3
 
4
4
  module.exports = {
5
5
  getConfig,
6
6
  checkSendArgs,
7
+ checkInsertArgs,
7
8
  checkWorkArgs,
8
9
  checkFetchArgs,
9
10
  warnClockSkew
@@ -60,6 +61,7 @@ function checkSendArgs (args, defaults) {
60
61
  applyExpirationConfig(options, defaults)
61
62
  applyRetentionConfig(options, defaults)
62
63
  applyCompletionConfig(options, defaults)
64
+ applySingletonKeyConfig(options)
63
65
 
64
66
  const { startAfter, singletonSeconds, singletonMinutes, singletonHours } = options
65
67
 
@@ -84,6 +86,22 @@ function checkSendArgs (args, defaults) {
84
86
  return { name, data, options }
85
87
  }
86
88
 
89
+ function checkInsertArgs (jobs) {
90
+ assert(Array.isArray(jobs), `jobs argument should be an array. Received '${typeof jobs}'`)
91
+ return jobs.map(job => {
92
+ job = { ...job }
93
+ applySingletonKeyConfig(job)
94
+ return job
95
+ })
96
+ }
97
+
98
+ function applySingletonKeyConfig (options) {
99
+ if (options.singletonKey && options.useSingletonQueue && options.singletonKey !== SINGLETON_QUEUE_KEY) {
100
+ options.singletonKey = SINGLETON_QUEUE_KEY + options.singletonKey
101
+ }
102
+ delete options.useSingletonQueue
103
+ }
104
+
87
105
  function checkWorkArgs (name, args, defaults) {
88
106
  let options, callback
89
107
 
package/src/manager.js CHANGED
@@ -74,6 +74,7 @@ class Manager extends EventEmitter {
74
74
  this.fetchCompleted,
75
75
  this.work,
76
76
  this.offWork,
77
+ this.notifyWorker,
77
78
  this.onComplete,
78
79
  this.offComplete,
79
80
  this.publish,
@@ -294,6 +295,12 @@ class Manager extends EventEmitter {
294
295
  })
295
296
  }
296
297
 
298
+ notifyWorker (workerId) {
299
+ if (this.workers.has(workerId)) {
300
+ this.workers.get(workerId).notify()
301
+ }
302
+ }
303
+
297
304
  async subscribe (event, name) {
298
305
  assert(event, 'Missing required argument')
299
306
  assert(name, 'Missing required argument')
@@ -439,8 +446,8 @@ class Manager extends EventEmitter {
439
446
  }
440
447
 
441
448
  async insert (jobs) {
442
- assert(Array.isArray(jobs), `jobs argument should be an array. Received '${typeof jobs}'`)
443
- const data = JSON.stringify(jobs)
449
+ const checkedJobs = Attorney.checkInsertArgs(jobs)
450
+ const data = JSON.stringify(checkedJobs)
444
451
  return await this.db.executeSql(this.insertJobsCommand, [data])
445
452
  }
446
453
 
@@ -62,11 +62,25 @@ function migrate (value, version, migrations) {
62
62
  return flatten(schema, result.install, result.version)
63
63
  }
64
64
 
65
- function getAll (schema, config) {
66
- const DEFAULT_RETENTION = '30 days'
67
- const keepUntil = config ? config.keepUntil : DEFAULT_RETENTION
68
-
65
+ function getAll (schema) {
69
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
+ },
70
84
  {
71
85
  release: '7.0.0',
72
86
  version: 19,
@@ -154,218 +168,6 @@ function getAll (schema, config) {
154
168
  uninstall: [
155
169
  `ALTER TABLE ${schema}.version DROP COLUMN maintained_on`
156
170
  ]
157
- },
158
- {
159
- release: '5.0.0-beta1',
160
- version: 13,
161
- previous: 12,
162
- install: [
163
- `CREATE TABLE ${schema}.schedule (
164
- name text primary key,
165
- cron text not null,
166
- timezone text,
167
- data jsonb,
168
- options jsonb,
169
- created_on timestamp with time zone not null default now(),
170
- updated_on timestamp with time zone not null default now()
171
- )`
172
- ],
173
- uninstall: [
174
- `DROP TABLE ${schema}.schedule`
175
- ]
176
- },
177
- {
178
- release: '4.0.0',
179
- version: 12,
180
- previous: 11,
181
- install: [
182
- `ALTER TABLE ${schema}.version ALTER COLUMN version TYPE int USING version::int`,
183
- `ALTER TABLE ${schema}.job ADD COLUMN keepUntil timestamptz`,
184
- `ALTER TABLE ${schema}.archive ADD COLUMN keepUntil timestamptz`,
185
- `ALTER TABLE ${schema}.job ALTER COLUMN keepUntil SET DEFAULT now() + interval '${DEFAULT_RETENTION}'`,
186
- `UPDATE ${schema}.job SET keepUntil = startAfter + interval '${keepUntil}'`,
187
- `ALTER TABLE ${schema}.job ALTER COLUMN keepUntil SET NOT NULL`
188
- ],
189
- uninstall: [
190
- `ALTER TABLE ${schema}.version ALTER COLUMN version TYPE text USING version::text`,
191
- `ALTER TABLE ${schema}.job DROP COLUMN keepUntil`,
192
- `ALTER TABLE ${schema}.archive DROP COLUMN keepUntil`
193
- ]
194
- },
195
- {
196
- release: '3.2.0',
197
- version: 11,
198
- previous: 10,
199
- install: [
200
- `CREATE INDEX archive_archivedon_idx ON ${schema}.archive(archivedon)`
201
- ],
202
- uninstall: [
203
- `DROP INDEX ${schema}.archive_archivedon_idx`
204
- ]
205
- },
206
- {
207
- release: '3.1.3',
208
- version: 10,
209
- previous: 9,
210
- install: [
211
- `CREATE INDEX archive_id_idx ON ${schema}.archive(id)`
212
- ],
213
- uninstall: [
214
- `DROP INDEX ${schema}.archive_id_idx`
215
- ]
216
- },
217
- {
218
- release: '3.1.0',
219
- version: 9,
220
- previous: 8,
221
- install: [
222
- `DROP INDEX ${schema}.job_fetch`,
223
- `DROP INDEX ${schema}.job_name`,
224
- `CREATE INDEX job_name ON ${schema}.job (name text_pattern_ops)`,
225
- `UPDATE ${schema}.job set name = '__state__completed__' || substr(name, 1, position('__state__completed' in name) - 1) WHERE name LIKE '%__state__completed'`
226
- ],
227
- uninstall: [
228
- `UPDATE ${schema}.job set name = substr(name, 21) || '__state__completed' WHERE name LIKE '__state__completed__%'`,
229
- `CREATE INDEX job_fetch ON ${schema}.job (name, priority desc, createdOn, id) WHERE state < 'active'`,
230
- `DROP INDEX ${schema}.job_name`,
231
- `CREATE INDEX job_name ON ${schema}.job (name) WHERE state < 'active'`
232
- ]
233
- },
234
- {
235
- release: '3.0.0',
236
- version: 8,
237
- previous: 7,
238
- install: [
239
- 'CREATE EXTENSION IF NOT EXISTS pgcrypto',
240
- `ALTER TABLE ${schema}.job ALTER COLUMN id SET DEFAULT gen_random_uuid()`,
241
- `ALTER TABLE ${schema}.job ADD retryDelay integer not null DEFAULT (0)`,
242
- `ALTER TABLE ${schema}.job ADD retryBackoff boolean not null DEFAULT false`,
243
- `ALTER TABLE ${schema}.job ADD startAfter timestamp with time zone not null default now()`,
244
- `UPDATE ${schema}.job SET startAfter = createdOn + startIn`,
245
- `ALTER TABLE ${schema}.job DROP COLUMN startIn`,
246
- `UPDATE ${schema}.job SET expireIn = interval '15 minutes' WHERE expireIn IS NULL`,
247
- `ALTER TABLE ${schema}.job ALTER COLUMN expireIn SET NOT NULL`,
248
- `ALTER TABLE ${schema}.job ALTER COLUMN expireIn SET DEFAULT interval '15 minutes'`,
249
- // archive table schema changes
250
- `ALTER TABLE ${schema}.archive ADD retryDelay integer not null DEFAULT (0)`,
251
- `ALTER TABLE ${schema}.archive ADD retryBackoff boolean not null DEFAULT false`,
252
- `ALTER TABLE ${schema}.archive ADD startAfter timestamp with time zone`,
253
- `UPDATE ${schema}.archive SET startAfter = createdOn + startIn`,
254
- `ALTER TABLE ${schema}.archive DROP COLUMN startIn`,
255
- // rename complete to completed for state enum - can't use ALTER TYPE :(
256
- `DROP INDEX ${schema}.job_fetch`,
257
- `DROP INDEX ${schema}.job_singletonOn`,
258
- `DROP INDEX ${schema}.job_singletonKeyOn`,
259
- `DROP INDEX ${schema}.job_singletonKey`,
260
- `ALTER TABLE ${schema}.job ALTER COLUMN state DROP DEFAULT`,
261
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DATA TYPE text USING state::text`,
262
- `ALTER TABLE ${schema}.archive ALTER COLUMN state SET DATA TYPE text USING state::text`,
263
- `DROP TYPE ${schema}.job_state`,
264
- `CREATE TYPE ${schema}.job_state AS ENUM ('created', 'retry', 'active', 'completed', 'expired', 'cancelled', 'failed')`,
265
- `UPDATE ${schema}.job SET state = 'completed' WHERE state = 'complete'`,
266
- `UPDATE ${schema}.archive SET state = 'completed' WHERE state = 'complete'`,
267
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DATA TYPE ${schema}.job_state USING state::${schema}.job_state`,
268
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DEFAULT 'created'`,
269
- `ALTER TABLE ${schema}.archive ALTER COLUMN state SET DATA TYPE ${schema}.job_state USING state::${schema}.job_state`,
270
- `CREATE INDEX job_fetch ON ${schema}.job (name, priority desc, createdOn, id) WHERE state < 'active'`,
271
- `CREATE UNIQUE INDEX job_singletonOn ON ${schema}.job (name, singletonOn) WHERE state < 'expired' AND singletonKey IS NULL`,
272
- `CREATE UNIQUE INDEX job_singletonKeyOn ON ${schema}.job (name, singletonOn, singletonKey) WHERE state < 'expired'`,
273
- `CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < 'completed' AND singletonOn IS NULL`,
274
- // add new job name index
275
- `CREATE INDEX job_name ON ${schema}.job (name) WHERE state < 'active'`
276
- ],
277
- uninstall: [
278
- `ALTER TABLE ${schema}.job ALTER COLUMN id DROP DEFAULT`,
279
- // won't know if we should drop pgcrypto extension so it stays
280
- `ALTER TABLE ${schema}.job DROP COLUMN retryDelay`,
281
- `ALTER TABLE ${schema}.job DROP COLUMN retryBackoff`,
282
- `ALTER TABLE ${schema}.job DROP COLUMN startAfter`,
283
- `ALTER TABLE ${schema}.job ADD COLUMN startIn interval not null default(interval '0')`,
284
- // leaving migrated default data for expireIn
285
- `ALTER TABLE ${schema}.job ALTER COLUMN expireIn DROP NOT NULL`,
286
- `ALTER TABLE ${schema}.job ALTER COLUMN expireIn DROP DEFAULT`,
287
- // archive table restore
288
- `ALTER TABLE ${schema}.archive DROP COLUMN retryDelay`,
289
- `ALTER TABLE ${schema}.archive DROP COLUMN retryBackoff`,
290
- `ALTER TABLE ${schema}.archive DROP COLUMN startAfter`,
291
- `ALTER TABLE ${schema}.archive ADD COLUMN startIn interval`,
292
- // drop new job name index
293
- `DROP INDEX ${schema}.job_name`,
294
- // roll back to old enum def
295
- `DROP INDEX ${schema}.job_fetch`,
296
- `DROP INDEX ${schema}.job_singletonOn`,
297
- `DROP INDEX ${schema}.job_singletonKeyOn`,
298
- `DROP INDEX ${schema}.job_singletonKey`,
299
- `ALTER TABLE ${schema}.job ALTER COLUMN state DROP DEFAULT`,
300
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DATA TYPE text USING state::text`,
301
- `ALTER TABLE ${schema}.archive ALTER COLUMN state SET DATA TYPE text USING state::text`,
302
- `DROP TYPE ${schema}.job_state`,
303
- `CREATE TYPE ${schema}.job_state AS ENUM ('created', 'retry', 'active', 'complete', 'expired', 'cancelled', 'failed')`,
304
- `UPDATE ${schema}.job SET state = 'completed' WHERE state = 'complete'`,
305
- `UPDATE ${schema}.archive SET state = 'complete' WHERE state = 'completed'`,
306
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DATA TYPE ${schema}.job_state USING state::${schema}.job_state`,
307
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DEFAULT 'created'`,
308
- `ALTER TABLE ${schema}.archive ALTER COLUMN state SET DATA TYPE ${schema}.job_state USING state::${schema}.job_state`,
309
- `CREATE INDEX job_fetch ON ${schema}.job (name, priority desc, createdOn, id) WHERE state < 'active'`,
310
- `CREATE UNIQUE INDEX job_singletonOn ON ${schema}.job (name, singletonOn) WHERE state < 'expired' AND singletonKey IS NULL`,
311
- `CREATE UNIQUE INDEX job_singletonKeyOn ON ${schema}.job (name, singletonOn, singletonKey) WHERE state < 'expired'`,
312
- `CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < 'complete' AND singletonOn IS NULL`
313
- ]
314
- },
315
- {
316
- release: '2.5.0',
317
- version: 7,
318
- previous: 6,
319
- install: [
320
- `CREATE TABLE IF NOT EXISTS ${schema}.archive (LIKE ${schema}.job)`,
321
- `ALTER TABLE ${schema}.archive ADD archivedOn timestamptz NOT NULL DEFAULT now()`
322
- ],
323
- uninstall: [
324
- `DROP TABLE ${schema}.archive`
325
- ]
326
- },
327
- {
328
- release: '2.0.0',
329
- version: 6,
330
- previous: 5,
331
- install: [
332
- `CREATE INDEX job_fetch ON ${schema}.job (priority desc, createdOn, id) WHERE state < 'active'`
333
- ],
334
- uninstall: [
335
- `DROP INDEX ${schema}.job_fetch`
336
- ]
337
- },
338
- {
339
- release: '2.0.0',
340
- version: 5,
341
- previous: 4,
342
- install: [
343
- `ALTER TABLE ${schema}.job ALTER COLUMN startIn SET DEFAULT (interval '0')`,
344
- `ALTER TABLE ${schema}.job ALTER COLUMN state SET DEFAULT ('created')`,
345
- `UPDATE ${schema}.job SET name = left(name, -9) || '__state__expired' WHERE name LIKE '%__expired'`
346
- ],
347
- uninstall: [
348
- `ALTER TABLE ${schema}.job ALTER COLUMN startIn DROP DEFAULT`,
349
- `ALTER TABLE ${schema}.job ALTER COLUMN state DROP DEFAULT`,
350
- `UPDATE ${schema}.job SET name = left(name, -16) || '__expired' WHERE name LIKE '%__state__expired'`
351
- ]
352
- },
353
- {
354
- release: '1.1.0',
355
- version: 4,
356
- previous: 3,
357
- install: [
358
- `ALTER TABLE ${schema}.job ADD COLUMN priority integer not null default(0)`,
359
- `ALTER TABLE ${schema}.job ALTER COLUMN createdOn SET DATA TYPE timestamptz`,
360
- `ALTER TABLE ${schema}.job ALTER COLUMN startedOn SET DATA TYPE timestamptz`,
361
- `ALTER TABLE ${schema}.job ALTER COLUMN completedOn SET DATA TYPE timestamptz`
362
- ],
363
- uninstall: [
364
- `ALTER TABLE ${schema}.job DROP COLUMN priority`,
365
- `ALTER TABLE ${schema}.job ALTER COLUMN createdOn SET DATA TYPE timestamp`,
366
- `ALTER TABLE ${schema}.job ALTER COLUMN startedOn SET DATA TYPE timestamp`,
367
- `ALTER TABLE ${schema}.job ALTER COLUMN completedOn SET DATA TYPE timestamp`
368
- ]
369
171
  }
370
172
  ]
371
173
  }
package/src/plans.js CHANGED
@@ -13,6 +13,7 @@ const states = {
13
13
  const DEFAULT_SCHEMA = 'pgboss'
14
14
  const COMPLETION_JOB_PREFIX = `__state__${states.completed}__`
15
15
  const SINGLETON_QUEUE_KEY = '__pgboss__singleton_queue'
16
+ const SINGLETON_QUEUE_KEY_ESCAPED = SINGLETON_QUEUE_KEY.replace(/_/g, '\\_')
16
17
 
17
18
  const MIGRATE_RACE_MESSAGE = 'division by zero'
18
19
  const CREATE_RACE_MESSAGE = 'already exists'
@@ -214,14 +215,14 @@ function getQueueSize (schema, options = {}) {
214
215
  function createIndexSingletonKey (schema) {
215
216
  // anything with singletonKey means "only 1 job can be queued or active at a time"
216
217
  return `
217
- CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < '${states.completed}' AND singletonOn IS NULL AND NOT singletonKey = '${SINGLETON_QUEUE_KEY}'
218
+ CREATE UNIQUE INDEX job_singletonKey ON ${schema}.job (name, singletonKey) WHERE state < '${states.completed}' AND singletonOn IS NULL AND NOT singletonKey LIKE '${SINGLETON_QUEUE_KEY_ESCAPED}%'
218
219
  `
219
220
  }
220
221
 
221
222
  function createIndexSingletonQueue (schema) {
222
223
  // "singleton queue" means "only 1 job can be queued at a time"
223
224
  return `
224
- CREATE UNIQUE INDEX job_singleton_queue ON ${schema}.job (name, singletonKey) WHERE state < '${states.active}' AND singletonOn IS NULL AND singletonKey = '${SINGLETON_QUEUE_KEY}'
225
+ CREATE UNIQUE INDEX job_singleton_queue ON ${schema}.job (name, singletonKey) WHERE state < '${states.active}' AND singletonOn IS NULL AND singletonKey LIKE '${SINGLETON_QUEUE_KEY_ESCAPED}%'
225
226
  `
226
227
  }
227
228
 
package/src/timekeeper.js CHANGED
@@ -44,18 +44,23 @@ class Timekeeper extends EventEmitter {
44
44
  }
45
45
 
46
46
  async start () {
47
+ // setting the archive config too low breaks the cron 60s debounce interval so don't even try
47
48
  if (this.config.archiveSeconds < 60) {
48
49
  return
49
50
  }
50
51
 
52
+ // cache the clock skew from the db server
51
53
  await this.cacheClockSkew()
52
54
 
53
55
  await this.manager.work(queues.CRON, { newJobCheckIntervalSeconds: 4 }, (job) => this.onCron(job))
54
56
  await this.manager.work(queues.SEND_IT, { newJobCheckIntervalSeconds: 4, teamSize: 50, teamConcurrency: 5 }, (job) => this.onSendIt(job))
55
57
 
56
- await this.cronMonitorAsync()
58
+ // uses sendDebounced() to enqueue a cron check
59
+ await this.checkSchedulesAsync()
57
60
 
61
+ // create monitoring interval to make sure cron hasn't crashed
58
62
  this.cronMonitorInterval = setInterval(async () => await this.monitorCron(), this.cronMonitorIntervalMs)
63
+ // create monitoring interval to measure and adjust for drift in clock skew
59
64
  this.skewMonitorInterval = setInterval(async () => await this.cacheClockSkew(), this.skewMonitorIntervalMs)
60
65
 
61
66
  this.stopped = false
@@ -86,7 +91,7 @@ class Timekeeper extends EventEmitter {
86
91
  const { secondsAgo } = await this.getCronTime()
87
92
 
88
93
  if (secondsAgo > 60) {
89
- await this.cronMonitorAsync()
94
+ await this.checkSchedulesAsync()
90
95
  }
91
96
  }
92
97
 
@@ -108,7 +113,7 @@ class Timekeeper extends EventEmitter {
108
113
  this.clockSkew = skew
109
114
  }
110
115
 
111
- async cronMonitorAsync () {
116
+ async checkSchedulesAsync () {
112
117
  const opts = {
113
118
  retryLimit: 2,
114
119
  retentionSeconds: 60,
@@ -136,6 +141,7 @@ class Timekeeper extends EventEmitter {
136
141
 
137
142
  if (this.stopped) return
138
143
 
144
+ // set last time cron was evaluated for downstream usage in cron monitoring
139
145
  await this.setCronTime()
140
146
  } catch (err) {
141
147
  this.emit(this.events.error, err)
@@ -143,7 +149,8 @@ class Timekeeper extends EventEmitter {
143
149
 
144
150
  if (this.stopped) return
145
151
 
146
- await this.cronMonitorAsync()
152
+ // uses sendDebounced() to enqueue a cron check
153
+ await this.checkSchedulesAsync()
147
154
  }
148
155
 
149
156
  shouldSendIt (cron, tz) {
package/src/worker.js CHANGED
@@ -27,6 +27,15 @@ class Worker {
27
27
  this.stopping = false
28
28
  this.stopped = false
29
29
  this.loopDelayPromise = null
30
+ this.beenNotified = false
31
+ }
32
+
33
+ notify () {
34
+ this.beenNotified = true
35
+
36
+ if (this.loopDelayPromise) {
37
+ this.loopDelayPromise.clear()
38
+ }
30
39
  }
31
40
 
32
41
  async start () {
@@ -36,6 +45,7 @@ class Worker {
36
45
  const started = Date.now()
37
46
 
38
47
  try {
48
+ this.beenNotified = false
39
49
  const jobs = await this.fetch()
40
50
 
41
51
  this.lastFetchedOn = Date.now()
@@ -64,7 +74,7 @@ class Worker {
64
74
 
65
75
  this.lastJobDuration = duration
66
76
 
67
- if (!this.stopping && duration < this.interval) {
77
+ if (!this.stopping && !this.beenNotified && duration < this.interval) {
68
78
  this.loopDelayPromise = delay(this.interval - duration)
69
79
  await this.loopDelayPromise
70
80
  this.loopDelayPromise = null
package/types.d.ts CHANGED
@@ -84,6 +84,7 @@ declare namespace PgBoss {
84
84
  priority?: number;
85
85
  startAfter?: number | string | Date;
86
86
  singletonKey?: string;
87
+ useSingletonQueue?: boolean;
87
88
  singletonSeconds?: number;
88
89
  singletonMinutes?: number;
89
90
  singletonHours?: number;
@@ -102,6 +103,7 @@ declare namespace PgBoss {
102
103
  interface JobFetchOptions {
103
104
  teamSize?: number;
104
105
  teamConcurrency?: number;
106
+ teamRefill?: boolean;
105
107
  batchSize?: number;
106
108
  includeMetadata?: boolean;
107
109
  }
@@ -307,6 +309,12 @@ declare class PgBoss extends EventEmitter {
307
309
  offWork(name: string): Promise<void>;
308
310
  offWork(options: PgBoss.OffWorkOptions): Promise<void>;
309
311
 
312
+ /**
313
+ * Notify worker that something has changed
314
+ * @param workerId
315
+ */
316
+ notifyWorker(workerId: string): void;
317
+
310
318
  subscribe(event: string, name: string): Promise<void>;
311
319
  unsubscribe(event: string, name: string): Promise<void>;
312
320
  publish(event: string): Promise<string[]>;
package/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "schema": 19
2
+ "schema": 20
3
3
  }