fix: fix internal measurements handling
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 5 Jul 2023 21:41:47 +0000 (23:41 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 5 Jul 2023 21:41:47 +0000 (23:41 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/pools/abstract-pool.ts
src/pools/selection-strategies/abstract-worker-choice-strategy.ts
src/pools/selection-strategies/least-busy-worker-choice-strategy.ts
src/pools/worker.ts
tests/pools/abstract/abstract-pool.test.js
tests/pools/selection-strategies/selection-strategies.test.js

index fc703d144f2862690c078aef485a72ad74328c2e..e97ce4f8395d35cb1b782c0843c1f324fef3a206 100644 (file)
@@ -303,12 +303,12 @@ export abstract class AbstractPool<
         runTime: {
           minimum: Math.min(
             ...this.workerNodes.map(
-              workerNode => workerNode.usage.runTime.minimum
+              workerNode => workerNode.usage.runTime?.minimum ?? Infinity
             )
           ),
           maximum: Math.max(
             ...this.workerNodes.map(
-              workerNode => workerNode.usage.runTime.maximum
+              workerNode => workerNode.usage.runTime?.maximum ?? -Infinity
             )
           )
         }
@@ -318,12 +318,12 @@ export abstract class AbstractPool<
         waitTime: {
           minimum: Math.min(
             ...this.workerNodes.map(
-              workerNode => workerNode.usage.waitTime.minimum
+              workerNode => workerNode.usage.waitTime?.minimum ?? Infinity
             )
           ),
           maximum: Math.max(
             ...this.workerNodes.map(
-              workerNode => workerNode.usage.waitTime.maximum
+              workerNode => workerNode.usage.waitTime?.maximum ?? -Infinity
             )
           )
         }
@@ -341,12 +341,12 @@ export abstract class AbstractPool<
       (performance.now() - this.startTimestamp) * this.maxSize
     const totalTasksRunTime = this.workerNodes.reduce(
       (accumulator, workerNode) =>
-        accumulator + workerNode.usage.runTime.aggregate,
+        accumulator + (workerNode.usage.runTime?.aggregate ?? 0),
       0
     )
     const totalTasksWaitTime = this.workerNodes.reduce(
       (accumulator, workerNode) =>
-        accumulator + workerNode.usage.waitTime.aggregate,
+        accumulator + (workerNode.usage.waitTime?.aggregate ?? 0),
       0
     )
     return (totalTasksRunTime + totalTasksWaitTime) / poolRunTimeCapacity
@@ -617,14 +617,15 @@ export abstract class AbstractPool<
         .aggregate
     ) {
       const taskRunTime = message.taskPerformance?.runTime ?? 0
-      workerUsage.runTime.aggregate += taskRunTime
+      workerUsage.runTime.aggregate =
+        (workerUsage.runTime.aggregate ?? 0) + taskRunTime
       workerUsage.runTime.minimum = Math.min(
         taskRunTime,
-        workerUsage.runTime.minimum ?? Infinity
+        workerUsage.runTime?.minimum ?? Infinity
       )
       workerUsage.runTime.maximum = Math.max(
         taskRunTime,
-        workerUsage.runTime.maximum ?? -Infinity
+        workerUsage.runTime?.maximum ?? -Infinity
       )
       if (
         this.workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
@@ -656,14 +657,15 @@ export abstract class AbstractPool<
       this.workerChoiceStrategyContext.getTaskStatisticsRequirements().waitTime
         .aggregate
     ) {
-      workerUsage.waitTime.aggregate += taskWaitTime
+      workerUsage.waitTime.aggregate =
+        (workerUsage.waitTime?.aggregate ?? 0) + taskWaitTime
       workerUsage.waitTime.minimum = Math.min(
         taskWaitTime,
-        workerUsage.waitTime.minimum ?? Infinity
+        workerUsage.waitTime?.minimum ?? Infinity
       )
       workerUsage.waitTime.maximum = Math.max(
         taskWaitTime,
-        workerUsage.waitTime.maximum ?? -Infinity
+        workerUsage.waitTime?.maximum ?? -Infinity
       )
       if (
         this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
@@ -676,8 +678,7 @@ export abstract class AbstractPool<
       }
       if (
         this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
-          .waitTime.median &&
-        taskWaitTime != null
+          .waitTime.median
       ) {
         workerUsage.waitTime.history.push(taskWaitTime)
         workerUsage.waitTime.median = median(workerUsage.waitTime.history)
@@ -694,8 +695,12 @@ export abstract class AbstractPool<
         .aggregate
     ) {
       if (message.taskPerformance?.elu != null) {
-        workerUsage.elu.idle.aggregate += message.taskPerformance.elu.idle
-        workerUsage.elu.active.aggregate += message.taskPerformance.elu.active
+        workerUsage.elu.idle.aggregate =
+          (workerUsage.elu.idle?.aggregate ?? 0) +
+          message.taskPerformance.elu.idle
+        workerUsage.elu.active.aggregate =
+          (workerUsage.elu.active?.aggregate ?? 0) +
+          message.taskPerformance.elu.active
         if (workerUsage.elu.utilization != null) {
           workerUsage.elu.utilization =
             (workerUsage.elu.utilization +
@@ -706,42 +711,44 @@ export abstract class AbstractPool<
         }
         workerUsage.elu.idle.minimum = Math.min(
           message.taskPerformance.elu.idle,
-          workerUsage.elu.idle.minimum ?? Infinity
+          workerUsage.elu.idle?.minimum ?? Infinity
         )
         workerUsage.elu.idle.maximum = Math.max(
           message.taskPerformance.elu.idle,
-          workerUsage.elu.idle.maximum ?? -Infinity
+          workerUsage.elu.idle?.maximum ?? -Infinity
         )
         workerUsage.elu.active.minimum = Math.min(
           message.taskPerformance.elu.active,
-          workerUsage.elu.active.minimum ?? Infinity
+          workerUsage.elu.active?.minimum ?? Infinity
         )
         workerUsage.elu.active.maximum = Math.max(
           message.taskPerformance.elu.active,
-          workerUsage.elu.active.maximum ?? -Infinity
+          workerUsage.elu.active?.maximum ?? -Infinity
         )
-      }
-      if (
-        this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu
-          .average &&
-        workerUsage.tasks.executed !== 0
-      ) {
-        const executedTasks =
-          workerUsage.tasks.executed - workerUsage.tasks.failed
-        workerUsage.elu.idle.average =
-          workerUsage.elu.idle.aggregate / executedTasks
-        workerUsage.elu.active.average =
-          workerUsage.elu.active.aggregate / executedTasks
-      }
-      if (
-        this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu
-          .median &&
-        message.taskPerformance?.elu != null
-      ) {
-        workerUsage.elu.idle.history.push(message.taskPerformance.elu.idle)
-        workerUsage.elu.active.history.push(message.taskPerformance.elu.active)
-        workerUsage.elu.idle.median = median(workerUsage.elu.idle.history)
-        workerUsage.elu.active.median = median(workerUsage.elu.active.history)
+        if (
+          this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu
+            .average &&
+          workerUsage.tasks.executed !== 0
+        ) {
+          const executedTasks =
+            workerUsage.tasks.executed - workerUsage.tasks.failed
+          workerUsage.elu.idle.average =
+            workerUsage.elu.idle.aggregate / executedTasks
+          workerUsage.elu.active.average =
+            workerUsage.elu.active.aggregate / executedTasks
+        }
+        if (
+          this.workerChoiceStrategyContext.getTaskStatisticsRequirements().elu
+            .median &&
+          message.taskPerformance?.elu != null
+        ) {
+          workerUsage.elu.idle.history.push(message.taskPerformance.elu.idle)
+          workerUsage.elu.active.history.push(
+            message.taskPerformance.elu.active
+          )
+          workerUsage.elu.idle.median = median(workerUsage.elu.idle.history)
+          workerUsage.elu.active.median = median(workerUsage.elu.active.history)
+        }
       }
     }
   }
@@ -1120,14 +1127,26 @@ export abstract class AbstractPool<
 
   private getInitialWorkerUsage (worker?: Worker): WorkerUsage {
     const getTasksQueueSize = (worker?: Worker): number => {
-      return worker != null
-        ? this.tasksQueueSize(this.getWorkerNodeKey(worker))
-        : 0
+      if (worker == null) {
+        return 0
+      }
+      // FIXME: Workaround tasks queue initialization issue.
+      try {
+        return this.tasksQueueSize(this.getWorkerNodeKey(worker))
+      } catch {
+        return 0
+      }
     }
     const getTasksMaxQueueSize = (worker?: Worker): number => {
-      return worker != null
-        ? this.tasksMaxQueueSize(this.getWorkerNodeKey(worker))
-        : 0
+      if (worker == null) {
+        return 0
+      }
+      // FIXME: Workaround tasks queue initialization issue.
+      try {
+        return this.tasksMaxQueueSize(this.getWorkerNodeKey(worker))
+      } catch {
+        return 0
+      }
     }
     return {
       tasks: {
@@ -1142,36 +1161,16 @@ export abstract class AbstractPool<
         failed: 0
       },
       runTime: {
-        aggregate: 0,
-        maximum: 0,
-        minimum: 0,
-        average: 0,
-        median: 0,
         history: new CircularArray()
       },
       waitTime: {
-        aggregate: 0,
-        maximum: 0,
-        minimum: 0,
-        average: 0,
-        median: 0,
         history: new CircularArray()
       },
       elu: {
         idle: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: new CircularArray()
         },
         active: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: new CircularArray()
         }
       }
index cd969ccd69d99fed15d08e9cca74450d424df5aa..7f26706d6d2a7d4b8780bb086a07a413279c23bd 100644 (file)
@@ -152,8 +152,8 @@ export abstract class AbstractWorkerChoiceStrategy<
    */
   protected getWorkerTaskRunTime (workerNodeKey: number): number {
     return this.taskStatisticsRequirements.runTime.median
-      ? this.pool.workerNodes[workerNodeKey].usage.runTime.median
-      : this.pool.workerNodes[workerNodeKey].usage.runTime.average
+      ? this.pool.workerNodes[workerNodeKey].usage.runTime?.median ?? 0
+      : this.pool.workerNodes[workerNodeKey].usage.runTime?.average ?? 0
   }
 
   /**
@@ -166,8 +166,8 @@ export abstract class AbstractWorkerChoiceStrategy<
    */
   protected getWorkerTaskWaitTime (workerNodeKey: number): number {
     return this.taskStatisticsRequirements.waitTime.median
-      ? this.pool.workerNodes[workerNodeKey].usage.waitTime.median
-      : this.pool.workerNodes[workerNodeKey].usage.waitTime.average
+      ? this.pool.workerNodes[workerNodeKey].usage.waitTime?.median ?? 0
+      : this.pool.workerNodes[workerNodeKey].usage.waitTime?.average ?? 0
   }
 
   /**
@@ -180,8 +180,8 @@ export abstract class AbstractWorkerChoiceStrategy<
    */
   protected getWorkerTaskElu (workerNodeKey: number): number {
     return this.taskStatisticsRequirements.elu.median
-      ? this.pool.workerNodes[workerNodeKey].usage.elu.active.median
-      : this.pool.workerNodes[workerNodeKey].usage.elu.active.average
+      ? this.pool.workerNodes[workerNodeKey].usage.elu.active?.median ?? 0
+      : this.pool.workerNodes[workerNodeKey].usage.elu.active?.average ?? 0
   }
 
   protected computeDefaultWorkerWeight (): number {
index 14aee44f97a090f5270fa23d41389e32f0c5ee8d..928b1b2a932488132e3dcbb5a3c8b3d2be4b9892 100644 (file)
@@ -64,7 +64,8 @@ export class LeastBusyWorkerChoiceStrategy<
     let minTime = Infinity
     for (const [workerNodeKey, workerNode] of this.pool.workerNodes.entries()) {
       const workerTime =
-        workerNode.usage.runTime.aggregate + workerNode.usage.waitTime.aggregate
+        (workerNode.usage.runTime?.aggregate ?? 0) +
+        (workerNode.usage.waitTime?.aggregate ?? 0)
       if (workerTime === 0) {
         this.nextWorkerNodeId = workerNodeKey
         break
index 35853b7dcf427b8281d65b74399106707b930577..d071413b04a0c1ab5f79f3bff17ac9c78354f2d5 100644 (file)
@@ -64,23 +64,23 @@ export interface MeasurementStatistics {
   /**
    * Measurement aggregate.
    */
-  aggregate: number
+  aggregate?: number
   /**
    * Measurement minimum.
    */
-  minimum: number
+  minimum?: number
   /**
    * Measurement maximum.
    */
-  maximum: number
+  maximum?: number
   /**
    * Measurement average.
    */
-  average: number
+  average?: number
   /**
    * Measurement median.
    */
-  median: number
+  median?: number
   /**
    * Measurement history.
    */
index d4b147f78f4cd13e62aedbf5a8cac0af8d56884a..da870774b1bd66ee74d8fa8513dfd67e98cb429b 100644 (file)
@@ -462,36 +462,16 @@ describe('Abstract pool test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           }
         }
@@ -534,36 +514,16 @@ describe('Abstract pool test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           }
         }
@@ -580,36 +540,16 @@ describe('Abstract pool test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           }
         }
@@ -640,36 +580,16 @@ describe('Abstract pool test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           }
         }
@@ -688,36 +608,16 @@ describe('Abstract pool test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          maximum: 0,
-          minimum: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            maximum: 0,
-            minimum: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           }
         }
index b2aa4a0b69fbd4e006ce6f53fecb90295edb535d..167736edb9f11c24da068a0b43da56c48db14344 100644 (file)
@@ -218,31 +218,18 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
     }
@@ -279,31 +266,18 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
     }
@@ -485,31 +459,18 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
@@ -545,31 +506,18 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
@@ -685,30 +633,23 @@ describe('Selection strategies test suite', () => {
         },
         runTime: {
           aggregate: expect.any(Number),
-          average: 0,
-          median: 0,
+          maximum: expect.any(Number),
+          minimum: expect.any(Number),
           history: expect.any(CircularArray)
         },
         waitTime: {
           aggregate: expect.any(Number),
-          average: 0,
-          median: 0,
+          maximum: expect.any(Number),
+          minimum: expect.any(Number),
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
@@ -747,30 +688,23 @@ describe('Selection strategies test suite', () => {
         },
         runTime: {
           aggregate: expect.any(Number),
-          average: 0,
-          median: 0,
+          maximum: expect.any(Number),
+          minimum: expect.any(Number),
           history: expect.any(CircularArray)
         },
         waitTime: {
           aggregate: expect.any(Number),
-          average: 0,
-          median: 0,
+          maximum: expect.any(Number),
+          minimum: expect.any(Number),
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
@@ -878,7 +812,7 @@ describe('Selection strategies test suite', () => {
     }
     await Promise.all(promises)
     for (const workerNode of pool.workerNodes) {
-      expect(workerNode.usage).toStrictEqual({
+      expect(workerNode.usage).toMatchObject({
         tasks: {
           executed: expect.any(Number),
           executing: 0,
@@ -887,39 +821,30 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
-          idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
+          idle: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          active: {
-            aggregate: expect.any(Number),
-            average: 0,
-            median: 0,
+          }),
+          active: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          utilization: expect.any(Number)
+          })
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
       expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
         max * maxMultiplier
       )
-      expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      if (workerNode.usage.elu.utilization == null) {
+        expect(workerNode.usage.elu.utilization).toBeUndefined()
+      } else {
+        expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
+        expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      }
     }
     // We need to clean up the resources after our test
     await pool.destroy()
@@ -940,7 +865,7 @@ describe('Selection strategies test suite', () => {
     }
     await Promise.all(promises)
     for (const workerNode of pool.workerNodes) {
-      expect(workerNode.usage).toStrictEqual({
+      expect(workerNode.usage).toMatchObject({
         tasks: {
           executed: expect.any(Number),
           executing: 0,
@@ -949,39 +874,30 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
-          idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
+          idle: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          active: {
-            aggregate: expect.any(Number),
-            average: 0,
-            median: 0,
+          }),
+          active: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          utilization: expect.any(Number)
+          })
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
       expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
         max * maxMultiplier
       )
-      expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      if (workerNode.usage.elu.utilization == null) {
+        expect(workerNode.usage.elu.utilization).toBeUndefined()
+      } else {
+        expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
+        expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      }
     }
     // We need to clean up the resources after our test
     await pool.destroy()
@@ -1081,7 +997,7 @@ describe('Selection strategies test suite', () => {
     }
     await Promise.all(promises)
     for (const workerNode of pool.workerNodes) {
-      expect(workerNode.usage).toStrictEqual({
+      expect(workerNode.usage).toMatchObject({
         tasks: {
           executed: expect.any(Number),
           executing: 0,
@@ -1089,42 +1005,41 @@ describe('Selection strategies test suite', () => {
           maxQueued: 0,
           failed: 0
         },
-        runTime: {
-          aggregate: expect.any(Number),
-          average: expect.any(Number),
-          median: 0,
+        runTime: expect.objectContaining({
           history: expect.any(CircularArray)
-        },
+        }),
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
-          idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
+          idle: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          active: {
-            aggregate: expect.any(Number),
-            average: expect.any(Number),
-            median: 0,
+          }),
+          active: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          utilization: expect.any(Number)
+          })
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
       expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
         max * maxMultiplier
       )
-      expect(workerNode.usage.runTime.aggregate).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.runTime.average).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      if (workerNode.usage.runTime.aggregate == null) {
+        expect(workerNode.usage.runTime.aggregate).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.runTime.average == null) {
+        expect(workerNode.usage.runTime.average).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.elu.utilization == null) {
+        expect(workerNode.usage.elu.utilization).toBeUndefined()
+      } else {
+        expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
+        expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      }
     }
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -1150,7 +1065,7 @@ describe('Selection strategies test suite', () => {
     }
     await Promise.all(promises)
     for (const workerNode of pool.workerNodes) {
-      expect(workerNode.usage).toStrictEqual({
+      expect(workerNode.usage).toMatchObject({
         tasks: {
           executed: expect.any(Number),
           executing: 0,
@@ -1158,42 +1073,41 @@ describe('Selection strategies test suite', () => {
           maxQueued: 0,
           failed: 0
         },
-        runTime: {
-          aggregate: expect.any(Number),
-          average: expect.any(Number),
-          median: 0,
+        runTime: expect.objectContaining({
           history: expect.any(CircularArray)
-        },
+        }),
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
-          idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
+          idle: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          active: {
-            aggregate: expect.any(Number),
-            average: expect.any(Number),
-            median: 0,
+          }),
+          active: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          utilization: expect.any(Number)
+          })
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
       expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
         max * maxMultiplier
       )
-      expect(workerNode.usage.runTime.aggregate).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.runTime.average).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      if (workerNode.usage.runTime.aggregate == null) {
+        expect(workerNode.usage.runTime.aggregate).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.runTime.average == null) {
+        expect(workerNode.usage.runTime.average).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.elu.utilization == null) {
+        expect(workerNode.usage.elu.utilization).toBeUndefined()
+      } else {
+        expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
+        expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      }
     }
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -1224,7 +1138,7 @@ describe('Selection strategies test suite', () => {
     }
     await Promise.all(promises)
     for (const workerNode of pool.workerNodes) {
-      expect(workerNode.usage).toStrictEqual({
+      expect(workerNode.usage).toMatchObject({
         tasks: {
           executed: expect.any(Number),
           executing: 0,
@@ -1232,42 +1146,41 @@ describe('Selection strategies test suite', () => {
           maxQueued: 0,
           failed: 0
         },
-        runTime: {
-          aggregate: expect.any(Number),
-          average: 0,
-          median: expect.any(Number),
+        runTime: expect.objectContaining({
           history: expect.any(CircularArray)
-        },
+        }),
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
-          idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
+          idle: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          active: {
-            aggregate: expect.any(Number),
-            average: expect.any(Number),
-            median: 0,
+          }),
+          active: expect.objectContaining({
             history: expect.any(CircularArray)
-          },
-          utilization: expect.any(Number)
+          })
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
       expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
         max * maxMultiplier
       )
-      expect(workerNode.usage.runTime.aggregate).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.runTime.median).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      if (workerNode.usage.runTime.aggregate == null) {
+        expect(workerNode.usage.runTime.aggregate).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.runTime.median == null) {
+        expect(workerNode.usage.runTime.median).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.median).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.elu.utilization == null) {
+        expect(workerNode.usage.elu.utilization).toBeUndefined()
+      } else {
+        expect(workerNode.usage.elu.utilization).toBeGreaterThanOrEqual(0)
+        expect(workerNode.usage.elu.utilization).toBeLessThanOrEqual(1)
+      }
     }
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -1454,40 +1367,35 @@ describe('Selection strategies test suite', () => {
           maxQueued: 0,
           failed: 0
         },
-        runTime: {
-          aggregate: expect.any(Number),
-          average: expect.any(Number),
-          median: 0,
+        runTime: expect.objectContaining({
           history: expect.any(CircularArray)
-        },
+        }),
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
       expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
         max * maxMultiplier
       )
-      expect(workerNode.usage.runTime.aggregate).toBeGreaterThanOrEqual(0)
-      expect(workerNode.usage.runTime.average).toBeGreaterThanOrEqual(0)
+      if (workerNode.usage.runTime.aggregate == null) {
+        expect(workerNode.usage.runTime.aggregate).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.aggregate).toBeGreaterThan(0)
+      }
+      if (workerNode.usage.runTime.average == null) {
+        expect(workerNode.usage.runTime.average).toBeUndefined()
+      } else {
+        expect(workerNode.usage.runTime.average).toBeGreaterThan(0)
+      }
     }
     expect(
       pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
@@ -1528,30 +1436,21 @@ describe('Selection strategies test suite', () => {
         },
         runTime: {
           aggregate: expect.any(Number),
+          maximum: expect.any(Number),
+          minimum: expect.any(Number),
           average: expect.any(Number),
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
@@ -1605,30 +1504,21 @@ describe('Selection strategies test suite', () => {
         },
         runTime: {
           aggregate: expect.any(Number),
-          average: 0,
+          maximum: expect.any(Number),
+          minimum: expect.any(Number),
           median: expect.any(Number),
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
       expect(workerNode.usage.tasks.executed).toBeGreaterThanOrEqual(0)
@@ -1838,31 +1728,18 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
     }
@@ -1921,31 +1798,18 @@ describe('Selection strategies test suite', () => {
           failed: 0
         },
         runTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         waitTime: {
-          aggregate: 0,
-          average: 0,
-          median: 0,
           history: expect.any(CircularArray)
         },
         elu: {
           idle: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
           },
           active: {
-            aggregate: 0,
-            average: 0,
-            median: 0,
             history: expect.any(CircularArray)
-          },
-          utilization: 0
+          }
         }
       })
     }