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/README.md +19 -40
- package/package.json +7 -11
- package/src/attorney.js +81 -105
- package/src/boss.js +88 -139
- package/src/contractor.js +25 -4
- package/src/db.js +14 -8
- package/src/index.js +128 -93
- package/src/manager.js +222 -142
- package/src/migrationStore.js +0 -105
- package/src/plans.js +437 -361
- package/src/timekeeper.js +45 -82
- package/src/tools.js +28 -0
- package/src/worker.js +4 -4
- package/types.d.ts +76 -70
- package/version.json +1 -1
package/src/boss.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
const EventEmitter = require('events')
|
|
2
2
|
const plans = require('./plans')
|
|
3
|
-
const {
|
|
4
|
-
const { COMPLETION_JOB_PREFIX } = plans
|
|
5
|
-
|
|
6
|
-
const queues = {
|
|
7
|
-
MAINTENANCE: '__pgboss__maintenance',
|
|
8
|
-
MONITOR_STATES: '__pgboss__monitor-states'
|
|
9
|
-
}
|
|
3
|
+
const { delay } = require('./tools')
|
|
10
4
|
|
|
11
5
|
const events = {
|
|
12
6
|
error: 'error',
|
|
@@ -23,177 +17,131 @@ class Boss extends EventEmitter {
|
|
|
23
17
|
this.manager = config.manager
|
|
24
18
|
|
|
25
19
|
this.maintenanceIntervalSeconds = config.maintenanceIntervalSeconds
|
|
26
|
-
|
|
27
|
-
this.monitorStates = config.monitorStateIntervalSeconds !== null
|
|
28
|
-
|
|
29
|
-
if (this.monitorStates) {
|
|
30
|
-
this.monitorIntervalSeconds = config.monitorStateIntervalSeconds
|
|
31
|
-
}
|
|
20
|
+
this.monitorStateIntervalSeconds = config.monitorStateIntervalSeconds
|
|
32
21
|
|
|
33
22
|
this.events = events
|
|
34
23
|
|
|
35
|
-
this.
|
|
24
|
+
this.failJobsByTimeoutCommand = plans.locked(config.schema, plans.failJobsByTimeout(config.schema))
|
|
36
25
|
this.archiveCommand = plans.locked(config.schema, plans.archive(config.schema, config.archiveInterval, config.archiveFailedInterval))
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
39
|
-
this.
|
|
26
|
+
this.dropCommand = plans.locked(config.schema, plans.drop(config.schema, config.deleteAfter))
|
|
27
|
+
this.trySetMaintenanceTimeCommand = plans.trySetMaintenanceTime(config.schema)
|
|
28
|
+
this.trySetMonitorTimeCommand = plans.trySetMonitorTime(config.schema)
|
|
40
29
|
this.countStatesCommand = plans.countStates(config.schema)
|
|
41
30
|
|
|
42
31
|
this.functions = [
|
|
43
32
|
this.expire,
|
|
44
33
|
this.archive,
|
|
45
|
-
this.
|
|
34
|
+
this.drop,
|
|
46
35
|
this.countStates,
|
|
47
|
-
this.
|
|
36
|
+
this.maintain
|
|
48
37
|
]
|
|
49
38
|
}
|
|
50
39
|
|
|
51
40
|
async supervise () {
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
await this.manager.deleteQueue(COMPLETION_JOB_PREFIX + queues.MAINTENANCE)
|
|
55
|
-
await this.manager.deleteQueue(queues.MAINTENANCE)
|
|
56
|
-
|
|
57
|
-
await this.maintenanceAsync()
|
|
58
|
-
|
|
59
|
-
const maintenanceWorkOptions = {
|
|
60
|
-
newJobCheckIntervalSeconds: Math.max(1, this.maintenanceIntervalSeconds / 2)
|
|
61
|
-
}
|
|
41
|
+
this.maintenanceInterval = setInterval(() => this.onSupervise(), this.maintenanceIntervalSeconds * 1000)
|
|
42
|
+
}
|
|
62
43
|
|
|
63
|
-
|
|
44
|
+
async monitor () {
|
|
45
|
+
this.monitorInterval = setInterval(() => this.onMonitor(), this.monitorStateIntervalSeconds * 1000)
|
|
46
|
+
}
|
|
64
47
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
48
|
+
async onMonitor () {
|
|
49
|
+
try {
|
|
50
|
+
if (this.monitoring) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
68
53
|
|
|
69
|
-
|
|
54
|
+
this.monitoring = true
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
if (this.config.__test__delay_monitor) {
|
|
57
|
+
await delay(this.config.__test__delay_monitor)
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
60
|
+
if (this.config.__test__throw_monitor) {
|
|
61
|
+
throw new Error(this.config.__test__throw_monitor)
|
|
62
|
+
}
|
|
78
63
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
if (this.config.__test__throw_meta_monitor) {
|
|
83
|
-
throw new Error(this.config.__test__throw_meta_monitor)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const { secondsAgo } = await this.getMaintenanceTime()
|
|
87
|
-
|
|
88
|
-
if (secondsAgo > this.maintenanceIntervalSeconds * 2) {
|
|
89
|
-
await this.manager.deleteQueue(queues.MAINTENANCE, { before: states.completed })
|
|
90
|
-
await this.maintenanceAsync()
|
|
91
|
-
}
|
|
92
|
-
} catch (err) {
|
|
93
|
-
this.emit(events.error, err)
|
|
64
|
+
if (this.stopped) {
|
|
65
|
+
return
|
|
94
66
|
}
|
|
95
|
-
}, this.maintenanceIntervalSeconds * 2 * 1000)
|
|
96
|
-
}
|
|
97
67
|
|
|
98
|
-
|
|
99
|
-
const { startAfter } = options
|
|
68
|
+
const { rows } = await this.db.executeSql(this.trySetMonitorTimeCommand, [this.config.monitorStateIntervalSeconds])
|
|
100
69
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
70
|
+
if (rows.length === 1 && !this.stopped) {
|
|
71
|
+
const states = await this.countStates()
|
|
72
|
+
this.emit(events.monitorStates, states)
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
this.emit(events.error, err)
|
|
76
|
+
} finally {
|
|
77
|
+
this.monitoring = false
|
|
106
78
|
}
|
|
107
|
-
|
|
108
|
-
await this.manager.send(queues.MAINTENANCE, null, options)
|
|
109
79
|
}
|
|
110
80
|
|
|
111
|
-
async
|
|
112
|
-
|
|
81
|
+
async onSupervise () {
|
|
82
|
+
try {
|
|
83
|
+
if (this.maintaining) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
113
86
|
|
|
114
|
-
|
|
115
|
-
startAfter,
|
|
116
|
-
retentionSeconds: this.monitorIntervalSeconds * 4,
|
|
117
|
-
singletonKey: queues.MONITOR_STATES,
|
|
118
|
-
onComplete: false
|
|
119
|
-
}
|
|
87
|
+
this.maintaining = true
|
|
120
88
|
|
|
121
|
-
|
|
122
|
-
|
|
89
|
+
if (this.config.__test__delay_maintenance && !this.stopped) {
|
|
90
|
+
this.__testDelayPromise = delay(this.config.__test__delay_maintenance)
|
|
91
|
+
await this.__testDelayPromise
|
|
92
|
+
}
|
|
123
93
|
|
|
124
|
-
async onMaintenance (job) {
|
|
125
|
-
try {
|
|
126
94
|
if (this.config.__test__throw_maint) {
|
|
127
95
|
throw new Error(this.config.__test__throw_maint)
|
|
128
96
|
}
|
|
129
97
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
await this.archive()
|
|
134
|
-
await this.purge()
|
|
135
|
-
|
|
136
|
-
const ended = Date.now()
|
|
137
|
-
|
|
138
|
-
await this.setMaintenanceTime()
|
|
98
|
+
if (this.stopped) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
139
101
|
|
|
140
|
-
|
|
102
|
+
const { rows } = await this.db.executeSql(this.trySetMaintenanceTimeCommand, [this.config.maintenanceIntervalSeconds])
|
|
141
103
|
|
|
142
|
-
if (!this.stopped) {
|
|
143
|
-
await this.
|
|
144
|
-
|
|
104
|
+
if (rows.length === 1 && !this.stopped) {
|
|
105
|
+
const result = await this.maintain()
|
|
106
|
+
this.emit(events.maintenance, result)
|
|
145
107
|
}
|
|
146
108
|
} catch (err) {
|
|
147
109
|
this.emit(events.error, err)
|
|
110
|
+
} finally {
|
|
111
|
+
this.maintaining = false
|
|
148
112
|
}
|
|
149
113
|
}
|
|
150
114
|
|
|
151
|
-
async
|
|
152
|
-
|
|
153
|
-
if (this.config.__test__throw_monitor) {
|
|
154
|
-
throw new Error(this.config.__test__throw_monitor)
|
|
155
|
-
}
|
|
115
|
+
async maintain () {
|
|
116
|
+
const started = Date.now()
|
|
156
117
|
|
|
157
|
-
|
|
118
|
+
!this.stopped && await this.expire()
|
|
119
|
+
!this.stopped && await this.archive()
|
|
120
|
+
!this.stopped && await this.drop()
|
|
158
121
|
|
|
159
|
-
|
|
122
|
+
const ended = Date.now()
|
|
160
123
|
|
|
161
|
-
|
|
162
|
-
await this.manager.complete(job.id) // pre-complete to bypass throttling
|
|
163
|
-
await this.monitorStatesAsync({ startAfter: this.monitorIntervalSeconds })
|
|
164
|
-
}
|
|
165
|
-
} catch (err) {
|
|
166
|
-
this.emit(events.error, err)
|
|
167
|
-
}
|
|
124
|
+
return { ms: ended - started }
|
|
168
125
|
}
|
|
169
126
|
|
|
170
127
|
async stop () {
|
|
171
|
-
if (this.config.__test__throw_stop) {
|
|
172
|
-
throw new Error(this.config.__test__throw_stop)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
128
|
if (!this.stopped) {
|
|
176
|
-
if (this.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
await this.manager.offWork(queues.MAINTENANCE)
|
|
181
|
-
|
|
182
|
-
if (this.monitorStates) {
|
|
183
|
-
await this.manager.offWork(queues.MONITOR_STATES)
|
|
184
|
-
}
|
|
129
|
+
if (this.__testDelayPromise) this.__testDelayPromise.abort()
|
|
130
|
+
if (this.maintenanceInterval) clearInterval(this.maintenanceInterval)
|
|
131
|
+
if (this.monitorInterval) clearInterval(this.monitorInterval)
|
|
185
132
|
|
|
186
133
|
this.stopped = true
|
|
187
134
|
}
|
|
188
135
|
}
|
|
189
136
|
|
|
190
137
|
async countStates () {
|
|
191
|
-
const stateCountDefault = { ...plans.
|
|
138
|
+
const stateCountDefault = { ...plans.JOB_STATES }
|
|
192
139
|
|
|
193
|
-
Object.keys(stateCountDefault)
|
|
194
|
-
|
|
140
|
+
for (const key of Object.keys(stateCountDefault)) {
|
|
141
|
+
stateCountDefault[key] = 0
|
|
142
|
+
}
|
|
195
143
|
|
|
196
|
-
const counts = await this.executeSql(this.countStatesCommand)
|
|
144
|
+
const counts = await this.db.executeSql(this.countStatesCommand)
|
|
197
145
|
|
|
198
146
|
const states = counts.rows.reduce((acc, item) => {
|
|
199
147
|
if (item.name) {
|
|
@@ -213,43 +161,44 @@ class Boss extends EventEmitter {
|
|
|
213
161
|
}
|
|
214
162
|
|
|
215
163
|
async expire () {
|
|
216
|
-
await this.executeSql(this.
|
|
164
|
+
await this.db.executeSql(this.failJobsByTimeoutCommand)
|
|
217
165
|
}
|
|
218
166
|
|
|
219
167
|
async archive () {
|
|
220
|
-
await this.executeSql(this.archiveCommand)
|
|
168
|
+
await this.db.executeSql(this.archiveCommand)
|
|
221
169
|
}
|
|
222
170
|
|
|
223
|
-
async
|
|
224
|
-
await this.executeSql(this.
|
|
171
|
+
async drop () {
|
|
172
|
+
await this.db.executeSql(this.dropCommand)
|
|
225
173
|
}
|
|
226
174
|
|
|
227
175
|
async setMaintenanceTime () {
|
|
228
|
-
await this.executeSql(this.setMaintenanceTimeCommand)
|
|
176
|
+
await this.db.executeSql(this.setMaintenanceTimeCommand)
|
|
229
177
|
}
|
|
230
178
|
|
|
231
179
|
async getMaintenanceTime () {
|
|
232
|
-
|
|
233
|
-
const { rows } = await this.db.executeSql(this.getMaintenanceTimeCommand)
|
|
180
|
+
const { rows } = await this.db.executeSql(this.getMaintenanceTimeCommand)
|
|
234
181
|
|
|
235
|
-
|
|
182
|
+
let { maintained_on: maintainedOn, seconds_ago: secondsAgo } = rows[0]
|
|
236
183
|
|
|
237
|
-
|
|
184
|
+
secondsAgo = secondsAgo !== null ? parseFloat(secondsAgo) : 999_999_999
|
|
238
185
|
|
|
239
|
-
|
|
240
|
-
}
|
|
186
|
+
return { maintainedOn, secondsAgo }
|
|
241
187
|
}
|
|
242
188
|
|
|
243
|
-
|
|
244
|
-
|
|
189
|
+
async setMonitorTime () {
|
|
190
|
+
await this.db.executeSql(this.setMonitorTimeCommand)
|
|
245
191
|
}
|
|
246
192
|
|
|
247
|
-
async
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
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 }
|
|
251
201
|
}
|
|
252
202
|
}
|
|
253
203
|
|
|
254
204
|
module.exports = Boss
|
|
255
|
-
module.exports.QUEUES = queues
|
package/src/contractor.js
CHANGED
|
@@ -21,32 +21,53 @@ class Contractor {
|
|
|
21
21
|
this.config = config
|
|
22
22
|
this.db = db
|
|
23
23
|
this.migrations = this.config.migrations || migrationStore.getAll(this.config.schema)
|
|
24
|
+
|
|
25
|
+
// exported api to index
|
|
26
|
+
this.functions = [
|
|
27
|
+
this.schemaVersion,
|
|
28
|
+
this.isInstalled
|
|
29
|
+
]
|
|
24
30
|
}
|
|
25
31
|
|
|
26
|
-
async
|
|
32
|
+
async schemaVersion () {
|
|
27
33
|
const result = await this.db.executeSql(plans.getVersion(this.config.schema))
|
|
28
34
|
return result.rows.length ? parseInt(result.rows[0].version) : null
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
async isInstalled () {
|
|
32
38
|
const result = await this.db.executeSql(plans.versionTableExists(this.config.schema))
|
|
33
|
-
return result.rows
|
|
39
|
+
return !!result.rows[0].name
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
async start () {
|
|
37
43
|
const installed = await this.isInstalled()
|
|
38
44
|
|
|
39
45
|
if (installed) {
|
|
40
|
-
const version = await this.
|
|
46
|
+
const version = await this.schemaVersion()
|
|
41
47
|
|
|
42
48
|
if (schemaVersion > version) {
|
|
43
|
-
|
|
49
|
+
throw new Error('Migrations are not supported to v10')
|
|
50
|
+
// await this.migrate(version)
|
|
44
51
|
}
|
|
45
52
|
} else {
|
|
46
53
|
await this.create()
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
|
|
57
|
+
async check () {
|
|
58
|
+
const installed = await this.isInstalled()
|
|
59
|
+
|
|
60
|
+
if (!installed) {
|
|
61
|
+
throw new Error('pg-boss is not installed')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const version = await this.schemaVersion()
|
|
65
|
+
|
|
66
|
+
if (schemaVersion !== version) {
|
|
67
|
+
throw new Error('pg-boss database requires migrations')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
50
71
|
async create () {
|
|
51
72
|
try {
|
|
52
73
|
const commands = plans.create(this.config.schema, schemaVersion)
|
package/src/db.js
CHANGED
|
@@ -10,6 +10,10 @@ class Db extends EventEmitter {
|
|
|
10
10
|
this.config = config
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
events = {
|
|
14
|
+
error: 'error'
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
async open () {
|
|
14
18
|
this.pool = new pg.Pool(this.config)
|
|
15
19
|
this.pool.on('error', error => this.emit('error', error))
|
|
@@ -25,16 +29,18 @@ class Db extends EventEmitter {
|
|
|
25
29
|
|
|
26
30
|
async executeSql (text, values) {
|
|
27
31
|
if (this.opened) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
+
}
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
const delimeter = '$sanitize$'
|
|
34
|
-
if (str.includes(delimeter)) {
|
|
35
|
-
throw new Error(`Attempted to quote string that contains reserved Postgres delimeter: ${str}`)
|
|
42
|
+
return await this.pool.query(text, values)
|
|
36
43
|
}
|
|
37
|
-
return `${delimeter}${str}${delimeter}`
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
|