pg-boss 10.0.0-beta2 → 10.0.0-beta20
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 +12 -16
- package/package.json +1 -3
- package/src/attorney.js +35 -36
- package/src/boss.js +13 -57
- package/src/contractor.js +18 -12
- package/src/db.js +12 -38
- package/src/index.js +91 -66
- package/src/manager.js +109 -164
- package/src/plans.js +404 -298
- package/src/timekeeper.js +51 -98
- package/types.d.ts +67 -64
package/README.md
CHANGED
|
@@ -9,33 +9,29 @@ async function readme() {
|
|
|
9
9
|
const PgBoss = require('pg-boss');
|
|
10
10
|
const boss = new PgBoss('postgres://user:pass@host/database');
|
|
11
11
|
|
|
12
|
-
boss.on('error',
|
|
12
|
+
boss.on('error', console.error)
|
|
13
13
|
|
|
14
|
-
await boss.start()
|
|
14
|
+
await boss.start()
|
|
15
15
|
|
|
16
|
-
const queue = '
|
|
16
|
+
const queue = 'readme-queue'
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const id = await boss.send(queue, { arg1: 'read me' })
|
|
19
19
|
|
|
20
|
-
console.log(`created job in queue ${queue}
|
|
20
|
+
console.log(`created job ${id} in queue ${queue}`)
|
|
21
21
|
|
|
22
|
-
await boss.work(queue,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async function someAsyncJobHandler(job) {
|
|
26
|
-
console.log(`job ${job.id} received with data:`);
|
|
27
|
-
console.log(JSON.stringify(job.data));
|
|
28
|
-
|
|
29
|
-
await doSomethingAsyncWithThis(job.data);
|
|
22
|
+
await boss.work(queue, async ([ job ]) => {
|
|
23
|
+
console.log(`received job ${job.id} with data ${JSON.stringify(job.data)}`)
|
|
24
|
+
})
|
|
30
25
|
}
|
|
31
26
|
```
|
|
32
27
|
|
|
33
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.
|
|
34
29
|
|
|
35
|
-
pg-boss relies on [SKIP LOCKED](https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/), a feature added to postgres specifically for message queues
|
|
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 added to postgres 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 of a relational database to asynchronous job processing.
|
|
36
31
|
|
|
37
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.
|
|
38
33
|
|
|
34
|
+
|
|
39
35
|
## Features
|
|
40
36
|
* Exactly-once job delivery
|
|
41
37
|
* Backpressure-compatible polling workers
|
|
@@ -48,11 +44,11 @@ This will likely cater the most to teams already familiar with the simplicity of
|
|
|
48
44
|
|
|
49
45
|
## Requirements
|
|
50
46
|
* Node 20 or higher
|
|
51
|
-
* PostgreSQL
|
|
47
|
+
* PostgreSQL 13 or higher
|
|
52
48
|
|
|
53
49
|
## Installation
|
|
54
50
|
|
|
55
|
-
```
|
|
51
|
+
```bash
|
|
56
52
|
# npm
|
|
57
53
|
npm install pg-boss
|
|
58
54
|
|
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-beta20",
|
|
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
|
@@ -8,7 +8,8 @@ module.exports = {
|
|
|
8
8
|
checkWorkArgs,
|
|
9
9
|
checkFetchArgs,
|
|
10
10
|
warnClockSkew,
|
|
11
|
-
assertPostgresObjectName
|
|
11
|
+
assertPostgresObjectName,
|
|
12
|
+
assertQueueName
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const MAX_INTERVAL_HOURS = 24
|
|
@@ -29,8 +30,6 @@ const WARNINGS = {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function checkQueueArgs (name, options = {}) {
|
|
32
|
-
assertPostgresObjectName(name)
|
|
33
|
-
|
|
34
33
|
assert(!('deadLetter' in options) || (typeof options.deadLetter === 'string'), 'deadLetter must be a string')
|
|
35
34
|
|
|
36
35
|
applyRetryConfig(options)
|
|
@@ -123,26 +122,25 @@ function checkWorkArgs (name, args, defaults) {
|
|
|
123
122
|
|
|
124
123
|
options = { ...options }
|
|
125
124
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
assert(!('teamConcurrency' in options) ||
|
|
129
|
-
(Number.isInteger(options.teamConcurrency) && options.teamConcurrency >= 1 && options.teamConcurrency <= 1000),
|
|
130
|
-
'teamConcurrency must be an integer between 1 and 1000')
|
|
125
|
+
applyPollingInterval(options, defaults)
|
|
131
126
|
|
|
132
|
-
assert(!('teamSize' in options) || (Number.isInteger(options.teamSize) && options.teamSize >= 1), 'teamSize must be an integer > 0')
|
|
133
127
|
assert(!('batchSize' in options) || (Number.isInteger(options.batchSize) && options.batchSize >= 1), 'batchSize must be an integer > 0')
|
|
134
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
|
|
135
132
|
|
|
136
133
|
return { options, callback }
|
|
137
134
|
}
|
|
138
135
|
|
|
139
|
-
function checkFetchArgs (name,
|
|
136
|
+
function checkFetchArgs (name, options) {
|
|
140
137
|
assert(name, 'missing queue name')
|
|
141
138
|
|
|
142
|
-
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')
|
|
143
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')
|
|
144
142
|
|
|
145
|
-
|
|
143
|
+
options.batchSize = options.batchSize || 1
|
|
146
144
|
}
|
|
147
145
|
|
|
148
146
|
function getConfig (value) {
|
|
@@ -153,6 +151,10 @@ function getConfig (value) {
|
|
|
153
151
|
? { connectionString: value }
|
|
154
152
|
: { ...value }
|
|
155
153
|
|
|
154
|
+
config.schedule = ('schedule' in config) ? config.schedule : true
|
|
155
|
+
config.supervise = ('supervise' in config) ? config.supervise : true
|
|
156
|
+
config.migrate = ('migrate' in config) ? config.migrate : true
|
|
157
|
+
|
|
156
158
|
applySchemaConfig(config)
|
|
157
159
|
applyMaintenanceConfig(config)
|
|
158
160
|
applyArchiveConfig(config)
|
|
@@ -160,7 +162,7 @@ function getConfig (value) {
|
|
|
160
162
|
applyDeleteConfig(config)
|
|
161
163
|
applyMonitoringConfig(config)
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
applyPollingInterval(config)
|
|
164
166
|
applyExpirationConfig(config)
|
|
165
167
|
applyRetentionConfig(config)
|
|
166
168
|
|
|
@@ -178,8 +180,14 @@ function applySchemaConfig (config) {
|
|
|
178
180
|
function assertPostgresObjectName (name) {
|
|
179
181
|
assert(typeof name === 'string', 'Name must be a string')
|
|
180
182
|
assert(name.length <= 50, 'Name cannot exceed 50 characters')
|
|
181
|
-
assert(!/\W/.test(name), 'Name can only contain alphanumeric characters
|
|
182
|
-
assert(
|
|
183
|
+
assert(!/\W/.test(name), 'Name can only contain alphanumeric characters or underscores')
|
|
184
|
+
assert(!/^\d/.test(name), 'Name cannot start with a number')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function assertQueueName (name) {
|
|
188
|
+
assert(name, 'Name is required')
|
|
189
|
+
assert(typeof name === 'string', 'Name must be a string')
|
|
190
|
+
assert(/[\w-]/.test(name), 'Name can only contain alphanumeric characters, underscores, or hyphens')
|
|
183
191
|
}
|
|
184
192
|
|
|
185
193
|
function applyArchiveConfig (config) {
|
|
@@ -270,18 +278,13 @@ function applyRetryConfig (config, defaults) {
|
|
|
270
278
|
config.retryBackoffDefault = defaults?.retryBackoff
|
|
271
279
|
}
|
|
272
280
|
|
|
273
|
-
function
|
|
274
|
-
assert(!('
|
|
275
|
-
'configuration assert:
|
|
276
|
-
|
|
277
|
-
assert(!('newJobCheckIntervalSeconds' in config) || config.newJobCheckIntervalSeconds >= 1,
|
|
278
|
-
'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')
|
|
279
284
|
|
|
280
|
-
config.
|
|
281
|
-
? config.
|
|
282
|
-
:
|
|
283
|
-
? config.newJobCheckInterval
|
|
284
|
-
: defaults?.newJobCheckInterval || 2000
|
|
285
|
+
config.pollingInterval = ('pollingIntervalSeconds' in config)
|
|
286
|
+
? config.pollingIntervalSeconds * 1000
|
|
287
|
+
: defaults?.pollingInterval || 2000
|
|
285
288
|
}
|
|
286
289
|
|
|
287
290
|
function applyMaintenanceConfig (config) {
|
|
@@ -298,10 +301,6 @@ function applyMaintenanceConfig (config) {
|
|
|
298
301
|
: 120
|
|
299
302
|
|
|
300
303
|
assert(config.maintenanceIntervalSeconds / 60 / 60 < MAX_INTERVAL_HOURS, `configuration assert: maintenance interval cannot exceed ${MAX_INTERVAL_HOURS} hours`)
|
|
301
|
-
|
|
302
|
-
config.schedule = ('schedule' in config) ? config.schedule : true
|
|
303
|
-
config.supervise = ('supervise' in config) ? config.supervise : true
|
|
304
|
-
config.migrate = ('migrate' in config) ? config.migrate : true
|
|
305
304
|
}
|
|
306
305
|
|
|
307
306
|
function applyDeleteConfig (config) {
|
|
@@ -363,21 +362,21 @@ function applyMonitoringConfig (config) {
|
|
|
363
362
|
? config.clockMonitorIntervalSeconds
|
|
364
363
|
: TEN_MINUTES_IN_SECONDS
|
|
365
364
|
|
|
366
|
-
assert(!('cronMonitorIntervalSeconds' in config) || (config.cronMonitorIntervalSeconds >= 1 && config.cronMonitorIntervalSeconds <=
|
|
367
|
-
'configuration assert: cronMonitorIntervalSeconds must be between 1 and
|
|
365
|
+
assert(!('cronMonitorIntervalSeconds' in config) || (config.cronMonitorIntervalSeconds >= 1 && config.cronMonitorIntervalSeconds <= 45),
|
|
366
|
+
'configuration assert: cronMonitorIntervalSeconds must be between 1 and 45 seconds')
|
|
368
367
|
|
|
369
368
|
config.cronMonitorIntervalSeconds =
|
|
370
369
|
('cronMonitorIntervalSeconds' in config)
|
|
371
370
|
? config.cronMonitorIntervalSeconds
|
|
372
|
-
:
|
|
371
|
+
: 30
|
|
373
372
|
|
|
374
|
-
assert(!('cronWorkerIntervalSeconds' in config) || (config.cronWorkerIntervalSeconds >= 1 && config.cronWorkerIntervalSeconds <=
|
|
375
|
-
'configuration assert: cronWorkerIntervalSeconds must be between 1 and
|
|
373
|
+
assert(!('cronWorkerIntervalSeconds' in config) || (config.cronWorkerIntervalSeconds >= 1 && config.cronWorkerIntervalSeconds <= 45),
|
|
374
|
+
'configuration assert: cronWorkerIntervalSeconds must be between 1 and 45 seconds')
|
|
376
375
|
|
|
377
376
|
config.cronWorkerIntervalSeconds =
|
|
378
377
|
('cronWorkerIntervalSeconds' in config)
|
|
379
378
|
? config.cronWorkerIntervalSeconds
|
|
380
|
-
:
|
|
379
|
+
: 5
|
|
381
380
|
}
|
|
382
381
|
|
|
383
382
|
function warnClockSkew (message) {
|
package/src/boss.js
CHANGED
|
@@ -24,10 +24,8 @@ class Boss extends EventEmitter {
|
|
|
24
24
|
this.failJobsByTimeoutCommand = plans.locked(config.schema, plans.failJobsByTimeout(config.schema))
|
|
25
25
|
this.archiveCommand = plans.locked(config.schema, plans.archive(config.schema, config.archiveInterval, config.archiveFailedInterval))
|
|
26
26
|
this.dropCommand = plans.locked(config.schema, plans.drop(config.schema, config.deleteAfter))
|
|
27
|
-
this.
|
|
28
|
-
this.
|
|
29
|
-
this.getMonitorTimeCommand = plans.getMonitorTime(config.schema)
|
|
30
|
-
this.setMonitorTimeCommand = plans.setMonitorTime(config.schema)
|
|
27
|
+
this.trySetMaintenanceTimeCommand = plans.trySetMaintenanceTime(config.schema)
|
|
28
|
+
this.trySetMonitorTimeCommand = plans.trySetMonitorTime(config.schema)
|
|
31
29
|
this.countStatesCommand = plans.countStates(config.schema)
|
|
32
30
|
|
|
33
31
|
this.functions = [
|
|
@@ -48,8 +46,6 @@ class Boss extends EventEmitter {
|
|
|
48
46
|
}
|
|
49
47
|
|
|
50
48
|
async onMonitor () {
|
|
51
|
-
let locker
|
|
52
|
-
|
|
53
49
|
try {
|
|
54
50
|
if (this.monitoring) {
|
|
55
51
|
return
|
|
@@ -65,29 +61,24 @@ class Boss extends EventEmitter {
|
|
|
65
61
|
throw new Error(this.config.__test__throw_monitor)
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
|
|
64
|
+
if (this.stopped) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
69
67
|
|
|
70
|
-
const {
|
|
68
|
+
const { rows } = await this.db.executeSql(this.trySetMonitorTimeCommand, [this.config.monitorStateIntervalSeconds])
|
|
71
69
|
|
|
72
|
-
if (
|
|
70
|
+
if (rows.length === 1 && !this.stopped) {
|
|
73
71
|
const states = await this.countStates()
|
|
74
|
-
this.setMonitorTime()
|
|
75
72
|
this.emit(events.monitorStates, states)
|
|
76
73
|
}
|
|
77
74
|
} catch (err) {
|
|
78
75
|
this.emit(events.error, err)
|
|
79
76
|
} finally {
|
|
80
|
-
if (locker?.locked) {
|
|
81
|
-
await locker.unlock()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
77
|
this.monitoring = false
|
|
85
78
|
}
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
async onSupervise () {
|
|
89
|
-
let locker
|
|
90
|
-
|
|
91
82
|
try {
|
|
92
83
|
if (this.maintaining) {
|
|
93
84
|
return
|
|
@@ -108,21 +99,15 @@ class Boss extends EventEmitter {
|
|
|
108
99
|
return
|
|
109
100
|
}
|
|
110
101
|
|
|
111
|
-
|
|
102
|
+
const { rows } = await this.db.executeSql(this.trySetMaintenanceTimeCommand, [this.config.maintenanceIntervalSeconds])
|
|
112
103
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (secondsAgo > this.maintenanceIntervalSeconds) {
|
|
104
|
+
if (rows.length === 1 && !this.stopped) {
|
|
116
105
|
const result = await this.maintain()
|
|
117
106
|
this.emit(events.maintenance, result)
|
|
118
107
|
}
|
|
119
108
|
} catch (err) {
|
|
120
109
|
this.emit(events.error, err)
|
|
121
110
|
} finally {
|
|
122
|
-
if (locker?.locked) {
|
|
123
|
-
await locker.unlock()
|
|
124
|
-
}
|
|
125
|
-
|
|
126
111
|
this.maintaining = false
|
|
127
112
|
}
|
|
128
113
|
}
|
|
@@ -136,8 +121,6 @@ class Boss extends EventEmitter {
|
|
|
136
121
|
|
|
137
122
|
const ended = Date.now()
|
|
138
123
|
|
|
139
|
-
await this.setMaintenanceTime()
|
|
140
|
-
|
|
141
124
|
return { ms: ended - started }
|
|
142
125
|
}
|
|
143
126
|
|
|
@@ -152,10 +135,11 @@ class Boss extends EventEmitter {
|
|
|
152
135
|
}
|
|
153
136
|
|
|
154
137
|
async countStates () {
|
|
155
|
-
const stateCountDefault = { ...plans.
|
|
138
|
+
const stateCountDefault = { ...plans.JOB_STATES }
|
|
156
139
|
|
|
157
|
-
Object.keys(stateCountDefault)
|
|
158
|
-
|
|
140
|
+
for (const key of Object.keys(stateCountDefault)) {
|
|
141
|
+
stateCountDefault[key] = 0
|
|
142
|
+
}
|
|
159
143
|
|
|
160
144
|
const counts = await this.db.executeSql(this.countStatesCommand)
|
|
161
145
|
|
|
@@ -187,34 +171,6 @@ class Boss extends EventEmitter {
|
|
|
187
171
|
async drop () {
|
|
188
172
|
await this.db.executeSql(this.dropCommand)
|
|
189
173
|
}
|
|
190
|
-
|
|
191
|
-
async setMaintenanceTime () {
|
|
192
|
-
await this.db.executeSql(this.setMaintenanceTimeCommand)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async getMaintenanceTime () {
|
|
196
|
-
const { rows } = await this.db.executeSql(this.getMaintenanceTimeCommand)
|
|
197
|
-
|
|
198
|
-
let { maintained_on: maintainedOn, seconds_ago: secondsAgo } = rows[0]
|
|
199
|
-
|
|
200
|
-
secondsAgo = secondsAgo !== null ? parseFloat(secondsAgo) : 999_999_999
|
|
201
|
-
|
|
202
|
-
return { maintainedOn, secondsAgo }
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async setMonitorTime () {
|
|
206
|
-
await this.db.executeSql(this.setMonitorTimeCommand)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async getMonitorTime () {
|
|
210
|
-
const { rows } = await this.db.executeSql(this.getMonitorTimeCommand)
|
|
211
|
-
|
|
212
|
-
let { monitored_on: monitoredOn, seconds_ago: secondsAgo } = rows[0]
|
|
213
|
-
|
|
214
|
-
secondsAgo = secondsAgo !== null ? parseFloat(secondsAgo) : 999_999_999
|
|
215
|
-
|
|
216
|
-
return { monitoredOn, secondsAgo }
|
|
217
|
-
}
|
|
218
174
|
}
|
|
219
175
|
|
|
220
176
|
module.exports = Boss
|
package/src/contractor.js
CHANGED
|
@@ -21,23 +21,29 @@ 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')
|
|
@@ -55,7 +61,7 @@ class Contractor {
|
|
|
55
61
|
throw new Error('pg-boss is not installed')
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
const version = await this.
|
|
64
|
+
const version = await this.schemaVersion()
|
|
59
65
|
|
|
60
66
|
if (schemaVersion !== version) {
|
|
61
67
|
throw new Error('pg-boss database requires migrations')
|
|
@@ -80,15 +86,15 @@ class Contractor {
|
|
|
80
86
|
}
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
async next (version) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
89
|
+
// async next (version) {
|
|
90
|
+
// const commands = migrationStore.next(this.config.schema, version, this.migrations)
|
|
91
|
+
// await this.db.executeSql(commands)
|
|
92
|
+
// }
|
|
87
93
|
|
|
88
|
-
async rollback (version) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
94
|
+
// async rollback (version) {
|
|
95
|
+
// const commands = migrationStore.rollback(this.config.schema, version, this.migrations)
|
|
96
|
+
// await this.db.executeSql(commands)
|
|
97
|
+
// }
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
module.exports = Contractor
|
package/src/db.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const EventEmitter = require('events')
|
|
2
2
|
const pg = require('pg')
|
|
3
|
-
const { advisoryLock } = require('./plans')
|
|
4
3
|
|
|
5
4
|
class Db extends EventEmitter {
|
|
6
5
|
constructor (config) {
|
|
@@ -11,6 +10,10 @@ class Db extends EventEmitter {
|
|
|
11
10
|
this.config = config
|
|
12
11
|
}
|
|
13
12
|
|
|
13
|
+
events = {
|
|
14
|
+
error: 'error'
|
|
15
|
+
}
|
|
16
|
+
|
|
14
17
|
async open () {
|
|
15
18
|
this.pool = new pg.Pool(this.config)
|
|
16
19
|
this.pool.on('error', error => this.emit('error', error))
|
|
@@ -26,48 +29,19 @@ class Db extends EventEmitter {
|
|
|
26
29
|
|
|
27
30
|
async executeSql (text, values) {
|
|
28
31
|
if (this.opened) {
|
|
29
|
-
if (this.config.debug === true) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// if (this.config.debug === true) {
|
|
33
|
+
// console.log(`${new Date().toISOString()}: DEBUG SQL`)
|
|
34
|
+
// console.log(text)
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
36
|
+
// if (values) {
|
|
37
|
+
// console.log(`${new Date().toISOString()}: DEBUG VALUES`)
|
|
38
|
+
// console.log(values)
|
|
39
|
+
// }
|
|
40
|
+
// }
|
|
38
41
|
|
|
39
42
|
return await this.pool.query(text, values)
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
|
-
|
|
43
|
-
async lock ({ timeout = 30, key } = {}) {
|
|
44
|
-
// const lockedClient = new pg.Client(this.config)
|
|
45
|
-
// await lockedClient.connect()
|
|
46
|
-
const lockedClient = await this.pool.connect()
|
|
47
|
-
|
|
48
|
-
const query = `
|
|
49
|
-
BEGIN;
|
|
50
|
-
SET LOCAL lock_timeout = '${timeout}s';
|
|
51
|
-
SET LOCAL idle_in_transaction_session_timeout = '3600s';
|
|
52
|
-
${advisoryLock(this.config.schema, key)};
|
|
53
|
-
`
|
|
54
|
-
|
|
55
|
-
await lockedClient.query(query)
|
|
56
|
-
|
|
57
|
-
const locker = {
|
|
58
|
-
locked: true,
|
|
59
|
-
unlock: async function () {
|
|
60
|
-
try {
|
|
61
|
-
await lockedClient.query('COMMIT')
|
|
62
|
-
await lockedClient.end()
|
|
63
|
-
} finally {
|
|
64
|
-
this.locked = false
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return locker
|
|
70
|
-
}
|
|
71
45
|
}
|
|
72
46
|
|
|
73
47
|
module.exports = Db
|