@stamhoofd/queues 2.49.1 → 2.50.2
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/dist/src/QueueHandler.d.ts +32 -3
- package/dist/src/QueueHandler.d.ts.map +1 -1
- package/dist/src/QueueHandler.js +139 -9
- package/dist/src/QueueHandler.js.map +1 -1
- package/package.json +2 -2
- package/src/QueueHandler.ts +155 -10
|
@@ -4,22 +4,51 @@ declare class Queue {
|
|
|
4
4
|
items: QueueItem<any>[];
|
|
5
5
|
parallel: number;
|
|
6
6
|
runCount: number;
|
|
7
|
+
abortSignals: Set<AbortSignal>;
|
|
7
8
|
constructor(name: string, parallel?: number);
|
|
8
9
|
addItem(item: QueueItem<any>): void;
|
|
10
|
+
createAbortSignal(): AbortSignal;
|
|
11
|
+
removeAbortSignal(signal: AbortSignal): void;
|
|
12
|
+
abort(error?: Error): void;
|
|
9
13
|
}
|
|
14
|
+
export type QueueHandlerOptions = {
|
|
15
|
+
abort: AbortSignal;
|
|
16
|
+
};
|
|
10
17
|
declare class QueueItem<T> {
|
|
11
|
-
handler: () => Promise<T>;
|
|
18
|
+
handler: (options: QueueHandlerOptions) => Promise<T>;
|
|
12
19
|
resolve: (value: T) => void;
|
|
13
20
|
reject: (reason?: any) => void;
|
|
14
21
|
}
|
|
22
|
+
export declare function isDebouncedError(error: unknown): boolean;
|
|
23
|
+
export declare function isCanceledError(error: unknown): boolean;
|
|
24
|
+
export declare function isAbortedError(error: unknown): boolean;
|
|
25
|
+
export declare class AbortSignal {
|
|
26
|
+
protected abortWithError: unknown | null;
|
|
27
|
+
protected listeners: ((error: unknown) => Promise<void> | void)[];
|
|
28
|
+
get isAborted(): boolean;
|
|
29
|
+
abort(error?: unknown): void;
|
|
30
|
+
throwIfAborted(cleanup?: () => Promise<void> | void): Promise<void>;
|
|
31
|
+
on(on: 'abort', listener: (error: unknown) => Promise<void> | void): void;
|
|
32
|
+
}
|
|
15
33
|
/**
|
|
16
34
|
* Force the usage of a queue to prevent concurrency issues
|
|
17
35
|
*/
|
|
18
36
|
export declare class QueueHandler {
|
|
19
37
|
static queues: Map<string, Queue>;
|
|
20
38
|
static asyncLocalStorage: AsyncLocalStorage<string[]>;
|
|
21
|
-
static cancel(queue: string): void;
|
|
22
|
-
|
|
39
|
+
static cancel(queue: string, error?: Error): void;
|
|
40
|
+
/**
|
|
41
|
+
* Abort any running queue items. Same as 'cancel' but will also reject any running items if they support the 'abort' method (otherwise they will just resolve as normal)
|
|
42
|
+
*/
|
|
43
|
+
static abort(queue: string, error?: Error): void;
|
|
44
|
+
/**$
|
|
45
|
+
* Waits {timeout} ms before executing the handler, if another call is made to the same queue, the timeout is reset
|
|
46
|
+
* and the other users will receive an error.
|
|
47
|
+
*
|
|
48
|
+
* Note: this will throw if the handler was debounced and not yet executed
|
|
49
|
+
*/
|
|
50
|
+
static debounce<T>(queue: string, handler: (options: QueueHandlerOptions) => Promise<T>, timeout: number): Promise<T>;
|
|
51
|
+
static schedule<T>(queue: string, handler: (options: QueueHandlerOptions) => Promise<T>, parallel?: number): Promise<T>;
|
|
23
52
|
static isRunning(queue: string): boolean;
|
|
24
53
|
/**
|
|
25
54
|
* Returns amount of running jobs + pending jobs for a given queue
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueHandler.d.ts","sourceRoot":"","sources":["../../src/QueueHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"QueueHandler.d.ts","sourceRoot":"","sources":["../../src/QueueHandler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrD,cAAM,KAAK;IACP,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAK;IAC5B,QAAQ,SAAI;IACZ,QAAQ,SAAI;IACZ,YAAY,EAAE,GAAG,CAAC,WAAW,CAAC,CAAY;gBAE9B,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAI;IAKtC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC;IAI5B,iBAAiB;IAMjB,iBAAiB,CAAC,MAAM,EAAE,WAAW;IAIrC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK;CAMtB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAC9B,KAAK,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,cAAM,SAAS,CAAC,CAAC;IACb,OAAO,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IACrD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAA;IAC3B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,IAAI,CAAA;CACjC;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,WAE9C;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,WAE7C;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,WAE5C;AAED,qBAAa,WAAW;IACpB,SAAS,CAAC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAO;IAC/C,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAC,IAAI,CAAC,EAAE,CAAK;IAEpE,IAAI,SAAS,YAEZ;IAED,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO;IAgBf,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAC,IAAI;IAUvD,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAAC,IAAI;CAGnE;AAED;;GAEG;AACH,qBAAa,YAAY;IACrB,MAAM,CAAC,MAAM,qBAA2B;IACxC,MAAM,CAAC,iBAAiB,8BAAqC;IAE7D,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;IAe1C;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;IAiBzC;;;;;OAKG;WACU,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;WA0B9G,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,SAAI,GAAG,OAAO,CAAC,CAAC,CAAC;IAuCxH,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM;IAI9B;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM;mBAKP,OAAO;CAmD/B"}
|
package/dist/src/QueueHandler.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.QueueHandler = void 0;
|
|
3
|
+
exports.QueueHandler = exports.AbortSignal = void 0;
|
|
4
|
+
exports.isDebouncedError = isDebouncedError;
|
|
5
|
+
exports.isCanceledError = isCanceledError;
|
|
6
|
+
exports.isAbortedError = isAbortedError;
|
|
4
7
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
8
|
+
const simple_errors_1 = require("@simonbackx/simple-errors");
|
|
5
9
|
class Queue {
|
|
6
10
|
name;
|
|
7
11
|
items = [];
|
|
8
12
|
parallel = 1;
|
|
9
13
|
runCount = 0;
|
|
14
|
+
abortSignals = new Set();
|
|
10
15
|
constructor(name, parallel = 1) {
|
|
11
16
|
this.name = name;
|
|
12
17
|
this.parallel = parallel;
|
|
@@ -14,44 +19,150 @@ class Queue {
|
|
|
14
19
|
addItem(item) {
|
|
15
20
|
this.items.push(item);
|
|
16
21
|
}
|
|
22
|
+
createAbortSignal() {
|
|
23
|
+
const signal = new AbortSignal();
|
|
24
|
+
this.abortSignals.add(signal);
|
|
25
|
+
return signal;
|
|
26
|
+
}
|
|
27
|
+
removeAbortSignal(signal) {
|
|
28
|
+
this.abortSignals.delete(signal);
|
|
29
|
+
}
|
|
30
|
+
abort(error) {
|
|
31
|
+
for (const signal of this.abortSignals) {
|
|
32
|
+
signal.abort(error);
|
|
33
|
+
}
|
|
34
|
+
this.abortSignals.clear(); // Avoid swapping error midway the abort
|
|
35
|
+
}
|
|
17
36
|
}
|
|
18
37
|
class QueueItem {
|
|
19
38
|
handler;
|
|
20
39
|
resolve;
|
|
21
40
|
reject;
|
|
22
41
|
}
|
|
42
|
+
function isDebouncedError(error) {
|
|
43
|
+
return (0, simple_errors_1.isSimpleError)(error) && error.code === 'queue-debounced';
|
|
44
|
+
}
|
|
45
|
+
function isCanceledError(error) {
|
|
46
|
+
return (0, simple_errors_1.isSimpleError)(error) && error.code === 'queue-canceled';
|
|
47
|
+
}
|
|
48
|
+
function isAbortedError(error) {
|
|
49
|
+
return (0, simple_errors_1.isSimpleError)(error) && error.code === 'queue-aborted';
|
|
50
|
+
}
|
|
51
|
+
class AbortSignal {
|
|
52
|
+
abortWithError = null;
|
|
53
|
+
listeners = [];
|
|
54
|
+
get isAborted() {
|
|
55
|
+
return this.abortWithError !== null;
|
|
56
|
+
}
|
|
57
|
+
abort(error) {
|
|
58
|
+
if (this.isAborted) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.abortWithError = error ?? new simple_errors_1.SimpleError({
|
|
62
|
+
code: 'queue-aborted',
|
|
63
|
+
message: 'Queue was aborted',
|
|
64
|
+
statusCode: 500
|
|
65
|
+
});
|
|
66
|
+
for (const listener of this.listeners) {
|
|
67
|
+
listener(this.abortWithError);
|
|
68
|
+
}
|
|
69
|
+
this.listeners = [];
|
|
70
|
+
}
|
|
71
|
+
async throwIfAborted(cleanup) {
|
|
72
|
+
if (this.isAborted) {
|
|
73
|
+
if (cleanup) {
|
|
74
|
+
await cleanup();
|
|
75
|
+
}
|
|
76
|
+
throw this.abortWithError;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Add event listeners
|
|
80
|
+
on(on, listener) {
|
|
81
|
+
this.listeners.push(listener);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
exports.AbortSignal = AbortSignal;
|
|
23
85
|
/**
|
|
24
86
|
* Force the usage of a queue to prevent concurrency issues
|
|
25
87
|
*/
|
|
26
88
|
class QueueHandler {
|
|
27
89
|
static queues = new Map();
|
|
28
90
|
static asyncLocalStorage = new node_async_hooks_1.AsyncLocalStorage();
|
|
29
|
-
static cancel(queue) {
|
|
91
|
+
static cancel(queue, error) {
|
|
30
92
|
const q = this.queues.get(queue);
|
|
31
93
|
if (q) {
|
|
32
94
|
// This doesn't interfere any running items
|
|
33
95
|
for (const item of q.items) {
|
|
34
96
|
item.handler = () => {
|
|
35
|
-
return Promise.reject(new
|
|
97
|
+
return Promise.reject(error ?? new simple_errors_1.SimpleError({
|
|
98
|
+
code: 'queue-canceled',
|
|
99
|
+
message: 'Queue ' + queue + ' was canceled',
|
|
100
|
+
}));
|
|
36
101
|
};
|
|
37
102
|
}
|
|
38
103
|
}
|
|
39
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Abort any running queue items. Same as 'cancel' but will also reject any running items if they support the 'abort' method (otherwise they will just resolve as normal)
|
|
107
|
+
*/
|
|
108
|
+
static abort(queue, error) {
|
|
109
|
+
const q = this.queues.get(queue);
|
|
110
|
+
if (q) {
|
|
111
|
+
// This doesn't interfere any running items
|
|
112
|
+
for (const item of q.items) {
|
|
113
|
+
item.handler = () => {
|
|
114
|
+
return Promise.reject(error ?? new simple_errors_1.SimpleError({
|
|
115
|
+
code: 'queue-canceled',
|
|
116
|
+
message: 'Queue ' + queue + ' was canceled',
|
|
117
|
+
}));
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
q.abort(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**$
|
|
124
|
+
* Waits {timeout} ms before executing the handler, if another call is made to the same queue, the timeout is reset
|
|
125
|
+
* and the other users will receive an error.
|
|
126
|
+
*
|
|
127
|
+
* Note: this will throw if the handler was debounced and not yet executed
|
|
128
|
+
*/
|
|
129
|
+
static async debounce(queue, handler, timeout) {
|
|
130
|
+
// Stop any running items that were not yet executed
|
|
131
|
+
this.abort(queue, new simple_errors_1.SimpleError({
|
|
132
|
+
code: 'queue-debounced',
|
|
133
|
+
message: 'Debounced',
|
|
134
|
+
statusCode: 500
|
|
135
|
+
}));
|
|
136
|
+
// Schedule timeout (will throw if cancelled)
|
|
137
|
+
await this.schedule(queue, ({ abort }) => {
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const timeoutId = setTimeout(() => {
|
|
140
|
+
resolve();
|
|
141
|
+
}, timeout);
|
|
142
|
+
abort.on('abort', (error) => {
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
reject(error);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// Run the handler
|
|
149
|
+
return await this.schedule(queue, handler);
|
|
150
|
+
}
|
|
40
151
|
static async schedule(queue, handler, parallel = 1) {
|
|
41
152
|
// console.log("[QUEUE] Schedule "+queue)
|
|
42
153
|
const currentQueues = this.asyncLocalStorage.getStore();
|
|
43
154
|
if (currentQueues !== undefined && currentQueues.includes(queue)) {
|
|
44
155
|
console.warn('Recursive usage of queues detected. Ignored running in queue', queue, currentQueues);
|
|
45
|
-
return await handler();
|
|
156
|
+
return await handler({ abort: new AbortSignal() });
|
|
46
157
|
}
|
|
47
158
|
// We need to save the current AsyncLocalStorage context
|
|
48
159
|
// otherwise we could run items on the queue with the wrong context
|
|
49
160
|
const snapshot = node_async_hooks_1.AsyncLocalStorage.snapshot();
|
|
50
161
|
const item = new QueueItem();
|
|
51
|
-
item.handler = () => snapshot(async () => {
|
|
162
|
+
item.handler = (options) => snapshot(async () => {
|
|
52
163
|
const currentQueues = this.asyncLocalStorage.getStore() ?? [];
|
|
53
164
|
return await this.asyncLocalStorage.run([...currentQueues, queue], async () => {
|
|
54
|
-
return await handler();
|
|
165
|
+
return await handler(options);
|
|
55
166
|
});
|
|
56
167
|
});
|
|
57
168
|
const promise = new Promise((resolve, reject) => {
|
|
@@ -95,17 +206,36 @@ class QueueHandler {
|
|
|
95
206
|
}
|
|
96
207
|
q.runCount += 1;
|
|
97
208
|
// console.log("[QUEUE] ("+q.runCount+"/"+q.parallel+") Executing "+queue+" ("+q.items.length+" remaining)")
|
|
209
|
+
const abort = q.createAbortSignal();
|
|
98
210
|
try {
|
|
99
|
-
next.resolve(await next.handler(
|
|
211
|
+
next.resolve(await next.handler({
|
|
212
|
+
abort
|
|
213
|
+
}));
|
|
100
214
|
// console.log("[QUEUE] ("+(q.runCount-1)+"/"+q.parallel+") Resolved "+queue+" ("+q.items.length+" remaining)")
|
|
101
215
|
}
|
|
102
216
|
catch (e) {
|
|
103
217
|
next.reject(e);
|
|
104
218
|
if (STAMHOOFD.environment !== 'test') {
|
|
105
|
-
|
|
106
|
-
|
|
219
|
+
if (!isDebouncedError(e) && !isCanceledError(e) && !isAbortedError(e)) {
|
|
220
|
+
console.log("[QUEUE] (" + (q.runCount - 1) + "/" + q.parallel + ") Rejected " + queue + " (" + q.items.length + " remaining)");
|
|
221
|
+
console.error(e);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
if (isDebouncedError(e)) {
|
|
225
|
+
console.log("[QUEUE] (" + (q.runCount - 1) + "/" + q.parallel + ") Debounced " + queue + " (" + q.items.length + " remaining)");
|
|
226
|
+
}
|
|
227
|
+
else if (isCanceledError(e)) {
|
|
228
|
+
console.log("[QUEUE] (" + (q.runCount - 1) + "/" + q.parallel + ") Canceled " + queue + " (" + q.items.length + " remaining)");
|
|
229
|
+
}
|
|
230
|
+
else if (isAbortedError(e)) {
|
|
231
|
+
console.log("[QUEUE] (" + (q.runCount - 1) + "/" + q.parallel + ") Aborted " + queue + " (" + q.items.length + " remaining)");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
107
234
|
}
|
|
108
235
|
}
|
|
236
|
+
finally {
|
|
237
|
+
q.removeAbortSignal(abort);
|
|
238
|
+
}
|
|
109
239
|
q.runCount -= 1;
|
|
110
240
|
await this.runNext(queue);
|
|
111
241
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueHandler.js","sourceRoot":"","sources":["../../src/QueueHandler.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"QueueHandler.js","sourceRoot":"","sources":["../../src/QueueHandler.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stamhoofd/queues",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.50.2",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"license": "UNLICENCED",
|
|
@@ -16,5 +16,5 @@
|
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
|
-
"gitHead": "
|
|
19
|
+
"gitHead": "ffbc162ba4d0def7714d88b255b72a5f8991b4c3"
|
|
20
20
|
}
|
package/src/QueueHandler.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import { sleep } from '@stamhoofd/utility';
|
|
4
|
+
import { isSimpleError, SimpleError } from '@simonbackx/simple-errors';
|
|
3
5
|
|
|
4
6
|
class Queue {
|
|
5
7
|
name: string
|
|
6
8
|
items: QueueItem<any>[] = []
|
|
7
9
|
parallel = 1
|
|
8
10
|
runCount = 0
|
|
11
|
+
abortSignals: Set<AbortSignal> = new Set()
|
|
9
12
|
|
|
10
13
|
constructor(name: string, parallel = 1) {
|
|
11
14
|
this.name = name
|
|
@@ -15,14 +18,86 @@ class Queue {
|
|
|
15
18
|
addItem(item: QueueItem<any>) {
|
|
16
19
|
this.items.push(item)
|
|
17
20
|
}
|
|
21
|
+
|
|
22
|
+
createAbortSignal() {
|
|
23
|
+
const signal = new AbortSignal()
|
|
24
|
+
this.abortSignals.add(signal)
|
|
25
|
+
return signal
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
removeAbortSignal(signal: AbortSignal) {
|
|
29
|
+
this.abortSignals.delete(signal)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
abort(error?: Error) {
|
|
33
|
+
for (const signal of this.abortSignals) {
|
|
34
|
+
signal.abort(error)
|
|
35
|
+
}
|
|
36
|
+
this.abortSignals.clear() // Avoid swapping error midway the abort
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type QueueHandlerOptions = {
|
|
41
|
+
abort: AbortSignal
|
|
18
42
|
}
|
|
19
43
|
|
|
20
44
|
class QueueItem<T> {
|
|
21
|
-
handler: () => Promise<T>
|
|
45
|
+
handler: (options: QueueHandlerOptions) => Promise<T>
|
|
22
46
|
resolve: (value: T) => void
|
|
23
47
|
reject: (reason?: any) => void
|
|
24
48
|
}
|
|
25
49
|
|
|
50
|
+
export function isDebouncedError(error: unknown) {
|
|
51
|
+
return isSimpleError(error) && error.code === 'queue-debounced'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function isCanceledError(error: unknown) {
|
|
55
|
+
return isSimpleError(error) && error.code === 'queue-canceled'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isAbortedError(error: unknown) {
|
|
59
|
+
return isSimpleError(error) && error.code === 'queue-aborted'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class AbortSignal {
|
|
63
|
+
protected abortWithError: unknown | null = null
|
|
64
|
+
protected listeners: ((error: unknown) => Promise<void>|void)[] = []
|
|
65
|
+
|
|
66
|
+
get isAborted() {
|
|
67
|
+
return this.abortWithError !== null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
abort(error?: unknown) {
|
|
71
|
+
if (this.isAborted) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
this.abortWithError = error ?? new SimpleError({
|
|
75
|
+
code: 'queue-aborted',
|
|
76
|
+
message: 'Queue was aborted',
|
|
77
|
+
statusCode: 500
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
for (const listener of this.listeners) {
|
|
81
|
+
listener(this.abortWithError)
|
|
82
|
+
}
|
|
83
|
+
this.listeners = []
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async throwIfAborted(cleanup?: () => Promise<void>|void) {
|
|
87
|
+
if (this.isAborted) {
|
|
88
|
+
if (cleanup) {
|
|
89
|
+
await cleanup()
|
|
90
|
+
}
|
|
91
|
+
throw this.abortWithError
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Add event listeners
|
|
96
|
+
on(on: 'abort', listener: (error: unknown) => Promise<void>|void) {
|
|
97
|
+
this.listeners.push(listener)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
26
101
|
/**
|
|
27
102
|
* Force the usage of a queue to prevent concurrency issues
|
|
28
103
|
*/
|
|
@@ -30,25 +105,80 @@ export class QueueHandler {
|
|
|
30
105
|
static queues = new Map<string, Queue>()
|
|
31
106
|
static asyncLocalStorage = new AsyncLocalStorage<string[]>();
|
|
32
107
|
|
|
33
|
-
static cancel(queue: string) {
|
|
108
|
+
static cancel(queue: string, error?: Error) {
|
|
109
|
+
const q = this.queues.get(queue)
|
|
110
|
+
if (q) {
|
|
111
|
+
// This doesn't interfere any running items
|
|
112
|
+
for (const item of q.items) {
|
|
113
|
+
item.handler = () => {
|
|
114
|
+
return Promise.reject(error ?? new SimpleError({
|
|
115
|
+
code: 'queue-canceled',
|
|
116
|
+
message: 'Queue '+queue+' was canceled',
|
|
117
|
+
}))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Abort any running queue items. Same as 'cancel' but will also reject any running items if they support the 'abort' method (otherwise they will just resolve as normal)
|
|
125
|
+
*/
|
|
126
|
+
static abort(queue: string, error?: Error) {
|
|
34
127
|
const q = this.queues.get(queue)
|
|
35
128
|
if (q) {
|
|
36
129
|
// This doesn't interfere any running items
|
|
37
130
|
for (const item of q.items) {
|
|
38
131
|
item.handler = () => {
|
|
39
|
-
return Promise.reject(new
|
|
132
|
+
return Promise.reject(error ?? new SimpleError({
|
|
133
|
+
code: 'queue-canceled',
|
|
134
|
+
message: 'Queue '+queue+' was canceled',
|
|
135
|
+
}))
|
|
40
136
|
}
|
|
41
137
|
}
|
|
138
|
+
|
|
139
|
+
q.abort(error)
|
|
42
140
|
}
|
|
43
141
|
}
|
|
44
142
|
|
|
45
|
-
|
|
143
|
+
/**$
|
|
144
|
+
* Waits {timeout} ms before executing the handler, if another call is made to the same queue, the timeout is reset
|
|
145
|
+
* and the other users will receive an error.
|
|
146
|
+
*
|
|
147
|
+
* Note: this will throw if the handler was debounced and not yet executed
|
|
148
|
+
*/
|
|
149
|
+
static async debounce<T>(queue: string, handler: (options: QueueHandlerOptions) => Promise<T>, timeout: number): Promise<T> {
|
|
150
|
+
// Stop any running items that were not yet executed
|
|
151
|
+
this.abort(queue, new SimpleError({
|
|
152
|
+
code: 'queue-debounced',
|
|
153
|
+
message: 'Debounced',
|
|
154
|
+
statusCode: 500
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
// Schedule timeout (will throw if cancelled)
|
|
158
|
+
await this.schedule(queue, ({abort}) => {
|
|
159
|
+
return new Promise<void>((resolve, reject) => {
|
|
160
|
+
const timeoutId = setTimeout(() => {
|
|
161
|
+
resolve()
|
|
162
|
+
}, timeout)
|
|
163
|
+
|
|
164
|
+
abort.on('abort', (error) => {
|
|
165
|
+
clearTimeout(timeoutId)
|
|
166
|
+
reject(error)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Run the handler
|
|
172
|
+
return await this.schedule(queue, handler)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static async schedule<T>(queue: string, handler: (options: QueueHandlerOptions) => Promise<T>, parallel = 1): Promise<T> {
|
|
46
176
|
// console.log("[QUEUE] Schedule "+queue)
|
|
47
177
|
|
|
48
178
|
const currentQueues = this.asyncLocalStorage.getStore();
|
|
49
179
|
if (currentQueues !== undefined && currentQueues.includes(queue)) {
|
|
50
180
|
console.warn('Recursive usage of queues detected. Ignored running in queue', queue, currentQueues);
|
|
51
|
-
return await handler();
|
|
181
|
+
return await handler({abort: new AbortSignal()});
|
|
52
182
|
}
|
|
53
183
|
|
|
54
184
|
// We need to save the current AsyncLocalStorage context
|
|
@@ -56,10 +186,10 @@ export class QueueHandler {
|
|
|
56
186
|
const snapshot = AsyncLocalStorage.snapshot()
|
|
57
187
|
|
|
58
188
|
const item = new QueueItem<T>()
|
|
59
|
-
item.handler = () => snapshot(async () => {
|
|
189
|
+
item.handler = (options) => snapshot(async () => {
|
|
60
190
|
const currentQueues = this.asyncLocalStorage.getStore() ?? [];
|
|
61
191
|
return await this.asyncLocalStorage.run([...currentQueues, queue], async () => {
|
|
62
|
-
return await handler();
|
|
192
|
+
return await handler(options);
|
|
63
193
|
});
|
|
64
194
|
})
|
|
65
195
|
|
|
@@ -114,16 +244,31 @@ export class QueueHandler {
|
|
|
114
244
|
|
|
115
245
|
q.runCount += 1
|
|
116
246
|
// console.log("[QUEUE] ("+q.runCount+"/"+q.parallel+") Executing "+queue+" ("+q.items.length+" remaining)")
|
|
247
|
+
const abort = q.createAbortSignal()
|
|
117
248
|
|
|
118
249
|
try {
|
|
119
|
-
next.resolve(await next.handler(
|
|
250
|
+
next.resolve(await next.handler({
|
|
251
|
+
abort
|
|
252
|
+
}))
|
|
120
253
|
// console.log("[QUEUE] ("+(q.runCount-1)+"/"+q.parallel+") Resolved "+queue+" ("+q.items.length+" remaining)")
|
|
121
254
|
} catch (e) {
|
|
122
255
|
next.reject(e)
|
|
123
256
|
if (STAMHOOFD.environment !== 'test') {
|
|
124
|
-
|
|
125
|
-
|
|
257
|
+
if (!isDebouncedError(e) && !isCanceledError(e) && !isAbortedError(e)) {
|
|
258
|
+
console.log("[QUEUE] ("+(q.runCount-1)+"/"+q.parallel+") Rejected "+queue+" ("+q.items.length+" remaining)")
|
|
259
|
+
console.error(e)
|
|
260
|
+
} else {
|
|
261
|
+
if (isDebouncedError(e)) {
|
|
262
|
+
console.log("[QUEUE] ("+(q.runCount-1)+"/"+q.parallel+") Debounced "+queue+" ("+q.items.length+" remaining)")
|
|
263
|
+
} else if (isCanceledError(e)) {
|
|
264
|
+
console.log("[QUEUE] ("+(q.runCount-1)+"/"+q.parallel+") Canceled "+queue+" ("+q.items.length+" remaining)")
|
|
265
|
+
} else if (isAbortedError(e)) {
|
|
266
|
+
console.log("[QUEUE] ("+(q.runCount-1)+"/"+q.parallel+") Aborted "+queue+" ("+q.items.length+" remaining)")
|
|
267
|
+
}
|
|
268
|
+
}
|
|
126
269
|
}
|
|
270
|
+
} finally {
|
|
271
|
+
q.removeAbortSignal(abort)
|
|
127
272
|
}
|
|
128
273
|
|
|
129
274
|
q.runCount -= 1
|