console.info(`==== ${request.method} ${request.url} ====`)
console.info('> Headers')
- console.log(request.headers)
+ console.info(request.headers)
console.info('> Body')
console.info(body)
/**
* Constructs a new poolifier pool.
*
- * @param minimumNumberOfWorkers - Minimum number of workers that this pool should manage.
+ * @param minimumNumberOfWorkers - Minimum number of workers that this pool manages.
* @param filePath - Path to the worker file.
* @param opts - Options for the pool.
- * @param maximumNumberOfWorkers - Maximum number of workers that this pool should manage.
+ * @param maximumNumberOfWorkers - Maximum number of workers that this pool manages.
*/
public constructor (
protected readonly minimumNumberOfWorkers: number,
-import { cpus } from 'node:os'
import {
DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
- getDefaultInternalWorkerChoiceStrategyOptions
+ buildInternalWorkerChoiceStrategyOptions
} from '../../utils'
import type { IPool } from '../pool'
import type { IWorker } from '../worker'
/** @inheritDoc */
public setOptions (opts: InternalWorkerChoiceStrategyOptions): void {
- this.opts = {
- ...getDefaultInternalWorkerChoiceStrategyOptions(
- this.pool.info.maxSize + Object.keys(opts?.weights ?? {}).length
- ),
- ...opts
- }
+ this.opts = buildInternalWorkerChoiceStrategyOptions(
+ this.pool.info.maxSize,
+ opts
+ )
this.setTaskStatisticsRequirements(this.opts)
}
protected setPreviousWorkerNodeKey (workerNodeKey: number | undefined): void {
this.previousWorkerNodeKey = workerNodeKey ?? this.previousWorkerNodeKey
}
-
- protected computeDefaultWorkerWeight (): number {
- let cpusCycleTimeWeight = 0
- for (const cpu of cpus()) {
- // CPU estimated cycle time
- const numberOfDigits = cpu.speed.toString().length - 1
- const cpuCycleTime = 1 / (cpu.speed / Math.pow(10, numberOfDigits))
- cpusCycleTimeWeight += cpuCycleTime * Math.pow(10, numberOfDigits)
- }
- return Math.round(cpusCycleTimeWeight / cpus().length)
- }
}
* Round id.
*/
private roundId: number = 0
- /**
- * Default worker weight.
- */
- private readonly defaultWorkerWeight: number
/**
* Round weights.
*/
- private roundWeights: number[]
+ private roundWeights!: number[]
/**
* Worker node id.
*/
opts: InternalWorkerChoiceStrategyOptions
) {
super(pool, opts)
- this.setTaskStatisticsRequirements(this.opts)
- this.defaultWorkerWeight = this.computeDefaultWorkerWeight()
- this.roundWeights = this.getRoundWeights()
+ this.setOptions(this.opts)
}
/** @inheritDoc */
) {
this.workerNodeVirtualTaskRunTime = 0
}
- const workerWeight =
- this.opts.weights?.[workerNodeKey] ?? this.defaultWorkerWeight
+ const workerWeight = this.opts.weights?.[workerNodeKey] as number
if (
this.isWorkerNodeReady(workerNodeKey) &&
workerWeight >= this.roundWeights[roundIndex] &&
}
private getRoundWeights (): number[] {
- if (this.opts.weights == null) {
- return [this.defaultWorkerWeight]
- }
return [
...new Set(
- Object.values(this.opts.weights)
+ Object.values(this.opts.weights as Record<number, number>)
.slice()
.sort((a, b) => a - b)
)
*
* @defaultValue Weights computed automatically given the CPU performance.
*/
- readonly weights?: Record<number, number>
+ weights?: Record<number, number>
}
/**
elu: DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
}
- /**
- * Default worker weight.
- */
- private readonly defaultWorkerWeight: number
/**
* Worker node virtual task runtime.
*/
opts: InternalWorkerChoiceStrategyOptions
) {
super(pool, opts)
- this.setTaskStatisticsRequirements(this.opts)
- this.defaultWorkerWeight = this.computeDefaultWorkerWeight()
+ this.setOptions(this.opts)
}
/** @inheritDoc */
}
private weightedRoundRobinNextWorkerNodeKey (): number | undefined {
- const workerWeight =
- this.opts.weights?.[
- this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
- ] ?? this.defaultWorkerWeight
+ const workerWeight = this.opts.weights?.[
+ this.nextWorkerNodeKey ?? this.previousWorkerNodeKey
+ ] as number
if (this.workerNodeVirtualTaskRunTime < workerWeight) {
this.workerNodeVirtualTaskRunTime =
this.workerNodeVirtualTaskRunTime +
-import { getDefaultInternalWorkerChoiceStrategyOptions } from '../../utils'
+import { buildInternalWorkerChoiceStrategyOptions } from '../../utils'
import type { IPool } from '../pool'
import type { IWorker } from '../worker'
import { FairShareWorkerChoiceStrategy } from './fair-share-worker-choice-strategy'
private workerChoiceStrategy: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN,
private opts?: InternalWorkerChoiceStrategyOptions
) {
- this.opts = {
- ...getDefaultInternalWorkerChoiceStrategyOptions(
- pool.info.maxSize + Object.keys(this.opts?.weights ?? {}).length
- ),
- ...this.opts
- }
+ this.opts = buildInternalWorkerChoiceStrategyOptions(
+ pool.info.maxSize,
+ this.opts
+ )
this.execute = this.execute.bind(this)
this.workerChoiceStrategies = new Map<
WorkerChoiceStrategy,
pool: IPool<Worker, Data, Response>,
opts?: InternalWorkerChoiceStrategyOptions
): void {
- this.opts = {
- ...getDefaultInternalWorkerChoiceStrategyOptions(
- pool.info.maxSize + Object.keys(opts?.weights ?? {}).length
- ),
- ...opts
- }
+ this.opts = buildInternalWorkerChoiceStrategyOptions(
+ pool.info.maxSize,
+ opts
+ )
for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
workerChoiceStrategy.setOptions(this.opts)
}
import { getRandomValues } from 'node:crypto'
import { Worker as ClusterWorker } from 'node:cluster'
import { Worker as ThreadWorker } from 'node:worker_threads'
+import { cpus } from 'node:os'
import type {
InternalWorkerChoiceStrategyOptions,
MeasurementStatisticsRequirements
* @param retries - The number of worker choice retries.
* @returns The default worker choice strategy options.
*/
-export const getDefaultInternalWorkerChoiceStrategyOptions = (
+const getDefaultInternalWorkerChoiceStrategyOptions = (
retries: number
): InternalWorkerChoiceStrategyOptions => {
return {
return result
}
}
+
+const clone = <T extends object>(object: T): T => {
+ return JSON.parse(JSON.stringify(object)) as T
+}
+
+export const buildInternalWorkerChoiceStrategyOptions = (
+ poolMaxSize: number,
+ opts?: InternalWorkerChoiceStrategyOptions
+): InternalWorkerChoiceStrategyOptions => {
+ opts = clone(opts ?? {})
+ if (opts.weights == null) {
+ opts.weights = getDefaultWeights(poolMaxSize)
+ }
+ return {
+ ...getDefaultInternalWorkerChoiceStrategyOptions(
+ poolMaxSize + Object.keys(opts?.weights ?? {}).length
+ ),
+ ...opts
+ }
+}
+
+const getDefaultWeights = (
+ poolMaxSize: number,
+ defaultWorkerWeight: number = getDefaultWorkerWeight()
+): Record<number, number> => {
+ const weights: Record<number, number> = {}
+ for (let workerNodeKey = 0; workerNodeKey < poolMaxSize; workerNodeKey++) {
+ weights[workerNodeKey] = defaultWorkerWeight
+ }
+ return weights
+}
+
+const getDefaultWorkerWeight = (): number => {
+ let cpusCycleTimeWeight = 0
+ for (const cpu of cpus()) {
+ // CPU estimated cycle time
+ const numberOfDigits = cpu.speed.toString().length - 1
+ const cpuCycleTime = 1 / (cpu.speed / Math.pow(10, numberOfDigits))
+ cpusCycleTimeWeight += cpuCycleTime * Math.pow(10, numberOfDigits)
+ }
+ return Math.round(cpusCycleTimeWeight / cpus().length)
+}
workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN
})
expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(workerChoiceStrategy.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
}
await pool.destroy()
)
expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(workerChoiceStrategy.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
}
expect(
elu: { median: true }
})
expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
runTime: { median: true },
waitTime: { median: false },
- elu: { median: true }
+ elu: { median: true },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(workerChoiceStrategy.opts.weights).length,
runTime: { median: true },
waitTime: { median: false },
- elu: { median: true }
+ elu: { median: true },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
}
expect(
elu: { median: false }
})
expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(workerChoiceStrategy.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
}
expect(
)
expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
await pool.destroy()
}
)
expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
expect(pool.workerChoiceStrategyContext.opts).toStrictEqual({
- retries: pool.info.maxSize,
+ retries:
+ pool.info.maxSize +
+ Object.keys(pool.workerChoiceStrategyContext.opts.weights).length,
runTime: { median: false },
waitTime: { median: false },
- elu: { median: false }
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
})
await pool.destroy()
}
if (
workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
) {
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
workerChoiceStrategy ===
WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
) {
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
- ).roundWeights
- ).toStrictEqual([
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ])
+ ).roundWeights.length
+ ).toBe(1)
}
}
await pool.destroy()
pool.workerChoiceStrategyContext.workerChoiceStrategy
).previousWorkerNodeKey
).toBe(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
pool.workerChoiceStrategyContext.workerChoiceStrategy
).previousWorkerNodeKey
).toBe(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
pool.workerChoiceStrategyContext.workerChoiceStrategy
).previousWorkerNodeKey
).toBe(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
workerChoiceStrategy
).previousWorkerNodeKey
).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeDefined()
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
pool.workerChoiceStrategyContext.workerChoiceStrategy
).previousWorkerNodeKey
).toBe(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
workerChoiceStrategy
).previousWorkerNodeKey
).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeDefined()
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
pool.workerChoiceStrategyContext.workerChoiceStrategy
).previousWorkerNodeKey
).toBe(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
max * maxMultiplier
)
}
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).roundWeights
- ).toStrictEqual([
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ])
+ ).roundWeights.length
+ ).toBe(1)
// We need to clean up the resources after our test
await pool.destroy()
})
max * maxMultiplier
)
}
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).roundWeights
- ).toStrictEqual([
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ])
+ ).roundWeights.length
+ ).toBe(1)
// We need to clean up the resources after our test
await pool.destroy()
})
workerChoiceStrategy
).previousWorkerNodeKey
).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeDefined()
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).roundWeights
- ).toStrictEqual([
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ])
+ ).roundWeights.length
+ ).toBe(1)
await pool.destroy()
pool = new DynamicThreadPool(
min,
workerChoiceStrategy
).previousWorkerNodeKey
).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeDefined()
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
workerChoiceStrategy
expect(
pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ).toBeGreaterThan(0)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).roundWeights
- ).toStrictEqual([
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).defaultWorkerWeight
- ])
+ ).roundWeights.length
+ ).toBe(1)
// We need to clean up the resources after our test
await pool.destroy()
})
)
expect(() => workerChoiceStrategyContext.execute()).toThrow(
new Error(
- `Worker node key chosen is null or undefined after ${fixedPool.info.maxSize} retries`
+ `Worker node key chosen is null or undefined after ${
+ fixedPool.info.maxSize +
+ Object.keys(workerChoiceStrategyContext.opts.weights).length
+ } retries`
)
)
const workerChoiceStrategyNullStub = createStubInstance(
)
expect(() => workerChoiceStrategyContext.execute()).toThrow(
new Error(
- `Worker node key chosen is null or undefined after ${fixedPool.info.maxSize} retries`
+ `Worker node key chosen is null or undefined after ${
+ fixedPool.info.maxSize +
+ Object.keys(workerChoiceStrategyContext.opts.weights).length
+ } retries`
)
)
})
EMPTY_FUNCTION,
availableParallelism,
average,
+ buildInternalWorkerChoiceStrategyOptions,
exponentialDelay,
- getDefaultInternalWorkerChoiceStrategyOptions,
getWorkerId,
getWorkerType,
isAsyncFunction,
expect(EMPTY_FUNCTION).toStrictEqual(expect.any(Function))
})
- it('Verify getDefaultInternalWorkerChoiceStrategyOptions() values', () => {
- const poolMaxSize = 10
- expect(
- getDefaultInternalWorkerChoiceStrategyOptions(poolMaxSize)
- ).toStrictEqual({
- retries: poolMaxSize,
- runTime: { median: false },
- waitTime: { median: false },
- elu: { median: false }
- })
- })
-
it('Verify DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS values', () => {
expect(DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS).toStrictEqual({
aggregate: false,
expect(max(1, 1)).toBe(1)
})
+ it('Verify buildInternalWorkerChoiceStrategyOptions() behavior', () => {
+ const poolMaxSize = 10
+ const internalWorkerChoiceStrategyOptions =
+ buildInternalWorkerChoiceStrategyOptions(poolMaxSize)
+ expect(internalWorkerChoiceStrategyOptions).toStrictEqual({
+ retries:
+ poolMaxSize +
+ Object.keys(internalWorkerChoiceStrategyOptions.weights).length,
+ runTime: { median: false },
+ waitTime: { median: false },
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [poolMaxSize - 1]: expect.any(Number)
+ })
+ })
+ })
+
// it('Verify once()', () => {
// let called = 0
// const fn = () => ++called