pg-boss 10.0.0-beta8 → 10.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/README.md +9 -7
- package/package.json +1 -3
- package/src/attorney.js +16 -22
- package/src/boss.js +0 -28
- package/src/contractor.js +9 -9
- package/src/db.js +9 -9
- package/src/index.js +2 -2
- package/src/manager.js +100 -152
- package/src/plans.js +224 -161
- package/src/timekeeper.js +25 -34
- package/types.d.ts +24 -46
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ async function readme() {
|
|
|
19
19
|
|
|
20
20
|
console.log(`created job ${id} in queue ${queue}`)
|
|
21
21
|
|
|
22
|
-
await boss.work(queue, async job => {
|
|
22
|
+
await boss.work(queue, async ([ job ]) => {
|
|
23
23
|
console.log(`received job ${job.id} with data ${JSON.stringify(job.data)}`)
|
|
24
24
|
})
|
|
25
25
|
}
|
|
@@ -27,19 +27,21 @@ async function readme() {
|
|
|
27
27
|
|
|
28
28
|
pg-boss is a job queue built in Node.js on top of PostgreSQL in order to provide background processing and reliable asynchronous execution to Node.js applications.
|
|
29
29
|
|
|
30
|
-
pg-boss relies on [SKIP LOCKED](https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/), a feature
|
|
30
|
+
pg-boss relies on [SKIP LOCKED](https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/), a feature built specifically for message queues to resolve record locking challenges inherent with relational databases. This provides exactly-once delivery and the safety of guaranteed atomic commits to asynchronous job processing.
|
|
31
31
|
|
|
32
32
|
This will likely cater the most to teams already familiar with the simplicity of relational database semantics and operations (SQL, querying, and backups). It will be especially useful to those already relying on PostgreSQL that want to limit how many systems are required to monitor and support in their architecture.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
|
|
35
|
+
## Summary
|
|
35
36
|
* Exactly-once job delivery
|
|
36
37
|
* Backpressure-compatible polling workers
|
|
37
38
|
* Cron scheduling
|
|
39
|
+
* Queue storage policies to support a variety of rate limiting, debouncing, and concurrency use cases
|
|
40
|
+
* Priority queues, dead letter queues, job deferral, automatic retries with exponential backoff
|
|
38
41
|
* Pub/sub API for fan-out queue relationships
|
|
39
|
-
*
|
|
40
|
-
*
|
|
42
|
+
* Raw SQL support for non-Node.js runtimes via INSERT or COPY
|
|
43
|
+
* Serverless function compatible
|
|
41
44
|
* Multi-master compatible (for example, in a Kubernetes ReplicaSet)
|
|
42
|
-
* Dead letter queues
|
|
43
45
|
|
|
44
46
|
## Requirements
|
|
45
47
|
* Node 20 or higher
|
|
@@ -47,7 +49,7 @@ This will likely cater the most to teams already familiar with the simplicity of
|
|
|
47
49
|
|
|
48
50
|
## Installation
|
|
49
51
|
|
|
50
|
-
```
|
|
52
|
+
```bash
|
|
51
53
|
# npm
|
|
52
54
|
npm install pg-boss
|
|
53
55
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pg-boss",
|
|
3
|
-
"version": "10.0.0
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"description": "Queueing jobs in Postgres from Node.js like a boss",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"cron-parser": "^4.0.0",
|
|
11
|
-
"lodash.debounce": "^4.0.8",
|
|
12
|
-
"p-map": "^4.0.0",
|
|
13
11
|
"pg": "^8.5.1",
|
|
14
12
|
"serialize-error": "^8.1.0"
|
|
15
13
|
},
|
package/src/attorney.js
CHANGED
|
@@ -122,26 +122,25 @@ function checkWorkArgs (name, args, defaults) {
|
|
|
122
122
|
|
|
123
123
|
options = { ...options }
|
|
124
124
|
|
|
125
|
-
|
|
125
|
+
applyPollingInterval(options, defaults)
|
|
126
126
|
|
|
127
|
-
assert(!('teamConcurrency' in options) ||
|
|
128
|
-
(Number.isInteger(options.teamConcurrency) && options.teamConcurrency >= 1 && options.teamConcurrency <= 1000),
|
|
129
|
-
'teamConcurrency must be an integer between 1 and 1000')
|
|
130
|
-
|
|
131
|
-
assert(!('teamSize' in options) || (Number.isInteger(options.teamSize) && options.teamSize >= 1), 'teamSize must be an integer > 0')
|
|
132
127
|
assert(!('batchSize' in options) || (Number.isInteger(options.batchSize) && options.batchSize >= 1), 'batchSize must be an integer > 0')
|
|
133
128
|
assert(!('includeMetadata' in options) || typeof options.includeMetadata === 'boolean', 'includeMetadata must be a boolean')
|
|
129
|
+
assert(!('priority' in options) || typeof options.priority === 'boolean', 'priority must be a boolean')
|
|
130
|
+
|
|
131
|
+
options.batchSize = options.batchSize || 1
|
|
134
132
|
|
|
135
133
|
return { options, callback }
|
|
136
134
|
}
|
|
137
135
|
|
|
138
|
-
function checkFetchArgs (name,
|
|
136
|
+
function checkFetchArgs (name, options) {
|
|
139
137
|
assert(name, 'missing queue name')
|
|
140
138
|
|
|
141
|
-
assert(!batchSize || (Number.isInteger(batchSize) && batchSize >= 1), 'batchSize must be an integer > 0')
|
|
139
|
+
assert(!('batchSize' in options) || (Number.isInteger(options.batchSize) && options.batchSize >= 1), 'batchSize must be an integer > 0')
|
|
142
140
|
assert(!('includeMetadata' in options) || typeof options.includeMetadata === 'boolean', 'includeMetadata must be a boolean')
|
|
141
|
+
assert(!('priority' in options) || typeof options.priority === 'boolean', 'priority must be a boolean')
|
|
143
142
|
|
|
144
|
-
|
|
143
|
+
options.batchSize = options.batchSize || 1
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
function getConfig (value) {
|
|
@@ -163,7 +162,7 @@ function getConfig (value) {
|
|
|
163
162
|
applyDeleteConfig(config)
|
|
164
163
|
applyMonitoringConfig(config)
|
|
165
164
|
|
|
166
|
-
|
|
165
|
+
applyPollingInterval(config)
|
|
167
166
|
applyExpirationConfig(config)
|
|
168
167
|
applyRetentionConfig(config)
|
|
169
168
|
|
|
@@ -186,8 +185,8 @@ function assertPostgresObjectName (name) {
|
|
|
186
185
|
}
|
|
187
186
|
|
|
188
187
|
function assertQueueName (name) {
|
|
188
|
+
assert(name, 'Name is required')
|
|
189
189
|
assert(typeof name === 'string', 'Name must be a string')
|
|
190
|
-
assert(name.length <= 50, 'Name cannot exceed 50 characters')
|
|
191
190
|
assert(/[\w-]/.test(name), 'Name can only contain alphanumeric characters, underscores, or hyphens')
|
|
192
191
|
}
|
|
193
192
|
|
|
@@ -279,18 +278,13 @@ function applyRetryConfig (config, defaults) {
|
|
|
279
278
|
config.retryBackoffDefault = defaults?.retryBackoff
|
|
280
279
|
}
|
|
281
280
|
|
|
282
|
-
function
|
|
283
|
-
assert(!('
|
|
284
|
-
'configuration assert:
|
|
285
|
-
|
|
286
|
-
assert(!('newJobCheckIntervalSeconds' in config) || config.newJobCheckIntervalSeconds >= 1,
|
|
287
|
-
'configuration assert: newJobCheckIntervalSeconds must be at least every second')
|
|
281
|
+
function applyPollingInterval (config, defaults) {
|
|
282
|
+
assert(!('pollingIntervalSeconds' in config) || config.pollingIntervalSeconds >= 0.5,
|
|
283
|
+
'configuration assert: pollingIntervalSeconds must be at least every 500ms')
|
|
288
284
|
|
|
289
|
-
config.
|
|
290
|
-
? config.
|
|
291
|
-
:
|
|
292
|
-
? config.newJobCheckInterval
|
|
293
|
-
: defaults?.newJobCheckInterval || 2000
|
|
285
|
+
config.pollingInterval = ('pollingIntervalSeconds' in config)
|
|
286
|
+
? config.pollingIntervalSeconds * 1000
|
|
287
|
+
: defaults?.pollingInterval || 2000
|
|
294
288
|
}
|
|
295
289
|
|
|
296
290
|
function applyMaintenanceConfig (config) {
|
package/src/boss.js
CHANGED
|
@@ -171,34 +171,6 @@ class Boss extends EventEmitter {
|
|
|
171
171
|
async drop () {
|
|
172
172
|
await this.db.executeSql(this.dropCommand)
|
|
173
173
|
}
|
|
174
|
-
|
|
175
|
-
async setMaintenanceTime () {
|
|
176
|
-
await this.db.executeSql(this.setMaintenanceTimeCommand)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async getMaintenanceTime () {
|
|
180
|
-
const { rows } = await this.db.executeSql(this.getMaintenanceTimeCommand)
|
|
181
|
-
|
|
182
|
-
let { maintained_on: maintainedOn, seconds_ago: secondsAgo } = rows[0]
|
|
183
|
-
|
|
184
|
-
secondsAgo = secondsAgo !== null ? parseFloat(secondsAgo) : 999_999_999
|
|
185
|
-
|
|
186
|
-
return { maintainedOn, secondsAgo }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async setMonitorTime () {
|
|
190
|
-
await this.db.executeSql(this.setMonitorTimeCommand)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async getMonitorTime () {
|
|
194
|
-
const { rows } = await this.db.executeSql(this.getMonitorTimeCommand)
|
|
195
|
-
|
|
196
|
-
let { monitored_on: monitoredOn, seconds_ago: secondsAgo } = rows[0]
|
|
197
|
-
|
|
198
|
-
secondsAgo = secondsAgo !== null ? parseFloat(secondsAgo) : 999_999_999
|
|
199
|
-
|
|
200
|
-
return { monitoredOn, secondsAgo }
|
|
201
|
-
}
|
|
202
174
|
}
|
|
203
175
|
|
|
204
176
|
module.exports = Boss
|
package/src/contractor.js
CHANGED
|
@@ -86,15 +86,15 @@ class Contractor {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
async next (version) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async rollback (version) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
89
|
+
// async next (version) {
|
|
90
|
+
// const commands = migrationStore.next(this.config.schema, version, this.migrations)
|
|
91
|
+
// await this.db.executeSql(commands)
|
|
92
|
+
// }
|
|
93
|
+
|
|
94
|
+
// async rollback (version) {
|
|
95
|
+
// const commands = migrationStore.rollback(this.config.schema, version, this.migrations)
|
|
96
|
+
// await this.db.executeSql(commands)
|
|
97
|
+
// }
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
module.exports = Contractor
|
package/src/db.js
CHANGED
|
@@ -29,15 +29,15 @@ class Db extends EventEmitter {
|
|
|
29
29
|
|
|
30
30
|
async executeSql (text, values) {
|
|
31
31
|
if (this.opened) {
|
|
32
|
-
if (this.config.debug === true) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
32
|
+
// if (this.config.debug === true) {
|
|
33
|
+
// console.log(`${new Date().toISOString()}: DEBUG SQL`)
|
|
34
|
+
// console.log(text)
|
|
35
|
+
|
|
36
|
+
// if (values) {
|
|
37
|
+
// console.log(`${new Date().toISOString()}: DEBUG VALUES`)
|
|
38
|
+
// console.log(values)
|
|
39
|
+
// }
|
|
40
|
+
// }
|
|
41
41
|
|
|
42
42
|
return await this.pool.query(text, values)
|
|
43
43
|
}
|
package/src/index.js
CHANGED
|
@@ -149,7 +149,7 @@ class PgBoss extends EventEmitter {
|
|
|
149
149
|
return
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
let {
|
|
152
|
+
let { close = true, graceful = true, timeout = 30000, wait = true } = options
|
|
153
153
|
|
|
154
154
|
timeout = Math.max(timeout, 1000)
|
|
155
155
|
|
|
@@ -168,7 +168,7 @@ class PgBoss extends EventEmitter {
|
|
|
168
168
|
|
|
169
169
|
await this.#manager.failWip()
|
|
170
170
|
|
|
171
|
-
if (this.#db.isOurs && this.#db.opened &&
|
|
171
|
+
if (this.#db.isOurs && this.#db.opened && close) {
|
|
172
172
|
await this.#db.close()
|
|
173
173
|
}
|
|
174
174
|
|