Merge dependabot/npm_and_yarn/examples/typescript/http-client-pool/types/node-20...
[poolifier.git] / src / pools / abstract-pool.ts
index ba632bb14210eaed19701ba79abe867a08396724..07fec75f140a81ee10f39671274ecc4037a4fee3 100644 (file)
@@ -137,7 +137,7 @@ export abstract class AbstractPool<
   /**
    * The start timestamp of the pool.
    */
-  private readonly startTimestamp
+  private startTimestamp?: number
 
   /**
    * Constructs a new poolifier pool.
@@ -168,7 +168,7 @@ export abstract class AbstractPool<
     this.enqueueTask = this.enqueueTask.bind(this)
 
     if (this.opts.enableEvents === true) {
-      this.initializeEventEmitter()
+      this.initEventEmitter()
     }
     this.workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext<
     Worker,
@@ -193,8 +193,6 @@ export abstract class AbstractPool<
     if (this.opts.startWorkers === true) {
       this.start()
     }
-
-    this.startTimestamp = performance.now()
   }
 
   private checkPoolType (): void {
@@ -283,7 +281,7 @@ export abstract class AbstractPool<
     }
   }
 
-  private initializeEventEmitter (): void {
+  private initEventEmitter (): void {
     this.emitter = new EventEmitterAsyncResource({
       name: `poolifier:${this.type}-${this.worker}-pool`
     })
@@ -487,6 +485,9 @@ export abstract class AbstractPool<
    * @returns The pool utilization.
    */
   private get utilization (): number {
+    if (this.startTimestamp == null) {
+      return 0
+    }
     const poolTimeCapacity =
       (performance.now() - this.startTimestamp) *
       (this.maximumNumberOfWorkers ?? this.minimumNumberOfWorkers)
@@ -867,6 +868,9 @@ export abstract class AbstractPool<
     this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
       this.getWorkerWorkerChoiceStrategies()
     )
+    for (const workerNodeKey of this.workerNodes.keys()) {
+      this.sendStatisticsMessageToWorker(workerNodeKey)
+    }
     return opResult
   }
 
@@ -884,11 +888,16 @@ export abstract class AbstractPool<
         this.taskFunctions.get(name)
       )
     })
-    this.deleteTaskFunctionWorkerUsages(name)
+    for (const workerNode of this.workerNodes) {
+      workerNode.deleteTaskFunctionWorkerUsage(name)
+    }
     this.taskFunctions.delete(name)
     this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
       this.getWorkerWorkerChoiceStrategies()
     )
+    for (const workerNodeKey of this.workerNodes.keys()) {
+      this.sendStatisticsMessageToWorker(workerNodeKey)
+    }
     return opResult
   }
 
@@ -973,12 +982,6 @@ export abstract class AbstractPool<
     })
   }
 
-  private deleteTaskFunctionWorkerUsages (name: string): void {
-    for (const workerNode of this.workerNodes) {
-      workerNode.deleteTaskFunctionWorkerUsage(name)
-    }
-  }
-
   private shallExecuteTask (workerNodeKey: number): boolean {
     return (
       this.tasksQueueSize(workerNodeKey) === 0 &&
@@ -1060,7 +1063,7 @@ export abstract class AbstractPool<
   /**
    * Starts the minimum number of workers.
    */
-  private startMinimumNumberOfWorkers (): void {
+  private startMinimumNumberOfWorkers (initWorkerNodeUsage = false): void {
     this.startingMinimumNumberOfWorkers = true
     while (
       this.workerNodes.reduce(
@@ -1069,7 +1072,9 @@ export abstract class AbstractPool<
         0
       ) < this.minimumNumberOfWorkers
     ) {
-      this.createAndSetupWorkerNode()
+      const workerNodeKey = this.createAndSetupWorkerNode()
+      initWorkerNodeUsage &&
+        this.initWorkerNodeUsage(this.workerNodes[workerNodeKey])
     }
     this.startingMinimumNumberOfWorkers = false
   }
@@ -1087,6 +1092,7 @@ export abstract class AbstractPool<
     }
     this.starting = true
     this.startMinimumNumberOfWorkers()
+    this.startTimestamp = performance.now()
     this.starting = false
     this.started = true
   }
@@ -1111,6 +1117,7 @@ export abstract class AbstractPool<
     this.emitter?.emit(PoolEvents.destroy, this.info)
     this.emitter?.emitDestroy()
     this.readyEventEmitted = false
+    delete this.startTimestamp
     this.destroying = false
     this.started = false
   }
@@ -1231,7 +1238,7 @@ export abstract class AbstractPool<
     workerNodeKey: number,
     message: MessageValue<Response>
   ): void {
-    let needWorkerChoiceStrategyUpdate = false
+    let needWorkerChoiceStrategiesUpdate = false
     // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
     if (this.workerNodes[workerNodeKey]?.usage != null) {
       const workerUsage = this.workerNodes[workerNodeKey].usage
@@ -1246,7 +1253,7 @@ export abstract class AbstractPool<
         workerUsage,
         message
       )
-      needWorkerChoiceStrategyUpdate = true
+      needWorkerChoiceStrategiesUpdate = true
     }
     if (
       this.shallUpdateTaskFunctionWorkerUsage(workerNodeKey) &&
@@ -1271,9 +1278,9 @@ export abstract class AbstractPool<
         taskFunctionWorkerUsage,
         message
       )
-      needWorkerChoiceStrategyUpdate = true
+      needWorkerChoiceStrategiesUpdate = true
     }
-    if (needWorkerChoiceStrategyUpdate) {
+    if (needWorkerChoiceStrategiesUpdate) {
       this.workerChoiceStrategiesContext?.update(workerNodeKey)
     }
   }
@@ -1335,6 +1342,44 @@ export abstract class AbstractPool<
     transferList?: readonly TransferListItem[]
   ): void
 
+  /**
+   * Initializes the worker node usage with sensible default values gathered during runtime.
+   *
+   * @param workerNode - The worker node.
+   */
+  private initWorkerNodeUsage (workerNode: IWorkerNode<Worker, Data>): void {
+    if (
+      this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
+        .runTime.aggregate === true
+    ) {
+      workerNode.usage.runTime.aggregate = min(
+        ...this.workerNodes.map(
+          workerNode => workerNode.usage.runTime.aggregate ?? Infinity
+        )
+      )
+    }
+    if (
+      this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
+        .waitTime.aggregate === true
+    ) {
+      workerNode.usage.waitTime.aggregate = min(
+        ...this.workerNodes.map(
+          workerNode => workerNode.usage.waitTime.aggregate ?? Infinity
+        )
+      )
+    }
+    if (
+      this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements().elu
+        .aggregate === true
+    ) {
+      workerNode.usage.elu.active.aggregate = min(
+        ...this.workerNodes.map(
+          workerNode => workerNode.usage.elu.active.aggregate ?? Infinity
+        )
+      )
+    }
+  }
+
   /**
    * Creates a new, completely set up worker node.
    *
@@ -1365,7 +1410,7 @@ export abstract class AbstractPool<
         if (workerNode.info.dynamic) {
           this.createAndSetupDynamicWorkerNode()
         } else if (!this.startingMinimumNumberOfWorkers) {
-          this.startMinimumNumberOfWorkers()
+          this.startMinimumNumberOfWorkers(true)
         }
       }
       if (
@@ -1391,7 +1436,7 @@ export abstract class AbstractPool<
         !this.startingMinimumNumberOfWorkers &&
         !this.destroying
       ) {
-        this.startMinimumNumberOfWorkers()
+        this.startMinimumNumberOfWorkers(true)
       }
     })
     const workerNodeKey = this.addWorkerNode(workerNode)
@@ -1456,6 +1501,7 @@ export abstract class AbstractPool<
     ) {
       workerNode.info.ready = true
     }
+    this.initWorkerNodeUsage(workerNode)
     this.checkAndEmitDynamicWorkerCreationEvents()
     return workerNodeKey
   }
@@ -1839,11 +1885,11 @@ export abstract class AbstractPool<
       this.handleWorkerReadyResponse(message)
     } else if (taskFunctionsProperties != null) {
       // Task function properties message received from worker
-      const workerInfo = this.getWorkerInfo(
-        this.getWorkerNodeKeyByWorkerId(workerId)
-      )
+      const workerNodeKey = this.getWorkerNodeKeyByWorkerId(workerId)
+      const workerInfo = this.getWorkerInfo(workerNodeKey)
       if (workerInfo != null) {
         workerInfo.taskFunctionsProperties = taskFunctionsProperties
+        this.sendStatisticsMessageToWorker(workerNodeKey)
       }
     } else if (taskId != null) {
       // Task execution response received from worker
@@ -1863,10 +1909,11 @@ export abstract class AbstractPool<
     if (ready == null || !ready) {
       throw new Error(`Worker ${workerId} failed to initialize`)
     }
-    const workerNode =
-      this.workerNodes[this.getWorkerNodeKeyByWorkerId(workerId)]
+    const workerNodeKey = this.getWorkerNodeKeyByWorkerId(workerId)
+    const workerNode = this.workerNodes[workerNodeKey]
     workerNode.info.ready = ready
     workerNode.info.taskFunctionsProperties = taskFunctionsProperties
+    this.sendStatisticsMessageToWorker(workerNodeKey)
     this.checkAndEmitReadyEvent()
   }