poolifier 2.3.4 → 2.3.6
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/lib/index.d.ts +143 -142
- package/lib/index.js +1 -1
- package/package.json +14 -15
package/lib/index.d.ts
CHANGED
|
@@ -5,109 +5,6 @@ import { Worker as ClusterWorker } from "cluster";
|
|
|
5
5
|
import { MessagePort, MessageChannel } from "worker_threads";
|
|
6
6
|
import { Worker as Worker$0 } from "worker_threads";
|
|
7
7
|
import { AsyncResource } from "async_hooks";
|
|
8
|
-
/**
|
|
9
|
-
* Enumeration of kill behaviors.
|
|
10
|
-
*/
|
|
11
|
-
declare const KillBehaviors: Readonly<{
|
|
12
|
-
readonly SOFT: "SOFT";
|
|
13
|
-
readonly HARD: "HARD";
|
|
14
|
-
}>;
|
|
15
|
-
/**
|
|
16
|
-
* Kill behavior.
|
|
17
|
-
*/
|
|
18
|
-
type KillBehavior = keyof typeof KillBehaviors;
|
|
19
|
-
/**
|
|
20
|
-
* Options for workers.
|
|
21
|
-
*/
|
|
22
|
-
interface WorkerOptions {
|
|
23
|
-
/**
|
|
24
|
-
* Maximum waiting time in milliseconds for tasks.
|
|
25
|
-
*
|
|
26
|
-
* After this time, newly created workers will be terminated.
|
|
27
|
-
* The last active time of your worker unit will be updated when a task is submitted to a worker or when a worker terminate a task.
|
|
28
|
-
*
|
|
29
|
-
* - If `killBehavior` is set to `KillBehaviors.HARD` this value represents also the timeout for the tasks that you submit to the pool,
|
|
30
|
-
* when this timeout expires your tasks is interrupted and the worker is killed if is not part of the minimum size of the pool.
|
|
31
|
-
* - If `killBehavior` is set to `KillBehaviors.SOFT` your tasks have no timeout and your workers will not be terminated until your task is completed.
|
|
32
|
-
*
|
|
33
|
-
* @default 60000 ms
|
|
34
|
-
*/
|
|
35
|
-
maxInactiveTime?: number;
|
|
36
|
-
/**
|
|
37
|
-
* Whether your worker will perform asynchronous or not.
|
|
38
|
-
*
|
|
39
|
-
* @default false
|
|
40
|
-
*/
|
|
41
|
-
async?: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* `killBehavior` dictates if your async unit (worker/process) will be deleted in case that a task is active on it.
|
|
44
|
-
*
|
|
45
|
-
* - SOFT: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **won't** be deleted.
|
|
46
|
-
* - HARD: If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted.
|
|
47
|
-
*
|
|
48
|
-
* This option only apply to the newly created workers.
|
|
49
|
-
*
|
|
50
|
-
* @default KillBehaviors.SOFT
|
|
51
|
-
*/
|
|
52
|
-
killBehavior?: KillBehavior;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Make all properties in T non-readonly.
|
|
56
|
-
*/
|
|
57
|
-
type Draft<T> = {
|
|
58
|
-
-readonly [P in keyof T]?: T[P];
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* Message object that is passed between worker and main worker.
|
|
62
|
-
*/
|
|
63
|
-
interface MessageValue<Data = unknown, MainWorker extends ClusterWorker | MessagePort | unknown = unknown> {
|
|
64
|
-
/**
|
|
65
|
-
* Input data that will be passed to the worker.
|
|
66
|
-
*/
|
|
67
|
-
readonly data?: Data;
|
|
68
|
-
/**
|
|
69
|
-
* Id of the message.
|
|
70
|
-
*/
|
|
71
|
-
readonly id?: number;
|
|
72
|
-
/**
|
|
73
|
-
* Kill code.
|
|
74
|
-
*/
|
|
75
|
-
readonly kill?: KillBehavior | 1;
|
|
76
|
-
/**
|
|
77
|
-
* Error.
|
|
78
|
-
*/
|
|
79
|
-
readonly error?: string;
|
|
80
|
-
/**
|
|
81
|
-
* Task runtime.
|
|
82
|
-
*/
|
|
83
|
-
readonly taskRunTime?: number;
|
|
84
|
-
/**
|
|
85
|
-
* Reference to main worker.
|
|
86
|
-
*
|
|
87
|
-
* Only for internal use.
|
|
88
|
-
*/
|
|
89
|
-
readonly parent?: MainWorker;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* An object holding the worker that will be used to resolve/rejects the promise later on.
|
|
93
|
-
*
|
|
94
|
-
* @template Worker Type of worker.
|
|
95
|
-
* @template Response Type of response of execution. This can only be serializable data.
|
|
96
|
-
*/
|
|
97
|
-
interface PromiseWorkerResponseWrapper<Worker extends IPoolWorker, Response = unknown> {
|
|
98
|
-
/**
|
|
99
|
-
* Resolve callback to fulfill the promise.
|
|
100
|
-
*/
|
|
101
|
-
readonly resolve: (value: Response) => void;
|
|
102
|
-
/**
|
|
103
|
-
* Reject callback to reject the promise.
|
|
104
|
-
*/
|
|
105
|
-
readonly reject: (reason?: string) => void;
|
|
106
|
-
/**
|
|
107
|
-
* The worker that has the assigned task.
|
|
108
|
-
*/
|
|
109
|
-
readonly worker: Worker;
|
|
110
|
-
}
|
|
111
8
|
/**
|
|
112
9
|
* Callback invoked if the worker has received a message.
|
|
113
10
|
*/
|
|
@@ -206,6 +103,11 @@ interface IWorkerChoiceStrategy<Worker extends IPoolWorker> {
|
|
|
206
103
|
*/
|
|
207
104
|
choose(): Worker;
|
|
208
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Pool events emitter.
|
|
108
|
+
*/
|
|
109
|
+
declare class PoolEmitter extends EventEmitter {
|
|
110
|
+
}
|
|
209
111
|
/**
|
|
210
112
|
* Options for a poolifier pool.
|
|
211
113
|
*/
|
|
@@ -244,6 +146,14 @@ interface PoolOptions<Worker> {
|
|
|
244
146
|
* @template Response Type of response of execution. This can only be serializable data.
|
|
245
147
|
*/
|
|
246
148
|
interface IPool<Data = unknown, Response = unknown> {
|
|
149
|
+
/**
|
|
150
|
+
* Emitter on which events can be listened to.
|
|
151
|
+
*
|
|
152
|
+
* Events that can currently be listened to:
|
|
153
|
+
*
|
|
154
|
+
* - `'busy'`
|
|
155
|
+
*/
|
|
156
|
+
readonly emitter?: PoolEmitter;
|
|
247
157
|
/**
|
|
248
158
|
* Performs the task specified in the constructor with the data parameter.
|
|
249
159
|
*
|
|
@@ -263,14 +173,14 @@ interface IPool<Data = unknown, Response = unknown> {
|
|
|
263
173
|
setWorkerChoiceStrategy(workerChoiceStrategy: WorkerChoiceStrategy): void;
|
|
264
174
|
}
|
|
265
175
|
/**
|
|
266
|
-
*
|
|
176
|
+
* Internal pool types.
|
|
267
177
|
*/
|
|
268
178
|
declare enum PoolType {
|
|
269
179
|
FIXED = "fixed",
|
|
270
180
|
DYNAMIC = "dynamic"
|
|
271
181
|
}
|
|
272
182
|
/**
|
|
273
|
-
*
|
|
183
|
+
* Internal tasks usage statistics.
|
|
274
184
|
*/
|
|
275
185
|
interface TasksUsage {
|
|
276
186
|
run: number;
|
|
@@ -278,11 +188,6 @@ interface TasksUsage {
|
|
|
278
188
|
runTime: number;
|
|
279
189
|
avgRunTime: number;
|
|
280
190
|
}
|
|
281
|
-
/**
|
|
282
|
-
* Internal poolifier pool emitter.
|
|
283
|
-
*/
|
|
284
|
-
declare class PoolEmitter extends EventEmitter {
|
|
285
|
-
}
|
|
286
191
|
/**
|
|
287
192
|
* Internal contract definition for a poolifier pool.
|
|
288
193
|
*
|
|
@@ -302,24 +207,12 @@ interface IPoolInternal<Worker extends IPoolWorker, Data = unknown, Response = u
|
|
|
302
207
|
* `value`: Worker tasks usage statistics.
|
|
303
208
|
*/
|
|
304
209
|
readonly workersTasksUsage: Map<Worker, TasksUsage>;
|
|
305
|
-
/**
|
|
306
|
-
* Emitter on which events can be listened to.
|
|
307
|
-
*
|
|
308
|
-
* Events that can currently be listened to:
|
|
309
|
-
*
|
|
310
|
-
* - `'busy'`
|
|
311
|
-
*/
|
|
312
|
-
readonly emitter?: PoolEmitter;
|
|
313
210
|
/**
|
|
314
211
|
* Pool type.
|
|
315
212
|
*
|
|
316
213
|
* If it is `'dynamic'`, it provides the `max` property.
|
|
317
214
|
*/
|
|
318
215
|
readonly type: PoolType;
|
|
319
|
-
/**
|
|
320
|
-
* Maximum number of workers that can be created by this pool.
|
|
321
|
-
*/
|
|
322
|
-
readonly max?: number;
|
|
323
216
|
/**
|
|
324
217
|
* Whether the pool is busy or not.
|
|
325
218
|
*
|
|
@@ -362,6 +255,109 @@ interface IPoolInternal<Worker extends IPoolWorker, Data = unknown, Response = u
|
|
|
362
255
|
*/
|
|
363
256
|
getWorkerAverageTasksRunTime(worker: Worker): number | undefined;
|
|
364
257
|
}
|
|
258
|
+
/**
|
|
259
|
+
* Enumeration of kill behaviors.
|
|
260
|
+
*/
|
|
261
|
+
declare const KillBehaviors: Readonly<{
|
|
262
|
+
readonly SOFT: "SOFT";
|
|
263
|
+
readonly HARD: "HARD";
|
|
264
|
+
}>;
|
|
265
|
+
/**
|
|
266
|
+
* Kill behavior.
|
|
267
|
+
*/
|
|
268
|
+
type KillBehavior = keyof typeof KillBehaviors;
|
|
269
|
+
/**
|
|
270
|
+
* Options for workers.
|
|
271
|
+
*/
|
|
272
|
+
interface WorkerOptions {
|
|
273
|
+
/**
|
|
274
|
+
* Maximum waiting time in milliseconds for tasks.
|
|
275
|
+
*
|
|
276
|
+
* After this time, newly created workers will be terminated.
|
|
277
|
+
* The last active time of your worker unit will be updated when a task is submitted to a worker or when a worker terminate a task.
|
|
278
|
+
*
|
|
279
|
+
* - If `killBehavior` is set to `KillBehaviors.HARD` this value represents also the timeout for the tasks that you submit to the pool,
|
|
280
|
+
* when this timeout expires your tasks is interrupted and the worker is killed if is not part of the minimum size of the pool.
|
|
281
|
+
* - If `killBehavior` is set to `KillBehaviors.SOFT` your tasks have no timeout and your workers will not be terminated until your task is completed.
|
|
282
|
+
*
|
|
283
|
+
* @default 60000 ms
|
|
284
|
+
*/
|
|
285
|
+
maxInactiveTime?: number;
|
|
286
|
+
/**
|
|
287
|
+
* Whether your worker will perform asynchronous or not.
|
|
288
|
+
*
|
|
289
|
+
* @default false
|
|
290
|
+
*/
|
|
291
|
+
async?: boolean;
|
|
292
|
+
/**
|
|
293
|
+
* `killBehavior` dictates if your async unit (worker/process) will be deleted in case that a task is active on it.
|
|
294
|
+
*
|
|
295
|
+
* - SOFT: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **won't** be deleted.
|
|
296
|
+
* - HARD: If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted.
|
|
297
|
+
*
|
|
298
|
+
* This option only apply to the newly created workers.
|
|
299
|
+
*
|
|
300
|
+
* @default KillBehaviors.SOFT
|
|
301
|
+
*/
|
|
302
|
+
killBehavior?: KillBehavior;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Make all properties in T non-readonly.
|
|
306
|
+
*/
|
|
307
|
+
type Draft<T> = {
|
|
308
|
+
-readonly [P in keyof T]?: T[P];
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* Message object that is passed between worker and main worker.
|
|
312
|
+
*/
|
|
313
|
+
interface MessageValue<Data = unknown, MainWorker extends ClusterWorker | MessagePort | unknown = unknown> {
|
|
314
|
+
/**
|
|
315
|
+
* Input data that will be passed to the worker.
|
|
316
|
+
*/
|
|
317
|
+
readonly data?: Data;
|
|
318
|
+
/**
|
|
319
|
+
* Id of the message.
|
|
320
|
+
*/
|
|
321
|
+
readonly id?: number;
|
|
322
|
+
/**
|
|
323
|
+
* Kill code.
|
|
324
|
+
*/
|
|
325
|
+
readonly kill?: KillBehavior | 1;
|
|
326
|
+
/**
|
|
327
|
+
* Error.
|
|
328
|
+
*/
|
|
329
|
+
readonly error?: string;
|
|
330
|
+
/**
|
|
331
|
+
* Task runtime.
|
|
332
|
+
*/
|
|
333
|
+
readonly taskRunTime?: number;
|
|
334
|
+
/**
|
|
335
|
+
* Reference to main worker.
|
|
336
|
+
*
|
|
337
|
+
* Only for internal use.
|
|
338
|
+
*/
|
|
339
|
+
readonly parent?: MainWorker;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* An object holding the worker that will be used to resolve/rejects the promise later on.
|
|
343
|
+
*
|
|
344
|
+
* @template Worker Type of worker.
|
|
345
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
346
|
+
*/
|
|
347
|
+
interface PromiseWorkerResponseWrapper<Worker extends IPoolWorker, Response = unknown> {
|
|
348
|
+
/**
|
|
349
|
+
* Resolve callback to fulfill the promise.
|
|
350
|
+
*/
|
|
351
|
+
readonly resolve: (value: Response) => void;
|
|
352
|
+
/**
|
|
353
|
+
* Reject callback to reject the promise.
|
|
354
|
+
*/
|
|
355
|
+
readonly reject: (reason?: string) => void;
|
|
356
|
+
/**
|
|
357
|
+
* The worker that has the assigned task.
|
|
358
|
+
*/
|
|
359
|
+
readonly worker: Worker;
|
|
360
|
+
}
|
|
365
361
|
/**
|
|
366
362
|
* The worker choice strategy context.
|
|
367
363
|
*
|
|
@@ -424,8 +420,6 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
424
420
|
readonly workersTasksUsage: Map<Worker, TasksUsage>;
|
|
425
421
|
/** @inheritDoc */
|
|
426
422
|
readonly emitter?: PoolEmitter;
|
|
427
|
-
/** @inheritDoc */
|
|
428
|
-
readonly max?: number;
|
|
429
423
|
/**
|
|
430
424
|
* The promise map.
|
|
431
425
|
*
|
|
@@ -594,12 +588,19 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
594
588
|
* @param taskRunTime Worker task runtime.
|
|
595
589
|
*/
|
|
596
590
|
private updateWorkerTasksRunTime;
|
|
591
|
+
/**
|
|
592
|
+
* Checks if the given worker is registered in the workers tasks usage map.
|
|
593
|
+
*
|
|
594
|
+
* @param worker Worker to check.
|
|
595
|
+
* @returns `true` if the worker is registered in the workers tasks usage map. `false` otherwise.
|
|
596
|
+
*/
|
|
597
|
+
private checkWorkerTasksUsage;
|
|
597
598
|
/**
|
|
598
599
|
* Initializes tasks usage statistics.
|
|
599
600
|
*
|
|
600
601
|
* @param worker The worker.
|
|
601
602
|
*/
|
|
602
|
-
initWorkerTasksUsage
|
|
603
|
+
private initWorkerTasksUsage;
|
|
603
604
|
/**
|
|
604
605
|
* Removes worker tasks usage statistics.
|
|
605
606
|
*
|
|
@@ -638,8 +639,8 @@ interface ClusterPoolOptions extends PoolOptions<Worker> {
|
|
|
638
639
|
*
|
|
639
640
|
* This pool selects the workers in a round robin fashion.
|
|
640
641
|
*
|
|
641
|
-
* @template
|
|
642
|
-
* @template
|
|
642
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
643
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
643
644
|
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
644
645
|
* @since 2.0.0
|
|
645
646
|
*/
|
|
@@ -678,13 +679,13 @@ declare class FixedClusterPool<Data = unknown, Response = unknown> extends Abstr
|
|
|
678
679
|
* This cluster pool creates new workers when the others are busy, up to the maximum number of workers.
|
|
679
680
|
* When the maximum number of workers is reached, an event is emitted. If you want to listen to this event, use the pool's `emitter`.
|
|
680
681
|
*
|
|
681
|
-
* @template
|
|
682
|
-
* @template
|
|
682
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
683
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
683
684
|
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
684
685
|
* @since 2.0.0
|
|
685
686
|
*/
|
|
686
687
|
declare class DynamicClusterPool<Data = unknown, Response = unknown> extends FixedClusterPool<Data, Response> {
|
|
687
|
-
readonly max: number;
|
|
688
|
+
protected readonly max: number;
|
|
688
689
|
/**
|
|
689
690
|
* Constructs a new poolifier dynamic cluster pool.
|
|
690
691
|
*
|
|
@@ -710,8 +711,8 @@ type ThreadWorkerWithMessageChannel = Worker$0 & Draft<MessageChannel>;
|
|
|
710
711
|
*
|
|
711
712
|
* This pool selects the threads in a round robin fashion.
|
|
712
713
|
*
|
|
713
|
-
* @template
|
|
714
|
-
* @template
|
|
714
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
715
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
715
716
|
* @author [Alessandro Pio Ardizio](https://github.com/pioardi)
|
|
716
717
|
* @since 0.0.1
|
|
717
718
|
*/
|
|
@@ -747,13 +748,13 @@ declare class FixedThreadPool<Data = unknown, Response = unknown> extends Abstra
|
|
|
747
748
|
* This thread pool creates new threads when the others are busy, up to the maximum number of threads.
|
|
748
749
|
* When the maximum number of threads is reached, an event is emitted. If you want to listen to this event, use the pool's `emitter`.
|
|
749
750
|
*
|
|
750
|
-
* @template
|
|
751
|
-
* @template
|
|
751
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
752
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
752
753
|
* @author [Alessandro Pio Ardizio](https://github.com/pioardi)
|
|
753
754
|
* @since 0.0.1
|
|
754
755
|
*/
|
|
755
756
|
declare class DynamicThreadPool<Data = unknown, Response = unknown> extends FixedThreadPool<Data, Response> {
|
|
756
|
-
readonly max: number;
|
|
757
|
+
protected readonly max: number;
|
|
757
758
|
/**
|
|
758
759
|
* Constructs a new poolifier dynamic thread pool.
|
|
759
760
|
*
|
|
@@ -820,7 +821,7 @@ declare abstract class AbstractWorker<MainWorker extends Worker | MessagePort, D
|
|
|
820
821
|
*/
|
|
821
822
|
protected abstract sendToMainWorker(message: MessageValue<Response>): void;
|
|
822
823
|
/**
|
|
823
|
-
*
|
|
824
|
+
* Checks if the worker should be terminated, because its living too long.
|
|
824
825
|
*/
|
|
825
826
|
protected checkAlive(): void;
|
|
826
827
|
/**
|
|
@@ -854,8 +855,8 @@ declare abstract class AbstractWorker<MainWorker extends Worker | MessagePort, D
|
|
|
854
855
|
* If you use a `DynamicClusterPool` the extra workers that were created will be terminated,
|
|
855
856
|
* but the minimum number of workers will be guaranteed.
|
|
856
857
|
*
|
|
857
|
-
* @template
|
|
858
|
-
* @template
|
|
858
|
+
* @template Data Type of data this worker receives from pool's execution. This can only be serializable data.
|
|
859
|
+
* @template Response Type of response the worker sends back to the main worker. This can only be serializable data.
|
|
859
860
|
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
860
861
|
* @since 2.0.0
|
|
861
862
|
*/
|
|
@@ -881,8 +882,8 @@ declare class ClusterWorker$0<Data = unknown, Response = unknown> extends Abstra
|
|
|
881
882
|
* If you use a `DynamicThreadPool` the extra workers that were created will be terminated,
|
|
882
883
|
* but the minimum number of workers will be guaranteed.
|
|
883
884
|
*
|
|
884
|
-
* @template
|
|
885
|
-
* @template
|
|
885
|
+
* @template Data Type of data this worker receives from pool's execution. This can only be serializable data.
|
|
886
|
+
* @template Response Type of response the worker sends back to the main thread. This can only be serializable data.
|
|
886
887
|
* @author [Alessandro Pio Ardizio](https://github.com/pioardi)
|
|
887
888
|
* @since 0.0.1
|
|
888
889
|
*/
|
|
@@ -897,5 +898,5 @@ declare class ThreadWorker<Data = unknown, Response = unknown> extends AbstractW
|
|
|
897
898
|
/** @inheritDoc */
|
|
898
899
|
protected sendToMainWorker(message: MessageValue<Response>): void;
|
|
899
900
|
}
|
|
900
|
-
export { DynamicClusterPool, FixedClusterPool, WorkerChoiceStrategies, DynamicThreadPool, FixedThreadPool,
|
|
901
|
-
export type { ClusterPoolOptions, IPool, PoolOptions, ErrorHandler, ExitHandler,
|
|
901
|
+
export { DynamicClusterPool, FixedClusterPool, WorkerChoiceStrategies, DynamicThreadPool, FixedThreadPool, ClusterWorker$0 as ClusterWorker, ThreadWorker, KillBehaviors };
|
|
902
|
+
export type { ClusterPoolOptions, IPool, PoolEmitter, PoolOptions, ErrorHandler, ExitHandler, MessageHandler, OnlineHandler, WorkerChoiceStrategy, ThreadWorkerWithMessageChannel, KillBehavior, WorkerOptions };
|
package/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var e,r=require("events"),t=require("cluster"),s=require("os"),o=require("worker_threads"),i=require("async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));class n extends r{}const a=()=>{},h=Object.freeze({SOFT:"SOFT",HARD:"HARD"});const k=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_RECENTLY_USED:"LESS_RECENTLY_USED",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class u{constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.requiredStatistics={runTime:!1}}}class c extends u{constructor(){super(...arguments),this.requiredStatistics={runTime:!0},this.workerLastVirtualTaskTimestamp=new Map}reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const t of this.pool.workers){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(Date.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(this.pool.getWorkerAverageTasksRunTime(e)??0)})}}class l extends u{reset(){return!0}choose(){let e,r=1/0;for(const t of this.pool.workers){const s=this.pool.getWorkerRunningTasks(t);if(!1===this.isDynamicPool&&0===s)return t;s<r&&(e=t,r=s)}return e}}class p extends u{constructor(){super(...arguments),this.nextWorkerIndex=0}reset(){return this.nextWorkerIndex=0,!0}choose(){const e=this.pool.workers[this.nextWorkerIndex];return this.nextWorkerIndex=this.nextWorkerIndex===this.pool.workers.length-1?0:this.nextWorkerIndex+1,e}}class g extends u{constructor(e){super(e),this.requiredStatistics={runTime:!0},this.currentWorkerIndex=0,this.workersTaskRunTime=new Map,this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerIndex=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.pool.workers[this.currentWorkerIndex];!0===this.isDynamicPool&&!1===this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerIndex=this.currentWorkerIndex===this.pool.workers.length-1?0:this.currentWorkerIndex+1,this.setWorkerTaskRunTime(this.pool.workers[this.currentWorkerIndex],t,0)),e}initWorkersTaskRunTime(){for(const e of this.pool.workers)this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.pool.getWorkerAverageTasksRunTime(e)}computeWorkerWeight(){let e=0;for(const r of s.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/s.cpus().length)}}class m{static getWorkerChoiceStrategy(e,r=k.ROUND_ROBIN){switch(r){case k.ROUND_ROBIN:return new p(e);case k.LESS_RECENTLY_USED:return new l(e);case k.FAIR_SHARE:return new c(e);case k.WEIGHTED_ROUND_ROBIN:return new g(e);default:throw new Error(`Worker choice strategy '${r}' not found`)}}}class W extends u{constructor(e,r,t=k.ROUND_ROBIN){super(e),this.createDynamicallyWorkerCallback=r,this.workerChoiceStrategy=m.getWorkerChoiceStrategy(this.pool,t),this.requiredStatistics=this.workerChoiceStrategy.requiredStatistics}reset(){return this.workerChoiceStrategy.reset()}choose(){const e=this.pool.findFreeWorker();return e||(!0===this.pool.busy?this.workerChoiceStrategy.choose():this.createDynamicallyWorkerCallback())}}class T{constructor(e,r,t=k.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=r,this.setWorkerChoiceStrategy(t)}getPoolWorkerChoiceStrategy(r=k.ROUND_ROBIN){return this.pool.type===e.DYNAMIC?new W(this.pool,this.createDynamicallyWorkerCallback,r):m.getWorkerChoiceStrategy(this.pool,r)}getWorkerChoiceStrategy(){return this.workerChoiceStrategy}setWorkerChoiceStrategy(e){this.workerChoiceStrategy?.reset(),this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}}class d{constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,this.workers=[],this.workersTasksUsage=new Map,this.promiseMap=new Map,this.nextMessageId=0,!this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();this.opts.enableEvents&&(this.emitter=new n),this.workerChoiceStrategyContext=new T(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=h.HARD,(r.kill===t||0===this.getWorkerRunningTasks(e))&&this.destroyWorker(e)})),e}),this.opts.workerChoiceStrategy)}checkFilePath(e){if(!e)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!1===Number.isSafeInteger(r))throw new Error("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new Error("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??k.ROUND_ROBIN,this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseMap.size}getWorkerIndex(e){return this.workers.indexOf(e)}getWorkerRunningTasks(e){return this.workersTasksUsage.get(e)?.running}getWorkerAverageTasksRunTime(e){return this.workersTasksUsage.get(e)?.avgRunTime}setWorkerChoiceStrategy(e){this.opts.workerChoiceStrategy=e;for(const e of this.workers)this.resetWorkerTasksUsage(e);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeWorker()}findFreeWorker(){for(const e of this.workers)if(0===this.getWorkerRunningTasks(e))return e;return!1}execute(e){const r=this.chooseWorker(),t=++this.nextMessageId,s=this.internalExecute(r,t);return this.checkAndEmitBusy(),e=e??{},this.sendToWorker(r,{data:e,id:t}),s}async destroy(){await Promise.all(this.workers.map((e=>this.destroyWorker(e))))}setupHook(){}beforePromiseWorkerResponseHook(e){this.increaseWorkerRunningTasks(e)}afterPromiseWorkerResponseHook(e,r){this.decreaseWorkerRunningTasks(r.worker),this.stepWorkerRunTasks(r.worker,1),this.updateWorkerTasksRunTime(r.worker,e.taskRunTime)}removeWorker(e){this.workers.splice(this.getWorkerIndex(e),1),this.removeWorkerTasksUsage(e)}chooseWorker(){return this.workerChoiceStrategyContext.execute()}internalExecute(e,r){return this.beforePromiseWorkerResponseHook(e),new Promise(((t,s)=>{this.promiseMap.set(r,{resolve:t,reject:s,worker:e})}))}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??a),e.on("error",this.opts.errorHandler??a),e.on("online",this.opts.onlineHandler??a),e.on("exit",this.opts.exitHandler??a),e.once("exit",(()=>this.removeWorker(e))),this.workers.push(e),this.initWorkerTasksUsage(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(void 0!==e.id){const r=this.promiseMap.get(e.id);void 0!==r&&(this.afterPromiseWorkerResponseHook(e,r),e.error?r.reject(e.error):r.resolve(e.data),this.promiseMap.delete(e.id))}}}checkAndEmitBusy(){this.opts.enableEvents&&this.busy&&this.emitter?.emit("busy")}increaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,1)}decreaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,-1)}stepWorkerRunningTasks(e,r){const t=this.workersTasksUsage.get(e);if(void 0===t)throw new Error("Worker could not be found in worker tasks usage map");t.running=t.running+r,this.workersTasksUsage.set(e,t)}stepWorkerRunTasks(e,r){const t=this.workersTasksUsage.get(e);if(void 0===t)throw new Error("Worker could not be found in worker tasks usage map");t.run=t.run+r,this.workersTasksUsage.set(e,t)}updateWorkerTasksRunTime(e,r){if(!0===this.workerChoiceStrategyContext.getWorkerChoiceStrategy().requiredStatistics.runTime){const t=this.workersTasksUsage.get(e);if(void 0===t)throw new Error("Worker could not be found in worker tasks usage map");t.runTime+=r??0,0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workersTasksUsage.set(e,t)}}initWorkerTasksUsage(e){this.workersTasksUsage.set(e,{run:0,running:0,runTime:0,avgRunTime:0})}removeWorkerTasksUsage(e){this.workersTasksUsage.delete(e)}resetWorkerTasksUsage(e){this.removeWorkerTasksUsage(e),this.initWorkerTasksUsage(e)}}class w extends d{constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){t.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return t.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return t.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}class y extends d{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}const R=h.SOFT;class f extends i.AsyncResource{constructor(e,r,t,s,o={killBehavior:R,maxInactiveTime:6e4}){super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.lastTaskTimestamp=Date.now(),!1===r&&(this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??6e4)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){void 0!==e.data&&void 0!==e.id?this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):void 0!==e.parent?this.mainWorker=e.parent:void 0!==e.kill&&(this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??6e4,this.opts.async=!!e.async}checkFunctionInput(e){if(!e)throw new Error("fn parameter is mandatory")}getMainWorker(){if(!this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){Date.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??6e4)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),o=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,taskRunTime:o})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{this.lastTaskTimestamp=Date.now()}}runAsync(e,r){const t=Date.now();e(r.data).then((e=>{const s=Date.now()-t;return this.sendToMainWorker({data:e,id:r.id,taskRunTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{this.lastTaskTimestamp=Date.now()})).catch(a)}}exports.AbstractWorker=f,exports.ClusterWorker=class extends f{constructor(e,r={}){super("worker-cluster-pool:poolifier",t.isPrimary,e,t.worker,r)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends w{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends y{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=w,exports.FixedThreadPool=y,exports.KillBehaviors=h,exports.ThreadWorker=class extends f{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=k;
|
|
1
|
+
"use strict";var e,r=require("cluster"),t=require("events"),s=require("os"),o=require("worker_threads"),i=require("async_hooks");!function(e){e.FIXED="fixed",e.DYNAMIC="dynamic"}(e||(e={}));const n=()=>{},a=Object.freeze({SOFT:"SOFT",HARD:"HARD"});class h extends t{}const k=Object.freeze({ROUND_ROBIN:"ROUND_ROBIN",LESS_RECENTLY_USED:"LESS_RECENTLY_USED",FAIR_SHARE:"FAIR_SHARE",WEIGHTED_ROUND_ROBIN:"WEIGHTED_ROUND_ROBIN"});class c{constructor(r){this.pool=r,this.isDynamicPool=this.pool.type===e.DYNAMIC,this.requiredStatistics={runTime:!1}}}class u extends c{constructor(){super(...arguments),this.requiredStatistics={runTime:!0},this.workerLastVirtualTaskTimestamp=new Map}reset(){return this.workerLastVirtualTaskTimestamp.clear(),!0}choose(){let e,r=1/0;for(const t of this.pool.workers){this.computeWorkerLastVirtualTaskTimestamp(t);const s=this.workerLastVirtualTaskTimestamp.get(t)?.end??0;s<r&&(r=s,e=t)}return e}computeWorkerLastVirtualTaskTimestamp(e){const r=Math.max(Date.now(),this.workerLastVirtualTaskTimestamp.get(e)?.end??-1/0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:r+(this.pool.getWorkerAverageTasksRunTime(e)??0)})}}class l extends c{reset(){return!0}choose(){let e,r=1/0;for(const t of this.pool.workers){const s=this.pool.getWorkerRunningTasks(t);if(!1===this.isDynamicPool&&0===s)return t;s<r&&(e=t,r=s)}return e}}class p extends c{constructor(){super(...arguments),this.nextWorkerIndex=0}reset(){return this.nextWorkerIndex=0,!0}choose(){const e=this.pool.workers[this.nextWorkerIndex];return this.nextWorkerIndex=this.nextWorkerIndex===this.pool.workers.length-1?0:this.nextWorkerIndex+1,e}}class g extends c{constructor(e){super(e),this.requiredStatistics={runTime:!0},this.currentWorkerIndex=0,this.workersTaskRunTime=new Map,this.defaultWorkerWeight=this.computeWorkerWeight(),this.initWorkersTaskRunTime()}reset(){return this.currentWorkerIndex=0,this.workersTaskRunTime.clear(),this.initWorkersTaskRunTime(),!0}choose(){const e=this.pool.workers[this.currentWorkerIndex];!0===this.isDynamicPool&&!1===this.workersTaskRunTime.has(e)&&this.initWorkerTaskRunTime(e);const r=this.workersTaskRunTime.get(e)?.runTime??0,t=this.workersTaskRunTime.get(e)?.weight??this.defaultWorkerWeight;return r<t?this.setWorkerTaskRunTime(e,t,r+(this.getWorkerVirtualTaskRunTime(e)??0)):(this.currentWorkerIndex=this.currentWorkerIndex===this.pool.workers.length-1?0:this.currentWorkerIndex+1,this.setWorkerTaskRunTime(this.pool.workers[this.currentWorkerIndex],t,0)),e}initWorkersTaskRunTime(){for(const e of this.pool.workers)this.initWorkerTaskRunTime(e)}initWorkerTaskRunTime(e){this.setWorkerTaskRunTime(e,this.defaultWorkerWeight,0)}setWorkerTaskRunTime(e,r,t){this.workersTaskRunTime.set(e,{weight:r,runTime:t})}getWorkerVirtualTaskRunTime(e){return this.pool.getWorkerAverageTasksRunTime(e)}computeWorkerWeight(){let e=0;for(const r of s.cpus()){const t=r.speed.toString().length-1;e+=1/(r.speed/Math.pow(10,t))*Math.pow(10,t)}return Math.round(e/s.cpus().length)}}class T{static getWorkerChoiceStrategy(e,r=k.ROUND_ROBIN){switch(r){case k.ROUND_ROBIN:return new p(e);case k.LESS_RECENTLY_USED:return new l(e);case k.FAIR_SHARE:return new u(e);case k.WEIGHTED_ROUND_ROBIN:return new g(e);default:throw new Error(`Worker choice strategy '${r}' not found`)}}}class W extends c{constructor(e,r,t=k.ROUND_ROBIN){super(e),this.createDynamicallyWorkerCallback=r,this.workerChoiceStrategy=T.getWorkerChoiceStrategy(this.pool,t),this.requiredStatistics=this.workerChoiceStrategy.requiredStatistics}reset(){return this.workerChoiceStrategy.reset()}choose(){const e=this.pool.findFreeWorker();return e||(!0===this.pool.busy?this.workerChoiceStrategy.choose():this.createDynamicallyWorkerCallback())}}class m{constructor(e,r,t=k.ROUND_ROBIN){this.pool=e,this.createDynamicallyWorkerCallback=r,this.setWorkerChoiceStrategy(t)}getPoolWorkerChoiceStrategy(r=k.ROUND_ROBIN){return this.pool.type===e.DYNAMIC?new W(this.pool,this.createDynamicallyWorkerCallback,r):T.getWorkerChoiceStrategy(this.pool,r)}getWorkerChoiceStrategy(){return this.workerChoiceStrategy}setWorkerChoiceStrategy(e){this.workerChoiceStrategy?.reset(),this.workerChoiceStrategy=this.getPoolWorkerChoiceStrategy(e)}execute(){return this.workerChoiceStrategy.choose()}}class d{constructor(e,r,t){if(this.numberOfWorkers=e,this.filePath=r,this.opts=t,this.workers=[],this.workersTasksUsage=new Map,this.promiseMap=new Map,this.nextMessageId=0,!1===this.isMain())throw new Error("Cannot start a pool from a worker!");this.checkNumberOfWorkers(this.numberOfWorkers),this.checkFilePath(this.filePath),this.checkPoolOptions(this.opts),this.setupHook();for(let e=1;e<=this.numberOfWorkers;e++)this.createAndSetupWorker();!0===this.opts.enableEvents&&(this.emitter=new h),this.workerChoiceStrategyContext=new m(this,(()=>{const e=this.createAndSetupWorker();return this.registerWorkerMessageListener(e,(r=>{var t;t=a.HARD,(r.kill===t||0===this.getWorkerRunningTasks(e))&&this.destroyWorker(e)})),e}),this.opts.workerChoiceStrategy)}checkFilePath(e){if(!e)throw new Error("Please specify a file with a worker implementation")}checkNumberOfWorkers(r){if(null==r)throw new Error("Cannot instantiate a pool without specifying the number of workers");if(!1===Number.isSafeInteger(r))throw new TypeError("Cannot instantiate a pool with a non integer number of workers");if(r<0)throw new RangeError("Cannot instantiate a pool with a negative number of workers");if(this.type===e.FIXED&&0===r)throw new Error("Cannot instantiate a fixed pool with no worker")}checkPoolOptions(e){this.opts.workerChoiceStrategy=e.workerChoiceStrategy??k.ROUND_ROBIN,this.opts.enableEvents=e.enableEvents??!0}get numberOfRunningTasks(){return this.promiseMap.size}getWorkerIndex(e){return this.workers.indexOf(e)}getWorkerRunningTasks(e){return this.workersTasksUsage.get(e)?.running}getWorkerAverageTasksRunTime(e){return this.workersTasksUsage.get(e)?.avgRunTime}setWorkerChoiceStrategy(e){this.opts.workerChoiceStrategy=e;for(const e of this.workers)this.resetWorkerTasksUsage(e);this.workerChoiceStrategyContext.setWorkerChoiceStrategy(e)}internalGetBusyStatus(){return this.numberOfRunningTasks>=this.numberOfWorkers&&!1===this.findFreeWorker()}findFreeWorker(){for(const e of this.workers)if(0===this.getWorkerRunningTasks(e))return e;return!1}execute(e){const r=this.chooseWorker(),t=this.internalExecute(r,this.nextMessageId);return this.checkAndEmitBusy(),this.sendToWorker(r,{data:e??{},id:this.nextMessageId}),++this.nextMessageId,t}async destroy(){await Promise.all(this.workers.map((e=>this.destroyWorker(e))))}setupHook(){}beforePromiseWorkerResponseHook(e){this.increaseWorkerRunningTasks(e)}afterPromiseWorkerResponseHook(e,r){this.decreaseWorkerRunningTasks(r.worker),this.stepWorkerRunTasks(r.worker,1),this.updateWorkerTasksRunTime(r.worker,e.taskRunTime)}removeWorker(e){this.workers.splice(this.getWorkerIndex(e),1),this.removeWorkerTasksUsage(e)}chooseWorker(){return this.workerChoiceStrategyContext.execute()}internalExecute(e,r){return this.beforePromiseWorkerResponseHook(e),new Promise(((t,s)=>{this.promiseMap.set(r,{resolve:t,reject:s,worker:e})}))}createAndSetupWorker(){const e=this.createWorker();return e.on("message",this.opts.messageHandler??n),e.on("error",this.opts.errorHandler??n),e.on("online",this.opts.onlineHandler??n),e.on("exit",this.opts.exitHandler??n),e.once("exit",(()=>this.removeWorker(e))),this.workers.push(e),this.initWorkerTasksUsage(e),this.afterWorkerSetup(e),e}workerListener(){return e=>{if(void 0!==e.id){const r=this.promiseMap.get(e.id);void 0!==r&&(e.error?r.reject(e.error):r.resolve(e.data),this.afterPromiseWorkerResponseHook(e,r),this.promiseMap.delete(e.id))}}}checkAndEmitBusy(){!0===this.opts.enableEvents&&!0===this.busy&&this.emitter?.emit("busy")}increaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,1)}decreaseWorkerRunningTasks(e){this.stepWorkerRunningTasks(e,-1)}stepWorkerRunningTasks(e,r){if(!0===this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.running=t.running+r,this.workersTasksUsage.set(e,t)}}stepWorkerRunTasks(e,r){if(!0===this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.run=t.run+r,this.workersTasksUsage.set(e,t)}}updateWorkerTasksRunTime(e,r){if(!0===this.workerChoiceStrategyContext.getWorkerChoiceStrategy().requiredStatistics.runTime&&!0===this.checkWorkerTasksUsage(e)){const t=this.workersTasksUsage.get(e);t.runTime+=r??0,0!==t.run&&(t.avgRunTime=t.runTime/t.run),this.workersTasksUsage.set(e,t)}}checkWorkerTasksUsage(e){const r=this.workersTasksUsage.has(e);if(!1===r)throw new Error("Worker could not be found in workers tasks usage map");return r}initWorkerTasksUsage(e){this.workersTasksUsage.set(e,{run:0,running:0,runTime:0,avgRunTime:0})}removeWorkerTasksUsage(e){this.workersTasksUsage.delete(e)}resetWorkerTasksUsage(e){this.removeWorkerTasksUsage(e),this.initWorkerTasksUsage(e)}}class w extends d{constructor(e,r,t={}){super(e,r,t),this.opts=t}setupHook(){r.setupPrimary({...this.opts.settings,exec:this.filePath})}isMain(){return r.isPrimary}destroyWorker(e){this.sendToWorker(e,{kill:1}),e.kill()}sendToWorker(e,r){e.send(r)}registerWorkerMessageListener(e,r){e.on("message",r)}createWorker(){return r.fork(this.opts.env)}afterWorkerSetup(e){this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}class y extends d{constructor(e,r,t={}){super(e,r,t)}isMain(){return o.isMainThread}async destroyWorker(e){this.sendToWorker(e,{kill:1}),await e.terminate()}sendToWorker(e,r){e.postMessage(r)}registerWorkerMessageListener(e,r){e.port2?.on("message",r)}createWorker(){return new o.Worker(this.filePath,{env:o.SHARE_ENV})}afterWorkerSetup(e){const{port1:r,port2:t}=new o.MessageChannel;e.postMessage({parent:r},[r]),e.port1=r,e.port2=t,this.registerWorkerMessageListener(e,super.workerListener())}get type(){return e.FIXED}get busy(){return this.internalGetBusyStatus()}}const R=a.SOFT;class f extends i.AsyncResource{constructor(e,r,t,s,o={killBehavior:R,maxInactiveTime:6e4}){super(e),this.mainWorker=s,this.opts=o,this.checkFunctionInput(t),this.checkWorkerOptions(this.opts),this.lastTaskTimestamp=Date.now(),!1===r&&(this.aliveInterval=setInterval(this.checkAlive.bind(this),(this.opts.maxInactiveTime??6e4)/2),this.checkAlive.bind(this)()),this.mainWorker?.on("message",(e=>{this.messageListener(e,t)}))}messageListener(e,r){void 0!==e.data&&void 0!==e.id?!0===this.opts.async?this.runInAsyncScope(this.runAsync.bind(this),this,r,e):this.runInAsyncScope(this.run.bind(this),this,r,e):void 0!==e.parent?this.mainWorker=e.parent:void 0!==e.kill&&(this.aliveInterval&&clearInterval(this.aliveInterval),this.emitDestroy())}checkWorkerOptions(e){this.opts.killBehavior=e.killBehavior??R,this.opts.maxInactiveTime=e.maxInactiveTime??6e4,this.opts.async=!!e.async}checkFunctionInput(e){if(!e)throw new Error("fn parameter is mandatory")}getMainWorker(){if(!this.mainWorker)throw new Error("Main worker was not set");return this.mainWorker}checkAlive(){Date.now()-this.lastTaskTimestamp>(this.opts.maxInactiveTime??6e4)&&this.sendToMainWorker({kill:this.opts.killBehavior})}handleError(e){return e}run(e,r){try{const t=Date.now(),s=e(r.data),o=Date.now()-t;this.sendToMainWorker({data:s,id:r.id,taskRunTime:o})}catch(e){const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})}finally{this.lastTaskTimestamp=Date.now()}}runAsync(e,r){const t=Date.now();e(r.data).then((e=>{const s=Date.now()-t;return this.sendToMainWorker({data:e,id:r.id,taskRunTime:s}),null})).catch((e=>{const t=this.handleError(e);this.sendToMainWorker({error:t,id:r.id})})).finally((()=>{this.lastTaskTimestamp=Date.now()})).catch(n)}}exports.ClusterWorker=class extends f{constructor(e,t={}){super("worker-cluster-pool:poolifier",r.isPrimary,e,r.worker,t)}sendToMainWorker(e){this.getMainWorker().send(e)}handleError(e){return e instanceof Error?e.message:e}},exports.DynamicClusterPool=class extends w{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.DynamicThreadPool=class extends y{constructor(e,r,t,s={}){super(e,t,s),this.max=r}get type(){return e.DYNAMIC}get busy(){return this.workers.length===this.max}},exports.FixedClusterPool=w,exports.FixedThreadPool=y,exports.KillBehaviors=a,exports.ThreadWorker=class extends f{constructor(e,r={}){super("worker-thread-pool:poolifier",o.isMainThread,e,o.parentPort,r)}sendToMainWorker(e){this.getMainWorker().postMessage(e)}},exports.WorkerChoiceStrategies=k;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poolifier",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.6",
|
|
4
4
|
"description": "A fast, easy to use Node.js Worker Thread Pool and Cluster Pool implementation",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"benchmark": "npm run build && node -r source-map-support/register benchmarks/internal/bench.js",
|
|
12
12
|
"benchmark:debug": "npm run build && node -r source-map-support/register --inspect benchmarks/internal/bench.js",
|
|
13
13
|
"benchmark:prod": "npm run build:prod && node -r source-map-support/register benchmarks/internal/bench.js",
|
|
14
|
-
"test": "npm run build &&
|
|
14
|
+
"test": "npm run build && c8 mocha 'tests/**/*.test.js'",
|
|
15
15
|
"test:debug": "npm run build && mocha --no-parallel --inspect 'tests/**/*.test.js'",
|
|
16
|
-
"coverage": "
|
|
17
|
-
"coverage:html": "
|
|
16
|
+
"coverage": "c8 report --reporter=lcov",
|
|
17
|
+
"coverage:html": "c8 report --reporter=html",
|
|
18
18
|
"format": "prettier --loglevel silent --write .; prettierx --write .",
|
|
19
19
|
"lint": "eslint . --cache",
|
|
20
20
|
"lint:fix": "eslint . --cache --fix",
|
|
@@ -66,33 +66,32 @@
|
|
|
66
66
|
],
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@release-it/keep-a-changelog": "^3.1.0",
|
|
69
|
-
"@types/node": "^18.11.
|
|
70
|
-
"@typescript-eslint/eslint-plugin": "^5.40.
|
|
71
|
-
"@typescript-eslint/parser": "^5.40.
|
|
69
|
+
"@types/node": "^18.11.3",
|
|
70
|
+
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
|
71
|
+
"@typescript-eslint/parser": "^5.40.1",
|
|
72
72
|
"benchmark": "^2.1.4",
|
|
73
|
+
"c8": "^7.12.0",
|
|
73
74
|
"eslint": "^8.25.0",
|
|
74
75
|
"eslint-config-standard": "^17.0.0",
|
|
75
76
|
"eslint-define-config": "^1.7.0",
|
|
76
|
-
"eslint-import-resolver-typescript": "^3.5.
|
|
77
|
+
"eslint-import-resolver-typescript": "^3.5.2",
|
|
77
78
|
"eslint-plugin-import": "^2.26.0",
|
|
78
|
-
"eslint-plugin-jsdoc": "^39.3.
|
|
79
|
+
"eslint-plugin-jsdoc": "^39.3.14",
|
|
79
80
|
"eslint-plugin-n": "^15.3.0",
|
|
80
|
-
"eslint-plugin-node": "^11.1.0",
|
|
81
81
|
"eslint-plugin-prettierx": "^0.18.0",
|
|
82
|
-
"eslint-plugin-promise": "^6.1.
|
|
82
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
83
83
|
"eslint-plugin-spellcheck": "^0.0.19",
|
|
84
|
-
"expect": "^29.2.
|
|
84
|
+
"expect": "^29.2.1",
|
|
85
85
|
"husky": "^8.0.1",
|
|
86
86
|
"lint-staged": "^13.0.3",
|
|
87
87
|
"microtime": "^3.1.1",
|
|
88
88
|
"mocha": "^10.1.0",
|
|
89
89
|
"mochawesome": "^7.1.3",
|
|
90
|
-
"nyc": "^15.1.0",
|
|
91
90
|
"prettier": "^2.7.1",
|
|
92
91
|
"prettier-plugin-organize-imports": "^3.1.1",
|
|
93
92
|
"prettierx": "^0.18.3",
|
|
94
93
|
"release-it": "^15.5.0",
|
|
95
|
-
"rollup": "^3.2.
|
|
94
|
+
"rollup": "^3.2.3",
|
|
96
95
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
97
96
|
"rollup-plugin-command": "^1.1.3",
|
|
98
97
|
"rollup-plugin-delete": "^2.0.0",
|
|
@@ -101,7 +100,7 @@
|
|
|
101
100
|
"rollup-plugin-ts": "^3.0.2",
|
|
102
101
|
"sinon": "^14.0.1",
|
|
103
102
|
"source-map-support": "^0.5.21",
|
|
104
|
-
"typedoc": "^0.23.
|
|
103
|
+
"typedoc": "^0.23.17",
|
|
105
104
|
"typescript": "^4.8.4"
|
|
106
105
|
},
|
|
107
106
|
"engines": {
|