pg-boss 10.0.0-beta1 → 10.0.0-beta11
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 +13 -21
- package/package.json +6 -5
- package/src/attorney.js +27 -41
- package/src/boss.js +13 -29
- package/src/contractor.js +12 -5
- package/src/db.js +11 -45
- package/src/index.js +91 -66
- package/src/manager.js +45 -61
- package/src/migrationStore.js +0 -161
- package/src/plans.js +248 -215
- package/src/timekeeper.js +45 -80
- package/src/worker.js +1 -1
- package/types.d.ts +62 -38
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Queueing jobs in Node.js
|
|
1
|
+
Queueing jobs in Postgres from Node.js like a boss.
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/js/pg-boss)
|
|
4
4
|
[](https://github.com/timgit/pg-boss/actions/workflows/ci.yml)
|
|
@@ -9,24 +9,19 @@ 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
|
|
|
@@ -41,16 +36,14 @@ This will likely cater the most to teams already familiar with the simplicity of
|
|
|
41
36
|
* Backpressure-compatible polling workers
|
|
42
37
|
* Cron scheduling
|
|
43
38
|
* Pub/sub API for fan-out queue relationships
|
|
44
|
-
* Priority, deferral, retries (with exponential backoff), rate limiting, debouncing
|
|
45
|
-
*
|
|
39
|
+
* Priority queues, deferral, retries (with exponential backoff), rate limiting, debouncing
|
|
40
|
+
* Table operations via SQL for bulk loads via COPY or INSERT
|
|
46
41
|
* Multi-master compatible (for example, in a Kubernetes ReplicaSet)
|
|
47
42
|
* Dead letter queues
|
|
48
|
-
* Automatic creation and migration of storage tables
|
|
49
|
-
* Automatic maintenance operations to manage table growth
|
|
50
43
|
|
|
51
44
|
## Requirements
|
|
52
|
-
* Node
|
|
53
|
-
* PostgreSQL
|
|
45
|
+
* Node 20 or higher
|
|
46
|
+
* PostgreSQL 13 or higher
|
|
54
47
|
|
|
55
48
|
## Installation
|
|
56
49
|
|
|
@@ -66,7 +59,6 @@ yarn add pg-boss
|
|
|
66
59
|
* [Docs](docs/readme.md)
|
|
67
60
|
|
|
68
61
|
## Contributing
|
|
69
|
-
|
|
70
62
|
To setup a development environment for this library:
|
|
71
63
|
|
|
72
64
|
```bash
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pg-boss",
|
|
3
|
-
"version": "10.0.0-
|
|
4
|
-
"description": "Queueing jobs in Node.js
|
|
3
|
+
"version": "10.0.0-beta11",
|
|
4
|
+
"description": "Queueing jobs in Postgres from Node.js like a boss",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
7
|
+
"node": ">=20"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"cron-parser": "^4.0.0",
|
|
@@ -17,14 +17,15 @@
|
|
|
17
17
|
"@types/node": "^20.3.3",
|
|
18
18
|
"luxon": "^3.0.1",
|
|
19
19
|
"mocha": "^10.0.0",
|
|
20
|
-
"nyc": "^
|
|
20
|
+
"nyc": "^17.0.0",
|
|
21
21
|
"standard": "^17.0.0"
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"test": "standard && mocha",
|
|
25
25
|
"cover": "nyc npm test",
|
|
26
26
|
"tsc": "tsc --noEmit types.d.ts",
|
|
27
|
-
"readme": "node ./test/readme.js"
|
|
27
|
+
"readme": "node ./test/readme.js",
|
|
28
|
+
"migrate": "node -e 'console.log(require(\"./src\").getMigrationPlans())'"
|
|
28
29
|
},
|
|
29
30
|
"mocha": {
|
|
30
31
|
"timeout": 10000,
|
package/src/attorney.js
CHANGED
|
@@ -8,8 +8,8 @@ module.exports = {
|
|
|
8
8
|
checkWorkArgs,
|
|
9
9
|
checkFetchArgs,
|
|
10
10
|
warnClockSkew,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
assertPostgresObjectName,
|
|
12
|
+
assertQueueName
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const MAX_INTERVAL_HOURS = 24
|
|
@@ -30,8 +30,6 @@ const WARNINGS = {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function checkQueueArgs (name, options = {}) {
|
|
33
|
-
assertPostgresObjectName(name)
|
|
34
|
-
|
|
35
33
|
assert(!('deadLetter' in options) || (typeof options.deadLetter === 'string'), 'deadLetter must be a string')
|
|
36
34
|
|
|
37
35
|
applyRetryConfig(options)
|
|
@@ -124,7 +122,7 @@ function checkWorkArgs (name, args, defaults) {
|
|
|
124
122
|
|
|
125
123
|
options = { ...options }
|
|
126
124
|
|
|
127
|
-
|
|
125
|
+
applyPollingInterval(options, defaults)
|
|
128
126
|
|
|
129
127
|
assert(!('teamConcurrency' in options) ||
|
|
130
128
|
(Number.isInteger(options.teamConcurrency) && options.teamConcurrency >= 1 && options.teamConcurrency <= 1000),
|
|
@@ -140,24 +138,12 @@ function checkWorkArgs (name, args, defaults) {
|
|
|
140
138
|
function checkFetchArgs (name, batchSize, options) {
|
|
141
139
|
assert(name, 'missing queue name')
|
|
142
140
|
|
|
143
|
-
if (queueNameHasPatternMatch(name)) {
|
|
144
|
-
name = sanitizeQueueNameForFetch(name)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
141
|
assert(!batchSize || (Number.isInteger(batchSize) && batchSize >= 1), 'batchSize must be an integer > 0')
|
|
148
142
|
assert(!('includeMetadata' in options) || typeof options.includeMetadata === 'boolean', 'includeMetadata must be a boolean')
|
|
149
143
|
|
|
150
144
|
return { name }
|
|
151
145
|
}
|
|
152
146
|
|
|
153
|
-
function sanitizeQueueNameForFetch (name) {
|
|
154
|
-
return name.replace(/[%_*]/g, match => match === '*' ? '%' : '\\' + match)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function queueNameHasPatternMatch (name) {
|
|
158
|
-
return name.includes('*')
|
|
159
|
-
}
|
|
160
|
-
|
|
161
147
|
function getConfig (value) {
|
|
162
148
|
assert(value && (typeof value === 'object' || typeof value === 'string'),
|
|
163
149
|
'configuration assert: string or config object is required to connect to postgres')
|
|
@@ -166,6 +152,10 @@ function getConfig (value) {
|
|
|
166
152
|
? { connectionString: value }
|
|
167
153
|
: { ...value }
|
|
168
154
|
|
|
155
|
+
config.schedule = ('schedule' in config) ? config.schedule : true
|
|
156
|
+
config.supervise = ('supervise' in config) ? config.supervise : true
|
|
157
|
+
config.migrate = ('migrate' in config) ? config.migrate : true
|
|
158
|
+
|
|
169
159
|
applySchemaConfig(config)
|
|
170
160
|
applyMaintenanceConfig(config)
|
|
171
161
|
applyArchiveConfig(config)
|
|
@@ -173,7 +163,7 @@ function getConfig (value) {
|
|
|
173
163
|
applyDeleteConfig(config)
|
|
174
164
|
applyMonitoringConfig(config)
|
|
175
165
|
|
|
176
|
-
|
|
166
|
+
applyPollingInterval(config)
|
|
177
167
|
applyExpirationConfig(config)
|
|
178
168
|
applyRetentionConfig(config)
|
|
179
169
|
|
|
@@ -191,8 +181,13 @@ function applySchemaConfig (config) {
|
|
|
191
181
|
function assertPostgresObjectName (name) {
|
|
192
182
|
assert(typeof name === 'string', 'Name must be a string')
|
|
193
183
|
assert(name.length <= 50, 'Name cannot exceed 50 characters')
|
|
194
|
-
assert(!/\W/.test(name), 'Name can only contain alphanumeric characters
|
|
195
|
-
assert(
|
|
184
|
+
assert(!/\W/.test(name), 'Name can only contain alphanumeric characters or underscores')
|
|
185
|
+
assert(!/^\d/.test(name), 'Name cannot start with a number')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function assertQueueName (name) {
|
|
189
|
+
assert(typeof name === 'string', 'Name must be a string')
|
|
190
|
+
assert(/[\w-]/.test(name), 'Name can only contain alphanumeric characters, underscores, or hyphens')
|
|
196
191
|
}
|
|
197
192
|
|
|
198
193
|
function applyArchiveConfig (config) {
|
|
@@ -283,18 +278,13 @@ function applyRetryConfig (config, defaults) {
|
|
|
283
278
|
config.retryBackoffDefault = defaults?.retryBackoff
|
|
284
279
|
}
|
|
285
280
|
|
|
286
|
-
function
|
|
287
|
-
assert(!('
|
|
288
|
-
'configuration assert:
|
|
289
|
-
|
|
290
|
-
assert(!('newJobCheckIntervalSeconds' in config) || config.newJobCheckIntervalSeconds >= 1,
|
|
291
|
-
'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')
|
|
292
284
|
|
|
293
|
-
config.
|
|
294
|
-
? config.
|
|
295
|
-
:
|
|
296
|
-
? config.newJobCheckInterval
|
|
297
|
-
: defaults?.newJobCheckInterval || 2000
|
|
285
|
+
config.pollingInterval = ('pollingIntervalSeconds' in config)
|
|
286
|
+
? config.pollingIntervalSeconds * 1000
|
|
287
|
+
: defaults?.pollingInterval || 2000
|
|
298
288
|
}
|
|
299
289
|
|
|
300
290
|
function applyMaintenanceConfig (config) {
|
|
@@ -311,10 +301,6 @@ function applyMaintenanceConfig (config) {
|
|
|
311
301
|
: 120
|
|
312
302
|
|
|
313
303
|
assert(config.maintenanceIntervalSeconds / 60 / 60 < MAX_INTERVAL_HOURS, `configuration assert: maintenance interval cannot exceed ${MAX_INTERVAL_HOURS} hours`)
|
|
314
|
-
|
|
315
|
-
config.schedule = ('schedule' in config) ? config.schedule : true
|
|
316
|
-
config.supervise = ('supervise' in config) ? config.supervise : true
|
|
317
|
-
config.migrate = ('migrate' in config) ? config.migrate : true
|
|
318
304
|
}
|
|
319
305
|
|
|
320
306
|
function applyDeleteConfig (config) {
|
|
@@ -376,21 +362,21 @@ function applyMonitoringConfig (config) {
|
|
|
376
362
|
? config.clockMonitorIntervalSeconds
|
|
377
363
|
: TEN_MINUTES_IN_SECONDS
|
|
378
364
|
|
|
379
|
-
assert(!('cronMonitorIntervalSeconds' in config) || (config.cronMonitorIntervalSeconds >= 1 && config.cronMonitorIntervalSeconds <=
|
|
380
|
-
'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')
|
|
381
367
|
|
|
382
368
|
config.cronMonitorIntervalSeconds =
|
|
383
369
|
('cronMonitorIntervalSeconds' in config)
|
|
384
370
|
? config.cronMonitorIntervalSeconds
|
|
385
|
-
:
|
|
371
|
+
: 30
|
|
386
372
|
|
|
387
|
-
assert(!('cronWorkerIntervalSeconds' in config) || (config.cronWorkerIntervalSeconds >= 1 && config.cronWorkerIntervalSeconds <=
|
|
388
|
-
'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')
|
|
389
375
|
|
|
390
376
|
config.cronWorkerIntervalSeconds =
|
|
391
377
|
('cronWorkerIntervalSeconds' in config)
|
|
392
378
|
? config.cronWorkerIntervalSeconds
|
|
393
|
-
:
|
|
379
|
+
: 5
|
|
394
380
|
}
|
|
395
381
|
|
|
396
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
|
-
|
|
112
|
-
|
|
113
|
-
const { secondsAgo } = await this.getMaintenanceTime()
|
|
102
|
+
const { rows } = await this.db.executeSql(this.trySetMaintenanceTimeCommand, [this.config.maintenanceIntervalSeconds])
|
|
114
103
|
|
|
115
|
-
if (
|
|
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
|
|
package/src/contractor.js
CHANGED
|
@@ -21,26 +21,33 @@ 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()
|
|
@@ -54,7 +61,7 @@ class Contractor {
|
|
|
54
61
|
throw new Error('pg-boss is not installed')
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
const version = await this.
|
|
64
|
+
const version = await this.schemaVersion()
|
|
58
65
|
|
|
59
66
|
if (schemaVersion !== version) {
|
|
60
67
|
throw new Error('pg-boss database requires migrations')
|
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,55 +29,18 @@ class Db extends EventEmitter {
|
|
|
26
29
|
|
|
27
30
|
async executeSql (text, values) {
|
|
28
31
|
if (this.opened) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
// if (values) {
|
|
34
|
-
// console.log(`${new Date().toISOString()}: DEBUG VALUES`)
|
|
35
|
-
// console.log(values)
|
|
36
|
-
// }
|
|
37
|
-
// }
|
|
38
|
-
|
|
39
|
-
return await this.pool.query(text, values)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
32
|
+
if (this.config.debug === true) {
|
|
33
|
+
console.log(`${new Date().toISOString()}: DEBUG SQL`)
|
|
34
|
+
console.log(text)
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
36
|
+
if (values) {
|
|
37
|
+
console.log(`${new Date().toISOString()}: DEBUG VALUES`)
|
|
38
|
+
console.log(values)
|
|
65
39
|
}
|
|
66
40
|
}
|
|
67
|
-
}
|
|
68
41
|
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
static quotePostgresStr (str) {
|
|
73
|
-
const delimeter = '$sanitize$'
|
|
74
|
-
if (str.includes(delimeter)) {
|
|
75
|
-
throw new Error(`Attempted to quote string that contains reserved Postgres delimeter: ${str}`)
|
|
42
|
+
return await this.pool.query(text, values)
|
|
76
43
|
}
|
|
77
|
-
return `${delimeter}${str}${delimeter}`
|
|
78
44
|
}
|
|
79
45
|
}
|
|
80
46
|
|