Commit | Line | Data |
---|---|---|
c97c7edb | 1 | import type { Worker } from 'cluster' |
4f7fa42a | 2 | import type { JSONValue } from '../../utility-types' |
c97c7edb | 3 | import type { ClusterPoolOptions } from './fixed' |
325f50bc | 4 | import { FixedClusterPool } from './fixed' |
4c35177b | 5 | import { killBehaviorEnumeration } from '../../worker/worker-options' |
f045358d | 6 | |
4ade5f1f | 7 | /** |
729c563d | 8 | * A cluster pool with a dynamic number of workers, but a guaranteed minimum number of workers. |
4ade5f1f | 9 | * |
729c563d S |
10 | * This cluster pool creates new workers when the others are busy, up to the maximum number of workers. |
11 | * 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`. | |
12 | * | |
13 | * @template Data Type of data sent to the worker. | |
14 | * @template Response Type of response of execution. | |
4ade5f1f | 15 | * |
325f50bc S |
16 | * @author [Christopher Quadflieg](https://github.com/Shinigami92) |
17 | * @since 2.0.0 | |
4ade5f1f | 18 | */ |
325f50bc | 19 | export class DynamicClusterPool< |
d3c8a1a8 S |
20 | Data extends JSONValue = JSONValue, |
21 | Response extends JSONValue = JSONValue | |
325f50bc | 22 | > extends FixedClusterPool<Data, Response> { |
4ade5f1f | 23 | /** |
729c563d S |
24 | * Constructs a new poolifier dynamic cluster pool. |
25 | * | |
26 | * @param min Minimum number of workers which are always active. | |
27 | * @param max Maximum number of workers that can be created by this pool. | |
28 | * @param filename Path to an implementation of a `ClusterWorker` file, which can be relative or absolute. | |
29 | * @param opts Options for this fixed cluster pool. Default: `{ maxTasks: 1000 }` | |
4ade5f1f S |
30 | */ |
31 | public constructor ( | |
c97c7edb | 32 | min: number, |
4ade5f1f | 33 | public readonly max: number, |
c97c7edb S |
34 | filename: string, |
35 | opts: ClusterPoolOptions = { maxTasks: 1000 } | |
4ade5f1f S |
36 | ) { |
37 | super(min, filename, opts) | |
4ade5f1f S |
38 | } |
39 | ||
729c563d S |
40 | /** |
41 | * Choose a worker for the next task. | |
42 | * | |
43 | * It will first check for and return an idle worker. | |
44 | * If all workers are busy, then it will try to create a new one up to the `max` worker count. | |
45 | * If the max worker count is reached, the emitter will emit a `FullPool` event and it will fall back to using a round robin algorithm to distribute the load. | |
50eceb07 S |
46 | * |
47 | * @returns Cluster worker. | |
729c563d | 48 | */ |
c97c7edb | 49 | protected chooseWorker (): Worker { |
4f7fa42a S |
50 | for (const [worker, numberOfTasks] of this.tasks) { |
51 | if (numberOfTasks === 0) { | |
52 | // A worker is free, use it | |
53 | return worker | |
4ade5f1f S |
54 | } |
55 | } | |
56 | ||
4f7fa42a S |
57 | if (this.workers.length === this.max) { |
58 | this.emitter.emit('FullPool') | |
59 | return super.chooseWorker() | |
4ade5f1f | 60 | } |
4f7fa42a S |
61 | |
62 | // All workers are busy, create a new worker | |
63 | const worker = this.createAndSetupWorker() | |
64 | this.registerWorkerMessageListener<Data>(worker, message => { | |
c01733f1 | 65 | const tasksInProgress = this.tasks.get(worker) |
4c35177b | 66 | const isKillBehaviorOptionHard = |
67 | message.kill === killBehaviorEnumeration.HARD | |
68 | if (isKillBehaviorOptionHard || tasksInProgress === 0) { | |
d63d3be3 | 69 | // Kill received from the worker, means that no new tasks are submitted to that worker for a while ( > maxInactiveTime) |
4f7fa42a S |
70 | this.sendToWorker(worker, { kill: 1 }) |
71 | void this.destroyWorker(worker) | |
72 | } | |
73 | }) | |
74 | return worker | |
4ade5f1f S |
75 | } |
76 | } |