poolifier 2.3.3 → 2.3.5
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 +140 -133
- package/lib/index.js +1 -1
- package/package.json +14 -12
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,14 +207,6 @@ 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
|
*
|
|
@@ -362,6 +259,109 @@ interface IPoolInternal<Worker extends IPoolWorker, Data = unknown, Response = u
|
|
|
362
259
|
*/
|
|
363
260
|
getWorkerAverageTasksRunTime(worker: Worker): number | undefined;
|
|
364
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Enumeration of kill behaviors.
|
|
264
|
+
*/
|
|
265
|
+
declare const KillBehaviors: Readonly<{
|
|
266
|
+
readonly SOFT: "SOFT";
|
|
267
|
+
readonly HARD: "HARD";
|
|
268
|
+
}>;
|
|
269
|
+
/**
|
|
270
|
+
* Kill behavior.
|
|
271
|
+
*/
|
|
272
|
+
type KillBehavior = keyof typeof KillBehaviors;
|
|
273
|
+
/**
|
|
274
|
+
* Options for workers.
|
|
275
|
+
*/
|
|
276
|
+
interface WorkerOptions {
|
|
277
|
+
/**
|
|
278
|
+
* Maximum waiting time in milliseconds for tasks.
|
|
279
|
+
*
|
|
280
|
+
* After this time, newly created workers will be terminated.
|
|
281
|
+
* 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.
|
|
282
|
+
*
|
|
283
|
+
* - If `killBehavior` is set to `KillBehaviors.HARD` this value represents also the timeout for the tasks that you submit to the pool,
|
|
284
|
+
* when this timeout expires your tasks is interrupted and the worker is killed if is not part of the minimum size of the pool.
|
|
285
|
+
* - If `killBehavior` is set to `KillBehaviors.SOFT` your tasks have no timeout and your workers will not be terminated until your task is completed.
|
|
286
|
+
*
|
|
287
|
+
* @default 60000 ms
|
|
288
|
+
*/
|
|
289
|
+
maxInactiveTime?: number;
|
|
290
|
+
/**
|
|
291
|
+
* Whether your worker will perform asynchronous or not.
|
|
292
|
+
*
|
|
293
|
+
* @default false
|
|
294
|
+
*/
|
|
295
|
+
async?: boolean;
|
|
296
|
+
/**
|
|
297
|
+
* `killBehavior` dictates if your async unit (worker/process) will be deleted in case that a task is active on it.
|
|
298
|
+
*
|
|
299
|
+
* - SOFT: If `currentTime - lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker **won't** be deleted.
|
|
300
|
+
* - HARD: If `lastActiveTime` is greater than `maxInactiveTime` but a task is still running, then the worker will be deleted.
|
|
301
|
+
*
|
|
302
|
+
* This option only apply to the newly created workers.
|
|
303
|
+
*
|
|
304
|
+
* @default KillBehaviors.SOFT
|
|
305
|
+
*/
|
|
306
|
+
killBehavior?: KillBehavior;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Make all properties in T non-readonly.
|
|
310
|
+
*/
|
|
311
|
+
type Draft<T> = {
|
|
312
|
+
-readonly [P in keyof T]?: T[P];
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Message object that is passed between worker and main worker.
|
|
316
|
+
*/
|
|
317
|
+
interface MessageValue<Data = unknown, MainWorker extends ClusterWorker | MessagePort | unknown = unknown> {
|
|
318
|
+
/**
|
|
319
|
+
* Input data that will be passed to the worker.
|
|
320
|
+
*/
|
|
321
|
+
readonly data?: Data;
|
|
322
|
+
/**
|
|
323
|
+
* Id of the message.
|
|
324
|
+
*/
|
|
325
|
+
readonly id?: number;
|
|
326
|
+
/**
|
|
327
|
+
* Kill code.
|
|
328
|
+
*/
|
|
329
|
+
readonly kill?: KillBehavior | 1;
|
|
330
|
+
/**
|
|
331
|
+
* Error.
|
|
332
|
+
*/
|
|
333
|
+
readonly error?: string;
|
|
334
|
+
/**
|
|
335
|
+
* Task runtime.
|
|
336
|
+
*/
|
|
337
|
+
readonly taskRunTime?: number;
|
|
338
|
+
/**
|
|
339
|
+
* Reference to main worker.
|
|
340
|
+
*
|
|
341
|
+
* Only for internal use.
|
|
342
|
+
*/
|
|
343
|
+
readonly parent?: MainWorker;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* An object holding the worker that will be used to resolve/rejects the promise later on.
|
|
347
|
+
*
|
|
348
|
+
* @template Worker Type of worker.
|
|
349
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
350
|
+
*/
|
|
351
|
+
interface PromiseWorkerResponseWrapper<Worker extends IPoolWorker, Response = unknown> {
|
|
352
|
+
/**
|
|
353
|
+
* Resolve callback to fulfill the promise.
|
|
354
|
+
*/
|
|
355
|
+
readonly resolve: (value: Response) => void;
|
|
356
|
+
/**
|
|
357
|
+
* Reject callback to reject the promise.
|
|
358
|
+
*/
|
|
359
|
+
readonly reject: (reason?: string) => void;
|
|
360
|
+
/**
|
|
361
|
+
* The worker that has the assigned task.
|
|
362
|
+
*/
|
|
363
|
+
readonly worker: Worker;
|
|
364
|
+
}
|
|
365
365
|
/**
|
|
366
366
|
* The worker choice strategy context.
|
|
367
367
|
*
|
|
@@ -594,6 +594,13 @@ declare abstract class AbstractPool<Worker extends IPoolWorker, Data = unknown,
|
|
|
594
594
|
* @param taskRunTime Worker task runtime.
|
|
595
595
|
*/
|
|
596
596
|
private updateWorkerTasksRunTime;
|
|
597
|
+
/**
|
|
598
|
+
* Checks if the given worker is registered in the workers tasks usage map.
|
|
599
|
+
*
|
|
600
|
+
* @param worker Worker to check.
|
|
601
|
+
* @returns `true` if the worker is registered in the workers tasks usage map. `false` otherwise.
|
|
602
|
+
*/
|
|
603
|
+
private checkWorkerTasksUsage;
|
|
597
604
|
/**
|
|
598
605
|
* Initializes tasks usage statistics.
|
|
599
606
|
*
|
|
@@ -638,8 +645,8 @@ interface ClusterPoolOptions extends PoolOptions<Worker> {
|
|
|
638
645
|
*
|
|
639
646
|
* This pool selects the workers in a round robin fashion.
|
|
640
647
|
*
|
|
641
|
-
* @template
|
|
642
|
-
* @template
|
|
648
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
649
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
643
650
|
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
644
651
|
* @since 2.0.0
|
|
645
652
|
*/
|
|
@@ -678,8 +685,8 @@ declare class FixedClusterPool<Data = unknown, Response = unknown> extends Abstr
|
|
|
678
685
|
* This cluster pool creates new workers when the others are busy, up to the maximum number of workers.
|
|
679
686
|
* 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
687
|
*
|
|
681
|
-
* @template
|
|
682
|
-
* @template
|
|
688
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
689
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
683
690
|
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
684
691
|
* @since 2.0.0
|
|
685
692
|
*/
|
|
@@ -710,8 +717,8 @@ type ThreadWorkerWithMessageChannel = Worker$0 & Draft<MessageChannel>;
|
|
|
710
717
|
*
|
|
711
718
|
* This pool selects the threads in a round robin fashion.
|
|
712
719
|
*
|
|
713
|
-
* @template
|
|
714
|
-
* @template
|
|
720
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
721
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
715
722
|
* @author [Alessandro Pio Ardizio](https://github.com/pioardi)
|
|
716
723
|
* @since 0.0.1
|
|
717
724
|
*/
|
|
@@ -747,8 +754,8 @@ declare class FixedThreadPool<Data = unknown, Response = unknown> extends Abstra
|
|
|
747
754
|
* This thread pool creates new threads when the others are busy, up to the maximum number of threads.
|
|
748
755
|
* 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
756
|
*
|
|
750
|
-
* @template
|
|
751
|
-
* @template
|
|
757
|
+
* @template Data Type of data sent to the worker. This can only be serializable data.
|
|
758
|
+
* @template Response Type of response of execution. This can only be serializable data.
|
|
752
759
|
* @author [Alessandro Pio Ardizio](https://github.com/pioardi)
|
|
753
760
|
* @since 0.0.1
|
|
754
761
|
*/
|
|
@@ -820,7 +827,7 @@ declare abstract class AbstractWorker<MainWorker extends Worker | MessagePort, D
|
|
|
820
827
|
*/
|
|
821
828
|
protected abstract sendToMainWorker(message: MessageValue<Response>): void;
|
|
822
829
|
/**
|
|
823
|
-
*
|
|
830
|
+
* Checks if the worker should be terminated, because its living too long.
|
|
824
831
|
*/
|
|
825
832
|
protected checkAlive(): void;
|
|
826
833
|
/**
|
|
@@ -854,8 +861,8 @@ declare abstract class AbstractWorker<MainWorker extends Worker | MessagePort, D
|
|
|
854
861
|
* If you use a `DynamicClusterPool` the extra workers that were created will be terminated,
|
|
855
862
|
* but the minimum number of workers will be guaranteed.
|
|
856
863
|
*
|
|
857
|
-
* @template
|
|
858
|
-
* @template
|
|
864
|
+
* @template Data Type of data this worker receives from pool's execution. This can only be serializable data.
|
|
865
|
+
* @template Response Type of response the worker sends back to the main worker. This can only be serializable data.
|
|
859
866
|
* @author [Christopher Quadflieg](https://github.com/Shinigami92)
|
|
860
867
|
* @since 2.0.0
|
|
861
868
|
*/
|
|
@@ -881,8 +888,8 @@ declare class ClusterWorker$0<Data = unknown, Response = unknown> extends Abstra
|
|
|
881
888
|
* If you use a `DynamicThreadPool` the extra workers that were created will be terminated,
|
|
882
889
|
* but the minimum number of workers will be guaranteed.
|
|
883
890
|
*
|
|
884
|
-
* @template
|
|
885
|
-
* @template
|
|
891
|
+
* @template Data Type of data this worker receives from pool's execution. This can only be serializable data.
|
|
892
|
+
* @template Response Type of response the worker sends back to the main thread. This can only be serializable data.
|
|
886
893
|
* @author [Alessandro Pio Ardizio](https://github.com/pioardi)
|
|
887
894
|
* @since 0.0.1
|
|
888
895
|
*/
|
|
@@ -897,5 +904,5 @@ declare class ThreadWorker<Data = unknown, Response = unknown> extends AbstractW
|
|
|
897
904
|
/** @inheritDoc */
|
|
898
905
|
protected sendToMainWorker(message: MessageValue<Response>): void;
|
|
899
906
|
}
|
|
900
|
-
export { DynamicClusterPool, FixedClusterPool, WorkerChoiceStrategies, DynamicThreadPool, FixedThreadPool,
|
|
901
|
-
export type { ClusterPoolOptions, IPool, PoolOptions, ErrorHandler, ExitHandler,
|
|
907
|
+
export { DynamicClusterPool, FixedClusterPool, WorkerChoiceStrategies, DynamicThreadPool, FixedThreadPool, ClusterWorker$0 as ClusterWorker, ThreadWorker, KillBehaviors };
|
|
908
|
+
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),t=r+(this.pool.getWorkerAverageTasksRunTime(e)??0);this.workerLastVirtualTaskTimestamp.set(e,{start:r,end:t})}}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 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.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.5",
|
|
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": {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"lint": "eslint . --cache",
|
|
20
20
|
"lint:fix": "eslint . --cache --fix",
|
|
21
21
|
"lint:report": "eslint . --cache --format json --output-file reports/eslint.json",
|
|
22
|
+
"release": "release-it",
|
|
22
23
|
"typedoc": "typedoc",
|
|
23
24
|
"sonar:properties": "./updateSonarProps.sh",
|
|
24
25
|
"prepublishOnly": "npm run build:prod"
|
|
@@ -64,32 +65,33 @@
|
|
|
64
65
|
"lib"
|
|
65
66
|
],
|
|
66
67
|
"devDependencies": {
|
|
67
|
-
"@
|
|
68
|
-
"@
|
|
69
|
-
"@typescript-eslint/
|
|
68
|
+
"@release-it/keep-a-changelog": "^3.1.0",
|
|
69
|
+
"@types/node": "^18.11.3",
|
|
70
|
+
"@typescript-eslint/eslint-plugin": "^5.40.1",
|
|
71
|
+
"@typescript-eslint/parser": "^5.40.1",
|
|
70
72
|
"benchmark": "^2.1.4",
|
|
71
73
|
"eslint": "^8.25.0",
|
|
72
74
|
"eslint-config-standard": "^17.0.0",
|
|
73
75
|
"eslint-define-config": "^1.7.0",
|
|
74
|
-
"eslint-import-resolver-typescript": "^3.5.
|
|
76
|
+
"eslint-import-resolver-typescript": "^3.5.2",
|
|
75
77
|
"eslint-plugin-import": "^2.26.0",
|
|
76
|
-
"eslint-plugin-jsdoc": "^39.3.
|
|
78
|
+
"eslint-plugin-jsdoc": "^39.3.14",
|
|
77
79
|
"eslint-plugin-n": "^15.3.0",
|
|
78
|
-
"eslint-plugin-node": "^11.1.0",
|
|
79
80
|
"eslint-plugin-prettierx": "^0.18.0",
|
|
80
|
-
"eslint-plugin-promise": "^6.1.
|
|
81
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
81
82
|
"eslint-plugin-spellcheck": "^0.0.19",
|
|
82
|
-
"expect": "^29.2.
|
|
83
|
+
"expect": "^29.2.1",
|
|
83
84
|
"husky": "^8.0.1",
|
|
84
85
|
"lint-staged": "^13.0.3",
|
|
85
86
|
"microtime": "^3.1.1",
|
|
86
|
-
"mocha": "^10.
|
|
87
|
+
"mocha": "^10.1.0",
|
|
87
88
|
"mochawesome": "^7.1.3",
|
|
88
89
|
"nyc": "^15.1.0",
|
|
89
90
|
"prettier": "^2.7.1",
|
|
90
91
|
"prettier-plugin-organize-imports": "^3.1.1",
|
|
91
92
|
"prettierx": "^0.18.3",
|
|
92
|
-
"
|
|
93
|
+
"release-it": "^15.5.0",
|
|
94
|
+
"rollup": "^3.2.3",
|
|
93
95
|
"rollup-plugin-analyzer": "^4.0.0",
|
|
94
96
|
"rollup-plugin-command": "^1.1.3",
|
|
95
97
|
"rollup-plugin-delete": "^2.0.0",
|
|
@@ -98,7 +100,7 @@
|
|
|
98
100
|
"rollup-plugin-ts": "^3.0.2",
|
|
99
101
|
"sinon": "^14.0.1",
|
|
100
102
|
"source-map-support": "^0.5.21",
|
|
101
|
-
"typedoc": "^0.23.
|
|
103
|
+
"typedoc": "^0.23.17",
|
|
102
104
|
"typescript": "^4.8.4"
|
|
103
105
|
},
|
|
104
106
|
"engines": {
|