From afe0d5bfcc23c3acb7278eef7fac7b1b39d241cc Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 1 Jul 2023 22:13:25 +0200 Subject: [PATCH] feat: add utilization to pool information MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- CHANGELOG.md | 4 +++ src/pools/abstract-pool.ts | 42 ++++++++++++++++++++-- src/pools/pool.ts | 1 + src/utils.ts | 14 +++++++- tests/pools/abstract/abstract-pool.test.js | 4 +++ tests/utils.test.js | 29 ++++++++++++--- 6 files changed, 86 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f47aeb8f..b1ca76b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add pool `utilization` ratio to pool information. + ## [2.6.6] - 2023-07-01 ### Added diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index 71f765ac..0f18de3f 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -5,7 +5,8 @@ import { DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS, EMPTY_FUNCTION, isPlainObject, - median + median, + round } from '../utils' import { KillBehaviors, isKillBehavior } from '../worker/worker-options' import { CircularArray } from '../circular-array' @@ -76,6 +77,11 @@ export abstract class AbstractPool< Response > + /** + * The start timestamp of the pool. + */ + private readonly startTimestamp + /** * Constructs a new poolifier pool. * @@ -115,9 +121,11 @@ export abstract class AbstractPool< this.setupHook() - for (let i = 1; i <= this.numberOfWorkers; i++) { + while (this.workerNodes.length < this.numberOfWorkers) { this.createAndSetupWorker() } + + this.startTimestamp = performance.now() } private checkFilePath (filePath: string): void { @@ -243,6 +251,7 @@ export abstract class AbstractPool< worker: this.worker, minSize: this.minSize, maxSize: this.maxSize, + utilization: round(this.utilization), workerNodes: this.workerNodes.length, idleWorkerNodes: this.workerNodes.reduce( (accumulator, workerNode) => @@ -284,6 +293,35 @@ export abstract class AbstractPool< } } + /** + * Gets the pool run time. + * + * @returns The pool run time in milliseconds. + */ + private get runTime (): number { + return performance.now() - this.startTimestamp + } + + /** + * Gets the approximate pool utilization. + * + * @returns The pool utilization. + */ + private get utilization (): number { + const poolRunTimeCapacity = this.runTime * this.maxSize + const totalTasksRunTime = this.workerNodes.reduce( + (accumulator, workerNode) => + accumulator + workerNode.usage.runTime.aggregate, + 0 + ) + const totalTasksWaitTime = this.workerNodes.reduce( + (accumulator, workerNode) => + accumulator + workerNode.usage.waitTime.aggregate, + 0 + ) + return (totalTasksRunTime + totalTasksWaitTime) / poolRunTimeCapacity + } + /** * Pool type. * diff --git a/src/pools/pool.ts b/src/pools/pool.ts index ef93982f..7c6f6769 100644 --- a/src/pools/pool.ts +++ b/src/pools/pool.ts @@ -72,6 +72,7 @@ export interface PoolInfo { worker: WorkerType minSize: number maxSize: number + utilization: number workerNodes: number idleWorkerNodes: number busyWorkerNodes: number diff --git a/src/utils.ts b/src/utils.ts index 87c4888d..6d11f88c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -51,7 +51,7 @@ export const availableParallelism = (): number => { } /** - * Compute the median of the given data set. + * Computes the median of the given data set. * * @param dataSet - Data set. * @returns The median of the given data set. @@ -71,6 +71,18 @@ export const median = (dataSet: number[]): number => { ) } +/** + * Rounds the given number to the given scale. + * + * @param num - The number to round. + * @param scale - The scale to round to. + * @returns The rounded number. + */ +export const round = (num: number, scale = 2): number => { + const rounder = Math.pow(10, scale) + return Math.round(num * rounder * (1 + Number.EPSILON)) / rounder +} + /** * Is the given object a plain object? * diff --git a/tests/pools/abstract/abstract-pool.test.js b/tests/pools/abstract/abstract-pool.test.js index adf2765f..023b56b4 100644 --- a/tests/pools/abstract/abstract-pool.test.js +++ b/tests/pools/abstract/abstract-pool.test.js @@ -397,6 +397,7 @@ describe('Abstract pool test suite', () => { worker: WorkerTypes.thread, minSize: numberOfWorkers, maxSize: numberOfWorkers, + utilization: 0, workerNodes: numberOfWorkers, idleWorkerNodes: numberOfWorkers, busyWorkerNodes: 0, @@ -417,6 +418,7 @@ describe('Abstract pool test suite', () => { worker: WorkerTypes.cluster, minSize: numberOfWorkers, maxSize: numberOfWorkers * 2, + utilization: 0, workerNodes: numberOfWorkers, idleWorkerNodes: numberOfWorkers, busyWorkerNodes: 0, @@ -715,6 +717,7 @@ describe('Abstract pool test suite', () => { worker: WorkerTypes.thread, minSize: expect.any(Number), maxSize: expect.any(Number), + utilization: 0, workerNodes: expect.any(Number), idleWorkerNodes: expect.any(Number), busyWorkerNodes: expect.any(Number), @@ -751,6 +754,7 @@ describe('Abstract pool test suite', () => { worker: WorkerTypes.thread, minSize: expect.any(Number), maxSize: expect.any(Number), + utilization: 0, workerNodes: expect.any(Number), idleWorkerNodes: expect.any(Number), busyWorkerNodes: expect.any(Number), diff --git a/tests/utils.test.js b/tests/utils.test.js index f62d49c6..0bf5e50c 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -1,11 +1,20 @@ const { expect } = require('expect') -const { isPlainObject, median, availableParallelism } = require('../lib/utils') +const { + availableParallelism, + isPlainObject, + median, + round +} = require('../lib/utils') const { isKillBehavior, KillBehaviors } = require('../lib/worker/worker-options') describe('Utils test suite', () => { + it('Verify availableParallelism() behavior', () => { + expect(typeof availableParallelism() === 'number').toBe(true) + }) + it('Verify median() computation', () => { expect(median([])).toBe(0) expect(median([0.08])).toBe(0.08) @@ -13,6 +22,20 @@ describe('Utils test suite', () => { expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535) }) + it('Verify round() behavior', () => { + expect(round(0)).toBe(0) + expect(round(0.5, 0)).toBe(1) + expect(round(0.5)).toBe(0.5) + expect(round(-0.5, 0)).toBe(-1) + expect(round(-0.5)).toBe(-0.5) + expect(round(1.005)).toBe(1.01) + expect(round(2.175)).toBe(2.18) + expect(round(5.015)).toBe(5.02) + expect(round(-1.005)).toBe(-1.01) + expect(round(-2.175)).toBe(-2.18) + expect(round(-5.015)).toBe(-5.02) + }) + it('Verify isPlainObject() behavior', () => { expect(isPlainObject(null)).toBe(false) expect(isPlainObject(undefined)).toBe(false) @@ -60,8 +83,4 @@ describe('Utils test suite', () => { expect(isKillBehavior(KillBehaviors.HARD, null)).toBe(false) expect(isKillBehavior(KillBehaviors.SOFT, 'unknown')).toBe(false) }) - - it('Verify availableParallelism() behavior', () => { - expect(typeof availableParallelism() === 'number').toBe(true) - }) }) -- 2.34.1