@balena/pinejs 16.2.0-build-joshbwlng-tasks-cd1aff5f4bbfacaad189b013578d5cbee51c3932-1 → 16.2.0-build-joshbwlng-tasks-a83c83b4c78803915d0cb6297cc8cc1862622d08-1
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/.husky/pre-commit +0 -2
- package/.versionbot/CHANGELOG.yml +28 -10
- package/CHANGELOG.md +11 -2
- package/out/tasks/worker.d.ts +3 -3
- package/out/tasks/worker.js +63 -63
- package/out/tasks/worker.js.map +1 -1
- package/package.json +5 -5
- package/src/tasks/worker.ts +91 -86
package/.husky/pre-commit
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
- commits:
|
2
|
-
- subject: Organize code
|
3
|
-
hash: cd1aff5f4bbfacaad189b013578d5cbee51c3932
|
4
|
-
body: ""
|
5
|
-
footer:
|
6
|
-
Change-type: patch
|
7
|
-
change-type: patch
|
8
|
-
author: Josh Bowling
|
9
|
-
nested: []
|
10
2
|
- subject: Add async tasks
|
11
|
-
hash:
|
3
|
+
hash: a83c83b4c78803915d0cb6297cc8cc1862622d08
|
12
4
|
body: ""
|
13
5
|
footer:
|
14
6
|
Change-type: minor
|
@@ -17,7 +9,33 @@
|
|
17
9
|
nested: []
|
18
10
|
version: 16.2.0
|
19
11
|
title: ""
|
20
|
-
date: 2024-04-
|
12
|
+
date: 2024-04-15T01:28:02.918Z
|
13
|
+
- commits:
|
14
|
+
- subject: Update dependency husky to v9
|
15
|
+
hash: 715d2dd993591d449bca7ece48f069ed6994c8c7
|
16
|
+
body: |
|
17
|
+
Update husky from 8.0.3 to 9.0.11
|
18
|
+
footer:
|
19
|
+
Change-type: patch
|
20
|
+
change-type: patch
|
21
|
+
author: Self-hosted Renovate Bot
|
22
|
+
nested: []
|
23
|
+
version: 16.1.2
|
24
|
+
title: ""
|
25
|
+
date: 2024-04-14T04:09:27.077Z
|
26
|
+
- commits:
|
27
|
+
- subject: Update dependency @balena/lint to v8
|
28
|
+
hash: 02ba2563aa4ac82ad90accc30534f744c00a0c16
|
29
|
+
body: |
|
30
|
+
Update @balena/lint from 7.3.0 to 8.0.0
|
31
|
+
footer:
|
32
|
+
Change-type: patch
|
33
|
+
change-type: patch
|
34
|
+
author: Self-hosted Renovate Bot
|
35
|
+
nested: []
|
36
|
+
version: 16.1.1
|
37
|
+
title: ""
|
38
|
+
date: 2024-04-11T22:45:27.398Z
|
21
39
|
- commits:
|
22
40
|
- subject: Add support for prettifying index constraint errors with their
|
23
41
|
description
|
package/CHANGELOG.md
CHANGED
@@ -5,11 +5,20 @@ automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
|
5
5
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
6
6
|
|
7
7
|
# v16.2.0
|
8
|
-
## (2024-04-
|
8
|
+
## (2024-04-15)
|
9
9
|
|
10
|
-
* Organize code [Josh Bowling]
|
11
10
|
* Add async tasks [Josh Bowling]
|
12
11
|
|
12
|
+
# v16.1.2
|
13
|
+
## (2024-04-14)
|
14
|
+
|
15
|
+
* Update dependency husky to v9 [Self-hosted Renovate Bot]
|
16
|
+
|
17
|
+
# v16.1.1
|
18
|
+
## (2024-04-11)
|
19
|
+
|
20
|
+
* Update dependency @balena/lint to v8 [Self-hosted Renovate Bot]
|
21
|
+
|
13
22
|
# v16.1.0
|
14
23
|
## (2024-04-02)
|
15
24
|
|
package/out/tasks/worker.d.ts
CHANGED
@@ -8,9 +8,9 @@ export declare class Worker {
|
|
8
8
|
private executing;
|
9
9
|
constructor(client: PinejsClient);
|
10
10
|
private canExecute;
|
11
|
-
private poll;
|
12
|
-
start(): void;
|
13
11
|
private execute;
|
12
|
+
private finalize;
|
14
13
|
private getNextAttemptTime;
|
15
|
-
private
|
14
|
+
private poll;
|
15
|
+
start(): void;
|
16
16
|
}
|
package/out/tasks/worker.js
CHANGED
@@ -52,73 +52,18 @@ class Worker {
|
|
52
52
|
canExecute() {
|
53
53
|
return (this.executing < this.concurrency && Object.keys(this.handlers).length > 0);
|
54
54
|
}
|
55
|
-
poll() {
|
56
|
-
let executed = false;
|
57
|
-
const names = Object.keys(this.handlers);
|
58
|
-
const binds = names.map((_, index) => `$${index + 1}`).join(', ');
|
59
|
-
module_1.sbvrUtils.db
|
60
|
-
.transaction(async (tx) => {
|
61
|
-
if (!this.canExecute()) {
|
62
|
-
return;
|
63
|
-
}
|
64
|
-
const result = await tx.executeSql(`SELECT ${selectColumns}
|
65
|
-
FROM task AS t
|
66
|
-
WHERE
|
67
|
-
t."is executed by-handler" IN (${binds}) AND
|
68
|
-
t."status" = 'pending' AND
|
69
|
-
t."attempt count" <= t."attempt limit" AND
|
70
|
-
(
|
71
|
-
t."is scheduled to execute on-time" IS NULL OR
|
72
|
-
t."is scheduled to execute on-time" <= CURRENT_TIMESTAMP + INTERVAL '${Math.ceil(this.interval / 1000)} second'
|
73
|
-
)
|
74
|
-
ORDER BY
|
75
|
-
t."is scheduled to execute on-time" ASC,
|
76
|
-
t."priority" DESC,
|
77
|
-
t."id" ASC
|
78
|
-
LIMIT ${Math.max(this.concurrency - this.executing, 0)}
|
79
|
-
FOR UPDATE SKIP LOCKED`, names);
|
80
|
-
if (result.rows.length === 0) {
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
await Promise.all(result.rows.map(async (row) => {
|
84
|
-
await this.execute(tx, row);
|
85
|
-
}));
|
86
|
-
executed = true;
|
87
|
-
})
|
88
|
-
.catch((err) => {
|
89
|
-
console.error('Failed polling for tasks:', err);
|
90
|
-
})
|
91
|
-
.finally(() => {
|
92
|
-
setTimeout(() => this.poll(), executed ? 0 : this.interval);
|
93
|
-
});
|
94
|
-
}
|
95
|
-
start() {
|
96
|
-
module_1.sbvrUtils.db.on?.('notification', async (msg) => {
|
97
|
-
if (this.canExecute()) {
|
98
|
-
await module_1.sbvrUtils.db.transaction(async (tx) => {
|
99
|
-
const result = await tx.executeSql(`SELECT ${selectColumns} FROM task AS t WHERE id = $1 FOR UPDATE SKIP LOCKED`, [msg.payload]);
|
100
|
-
if (result.rows.length > 0) {
|
101
|
-
await this.execute(tx, result.rows[0]);
|
102
|
-
}
|
103
|
-
});
|
104
|
-
}
|
105
|
-
}, {
|
106
|
-
channel: common_1.channel,
|
107
|
-
});
|
108
|
-
this.poll();
|
109
|
-
}
|
110
55
|
async execute(tx, task) {
|
111
56
|
this.executing++;
|
112
57
|
try {
|
113
58
|
const handler = this.handlers[task.is_executed_by__handler];
|
114
59
|
const startedOnTime = new Date();
|
115
60
|
if (handler == null) {
|
116
|
-
await this.
|
61
|
+
await this.finalize(tx, task, startedOnTime, 'failed', 'Matching task handler not found');
|
117
62
|
return;
|
118
63
|
}
|
119
64
|
if (handler.validate != null &&
|
120
65
|
!handler.validate(task.is_executed_with__parameter_set)) {
|
121
|
-
await this.
|
66
|
+
await this.finalize(tx, task, startedOnTime, 'failed', `Invalid parameter set: ${common_1.ajv.errorsText(handler.validate.errors)}`);
|
122
67
|
return;
|
123
68
|
}
|
124
69
|
const result = await handler.fn({
|
@@ -130,7 +75,7 @@ class Worker {
|
|
130
75
|
params: task.is_executed_with__parameter_set ?? {},
|
131
76
|
tx,
|
132
77
|
});
|
133
|
-
await this.
|
78
|
+
await this.finalize(tx, task, startedOnTime, result.status, result.error);
|
134
79
|
}
|
135
80
|
catch (err) {
|
136
81
|
console.error('Task execution failed:', err);
|
@@ -140,11 +85,7 @@ class Worker {
|
|
140
85
|
this.executing--;
|
141
86
|
}
|
142
87
|
}
|
143
|
-
|
144
|
-
const delay = Math.ceil(Math.exp(Math.min(10, attempt))) * 1000;
|
145
|
-
return new Date(Date.now() + delay);
|
146
|
-
}
|
147
|
-
async update(tx, task, startedOnTime, status, errorMessage) {
|
88
|
+
async finalize(tx, task, startedOnTime, status, errorMessage) {
|
148
89
|
const attemptCount = task.attempt_count + 1;
|
149
90
|
const body = {
|
150
91
|
started_on__time: startedOnTime,
|
@@ -186,6 +127,65 @@ class Worker {
|
|
186
127
|
});
|
187
128
|
}
|
188
129
|
}
|
130
|
+
getNextAttemptTime(attempt) {
|
131
|
+
const delay = Math.ceil(Math.exp(Math.min(10, attempt))) * 1000;
|
132
|
+
return new Date(Date.now() + delay);
|
133
|
+
}
|
134
|
+
poll() {
|
135
|
+
let executed = false;
|
136
|
+
const handlerNames = Object.keys(this.handlers);
|
137
|
+
const binds = handlerNames.map((_, index) => `$${index + 1}`).join(', ');
|
138
|
+
module_1.sbvrUtils.db
|
139
|
+
.transaction(async (tx) => {
|
140
|
+
if (!this.canExecute()) {
|
141
|
+
return;
|
142
|
+
}
|
143
|
+
const result = await tx.executeSql(`SELECT ${selectColumns}
|
144
|
+
FROM task AS t
|
145
|
+
WHERE
|
146
|
+
t."is executed by-handler" IN (${binds}) AND
|
147
|
+
t."status" = 'pending' AND
|
148
|
+
t."attempt count" <= t."attempt limit" AND
|
149
|
+
(
|
150
|
+
t."is scheduled to execute on-time" IS NULL OR
|
151
|
+
t."is scheduled to execute on-time" <= CURRENT_TIMESTAMP + INTERVAL '${Math.ceil(this.interval / 1000)} second'
|
152
|
+
)
|
153
|
+
ORDER BY
|
154
|
+
t."is scheduled to execute on-time" ASC,
|
155
|
+
t."priority" DESC,
|
156
|
+
t."id" ASC
|
157
|
+
LIMIT ${Math.max(this.concurrency - this.executing, 0)}
|
158
|
+
FOR UPDATE SKIP LOCKED`, handlerNames);
|
159
|
+
if (result.rows.length === 0) {
|
160
|
+
return;
|
161
|
+
}
|
162
|
+
await Promise.all(result.rows.map(async (row) => {
|
163
|
+
await this.execute(tx, row);
|
164
|
+
}));
|
165
|
+
executed = true;
|
166
|
+
})
|
167
|
+
.catch((err) => {
|
168
|
+
console.error('Failed polling for tasks:', err);
|
169
|
+
})
|
170
|
+
.finally(() => {
|
171
|
+
setTimeout(() => this.poll(), executed ? 0 : this.interval);
|
172
|
+
});
|
173
|
+
}
|
174
|
+
start() {
|
175
|
+
module_1.sbvrUtils.db.on?.('notification', async (msg) => {
|
176
|
+
if (this.canExecute()) {
|
177
|
+
await module_1.sbvrUtils.db.transaction(async (tx) => {
|
178
|
+
const result = await tx.executeSql(`SELECT ${selectColumns} FROM task AS t WHERE id = $1 FOR UPDATE SKIP LOCKED`, [msg.payload]);
|
179
|
+
if (result.rows.length > 0) {
|
180
|
+
await this.execute(tx, result.rows[0]);
|
181
|
+
}
|
182
|
+
});
|
183
|
+
}
|
184
|
+
}, {
|
185
|
+
channel: common_1.channel,
|
186
|
+
});
|
187
|
+
this.poll();
|
188
|
+
}
|
189
189
|
}
|
190
190
|
exports.Worker = Worker;
|
191
191
|
//# sourceMappingURL=worker.js.map
|
package/out/tasks/worker.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/tasks/worker.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,8CAAyD;AAEzD,qEAAuD;AACvD,uDAAsD;AACtD,kDAAkD;AAClD,qCAAwC;AAIxC,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC;IACpC,EAAE,EAAE,IAAI;IACR,wBAAwB,EAAE,yBAAyB;IACnD,gCAAgC,EAAE,iCAAiC;IACnE,mCAAmC,EAAE,oCAAoC;IACzE,eAAe,EAAE,eAAe;IAChC,eAAe,EAAE,eAAe;IAChC,QAAQ,EAAE,UAAU;IACpB,qBAAqB,EAAE,sBAAsB;CAC7C,CAAC;KACA,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,MAAM,GAAG,SAAS,KAAK,GAAG,CAAC;KACjD,IAAI,CAAC,IAAI,CAAC,CAAC;AAKb,MAAa,MAAM;IAOlB,YAAY,MAAoB;QANzB,aAAQ,GAAgC,EAAE,CAAC;QAI1C,cAAS,GAAG,CAAC,CAAC;QAGrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,WAAW,GAAG,WAAQ,CAAC,gBAAgB,CAAC;QAC7C,IAAI,CAAC,QAAQ,GAAG,WAAQ,CAAC,eAAe,CAAC;IAC1C,CAAC;
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/tasks/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@balena/pinejs",
|
3
|
-
"version": "16.2.0-build-joshbwlng-tasks-
|
3
|
+
"version": "16.2.0-build-joshbwlng-tasks-a83c83b4c78803915d0cb6297cc8cc1862622d08-1",
|
4
4
|
"main": "out/server-glue/module",
|
5
5
|
"type": "commonjs",
|
6
6
|
"repository": "git@github.com:balena-io/pinejs.git",
|
@@ -12,7 +12,7 @@
|
|
12
12
|
},
|
13
13
|
"scripts": {
|
14
14
|
"prepublish": "require-npm4-to-publish",
|
15
|
-
"prepare": "node -e \"try {
|
15
|
+
"prepare": "node -e \"try { (await import('husky')).default() } catch (e) { if (e.code !== 'ERR_MODULE_NOT_FOUND') throw e }\" --input-type module && npm run build",
|
16
16
|
"build": "grunt build",
|
17
17
|
"webpack-browser": "grunt browser",
|
18
18
|
"webpack-module": "grunt module",
|
@@ -65,7 +65,7 @@
|
|
65
65
|
"typed-error": "^3.2.2"
|
66
66
|
},
|
67
67
|
"devDependencies": {
|
68
|
-
"@balena/lint": "^
|
68
|
+
"@balena/lint": "^8.0.0",
|
69
69
|
"@faker-js/faker": "^8.3.1",
|
70
70
|
"@types/busboy": "^1.5.3",
|
71
71
|
"@types/chai": "^4.3.11",
|
@@ -90,7 +90,7 @@
|
|
90
90
|
"grunt-text-replace": "^0.4.0",
|
91
91
|
"grunt-ts": "^6.0.0-beta.22",
|
92
92
|
"grunt-webpack": "^6.0.0",
|
93
|
-
"husky": "^
|
93
|
+
"husky": "^9.0.0",
|
94
94
|
"json-schema-to-ts": "^3.0.1",
|
95
95
|
"lint-staged": "^15.2.0",
|
96
96
|
"load-grunt-tasks": "^5.1.0",
|
@@ -147,6 +147,6 @@
|
|
147
147
|
"recursive": true
|
148
148
|
},
|
149
149
|
"versionist": {
|
150
|
-
"publishedAt": "2024-04-
|
150
|
+
"publishedAt": "2024-04-15T01:28:03.598Z"
|
151
151
|
}
|
152
152
|
}
|
package/src/tasks/worker.ts
CHANGED
@@ -37,93 +37,22 @@ export class Worker {
|
|
37
37
|
this.interval = tasksEnv.queueIntervalMS;
|
38
38
|
}
|
39
39
|
|
40
|
+
// Check if instance can execute more tasks
|
40
41
|
private canExecute(): boolean {
|
41
42
|
return (
|
42
43
|
this.executing < this.concurrency && Object.keys(this.handlers).length > 0
|
43
44
|
);
|
44
45
|
}
|
45
46
|
|
46
|
-
|
47
|
-
let executed = false;
|
48
|
-
const names = Object.keys(this.handlers);
|
49
|
-
const binds = names.map((_, index) => `$${index + 1}`).join(', ');
|
50
|
-
sbvrUtils.db
|
51
|
-
.transaction(async (tx) => {
|
52
|
-
if (!this.canExecute()) {
|
53
|
-
return;
|
54
|
-
}
|
55
|
-
|
56
|
-
const result = await tx.executeSql(
|
57
|
-
`SELECT ${selectColumns}
|
58
|
-
FROM task AS t
|
59
|
-
WHERE
|
60
|
-
t."is executed by-handler" IN (${binds}) AND
|
61
|
-
t."status" = 'pending' AND
|
62
|
-
t."attempt count" <= t."attempt limit" AND
|
63
|
-
(
|
64
|
-
t."is scheduled to execute on-time" IS NULL OR
|
65
|
-
t."is scheduled to execute on-time" <= CURRENT_TIMESTAMP + INTERVAL '${Math.ceil(this.interval / 1000)} second'
|
66
|
-
)
|
67
|
-
ORDER BY
|
68
|
-
t."is scheduled to execute on-time" ASC,
|
69
|
-
t."priority" DESC,
|
70
|
-
t."id" ASC
|
71
|
-
LIMIT ${Math.max(this.concurrency - this.executing, 0)}
|
72
|
-
FOR UPDATE SKIP LOCKED`,
|
73
|
-
names,
|
74
|
-
);
|
75
|
-
if (result.rows.length === 0) {
|
76
|
-
return;
|
77
|
-
}
|
78
|
-
|
79
|
-
// Tasks found, execute them in parallel
|
80
|
-
await Promise.all(
|
81
|
-
result.rows.map(async (row) => {
|
82
|
-
await this.execute(tx, row as PartialTask);
|
83
|
-
}),
|
84
|
-
);
|
85
|
-
executed = true;
|
86
|
-
})
|
87
|
-
.catch((err) => {
|
88
|
-
console.error('Failed polling for tasks:', err);
|
89
|
-
})
|
90
|
-
.finally(() => {
|
91
|
-
setTimeout(() => this.poll(), executed ? 0 : this.interval);
|
92
|
-
});
|
93
|
-
}
|
94
|
-
|
95
|
-
// Start listening and polling for tasks
|
96
|
-
public start() {
|
97
|
-
sbvrUtils.db.on?.(
|
98
|
-
'notification',
|
99
|
-
async (msg) => {
|
100
|
-
if (this.canExecute()) {
|
101
|
-
await sbvrUtils.db.transaction(async (tx) => {
|
102
|
-
const result = await tx.executeSql(
|
103
|
-
`SELECT ${selectColumns} FROM task AS t WHERE id = $1 FOR UPDATE SKIP LOCKED`,
|
104
|
-
[msg.payload],
|
105
|
-
);
|
106
|
-
if (result.rows.length > 0) {
|
107
|
-
await this.execute(tx, result.rows[0] as PartialTask);
|
108
|
-
}
|
109
|
-
});
|
110
|
-
}
|
111
|
-
},
|
112
|
-
{
|
113
|
-
channel,
|
114
|
-
},
|
115
|
-
);
|
116
|
-
this.poll();
|
117
|
-
}
|
118
|
-
|
47
|
+
// Execute a task
|
119
48
|
private async execute(tx: Db.Tx, task: PartialTask): Promise<void> {
|
120
49
|
this.executing++;
|
121
50
|
try {
|
122
|
-
// Get
|
51
|
+
// Get specified handler
|
123
52
|
const handler = this.handlers[task.is_executed_by__handler];
|
124
53
|
const startedOnTime = new Date();
|
125
54
|
if (handler == null) {
|
126
|
-
await this.
|
55
|
+
await this.finalize(
|
127
56
|
tx,
|
128
57
|
task,
|
129
58
|
startedOnTime,
|
@@ -140,7 +69,7 @@ export class Worker {
|
|
140
69
|
handler.validate != null &&
|
141
70
|
!handler.validate(task.is_executed_with__parameter_set)
|
142
71
|
) {
|
143
|
-
await this.
|
72
|
+
await this.finalize(
|
144
73
|
tx,
|
145
74
|
task,
|
146
75
|
startedOnTime,
|
@@ -150,7 +79,7 @@ export class Worker {
|
|
150
79
|
return;
|
151
80
|
}
|
152
81
|
|
153
|
-
// Execute
|
82
|
+
// Execute handler
|
154
83
|
const result = await handler.fn({
|
155
84
|
api: new PinejsClient({
|
156
85
|
passthrough: {
|
@@ -161,10 +90,10 @@ export class Worker {
|
|
161
90
|
tx,
|
162
91
|
});
|
163
92
|
|
164
|
-
// Update
|
165
|
-
await this.
|
93
|
+
// Update task with results
|
94
|
+
await this.finalize(tx, task, startedOnTime, result.status, result.error);
|
166
95
|
} catch (err) {
|
167
|
-
// This shouldn't
|
96
|
+
// This shouldn't happen, but if it does we want to log and kill the process
|
168
97
|
console.error('Task execution failed:', err);
|
169
98
|
process.exit(1);
|
170
99
|
} finally {
|
@@ -172,12 +101,8 @@ export class Worker {
|
|
172
101
|
}
|
173
102
|
}
|
174
103
|
|
175
|
-
|
176
|
-
|
177
|
-
return new Date(Date.now() + delay);
|
178
|
-
}
|
179
|
-
|
180
|
-
private async update(
|
104
|
+
// Update task and schedule next attempt if needed
|
105
|
+
private async finalize(
|
181
106
|
tx: Db.Tx,
|
182
107
|
task: PartialTask,
|
183
108
|
startedOnTime: Date,
|
@@ -238,4 +163,84 @@ export class Worker {
|
|
238
163
|
});
|
239
164
|
}
|
240
165
|
}
|
166
|
+
|
167
|
+
// Calculate next attempt time using exponential backoff
|
168
|
+
private getNextAttemptTime(attempt: number): Date | null {
|
169
|
+
const delay = Math.ceil(Math.exp(Math.min(10, attempt))) * 1000;
|
170
|
+
return new Date(Date.now() + delay);
|
171
|
+
}
|
172
|
+
|
173
|
+
// Poll for tasks to execute
|
174
|
+
private poll(): void {
|
175
|
+
let executed = false;
|
176
|
+
const handlerNames = Object.keys(this.handlers);
|
177
|
+
const binds = handlerNames.map((_, index) => `$${index + 1}`).join(', ');
|
178
|
+
sbvrUtils.db
|
179
|
+
.transaction(async (tx) => {
|
180
|
+
if (!this.canExecute()) {
|
181
|
+
return;
|
182
|
+
}
|
183
|
+
|
184
|
+
const result = await tx.executeSql(
|
185
|
+
`SELECT ${selectColumns}
|
186
|
+
FROM task AS t
|
187
|
+
WHERE
|
188
|
+
t."is executed by-handler" IN (${binds}) AND
|
189
|
+
t."status" = 'pending' AND
|
190
|
+
t."attempt count" <= t."attempt limit" AND
|
191
|
+
(
|
192
|
+
t."is scheduled to execute on-time" IS NULL OR
|
193
|
+
t."is scheduled to execute on-time" <= CURRENT_TIMESTAMP + INTERVAL '${Math.ceil(this.interval / 1000)} second'
|
194
|
+
)
|
195
|
+
ORDER BY
|
196
|
+
t."is scheduled to execute on-time" ASC,
|
197
|
+
t."priority" DESC,
|
198
|
+
t."id" ASC
|
199
|
+
LIMIT ${Math.max(this.concurrency - this.executing, 0)}
|
200
|
+
FOR UPDATE SKIP LOCKED`,
|
201
|
+
handlerNames,
|
202
|
+
);
|
203
|
+
if (result.rows.length === 0) {
|
204
|
+
return;
|
205
|
+
}
|
206
|
+
|
207
|
+
// Tasks found, execute them in parallel
|
208
|
+
await Promise.all(
|
209
|
+
result.rows.map(async (row) => {
|
210
|
+
await this.execute(tx, row as PartialTask);
|
211
|
+
}),
|
212
|
+
);
|
213
|
+
executed = true;
|
214
|
+
})
|
215
|
+
.catch((err) => {
|
216
|
+
console.error('Failed polling for tasks:', err);
|
217
|
+
})
|
218
|
+
.finally(() => {
|
219
|
+
setTimeout(() => this.poll(), executed ? 0 : this.interval);
|
220
|
+
});
|
221
|
+
}
|
222
|
+
|
223
|
+
// Start listening and polling for tasks
|
224
|
+
public start(): void {
|
225
|
+
sbvrUtils.db.on?.(
|
226
|
+
'notification',
|
227
|
+
async (msg) => {
|
228
|
+
if (this.canExecute()) {
|
229
|
+
await sbvrUtils.db.transaction(async (tx) => {
|
230
|
+
const result = await tx.executeSql(
|
231
|
+
`SELECT ${selectColumns} FROM task AS t WHERE id = $1 FOR UPDATE SKIP LOCKED`,
|
232
|
+
[msg.payload],
|
233
|
+
);
|
234
|
+
if (result.rows.length > 0) {
|
235
|
+
await this.execute(tx, result.rows[0] as PartialTask);
|
236
|
+
}
|
237
|
+
});
|
238
|
+
}
|
239
|
+
},
|
240
|
+
{
|
241
|
+
channel,
|
242
|
+
},
|
243
|
+
);
|
244
|
+
this.poll();
|
245
|
+
}
|
241
246
|
}
|