build(deps-dev): apply updates
[poolifier.git] / src / pools / abstract-pool.ts
index 95d3970fdbd859f71ce67e5dd4b779bc6afeddcc..712de0b7da4c38c2808e1be61da7fced8e372da0 100644 (file)
@@ -4,11 +4,12 @@ import { EventEmitterAsyncResource } from 'node:events'
 import { performance } from 'node:perf_hooks'
 import type { TransferListItem } from 'node:worker_threads'
 
+import { defaultBucketSize } from '../priority-queue.js'
 import type {
   MessageValue,
   PromiseResponseWrapper,
   Task,
-  TaskFunctionProperties
+  TaskFunctionProperties,
 } from '../utility-types.js'
 import {
   average,
@@ -22,11 +23,11 @@ import {
   median,
   min,
   round,
-  sleep
+  sleep,
 } from '../utils.js'
 import type {
   TaskFunction,
-  TaskFunctionObject
+  TaskFunctionObject,
 } from '../worker/task-functions.js'
 import { KillBehaviors } from '../worker/worker-options.js'
 import {
@@ -36,13 +37,13 @@ import {
   type PoolOptions,
   type PoolType,
   PoolTypes,
-  type TasksQueueOptions
+  type TasksQueueOptions,
 } from './pool.js'
 import {
   Measurements,
   WorkerChoiceStrategies,
   type WorkerChoiceStrategy,
-  type WorkerChoiceStrategyOptions
+  type WorkerChoiceStrategyOptions,
 } from './selection-strategies/selection-strategies-types.js'
 import { WorkerChoiceStrategiesContext } from './selection-strategies/worker-choice-strategies-context.js'
 import {
@@ -55,7 +56,7 @@ import {
   updateRunTimeWorkerUsage,
   updateTaskStatisticsWorkerUsage,
   updateWaitTimeWorkerUsage,
-  waitWorkerNodeEvents
+  waitWorkerNodeEvents,
 } from './utils.js'
 import { version } from './version.js'
 import type {
@@ -63,13 +64,12 @@ import type {
   IWorkerNode,
   WorkerInfo,
   WorkerNodeEventDetail,
-  WorkerType
+  WorkerType,
 } from './worker.js'
 import { WorkerNode } from './worker-node.js'
 
 /**
  * Base class that implements some shared logic for all poolifier pools.
- *
  * @typeParam Worker - Type of worker which manages this pool.
  * @typeParam Data - Type of data sent to the worker. This can only be structured-cloneable data.
  * @typeParam Response - Type of execution response. This can only be structured-cloneable data.
@@ -80,7 +80,7 @@ export abstract class AbstractPool<
   Response = unknown
 > implements IPool<Worker, Data, Response> {
   /** @inheritDoc */
-  public readonly workerNodes: Array<IWorkerNode<Worker, Data>> = []
+  public readonly workerNodes: IWorkerNode<Worker, Data>[] = []
 
   /** @inheritDoc */
   public emitter?: EventEmitterAsyncResource
@@ -94,19 +94,19 @@ export abstract class AbstractPool<
    */
   protected promiseResponseMap: Map<
     `${string}-${string}-${string}-${string}-${string}`,
-  PromiseResponseWrapper<Response>
+    PromiseResponseWrapper<Response>
   > = new Map<
     `${string}-${string}-${string}-${string}-${string}`,
     PromiseResponseWrapper<Response>
-    >()
+  >()
 
   /**
    * Worker choice strategies context referencing worker choice algorithms implementation.
    */
   protected workerChoiceStrategiesContext?: WorkerChoiceStrategiesContext<
-  Worker,
-  Data,
-  Response
+    Worker,
+    Data,
+    Response
   >
 
   /**
@@ -115,8 +115,8 @@ export abstract class AbstractPool<
    * - `value`: The task function object.
    */
   private readonly taskFunctions: Map<
-  string,
-  TaskFunctionObject<Data, Response>
+    string,
+    TaskFunctionObject<Data, Response>
   >
 
   /**
@@ -146,7 +146,6 @@ export abstract class AbstractPool<
 
   /**
    * Constructs a new poolifier pool.
-   *
    * @param minimumNumberOfWorkers - Minimum number of workers that this pool manages.
    * @param filePath - Path to the worker file.
    * @param opts - Options for the pool.
@@ -176,9 +175,9 @@ export abstract class AbstractPool<
       this.initEventEmitter()
     }
     this.workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext<
-    Worker,
-    Data,
-    Response
+      Worker,
+      Data,
+      Response
     >(
       this,
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -288,7 +287,7 @@ export abstract class AbstractPool<
 
   private initEventEmitter (): void {
     this.emitter = new EventEmitterAsyncResource({
-      name: `poolifier:${this.type}-${this.worker}-pool`
+      name: `poolifier:${this.type}-${this.worker}-pool`,
     })
   }
 
@@ -309,7 +308,7 @@ export abstract class AbstractPool<
         .runTime.aggregate === true &&
         this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
           .waitTime.aggregate && {
-        utilization: round(this.utilization)
+        utilization: round(this.utilization),
       }),
       workerNodes: this.workerNodes.length,
       idleWorkerNodes: this.workerNodes.reduce(
@@ -324,7 +323,7 @@ export abstract class AbstractPool<
           (accumulator, workerNode) =>
             workerNode.info.stealing ? accumulator + 1 : accumulator,
           0
-        )
+        ),
       }),
       busyWorkerNodes: this.workerNodes.reduce(
         (accumulator, _, workerNodeKey) =>
@@ -346,24 +345,24 @@ export abstract class AbstractPool<
           (accumulator, workerNode) =>
             accumulator + workerNode.usage.tasks.queued,
           0
-        )
+        ),
       }),
       ...(this.opts.enableTasksQueue === true && {
         maxQueuedTasks: this.workerNodes.reduce(
           (accumulator, workerNode) =>
             accumulator + (workerNode.usage.tasks.maxQueued ?? 0),
           0
-        )
+        ),
       }),
       ...(this.opts.enableTasksQueue === true && {
-        backPressure: this.hasBackPressure()
+        backPressure: this.hasBackPressure(),
       }),
       ...(this.opts.enableTasksQueue === true && {
         stolenTasks: this.workerNodes.reduce(
           (accumulator, workerNode) =>
             accumulator + workerNode.usage.tasks.stolen,
           0
-        )
+        ),
       }),
       failedTasks: this.workerNodes.reduce(
         (accumulator, workerNode) =>
@@ -376,14 +375,16 @@ export abstract class AbstractPool<
           minimum: round(
             min(
               ...this.workerNodes.map(
-                workerNode => workerNode.usage.runTime.minimum ?? Infinity
+                workerNode =>
+                  workerNode.usage.runTime.minimum ?? Number.POSITIVE_INFINITY
               )
             )
           ),
           maximum: round(
             max(
               ...this.workerNodes.map(
-                workerNode => workerNode.usage.runTime.maximum ?? -Infinity
+                workerNode =>
+                  workerNode.usage.runTime.maximum ?? Number.NEGATIVE_INFINITY
               )
             )
           ),
@@ -393,11 +394,13 @@ export abstract class AbstractPool<
               average(
                 this.workerNodes.reduce<number[]>(
                   (accumulator, workerNode) =>
-                    accumulator.concat(workerNode.usage.runTime.history),
+                    accumulator.concat(
+                      workerNode.usage.runTime.history.toArray()
+                    ),
                   []
                 )
               )
-            )
+            ),
           }),
           ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
             .runTime.median && {
@@ -405,13 +408,15 @@ export abstract class AbstractPool<
               median(
                 this.workerNodes.reduce<number[]>(
                   (accumulator, workerNode) =>
-                    accumulator.concat(workerNode.usage.runTime.history),
+                    accumulator.concat(
+                      workerNode.usage.runTime.history.toArray()
+                    ),
                   []
                 )
               )
-            )
-          })
-        }
+            ),
+          }),
+        },
       }),
       ...(this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
         .waitTime.aggregate === true && {
@@ -419,14 +424,16 @@ export abstract class AbstractPool<
           minimum: round(
             min(
               ...this.workerNodes.map(
-                workerNode => workerNode.usage.waitTime.minimum ?? Infinity
+                workerNode =>
+                  workerNode.usage.waitTime.minimum ?? Number.POSITIVE_INFINITY
               )
             )
           ),
           maximum: round(
             max(
               ...this.workerNodes.map(
-                workerNode => workerNode.usage.waitTime.maximum ?? -Infinity
+                workerNode =>
+                  workerNode.usage.waitTime.maximum ?? Number.NEGATIVE_INFINITY
               )
             )
           ),
@@ -436,11 +443,13 @@ export abstract class AbstractPool<
               average(
                 this.workerNodes.reduce<number[]>(
                   (accumulator, workerNode) =>
-                    accumulator.concat(workerNode.usage.waitTime.history),
+                    accumulator.concat(
+                      workerNode.usage.waitTime.history.toArray()
+                    ),
                   []
                 )
               )
-            )
+            ),
           }),
           ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
             .waitTime.median && {
@@ -448,13 +457,15 @@ export abstract class AbstractPool<
               median(
                 this.workerNodes.reduce<number[]>(
                   (accumulator, workerNode) =>
-                    accumulator.concat(workerNode.usage.waitTime.history),
+                    accumulator.concat(
+                      workerNode.usage.waitTime.history.toArray()
+                    ),
                   []
                 )
               )
-            )
-          })
-        }
+            ),
+          }),
+        },
       }),
       ...(this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
         .elu.aggregate === true && {
@@ -463,14 +474,18 @@ export abstract class AbstractPool<
             minimum: round(
               min(
                 ...this.workerNodes.map(
-                  workerNode => workerNode.usage.elu.idle.minimum ?? Infinity
+                  workerNode =>
+                    workerNode.usage.elu.idle.minimum ??
+                    Number.POSITIVE_INFINITY
                 )
               )
             ),
             maximum: round(
               max(
                 ...this.workerNodes.map(
-                  workerNode => workerNode.usage.elu.idle.maximum ?? -Infinity
+                  workerNode =>
+                    workerNode.usage.elu.idle.maximum ??
+                    Number.NEGATIVE_INFINITY
                 )
               )
             ),
@@ -480,11 +495,13 @@ export abstract class AbstractPool<
                 average(
                   this.workerNodes.reduce<number[]>(
                     (accumulator, workerNode) =>
-                      accumulator.concat(workerNode.usage.elu.idle.history),
+                      accumulator.concat(
+                        workerNode.usage.elu.idle.history.toArray()
+                      ),
                     []
                   )
                 )
-              )
+              ),
             }),
             ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
               .elu.median && {
@@ -492,25 +509,31 @@ export abstract class AbstractPool<
                 median(
                   this.workerNodes.reduce<number[]>(
                     (accumulator, workerNode) =>
-                      accumulator.concat(workerNode.usage.elu.idle.history),
+                      accumulator.concat(
+                        workerNode.usage.elu.idle.history.toArray()
+                      ),
                     []
                   )
                 )
-              )
-            })
+              ),
+            }),
           },
           active: {
             minimum: round(
               min(
                 ...this.workerNodes.map(
-                  workerNode => workerNode.usage.elu.active.minimum ?? Infinity
+                  workerNode =>
+                    workerNode.usage.elu.active.minimum ??
+                    Number.POSITIVE_INFINITY
                 )
               )
             ),
             maximum: round(
               max(
                 ...this.workerNodes.map(
-                  workerNode => workerNode.usage.elu.active.maximum ?? -Infinity
+                  workerNode =>
+                    workerNode.usage.elu.active.maximum ??
+                    Number.NEGATIVE_INFINITY
                 )
               )
             ),
@@ -520,11 +543,13 @@ export abstract class AbstractPool<
                 average(
                   this.workerNodes.reduce<number[]>(
                     (accumulator, workerNode) =>
-                      accumulator.concat(workerNode.usage.elu.active.history),
+                      accumulator.concat(
+                        workerNode.usage.elu.active.history.toArray()
+                      ),
                     []
                   )
                 )
-              )
+              ),
             }),
             ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
               .elu.median && {
@@ -532,12 +557,14 @@ export abstract class AbstractPool<
                 median(
                   this.workerNodes.reduce<number[]>(
                     (accumulator, workerNode) =>
-                      accumulator.concat(workerNode.usage.elu.active.history),
+                      accumulator.concat(
+                        workerNode.usage.elu.active.history.toArray()
+                      ),
                     []
                   )
                 )
-              )
-            })
+              ),
+            }),
           },
           utilization: {
             average: round(
@@ -553,15 +580,16 @@ export abstract class AbstractPool<
                   workerNode => workerNode.usage.elu.utilization ?? 0
                 )
               )
-            )
-          }
-        }
-      })
+            ),
+          },
+        },
+      }),
     }
   }
 
   /**
-   * The pool readiness boolean status.
+   * Whether the pool is ready or not.
+   * @returns The pool readiness boolean status.
    */
   private get ready (): boolean {
     if (this.empty) {
@@ -579,7 +607,8 @@ export abstract class AbstractPool<
   }
 
   /**
-   * The pool emptiness boolean status.
+   * Whether the pool is empty or not.
+   * @returns The pool emptiness boolean status.
    */
   protected get empty (): boolean {
     return this.minimumNumberOfWorkers === 0 && this.workerNodes.length === 0
@@ -587,7 +616,6 @@ export abstract class AbstractPool<
 
   /**
    * The approximate pool utilization.
-   *
    * @returns The pool utilization.
    */
   private get utilization (): number {
@@ -624,7 +652,6 @@ export abstract class AbstractPool<
 
   /**
    * Checks if the worker id sent in the received message from a worker is valid.
-   *
    * @param message - The received message.
    * @throws {@link https://nodejs.org/api/errors.html#class-error} If the worker id is invalid.
    */
@@ -633,14 +660,13 @@ export abstract class AbstractPool<
       throw new Error('Worker message received without worker id')
     } else if (this.getWorkerNodeKeyByWorkerId(message.workerId) === -1) {
       throw new Error(
-        `Worker message received from unknown worker '${message.workerId}'`
+        `Worker message received from unknown worker '${message.workerId.toString()}'`
       )
     }
   }
 
   /**
    * Gets the worker node key given its worker id.
-   *
    * @param workerId - The worker id.
    * @returns The worker node key if the worker id is found in the pool worker nodes, `-1` otherwise.
    */
@@ -751,7 +777,7 @@ export abstract class AbstractPool<
       ...getDefaultTasksQueueOptions(
         this.maximumNumberOfWorkers ?? this.minimumNumberOfWorkers
       ),
-      ...tasksQueueOptions
+      ...tasksQueueOptions,
     }
   }
 
@@ -796,8 +822,7 @@ export abstract class AbstractPool<
 
   /**
    * Whether the pool is full or not.
-   *
-   * The pool filling boolean status.
+   * @returns The pool fullness boolean status.
    */
   protected get full (): boolean {
     return (
@@ -808,14 +833,12 @@ export abstract class AbstractPool<
 
   /**
    * Whether the pool is busy or not.
-   *
-   * The pool busyness boolean status.
+   * @returns The pool busyness boolean status.
    */
   protected abstract get busy (): boolean
 
   /**
    * Whether worker nodes are executing concurrently their tasks quota or not.
-   *
    * @returns Worker nodes busyness boolean status.
    */
   protected internalBusy (): boolean {
@@ -868,7 +891,11 @@ export abstract class AbstractPool<
           } else {
             reject(
               new Error(
-                `Task function operation '${message.taskFunctionOperation}' failed on worker ${message.workerId} with error: '${message.workerError?.message}'`
+                // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+                `Task function operation '${message.taskFunctionOperation?.toString()}' failed on worker ${message.workerId?.toString()} with error: '${
+                  // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+                  message.workerError?.message
+                }'`
               )
             )
           }
@@ -916,7 +943,9 @@ export abstract class AbstractPool<
                 new Error(
                   `Task function operation '${
                     message.taskFunctionOperation as string
-                  }' failed on worker ${errorResponse?.workerId} with error: '${
+                    // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+                  }' failed on worker ${errorResponse?.workerId?.toString()} with error: '${
+                    // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
                     errorResponse?.workerError?.message
                   }'`
                 )
@@ -968,7 +997,7 @@ export abstract class AbstractPool<
     const opResult = await this.sendTaskFunctionOperationToWorkers({
       taskFunctionOperation: 'add',
       taskFunctionProperties: buildTaskFunctionProperties(name, fn),
-      taskFunction: fn.taskFunction.toString()
+      taskFunction: fn.taskFunction.toString(),
     })
     this.taskFunctions.set(name, fn)
     this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
@@ -992,7 +1021,7 @@ export abstract class AbstractPool<
       taskFunctionProperties: buildTaskFunctionProperties(
         name,
         this.taskFunctions.get(name)
-      )
+      ),
     })
     for (const workerNode of this.workerNodes) {
       workerNode.deleteTaskFunctionWorkerUsage(name)
@@ -1022,7 +1051,6 @@ export abstract class AbstractPool<
 
   /**
    * Gets task function worker choice strategy, if any.
-   *
    * @param name - The task function name.
    * @returns The task function worker choice strategy if the task function worker choice strategy is defined, `undefined` otherwise.
    */
@@ -1042,7 +1070,6 @@ export abstract class AbstractPool<
 
   /**
    * Gets worker node task function worker choice strategy, if any.
-   *
    * @param workerNodeKey - The worker node key.
    * @param name - The task function name.
    * @returns The worker node task function worker choice strategy if the worker node task function worker choice strategy is defined, `undefined` otherwise.
@@ -1067,7 +1094,6 @@ export abstract class AbstractPool<
 
   /**
    * Gets worker node task function priority, if any.
-   *
    * @param workerNodeKey - The worker node key.
    * @param name - The task function name.
    * @returns The worker node task function priority if the worker node task function priority is defined, `undefined` otherwise.
@@ -1092,7 +1118,6 @@ export abstract class AbstractPool<
 
   /**
    * Gets the worker choice strategies registered in this pool.
-   *
    * @returns The worker choice strategies.
    */
   private readonly getWorkerChoiceStrategies =
@@ -1107,7 +1132,7 @@ export abstract class AbstractPool<
           )
           .filter(
             (strategy: WorkerChoiceStrategy | undefined) => strategy != null
-          ) as WorkerChoiceStrategy[])
+          ) as WorkerChoiceStrategy[]),
       ])
     }
 
@@ -1118,7 +1143,7 @@ export abstract class AbstractPool<
       taskFunctionProperties: buildTaskFunctionProperties(
         name,
         this.taskFunctions.get(name)
-      )
+      ),
     })
   }
 
@@ -1166,7 +1191,6 @@ export abstract class AbstractPool<
       const workerNodeKey = this.chooseWorkerNode(name)
       const task: Task<Data> = {
         name: name ?? DEFAULT_TASK_NAME,
-        // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
         data: data ?? ({} as Data),
         priority: this.getWorkerNodeTaskFunctionPriority(workerNodeKey, name),
         strategy: this.getWorkerNodeTaskFunctionWorkerChoiceStrategy(
@@ -1175,7 +1199,7 @@ export abstract class AbstractPool<
         ),
         transferList,
         timestamp,
-        taskId: randomUUID()
+        taskId: randomUUID(),
       }
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       this.promiseResponseMap.set(task.taskId!, {
@@ -1185,9 +1209,9 @@ export abstract class AbstractPool<
         ...(this.emitter != null && {
           asyncResource: new AsyncResource('poolifier:task', {
             triggerAsyncId: this.emitter.asyncId,
-            requireManualDestroy: true
-          })
-        })
+            requireManualDestroy: true,
+          }),
+        }),
       })
       if (
         this.opts.enableTasksQueue === false ||
@@ -1201,10 +1225,36 @@ export abstract class AbstractPool<
     })
   }
 
+
+  /** @inheritDoc */
+  public mapExecute (
+    data: Iterable<Data>,
+    name?: string,
+    transferList?: readonly TransferListItem[]
+  ): Promise<Response[]> {
+    // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+    if (data == null) {
+      throw new TypeError('data argument must be a defined iterable')
+    }
+    if (typeof data[Symbol.iterator] !== 'function') {
+      throw new TypeError('data argument must be an iterable')
+    }
+    if (!Array.isArray(data)) {
+      data = [...data]
+    }
+    return Promise.all(
+      (data as Data[]).map(data => this.execute(data, name, transferList))
+    )
+  }
+
   /**
    * Starts the minimum number of workers.
+   * @param initWorkerNodeUsage - Whether to initialize the worker node usage or not. @defaultValue false
    */
   private startMinimumNumberOfWorkers (initWorkerNodeUsage = false): void {
+    if (this.minimumNumberOfWorkers === 0) {
+      return
+    }
     this.startingMinimumNumberOfWorkers = true
     while (
       this.workerNodes.reduce(
@@ -1277,7 +1327,8 @@ export abstract class AbstractPool<
         } else if (message.kill === 'failure') {
           reject(
             new Error(
-              `Kill message handling failed on worker ${message.workerId}`
+              // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+              `Kill message handling failed on worker ${message.workerId?.toString()}`
             )
           )
         }
@@ -1290,7 +1341,6 @@ export abstract class AbstractPool<
 
   /**
    * Terminates the worker node given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    */
   protected async destroyWorkerNode (workerNodeKey: number): Promise<void> {
@@ -1313,8 +1363,6 @@ export abstract class AbstractPool<
   /**
    * Setup hook to execute code before worker nodes are created in the abstract constructor.
    * Can be overridden.
-   *
-   * @virtual
    */
   protected setupHook (): void {
     /* Intentionally empty */
@@ -1322,7 +1370,6 @@ export abstract class AbstractPool<
 
   /**
    * Returns whether the worker is the main worker or not.
-   *
    * @returns `true` if the worker is the main worker, `false` otherwise.
    */
   protected abstract isMain (): boolean
@@ -1330,7 +1377,6 @@ export abstract class AbstractPool<
   /**
    * Hook executed before the worker task execution.
    * Can be overridden.
-   *
    * @param workerNodeKey - The worker node key.
    * @param task - The task to execute.
    */
@@ -1371,7 +1417,6 @@ export abstract class AbstractPool<
   /**
    * Hook executed after the worker task execution.
    * Can be overridden.
-   *
    * @param workerNodeKey - The worker node key.
    * @param message - The received message.
    */
@@ -1428,7 +1473,6 @@ export abstract class AbstractPool<
 
   /**
    * Whether the worker node shall update its task function worker usage or not.
-   *
    * @param workerNodeKey - The worker node key.
    * @returns `true` if the worker node shall update its task function worker usage, `false` otherwise.
    */
@@ -1443,9 +1487,8 @@ export abstract class AbstractPool<
 
   /**
    * Chooses a worker node for the next task.
-   *
    * @param name - The task function name.
-   * @returns The chosen worker node key
+   * @returns The chosen worker node key.
    */
   private chooseWorkerNode (name?: string): number {
     if (this.shallCreateDynamicWorker()) {
@@ -1465,14 +1508,12 @@ export abstract class AbstractPool<
 
   /**
    * Conditions for dynamic worker creation.
-   *
    * @returns Whether to create a dynamic worker or not.
    */
   protected abstract shallCreateDynamicWorker (): boolean
 
   /**
    * Sends a message to worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    * @param message - The message.
    * @param transferList - The optional array of transferable objects.
@@ -1485,7 +1526,6 @@ export abstract class AbstractPool<
 
   /**
    * Initializes the worker node usage with sensible default values gathered during runtime.
-   *
    * @param workerNode - The worker node.
    */
   private initWorkerNodeUsage (workerNode: IWorkerNode<Worker, Data>): void {
@@ -1495,7 +1535,8 @@ export abstract class AbstractPool<
     ) {
       workerNode.usage.runTime.aggregate = min(
         ...this.workerNodes.map(
-          workerNode => workerNode.usage.runTime.aggregate ?? Infinity
+          workerNode =>
+            workerNode.usage.runTime.aggregate ?? Number.POSITIVE_INFINITY
         )
       )
     }
@@ -1505,7 +1546,8 @@ export abstract class AbstractPool<
     ) {
       workerNode.usage.waitTime.aggregate = min(
         ...this.workerNodes.map(
-          workerNode => workerNode.usage.waitTime.aggregate ?? Infinity
+          workerNode =>
+            workerNode.usage.waitTime.aggregate ?? Number.POSITIVE_INFINITY
         )
       )
     }
@@ -1515,7 +1557,8 @@ export abstract class AbstractPool<
     ) {
       workerNode.usage.elu.active.aggregate = min(
         ...this.workerNodes.map(
-          workerNode => workerNode.usage.elu.active.aggregate ?? Infinity
+          workerNode =>
+            workerNode.usage.elu.active.aggregate ?? Number.POSITIVE_INFINITY
         )
       )
     }
@@ -1523,7 +1566,6 @@ export abstract class AbstractPool<
 
   /**
    * Creates a new, completely set up worker node.
-   *
    * @returns New, completely set up worker node key.
    */
   protected createAndSetupWorkerNode (): number {
@@ -1587,7 +1629,6 @@ export abstract class AbstractPool<
 
   /**
    * Creates a new, completely set up dynamic worker node.
-   *
    * @returns New, completely set up dynamic worker node key.
    */
   protected createAndSetupDynamicWorkerNode (): number {
@@ -1619,7 +1660,7 @@ export abstract class AbstractPool<
       }
     })
     this.sendToWorker(workerNodeKey, {
-      checkActive: true
+      checkActive: true,
     })
     if (this.taskFunctions.size > 0) {
       for (const [taskFunctionName, taskFunctionObject] of this.taskFunctions) {
@@ -1629,7 +1670,7 @@ export abstract class AbstractPool<
             taskFunctionName,
             taskFunctionObject
           ),
-          taskFunction: taskFunctionObject.taskFunction.toString()
+          taskFunction: taskFunctionObject.taskFunction.toString(),
         }).catch((error: unknown) => {
           this.emitter?.emit(PoolEvents.error, error)
         })
@@ -1652,7 +1693,6 @@ export abstract class AbstractPool<
 
   /**
    * Registers a listener callback on the worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    * @param listener - The message listener callback.
    */
@@ -1665,7 +1705,6 @@ export abstract class AbstractPool<
 
   /**
    * Registers once a listener callback on the worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    * @param listener - The message listener callback.
    */
@@ -1678,7 +1717,6 @@ export abstract class AbstractPool<
 
   /**
    * Deregisters a listener callback on the worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    * @param listener - The message listener callback.
    */
@@ -1692,7 +1730,6 @@ export abstract class AbstractPool<
   /**
    * Method hooked up after a worker node has been newly created.
    * Can be overridden.
-   *
    * @param workerNodeKey - The newly created worker node key.
    */
   protected afterWorkerNodeSetup (workerNodeKey: number): void {
@@ -1723,14 +1760,12 @@ export abstract class AbstractPool<
 
   /**
    * Sends the startup message to worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    */
   protected abstract sendStartupMessageToWorker (workerNodeKey: number): void
 
   /**
    * Sends the statistics message to worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    */
   private sendStatisticsMessageToWorker (workerNodeKey: number): void {
@@ -1741,8 +1776,8 @@ export abstract class AbstractPool<
             .runTime.aggregate ?? false,
         elu:
           this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
-            .elu.aggregate ?? false
-      }
+            .elu.aggregate ?? false,
+      },
     })
   }
 
@@ -1863,7 +1898,7 @@ export abstract class AbstractPool<
     const workerInfo = this.getWorkerInfo(workerNodeKey)
     if (workerInfo == null) {
       throw new Error(
-        `Worker node with key '${workerNodeKey}' not found in pool`
+        `Worker node with key '${workerNodeKey.toString()}' not found in pool`
       )
     }
     if (
@@ -1979,7 +2014,7 @@ export abstract class AbstractPool<
         const workerInfo = this.getWorkerInfo(workerNodeKey)
         if (workerInfo == null) {
           throw new Error(
-            `Worker node with key '${workerNodeKey}' not found in pool`
+            `Worker node with key '${workerNodeKey.toString()}' not found in pool`
           )
         }
         workerInfo.stealing = true
@@ -1993,8 +2028,15 @@ export abstract class AbstractPool<
     }
   }
 
+  private setTasksQueuePriority (workerNodeKey: number): void {
+    this.workerNodes[workerNodeKey].setTasksQueuePriority(
+      this.getTasksQueuePriority()
+    )
+  }
+
   /**
    * This method is the message listener registered on each worker.
+   * @param message - The message received from the worker.
    */
   protected readonly workerMessageListener = (
     message: MessageValue<Response>
@@ -2011,6 +2053,7 @@ export abstract class AbstractPool<
       if (workerInfo != null) {
         workerInfo.taskFunctionsProperties = taskFunctionsProperties
         this.sendStatisticsMessageToWorker(workerNodeKey)
+        this.setTasksQueuePriority(workerNodeKey)
       }
     } else if (taskId != null) {
       // Task execution response received from worker
@@ -2028,13 +2071,15 @@ export abstract class AbstractPool<
   private handleWorkerReadyResponse (message: MessageValue<Response>): void {
     const { workerId, ready, taskFunctionsProperties } = message
     if (ready == null || !ready) {
-      throw new Error(`Worker ${workerId} failed to initialize`)
+      // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+      throw new Error(`Worker ${workerId?.toString()} failed to initialize`)
     }
     const workerNodeKey = this.getWorkerNodeKeyByWorkerId(workerId)
     const workerNode = this.workerNodes[workerNodeKey]
     workerNode.info.ready = ready
     workerNode.info.taskFunctionsProperties = taskFunctionsProperties
     this.sendStatisticsMessageToWorker(workerNodeKey)
+    this.setTasksQueuePriority(workerNodeKey)
     this.checkAndEmitReadyEvent()
   }
 
@@ -2088,7 +2133,7 @@ export abstract class AbstractPool<
         ) {
           workerNode.emit('idle', {
             workerId,
-            workerNodeKey
+            workerNodeKey,
           })
         }
       }
@@ -2114,7 +2159,6 @@ export abstract class AbstractPool<
 
   /**
    * Gets the worker information given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    * @returns The worker information.
    */
@@ -2122,9 +2166,14 @@ export abstract class AbstractPool<
     return this.workerNodes[workerNodeKey]?.info
   }
 
+  private getTasksQueuePriority (): boolean {
+    return this.listTaskFunctionsProperties().some(
+      taskFunctionProperties => taskFunctionProperties.priority != null
+    )
+  }
+
   /**
    * Creates a worker node.
-   *
    * @returns The created worker node.
    */
   private createWorkerNode (): IWorkerNode<Worker, Data> {
@@ -2139,8 +2188,8 @@ export abstract class AbstractPool<
           getDefaultTasksQueueOptions(
             this.maximumNumberOfWorkers ?? this.minimumNumberOfWorkers
           ).size,
-        tasksQueueBucketSize:
-          (this.maximumNumberOfWorkers ?? this.minimumNumberOfWorkers) * 2
+        tasksQueueBucketSize: defaultBucketSize,
+        tasksQueuePriority: this.getTasksQueuePriority(),
       }
     )
     // Flag the worker node as ready at pool startup.
@@ -2152,7 +2201,6 @@ export abstract class AbstractPool<
 
   /**
    * Adds the given worker node in the pool worker nodes.
-   *
    * @param workerNode - The worker node.
    * @returns The added worker node key.
    * @throws {@link https://nodejs.org/api/errors.html#class-error} If the added worker node is not found.
@@ -2175,7 +2223,6 @@ export abstract class AbstractPool<
 
   /**
    * Removes the worker node from the pool worker nodes.
-   *
    * @param workerNode - The worker node.
    */
   private removeWorkerNode (workerNode: IWorkerNode<Worker, Data>): void {
@@ -2205,7 +2252,6 @@ export abstract class AbstractPool<
 
   /**
    * Executes the given task on the worker given its worker node key.
-   *
    * @param workerNodeKey - The worker node key.
    * @param task - The task to execute.
    */