## [Unreleased]
+### Changed
+
+- BREAKING CHANGE: `listTaskFunctionNames()` to `listTaskFunctionsProperties()` in pool and worker API returning registered task functions properties.
+
## [3.1.30] - 2024-04-22
### Fixed:
- [`pool.hasTaskFunction(name)`](#poolhastaskfunctionname)
- [`pool.addTaskFunction(name, fn)`](#pooladdtaskfunctionname-fn)
- [`pool.removeTaskFunction(name)`](#poolremovetaskfunctionname)
- - [`pool.listTaskFunctionNames()`](#poollisttaskfunctionnames)
+ - [`pool.listTaskFunctionsProperties()`](#poollisttaskfunctionsproperties)
- [`pool.setDefaultTaskFunction(name)`](#poolsetdefaulttaskfunctionname)
- [Pool options](#pool-options)
- [Worker](#worker)
- [`YourWorker.hasTaskFunction(name)`](#yourworkerhastaskfunctionname)
- [`YourWorker.addTaskFunction(name, fn)`](#yourworkeraddtaskfunctionname-fn)
- [`YourWorker.removeTaskFunction(name)`](#yourworkerremovetaskfunctionname)
- - [`YourWorker.listTaskFunctionNames()`](#yourworkerlisttaskfunctionnames)
+ - [`YourWorker.listTaskFunctionsProperties()`](#yourworkerlisttaskfunctionsproperties)
- [`YourWorker.setDefaultTaskFunction(name)`](#yourworkersetdefaulttaskfunctionname)
## Pool
This method is available on both pool implementations and returns a boolean promise.
-### `pool.listTaskFunctionNames()`
+### `pool.listTaskFunctionsProperties()`
-This method is available on both pool implementations and returns an array of the task function names.
+This method is available on both pool implementations and returns an array of the task function properties.
### `pool.setDefaultTaskFunction(name)`
- `exitHandler` (optional) - A function that will listen for exit event on each worker.
Default: `() => {}`
-- `workerChoiceStrategy` (optional) - The worker choice strategy to use in this pool:
+- `workerChoiceStrategy` (optional) - The default worker choice strategy to use in this pool:
- `WorkerChoiceStrategies.ROUND_ROBIN`: Submit tasks to worker in a round robin fashion
- `WorkerChoiceStrategies.LEAST_USED`: Submit tasks to the worker with the minimum number of executed, executing and queued tasks
This method is available on both worker implementations and returns `{ status: boolean, error?: Error }`.
-#### `YourWorker.listTaskFunctionNames()`
+#### `YourWorker.listTaskFunctionsProperties()`
-This method is available on both worker implementations and returns an array of the task function names.
+This method is available on both worker implementations and returns an array of the task function properties.
#### `YourWorker.setDefaultTaskFunction(name)`
}
}
+ /**
+ * Increments the size of the deque.
+ *
+ * @returns The new size of the deque.
+ */
private incrementSize (): number {
++this.size
if (this.size > this.maxSize) {
Measurements,
WorkerChoiceStrategies
} from './pools/selection-strategies/selection-strategies-types.js'
-export type { WorkerChoiceStrategyContext } from './pools/selection-strategies/worker-choice-strategy-context.js'
+export type { WorkerChoiceStrategiesContext } from './pools/selection-strategies/worker-choice-strategies-context.js'
export { DynamicThreadPool } from './pools/thread/dynamic.js'
export type { ThreadPoolOptions } from './pools/thread/fixed.js'
export { FixedThreadPool } from './pools/thread/fixed.js'
import type {
MessageValue,
PromiseResponseWrapper,
- Task
+ Task,
+ TaskFunctionProperties
} from '../utility-types.js'
import {
average,
+ buildTaskFunctionProperties,
DEFAULT_TASK_NAME,
EMPTY_FUNCTION,
exponentialDelay,
round,
sleep
} from '../utils.js'
-import type { TaskFunction } from '../worker/task-functions.js'
+import type {
+ TaskFunction,
+ TaskFunctionObject
+} from '../worker/task-functions.js'
import { KillBehaviors } from '../worker/worker-options.js'
import {
type IPool,
type WorkerChoiceStrategy,
type WorkerChoiceStrategyOptions
} from './selection-strategies/selection-strategies-types.js'
-import { WorkerChoiceStrategyContext } from './selection-strategies/worker-choice-strategy-context.js'
+import { WorkerChoiceStrategiesContext } from './selection-strategies/worker-choice-strategies-context.js'
import {
checkFilePath,
checkValidTasksQueueOptions,
/**
* The task execution response promise map:
* - `key`: The message id of each submitted task.
- * - `value`: An object that contains the worker, the execution response promise resolve and reject callbacks.
+ * - `value`: An object that contains task's worker node key, execution response promise resolve and reject callbacks, async resource.
*
* When we receive a message from the worker, we get a map entry with the promise resolve/reject bound to the message id.
*/
new Map<string, PromiseResponseWrapper<Response>>()
/**
- * Worker choice strategy context referencing a worker choice algorithm implementation.
+ * Worker choice strategies context referencing worker choice algorithms implementation.
*/
- protected workerChoiceStrategyContext?: WorkerChoiceStrategyContext<
+ protected workerChoiceStrategiesContext?: WorkerChoiceStrategiesContext<
Worker,
Data,
Response
/**
* The task functions added at runtime map:
* - `key`: The task function name.
- * - `value`: The task function itself.
+ * - `value`: The task function object.
*/
- private readonly taskFunctions: Map<string, TaskFunction<Data, Response>>
+ private readonly taskFunctions: Map<
+ string,
+ TaskFunctionObject<Data, Response>
+ >
/**
* Whether the pool is started or not.
if (this.opts.enableEvents === true) {
this.initializeEventEmitter()
}
- this.workerChoiceStrategyContext = new WorkerChoiceStrategyContext<
+ this.workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext<
Worker,
Data,
Response
>(
this,
- this.opts.workerChoiceStrategy,
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ [this.opts.workerChoiceStrategy!],
this.opts.workerChoiceStrategyOptions
)
this.setupHook()
- this.taskFunctions = new Map<string, TaskFunction<Data, Response>>()
+ this.taskFunctions = new Map<string, TaskFunctionObject<Data, Response>>()
this.started = false
this.starting = false
started: this.started,
ready: this.ready,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- strategy: this.opts.workerChoiceStrategy!,
- strategyRetries: this.workerChoiceStrategyContext?.retriesCount ?? 0,
+ defaultStrategy: this.opts.workerChoiceStrategy!,
+ strategyRetries: this.workerChoiceStrategiesContext?.retriesCount ?? 0,
minSize: this.minimumNumberOfWorkers,
maxSize: this.maximumNumberOfWorkers ?? this.minimumNumberOfWorkers,
- ...(this.workerChoiceStrategyContext?.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
.runTime.aggregate === true &&
- this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
.waitTime.aggregate && {
utilization: round(this.utilization)
}),
accumulator + workerNode.usage.tasks.failed,
0
),
- ...(this.workerChoiceStrategyContext?.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
.runTime.aggregate === true && {
runTime: {
minimum: round(
)
)
),
- ...(this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
.runTime.average && {
average: round(
average(
)
)
}),
- ...(this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
.runTime.median && {
median: round(
median(
})
}
}),
- ...(this.workerChoiceStrategyContext?.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
.waitTime.aggregate === true && {
waitTime: {
minimum: round(
)
)
),
- ...(this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
.waitTime.average && {
average: round(
average(
)
)
}),
- ...(this.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ ...(this.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
.waitTime.median && {
median: round(
median(
workerChoiceStrategy: WorkerChoiceStrategy,
workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions
): void {
+ let requireSync = false
checkValidWorkerChoiceStrategy(workerChoiceStrategy)
- this.opts.workerChoiceStrategy = workerChoiceStrategy
- this.workerChoiceStrategyContext?.setWorkerChoiceStrategy(
- this.opts.workerChoiceStrategy
- )
if (workerChoiceStrategyOptions != null) {
- this.setWorkerChoiceStrategyOptions(workerChoiceStrategyOptions)
+ requireSync = this.setWorkerChoiceStrategyOptions(
+ workerChoiceStrategyOptions
+ )
}
- for (const [workerNodeKey, workerNode] of this.workerNodes.entries()) {
- workerNode.resetUsage()
- this.sendStatisticsMessageToWorker(workerNodeKey)
+ if (workerChoiceStrategy !== this.opts.workerChoiceStrategy) {
+ this.opts.workerChoiceStrategy = workerChoiceStrategy
+ this.workerChoiceStrategiesContext?.setDefaultWorkerChoiceStrategy(
+ this.opts.workerChoiceStrategy,
+ this.opts.workerChoiceStrategyOptions
+ )
+ requireSync = true
+ }
+ if (requireSync) {
+ this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
+ this.getWorkerWorkerChoiceStrategies(),
+ this.opts.workerChoiceStrategyOptions
+ )
+ for (const workerNodeKey of this.workerNodes.keys()) {
+ this.sendStatisticsMessageToWorker(workerNodeKey)
+ }
}
}
/** @inheritDoc */
public setWorkerChoiceStrategyOptions (
workerChoiceStrategyOptions: WorkerChoiceStrategyOptions | undefined
- ): void {
+ ): boolean {
this.checkValidWorkerChoiceStrategyOptions(workerChoiceStrategyOptions)
if (workerChoiceStrategyOptions != null) {
this.opts.workerChoiceStrategyOptions = workerChoiceStrategyOptions
+ this.workerChoiceStrategiesContext?.setOptions(
+ this.opts.workerChoiceStrategyOptions
+ )
+ this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
+ this.getWorkerWorkerChoiceStrategies(),
+ this.opts.workerChoiceStrategyOptions
+ )
+ for (const workerNodeKey of this.workerNodes.keys()) {
+ this.sendStatisticsMessageToWorker(workerNodeKey)
+ }
+ return true
}
- this.workerChoiceStrategyContext?.setOptions(
- this.opts.workerChoiceStrategyOptions
- )
+ return false
}
/** @inheritDoc */
/** @inheritDoc */
public hasTaskFunction (name: string): boolean {
- for (const workerNode of this.workerNodes) {
- if (
- Array.isArray(workerNode.info.taskFunctionNames) &&
- workerNode.info.taskFunctionNames.includes(name)
- ) {
- return true
- }
- }
- return false
+ return this.listTaskFunctionsProperties().some(
+ taskFunctionProperties => taskFunctionProperties.name === name
+ )
}
/** @inheritDoc */
public async addTaskFunction (
name: string,
- fn: TaskFunction<Data, Response>
+ fn: TaskFunction<Data, Response> | TaskFunctionObject<Data, Response>
): Promise<boolean> {
if (typeof name !== 'string') {
throw new TypeError('name argument must be a string')
if (typeof name === 'string' && name.trim().length === 0) {
throw new TypeError('name argument must not be an empty string')
}
- if (typeof fn !== 'function') {
- throw new TypeError('fn argument must be a function')
+ if (typeof fn === 'function') {
+ fn = { taskFunction: fn } satisfies TaskFunctionObject<Data, Response>
+ }
+ if (typeof fn.taskFunction !== 'function') {
+ throw new TypeError('taskFunction property must be a function')
}
const opResult = await this.sendTaskFunctionOperationToWorkers({
taskFunctionOperation: 'add',
- taskFunctionName: name,
- taskFunction: fn.toString()
+ taskFunctionProperties: buildTaskFunctionProperties(name, fn),
+ taskFunction: fn.taskFunction.toString()
})
this.taskFunctions.set(name, fn)
+ this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
+ this.getWorkerWorkerChoiceStrategies()
+ )
return opResult
}
}
const opResult = await this.sendTaskFunctionOperationToWorkers({
taskFunctionOperation: 'remove',
- taskFunctionName: name
+ taskFunctionProperties: buildTaskFunctionProperties(
+ name,
+ this.taskFunctions.get(name)
+ )
})
this.deleteTaskFunctionWorkerUsages(name)
this.taskFunctions.delete(name)
+ this.workerChoiceStrategiesContext?.syncWorkerChoiceStrategies(
+ this.getWorkerWorkerChoiceStrategies()
+ )
return opResult
}
/** @inheritDoc */
- public listTaskFunctionNames (): string[] {
+ public listTaskFunctionsProperties (): TaskFunctionProperties[] {
for (const workerNode of this.workerNodes) {
if (
- Array.isArray(workerNode.info.taskFunctionNames) &&
- workerNode.info.taskFunctionNames.length > 0
+ Array.isArray(workerNode.info.taskFunctionsProperties) &&
+ workerNode.info.taskFunctionsProperties.length > 0
) {
- return workerNode.info.taskFunctionNames
+ return workerNode.info.taskFunctionsProperties
}
}
return []
}
+ /**
+ * Gets task function 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.
+ */
+ private readonly getTaskFunctionWorkerWorkerChoiceStrategy = (
+ name?: string
+ ): WorkerChoiceStrategy | undefined => {
+ if (name != null) {
+ return this.listTaskFunctionsProperties().find(
+ (taskFunctionProperties: TaskFunctionProperties) =>
+ taskFunctionProperties.name === name
+ )?.strategy
+ }
+ }
+
+ /**
+ * Gets the worker choice strategies registered in this pool.
+ *
+ * @returns The worker choice strategies.
+ */
+ private readonly getWorkerWorkerChoiceStrategies =
+ (): Set<WorkerChoiceStrategy> => {
+ return new Set([
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ this.opts.workerChoiceStrategy!,
+ ...(this.listTaskFunctionsProperties()
+ .map(
+ (taskFunctionProperties: TaskFunctionProperties) =>
+ taskFunctionProperties.strategy
+ )
+ .filter(
+ (strategy: WorkerChoiceStrategy | undefined) => strategy != null
+ ) as WorkerChoiceStrategy[])
+ ])
+ }
+
/** @inheritDoc */
public async setDefaultTaskFunction (name: string): Promise<boolean> {
return await this.sendTaskFunctionOperationToWorkers({
taskFunctionOperation: 'default',
- taskFunctionName: name
+ taskFunctionProperties: buildTaskFunctionProperties(
+ name,
+ this.taskFunctions.get(name)
+ )
})
}
return
}
const timestamp = performance.now()
- const workerNodeKey = this.chooseWorkerNode()
+ const workerNodeKey = this.chooseWorkerNode(
+ this.getTaskFunctionWorkerWorkerChoiceStrategy(name)
+ )
const task: Task<Data> = {
name: name ?? DEFAULT_TASK_NAME,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
const workerUsage = this.workerNodes[workerNodeKey].usage
++workerUsage.tasks.executing
updateWaitTimeWorkerUsage(
- this.workerChoiceStrategyContext,
+ this.workerChoiceStrategiesContext,
workerUsage,
task
)
].getTaskFunctionWorkerUsage(task.name!)!
++taskFunctionWorkerUsage.tasks.executing
updateWaitTimeWorkerUsage(
- this.workerChoiceStrategyContext,
+ this.workerChoiceStrategiesContext,
taskFunctionWorkerUsage,
task
)
const workerUsage = this.workerNodes[workerNodeKey].usage
updateTaskStatisticsWorkerUsage(workerUsage, message)
updateRunTimeWorkerUsage(
- this.workerChoiceStrategyContext,
+ this.workerChoiceStrategiesContext,
workerUsage,
message
)
updateEluWorkerUsage(
- this.workerChoiceStrategyContext,
+ this.workerChoiceStrategiesContext,
workerUsage,
message
)
].getTaskFunctionWorkerUsage(message.taskPerformance!.name)!
updateTaskStatisticsWorkerUsage(taskFunctionWorkerUsage, message)
updateRunTimeWorkerUsage(
- this.workerChoiceStrategyContext,
+ this.workerChoiceStrategiesContext,
taskFunctionWorkerUsage,
message
)
updateEluWorkerUsage(
- this.workerChoiceStrategyContext,
+ this.workerChoiceStrategiesContext,
taskFunctionWorkerUsage,
message
)
needWorkerChoiceStrategyUpdate = true
}
if (needWorkerChoiceStrategyUpdate) {
- this.workerChoiceStrategyContext?.update(workerNodeKey)
+ this.workerChoiceStrategiesContext?.update(workerNodeKey)
}
}
const workerInfo = this.getWorkerInfo(workerNodeKey)
return (
workerInfo != null &&
- Array.isArray(workerInfo.taskFunctionNames) &&
- workerInfo.taskFunctionNames.length > 2
+ Array.isArray(workerInfo.taskFunctionsProperties) &&
+ workerInfo.taskFunctionsProperties.length > 2
)
}
/**
* Chooses a worker node for the next task.
*
- * The default worker choice strategy uses a round robin algorithm to distribute the tasks.
- *
+ * @param workerChoiceStrategy - The worker choice strategy.
* @returns The chosen worker node key
*/
- private chooseWorkerNode (): number {
+ private chooseWorkerNode (
+ workerChoiceStrategy?: WorkerChoiceStrategy
+ ): number {
if (this.shallCreateDynamicWorker()) {
const workerNodeKey = this.createAndSetupDynamicWorkerNode()
if (
- this.workerChoiceStrategyContext?.getStrategyPolicy()
- .dynamicWorkerUsage === true
+ this.workerChoiceStrategiesContext?.getPolicy().dynamicWorkerUsage ===
+ true
) {
return workerNodeKey
}
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.workerChoiceStrategyContext!.execute()
+ return this.workerChoiceStrategiesContext!.execute(workerChoiceStrategy)
}
/**
checkActive: true
})
if (this.taskFunctions.size > 0) {
- for (const [taskFunctionName, taskFunction] of this.taskFunctions) {
+ for (const [taskFunctionName, taskFunctionObject] of this.taskFunctions) {
this.sendTaskFunctionOperationToWorker(workerNodeKey, {
taskFunctionOperation: 'add',
- taskFunctionName,
- taskFunction: taskFunction.toString()
+ taskFunctionProperties: buildTaskFunctionProperties(
+ taskFunctionName,
+ taskFunctionObject
+ ),
+ taskFunction: taskFunctionObject.taskFunction.toString()
}).catch((error: unknown) => {
this.emitter?.emit(PoolEvents.error, error)
})
const workerNode = this.workerNodes[workerNodeKey]
workerNode.info.dynamic = true
if (
- this.workerChoiceStrategyContext?.getStrategyPolicy()
- .dynamicWorkerReady === true ||
- this.workerChoiceStrategyContext?.getStrategyPolicy()
- .dynamicWorkerUsage === true
+ this.workerChoiceStrategiesContext?.getPolicy().dynamicWorkerReady ===
+ true ||
+ this.workerChoiceStrategiesContext?.getPolicy().dynamicWorkerUsage ===
+ true
) {
workerNode.info.ready = true
}
this.sendToWorker(workerNodeKey, {
statistics: {
runTime:
- this.workerChoiceStrategyContext?.getTaskStatisticsRequirements()
+ this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
.runTime.aggregate ?? false,
elu:
- this.workerChoiceStrategyContext?.getTaskStatisticsRequirements().elu
- .aggregate ?? false
+ this.workerChoiceStrategiesContext?.getTaskStatisticsRequirements()
+ .elu.aggregate ?? false
}
})
}
) {
workerInfo.stealing = false
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- for (const taskName of workerInfo.taskFunctionNames!) {
+ for (const taskFunctionProperties of workerInfo.taskFunctionsProperties!) {
this.resetTaskSequentiallyStolenStatisticsTaskFunctionWorkerUsage(
workerNodeKey,
- taskName
+ taskFunctionProperties.name
)
}
this.resetTaskSequentiallyStolenStatisticsWorkerUsage(workerNodeKey)
message: MessageValue<Response>
): void => {
this.checkMessageWorkerId(message)
- const { workerId, ready, taskId, taskFunctionNames } = message
- if (ready != null && taskFunctionNames != null) {
+ const { workerId, ready, taskId, taskFunctionsProperties } = message
+ if (ready != null && taskFunctionsProperties != null) {
// Worker ready response received from worker
this.handleWorkerReadyResponse(message)
- } else if (taskId != null) {
- // Task execution response received from worker
- this.handleTaskExecutionResponse(message)
- } else if (taskFunctionNames != null) {
- // Task function names message received from worker
+ } else if (taskFunctionsProperties != null) {
+ // Task function properties message received from worker
const workerInfo = this.getWorkerInfo(
this.getWorkerNodeKeyByWorkerId(workerId)
)
if (workerInfo != null) {
- workerInfo.taskFunctionNames = taskFunctionNames
+ workerInfo.taskFunctionsProperties = taskFunctionsProperties
}
+ } else if (taskId != null) {
+ // Task execution response received from worker
+ this.handleTaskExecutionResponse(message)
}
}
}
private handleWorkerReadyResponse (message: MessageValue<Response>): void {
- const { workerId, ready, taskFunctionNames } = message
+ const { workerId, ready, taskFunctionsProperties } = message
if (ready == null || !ready) {
throw new Error(`Worker ${workerId} failed to initialize`)
}
const workerNode =
this.workerNodes[this.getWorkerNodeKeyByWorkerId(workerId)]
workerNode.info.ready = ready
- workerNode.info.taskFunctionNames = taskFunctionNames
+ workerNode.info.taskFunctionsProperties = taskFunctionsProperties
this.checkAndEmitReadyEvent()
}
const workerNodeKey = this.workerNodes.indexOf(workerNode)
if (workerNodeKey !== -1) {
this.workerNodes.splice(workerNodeKey, 1)
- this.workerChoiceStrategyContext?.remove(workerNodeKey)
+ this.workerChoiceStrategiesContext?.remove(workerNodeKey)
}
this.checkAndEmitEmptyEvent()
}
import type { EventEmitterAsyncResource } from 'node:events'
import type { TransferListItem, WorkerOptions } from 'node:worker_threads'
+import type { TaskFunctionProperties } from '../utility-types.js'
import type { TaskFunction } from '../worker/task-functions.js'
import type {
WorkerChoiceStrategy,
readonly worker: WorkerType
readonly started: boolean
readonly ready: boolean
- readonly strategy: WorkerChoiceStrategy
+ readonly defaultStrategy: WorkerChoiceStrategy
readonly strategyRetries: number
readonly minSize: number
readonly maxSize: number
*/
startWorkers?: boolean
/**
- * The worker choice strategy to use in this pool.
+ * The default worker choice strategy to use in this pool.
*
* @defaultValue WorkerChoiceStrategies.ROUND_ROBIN
*/
*/
readonly removeTaskFunction: (name: string) => Promise<boolean>
/**
- * Lists the names of task function available in this pool.
+ * Lists the properties of task functions available in this pool.
*
- * @returns The names of task function available in this pool.
+ * @returns The properties of task functions available in this pool.
*/
- readonly listTaskFunctionNames: () => string[]
+ readonly listTaskFunctionsProperties: () => TaskFunctionProperties[]
/**
* Sets the default task function in this pool.
*
*/
readonly setDefaultTaskFunction: (name: string) => Promise<boolean>
/**
- * Sets the worker choice strategy in this pool.
+ * Sets the default worker choice strategy in this pool.
*
- * @param workerChoiceStrategy - The worker choice strategy.
+ * @param workerChoiceStrategy - The default worker choice strategy.
* @param workerChoiceStrategyOptions - The worker choice strategy options.
*/
readonly setWorkerChoiceStrategy: (
* Sets the worker choice strategy options in this pool.
*
* @param workerChoiceStrategyOptions - The worker choice strategy options.
+ * @returns `true` if the worker choice strategy options were set, `false` otherwise.
*/
readonly setWorkerChoiceStrategyOptions: (
workerChoiceStrategyOptions: WorkerChoiceStrategyOptions
- ) => void
+ ) => boolean
/**
* Enables/disables the worker node tasks queue in this pool.
*
import type { IPool } from '../pool.js'
-import {
- buildWorkerChoiceStrategyOptions,
- DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS
-} from '../utils.js'
+import { DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS } from '../utils.js'
import type { IWorker } from '../worker.js'
import type {
IWorkerChoiceStrategy,
TaskStatisticsRequirements,
WorkerChoiceStrategyOptions
} from './selection-strategies-types.js'
+import { buildWorkerChoiceStrategyOptions } from './selection-strategies-utils.js'
/**
* Worker choice strategy abstract base class.
--- /dev/null
+import { cpus } from 'node:os'
+
+import type { IPool } from '../pool.js'
+import type { IWorker } from '../worker.js'
+import { FairShareWorkerChoiceStrategy } from './fair-share-worker-choice-strategy.js'
+import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from './interleaved-weighted-round-robin-worker-choice-strategy.js'
+import { LeastBusyWorkerChoiceStrategy } from './least-busy-worker-choice-strategy.js'
+import { LeastEluWorkerChoiceStrategy } from './least-elu-worker-choice-strategy.js'
+import { LeastUsedWorkerChoiceStrategy } from './least-used-worker-choice-strategy.js'
+import { RoundRobinWorkerChoiceStrategy } from './round-robin-worker-choice-strategy.js'
+import {
+ type IWorkerChoiceStrategy,
+ type StrategyPolicy,
+ type TaskStatisticsRequirements,
+ WorkerChoiceStrategies,
+ type WorkerChoiceStrategy,
+ type WorkerChoiceStrategyOptions
+} from './selection-strategies-types.js'
+import { WeightedRoundRobinWorkerChoiceStrategy } from './weighted-round-robin-worker-choice-strategy.js'
+import type { WorkerChoiceStrategiesContext } from './worker-choice-strategies-context.js'
+
+const estimatedCpuSpeed = (): number => {
+ const runs = 150000000
+ const begin = performance.now()
+ // eslint-disable-next-line no-empty
+ for (let i = runs; i > 0; i--) {}
+ const end = performance.now()
+ const duration = end - begin
+ return Math.trunc(runs / duration / 1000) // in MHz
+}
+
+const getDefaultWorkerWeight = (): number => {
+ const currentCpus = cpus()
+ let estCpuSpeed: number | undefined
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (currentCpus.every(cpu => cpu.speed == null || cpu.speed === 0)) {
+ estCpuSpeed = estimatedCpuSpeed()
+ }
+ let cpusCycleTimeWeight = 0
+ for (const cpu of currentCpus) {
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (cpu.speed == null || cpu.speed === 0) {
+ cpu.speed =
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ currentCpus.find(cpu => cpu.speed != null && cpu.speed !== 0)?.speed ??
+ estCpuSpeed ??
+ 2000
+ }
+ // 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 / currentCpus.length)
+}
+
+const getDefaultWeights = (
+ poolMaxSize: number,
+ defaultWorkerWeight?: number
+): Record<number, number> => {
+ defaultWorkerWeight = defaultWorkerWeight ?? getDefaultWorkerWeight()
+ const weights: Record<number, number> = {}
+ for (let workerNodeKey = 0; workerNodeKey < poolMaxSize; workerNodeKey++) {
+ weights[workerNodeKey] = defaultWorkerWeight
+ }
+ return weights
+}
+
+export const getWorkerChoiceStrategiesRetries = <
+ Worker extends IWorker,
+ Data,
+ Response
+>(
+ pool: IPool<Worker, Data, Response>,
+ opts?: WorkerChoiceStrategyOptions
+ ): number => {
+ return (
+ pool.info.maxSize +
+ Object.keys(opts?.weights ?? getDefaultWeights(pool.info.maxSize)).length
+ )
+}
+
+export const buildWorkerChoiceStrategyOptions = <
+ Worker extends IWorker,
+ Data,
+ Response
+>(
+ pool: IPool<Worker, Data, Response>,
+ opts?: WorkerChoiceStrategyOptions
+ ): WorkerChoiceStrategyOptions => {
+ opts = structuredClone(opts ?? {})
+ opts.weights = opts.weights ?? getDefaultWeights(pool.info.maxSize)
+ return {
+ ...{
+ runTime: { median: false },
+ waitTime: { median: false },
+ elu: { median: false }
+ },
+ ...opts
+ }
+}
+
+export const buildWorkerChoiceStrategiesPolicy = (
+ workerChoiceStrategies: Map<WorkerChoiceStrategy, IWorkerChoiceStrategy>
+): StrategyPolicy => {
+ const policies: StrategyPolicy[] = []
+ for (const workerChoiceStrategy of workerChoiceStrategies.values()) {
+ policies.push(workerChoiceStrategy.strategyPolicy)
+ }
+ return {
+ dynamicWorkerUsage: policies.some(p => p.dynamicWorkerUsage),
+ dynamicWorkerReady: policies.some(p => p.dynamicWorkerReady)
+ }
+}
+
+export const buildWorkerChoiceStrategiesTaskStatisticsRequirements = (
+ workerChoiceStrategies: Map<WorkerChoiceStrategy, IWorkerChoiceStrategy>
+): TaskStatisticsRequirements => {
+ const taskStatisticsRequirements: TaskStatisticsRequirements[] = []
+ for (const workerChoiceStrategy of workerChoiceStrategies.values()) {
+ taskStatisticsRequirements.push(
+ workerChoiceStrategy.taskStatisticsRequirements
+ )
+ }
+ return {
+ runTime: {
+ aggregate: taskStatisticsRequirements.some(r => r.runTime.aggregate),
+ average: taskStatisticsRequirements.some(r => r.runTime.average),
+ median: taskStatisticsRequirements.some(r => r.runTime.median)
+ },
+ waitTime: {
+ aggregate: taskStatisticsRequirements.some(r => r.waitTime.aggregate),
+ average: taskStatisticsRequirements.some(r => r.waitTime.average),
+ median: taskStatisticsRequirements.some(r => r.waitTime.median)
+ },
+ elu: {
+ aggregate: taskStatisticsRequirements.some(r => r.elu.aggregate),
+ average: taskStatisticsRequirements.some(r => r.elu.average),
+ median: taskStatisticsRequirements.some(r => r.elu.median)
+ }
+ }
+}
+
+export const getWorkerChoiceStrategy = <Worker extends IWorker, Data, Response>(
+ workerChoiceStrategy: WorkerChoiceStrategy,
+ pool: IPool<Worker, Data, Response>,
+ context: ThisType<WorkerChoiceStrategiesContext<Worker, Data, Response>>,
+ opts?: WorkerChoiceStrategyOptions
+): IWorkerChoiceStrategy => {
+ switch (workerChoiceStrategy) {
+ case WorkerChoiceStrategies.ROUND_ROBIN:
+ return new (RoundRobinWorkerChoiceStrategy.bind(context))(pool, opts)
+ case WorkerChoiceStrategies.LEAST_USED:
+ return new (LeastUsedWorkerChoiceStrategy.bind(context))(pool, opts)
+ case WorkerChoiceStrategies.LEAST_BUSY:
+ return new (LeastBusyWorkerChoiceStrategy.bind(context))(pool, opts)
+ case WorkerChoiceStrategies.LEAST_ELU:
+ return new (LeastEluWorkerChoiceStrategy.bind(context))(pool, opts)
+ case WorkerChoiceStrategies.FAIR_SHARE:
+ return new (FairShareWorkerChoiceStrategy.bind(context))(pool, opts)
+ case WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN:
+ return new (WeightedRoundRobinWorkerChoiceStrategy.bind(context))(
+ pool,
+ opts
+ )
+ case WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN:
+ return new (InterleavedWeightedRoundRobinWorkerChoiceStrategy.bind(
+ context
+ ))(pool, opts)
+ default:
+ throw new Error(
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ `Worker choice strategy '${workerChoiceStrategy}' is not valid`
+ )
+ }
+}
--- /dev/null
+import type { IPool } from '../pool.js'
+import type { IWorker } from '../worker.js'
+import type {
+ IWorkerChoiceStrategy,
+ StrategyPolicy,
+ TaskStatisticsRequirements,
+ WorkerChoiceStrategy,
+ WorkerChoiceStrategyOptions
+} from './selection-strategies-types.js'
+import { WorkerChoiceStrategies } from './selection-strategies-types.js'
+import {
+ buildWorkerChoiceStrategiesPolicy,
+ buildWorkerChoiceStrategiesTaskStatisticsRequirements,
+ getWorkerChoiceStrategiesRetries,
+ getWorkerChoiceStrategy
+} from './selection-strategies-utils.js'
+
+/**
+ * The worker choice strategies context.
+ *
+ * @typeParam Worker - Type of worker.
+ * @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.
+ */
+export class WorkerChoiceStrategiesContext<
+ Worker extends IWorker,
+ Data = unknown,
+ Response = unknown
+> {
+ /**
+ * The number of worker choice strategies execution retries.
+ */
+ public retriesCount: number
+
+ /**
+ * The default worker choice strategy in the context.
+ */
+ private defaultWorkerChoiceStrategy: WorkerChoiceStrategy
+
+ /**
+ * The worker choice strategies registered in the context.
+ */
+ private readonly workerChoiceStrategies: Map<
+ WorkerChoiceStrategy,
+ IWorkerChoiceStrategy
+ >
+
+ /**
+ * The active worker choice strategies in the context policy.
+ */
+ private workerChoiceStrategiesPolicy: StrategyPolicy
+
+ /**
+ * The active worker choice strategies in the context task statistics requirements.
+ */
+ private workerChoiceStrategiesTaskStatisticsRequirements: TaskStatisticsRequirements
+
+ /**
+ * The maximum number of worker choice strategies execution retries.
+ */
+ private readonly retries: number
+
+ /**
+ * Worker choice strategies context constructor.
+ *
+ * @param pool - The pool instance.
+ * @param workerChoiceStrategies - The worker choice strategies. @defaultValue [WorkerChoiceStrategies.ROUND_ROBIN]
+ * @param opts - The worker choice strategy options.
+ */
+ public constructor (
+ private readonly pool: IPool<Worker, Data, Response>,
+ workerChoiceStrategies: WorkerChoiceStrategy[] = [
+ WorkerChoiceStrategies.ROUND_ROBIN
+ ],
+ opts?: WorkerChoiceStrategyOptions
+ ) {
+ this.execute = this.execute.bind(this)
+ this.defaultWorkerChoiceStrategy = workerChoiceStrategies[0]
+ this.workerChoiceStrategies = new Map<
+ WorkerChoiceStrategy,
+ IWorkerChoiceStrategy
+ >()
+ for (const workerChoiceStrategy of workerChoiceStrategies) {
+ this.addWorkerChoiceStrategy(workerChoiceStrategy, this.pool, opts)
+ }
+ this.workerChoiceStrategiesPolicy = buildWorkerChoiceStrategiesPolicy(
+ this.workerChoiceStrategies
+ )
+ this.workerChoiceStrategiesTaskStatisticsRequirements =
+ buildWorkerChoiceStrategiesTaskStatisticsRequirements(
+ this.workerChoiceStrategies
+ )
+ this.retriesCount = 0
+ this.retries = getWorkerChoiceStrategiesRetries<Worker, Data, Response>(
+ this.pool,
+ opts
+ )
+ }
+
+ /**
+ * Gets the active worker choice strategies in the context policy.
+ *
+ * @returns The strategies policy.
+ */
+ public getPolicy (): StrategyPolicy {
+ return this.workerChoiceStrategiesPolicy
+ }
+
+ /**
+ * Gets the active worker choice strategies in the context task statistics requirements.
+ *
+ * @returns The strategies task statistics requirements.
+ */
+ public getTaskStatisticsRequirements (): TaskStatisticsRequirements {
+ return this.workerChoiceStrategiesTaskStatisticsRequirements
+ }
+
+ /**
+ * Sets the default worker choice strategy to use in the context.
+ *
+ * @param workerChoiceStrategy - The default worker choice strategy to set.
+ * @param opts - The worker choice strategy options.
+ */
+ public setDefaultWorkerChoiceStrategy (
+ workerChoiceStrategy: WorkerChoiceStrategy,
+ opts?: WorkerChoiceStrategyOptions
+ ): void {
+ if (workerChoiceStrategy !== this.defaultWorkerChoiceStrategy) {
+ this.defaultWorkerChoiceStrategy = workerChoiceStrategy
+ this.addWorkerChoiceStrategy(workerChoiceStrategy, this.pool, opts)
+ }
+ }
+
+ /**
+ * Updates the worker node key in the active worker choice strategies in the context internals.
+ *
+ * @returns `true` if the update is successful, `false` otherwise.
+ */
+ public update (workerNodeKey: number): boolean {
+ const res: boolean[] = []
+ for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
+ res.push(workerChoiceStrategy.update(workerNodeKey))
+ }
+ return res.every(r => r)
+ }
+
+ /**
+ * Executes the given worker choice strategy in the context algorithm.
+ *
+ * @param workerChoiceStrategy - The worker choice strategy algorithm to execute. @defaultValue this.defaultWorkerChoiceStrategy
+ * @returns The key of the worker node.
+ * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
+ */
+ public execute (
+ workerChoiceStrategy: WorkerChoiceStrategy = this
+ .defaultWorkerChoiceStrategy
+ ): number {
+ return this.executeStrategy(
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ this.workerChoiceStrategies.get(workerChoiceStrategy)!
+ )
+ }
+
+ /**
+ * Executes the given worker choice strategy.
+ *
+ * @param workerChoiceStrategy - The worker choice strategy.
+ * @returns The key of the worker node.
+ * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
+ */
+ private executeStrategy (workerChoiceStrategy: IWorkerChoiceStrategy): number {
+ let workerNodeKey: number | undefined
+ let chooseCount = 0
+ let retriesCount = 0
+ do {
+ workerNodeKey = workerChoiceStrategy.choose()
+ if (workerNodeKey == null && chooseCount > 0) {
+ ++retriesCount
+ ++this.retriesCount
+ }
+ ++chooseCount
+ } while (workerNodeKey == null && retriesCount < this.retries)
+ if (workerNodeKey == null) {
+ throw new Error(
+ `Worker node key chosen is null or undefined after ${retriesCount} retries`
+ )
+ }
+ return workerNodeKey
+ }
+
+ /**
+ * Removes the worker node key from the active worker choice strategies in the context.
+ *
+ * @param workerNodeKey - The worker node key.
+ * @returns `true` if the removal is successful, `false` otherwise.
+ */
+ public remove (workerNodeKey: number): boolean {
+ const res: boolean[] = []
+ for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
+ res.push(workerChoiceStrategy.remove(workerNodeKey))
+ }
+ return res.every(r => r)
+ }
+
+ /**
+ * Sets the active worker choice strategies in the context options.
+ *
+ * @param opts - The worker choice strategy options.
+ */
+ public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
+ for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
+ workerChoiceStrategy.setOptions(opts)
+ }
+ }
+
+ /**
+ * Synchronizes the active worker choice strategies in the context with the given worker choice strategies.
+ *
+ * @param workerChoiceStrategies - The worker choice strategies to synchronize.
+ * @param opts - The worker choice strategy options.
+ */
+ public syncWorkerChoiceStrategies (
+ workerChoiceStrategies: Set<WorkerChoiceStrategy>,
+ opts?: WorkerChoiceStrategyOptions
+ ): void {
+ for (const workerChoiceStrategy of this.workerChoiceStrategies.keys()) {
+ if (!workerChoiceStrategies.has(workerChoiceStrategy)) {
+ this.removeWorkerChoiceStrategy(workerChoiceStrategy)
+ }
+ }
+ for (const workerChoiceStrategy of workerChoiceStrategies) {
+ if (!this.workerChoiceStrategies.has(workerChoiceStrategy)) {
+ this.addWorkerChoiceStrategy(workerChoiceStrategy, this.pool, opts)
+ }
+ }
+ this.workerChoiceStrategiesPolicy = buildWorkerChoiceStrategiesPolicy(
+ this.workerChoiceStrategies
+ )
+ this.workerChoiceStrategiesTaskStatisticsRequirements =
+ buildWorkerChoiceStrategiesTaskStatisticsRequirements(
+ this.workerChoiceStrategies
+ )
+ }
+
+ /**
+ * Adds a worker choice strategy to the context.
+ *
+ * @param workerChoiceStrategy - The worker choice strategy to add.
+ * @param opts - The worker choice strategy options.
+ * @returns The worker choice strategies.
+ */
+ private addWorkerChoiceStrategy (
+ workerChoiceStrategy: WorkerChoiceStrategy,
+ pool: IPool<Worker, Data, Response>,
+ opts?: WorkerChoiceStrategyOptions
+ ): Map<WorkerChoiceStrategy, IWorkerChoiceStrategy> {
+ if (!this.workerChoiceStrategies.has(workerChoiceStrategy)) {
+ return this.workerChoiceStrategies.set(
+ workerChoiceStrategy,
+ getWorkerChoiceStrategy<Worker, Data, Response>(
+ workerChoiceStrategy,
+ pool,
+ this,
+ opts
+ )
+ )
+ }
+ return this.workerChoiceStrategies
+ }
+
+ /**
+ * Removes a worker choice strategy from the context.
+ *
+ * @param workerChoiceStrategy - The worker choice strategy to remove.
+ * @returns `true` if the worker choice strategy is removed, `false` otherwise.
+ */
+ private removeWorkerChoiceStrategy (
+ workerChoiceStrategy: WorkerChoiceStrategy
+ ): boolean {
+ return this.workerChoiceStrategies.delete(workerChoiceStrategy)
+ }
+}
+++ /dev/null
-import type { IPool } from '../pool.js'
-import { getWorkerChoiceStrategyRetries } from '../utils.js'
-import type { IWorker } from '../worker.js'
-import { FairShareWorkerChoiceStrategy } from './fair-share-worker-choice-strategy.js'
-import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from './interleaved-weighted-round-robin-worker-choice-strategy.js'
-import { LeastBusyWorkerChoiceStrategy } from './least-busy-worker-choice-strategy.js'
-import { LeastEluWorkerChoiceStrategy } from './least-elu-worker-choice-strategy.js'
-import { LeastUsedWorkerChoiceStrategy } from './least-used-worker-choice-strategy.js'
-import { RoundRobinWorkerChoiceStrategy } from './round-robin-worker-choice-strategy.js'
-import type {
- IWorkerChoiceStrategy,
- StrategyPolicy,
- TaskStatisticsRequirements,
- WorkerChoiceStrategy,
- WorkerChoiceStrategyOptions
-} from './selection-strategies-types.js'
-import { WorkerChoiceStrategies } from './selection-strategies-types.js'
-import { WeightedRoundRobinWorkerChoiceStrategy } from './weighted-round-robin-worker-choice-strategy.js'
-
-/**
- * The worker choice strategy context.
- *
- * @typeParam Worker - Type of worker.
- * @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.
- */
-export class WorkerChoiceStrategyContext<
- Worker extends IWorker,
- Data = unknown,
- Response = unknown
-> {
- /**
- * The number of worker choice strategy execution retries.
- */
- public retriesCount: number
-
- /**
- * The worker choice strategy instances registered in the context.
- */
- private readonly workerChoiceStrategies: Map<
- WorkerChoiceStrategy,
- IWorkerChoiceStrategy
- >
-
- /**
- * The maximum number of worker choice strategy execution retries.
- */
- private readonly retries: number
-
- /**
- * Worker choice strategy context constructor.
- *
- * @param pool - The pool instance.
- * @param workerChoiceStrategy - The worker choice strategy.
- * @param opts - The worker choice strategy options.
- */
- public constructor (
- pool: IPool<Worker, Data, Response>,
- private workerChoiceStrategy: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN,
- opts?: WorkerChoiceStrategyOptions
- ) {
- this.execute = this.execute.bind(this)
- this.workerChoiceStrategies = new Map<
- WorkerChoiceStrategy,
- IWorkerChoiceStrategy
- >([
- [
- WorkerChoiceStrategies.ROUND_ROBIN,
- new (RoundRobinWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
- pool,
- opts
- )
- ],
- [
- WorkerChoiceStrategies.LEAST_USED,
- new (LeastUsedWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
- pool,
- opts
- )
- ],
- [
- WorkerChoiceStrategies.LEAST_BUSY,
- new (LeastBusyWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
- pool,
- opts
- )
- ],
- [
- WorkerChoiceStrategies.LEAST_ELU,
- new (LeastEluWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
- pool,
- opts
- )
- ],
- [
- WorkerChoiceStrategies.FAIR_SHARE,
- new (FairShareWorkerChoiceStrategy.bind(this))<Worker, Data, Response>(
- pool,
- opts
- )
- ],
- [
- WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN,
- new (WeightedRoundRobinWorkerChoiceStrategy.bind(this))<
- Worker,
- Data,
- Response
- >(pool, opts)
- ],
- [
- WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN,
- new (InterleavedWeightedRoundRobinWorkerChoiceStrategy.bind(this))<
- Worker,
- Data,
- Response
- >(pool, opts)
- ]
- ])
- this.retriesCount = 0
- this.retries = getWorkerChoiceStrategyRetries(pool, opts)
- }
-
- /**
- * Gets the strategy policy in the context.
- *
- * @returns The strategy policy.
- */
- public getStrategyPolicy (): StrategyPolicy {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.workerChoiceStrategies.get(this.workerChoiceStrategy)!
- .strategyPolicy
- }
-
- /**
- * Gets the worker choice strategy in the context task statistics requirements.
- *
- * @returns The task statistics requirements.
- */
- public getTaskStatisticsRequirements (): TaskStatisticsRequirements {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.workerChoiceStrategies.get(this.workerChoiceStrategy)!
- .taskStatisticsRequirements
- }
-
- /**
- * Sets the worker choice strategy to use in the context.
- *
- * @param workerChoiceStrategy - The worker choice strategy to set.
- */
- public setWorkerChoiceStrategy (
- workerChoiceStrategy: WorkerChoiceStrategy
- ): void {
- if (this.workerChoiceStrategy !== workerChoiceStrategy) {
- this.workerChoiceStrategy = workerChoiceStrategy
- }
- this.workerChoiceStrategies.get(this.workerChoiceStrategy)?.reset()
- }
-
- /**
- * Updates the worker node key in the worker choice strategy in the context internals.
- *
- * @returns `true` if the update is successful, `false` otherwise.
- */
- public update (workerNodeKey: number): boolean {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.workerChoiceStrategies
- .get(this.workerChoiceStrategy)!
- .update(workerNodeKey)
- }
-
- /**
- * Executes the worker choice strategy in the context algorithm.
- *
- * @returns The key of the worker node.
- * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
- */
- public execute (): number {
- return this.executeStrategy(
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- this.workerChoiceStrategies.get(this.workerChoiceStrategy)!
- )
- }
-
- /**
- * Executes the given worker choice strategy.
- *
- * @param workerChoiceStrategy - The worker choice strategy.
- * @returns The key of the worker node.
- * @throws {@link https://nodejs.org/api/errors.html#class-error} If after computed retries the worker node key is null or undefined.
- */
- private executeStrategy (workerChoiceStrategy: IWorkerChoiceStrategy): number {
- let workerNodeKey: number | undefined
- let chooseCount = 0
- let retriesCount = 0
- do {
- workerNodeKey = workerChoiceStrategy.choose()
- if (workerNodeKey == null && chooseCount > 0) {
- ++retriesCount
- ++this.retriesCount
- }
- ++chooseCount
- } while (workerNodeKey == null && retriesCount < this.retries)
- if (workerNodeKey == null) {
- throw new Error(
- `Worker node key chosen is null or undefined after ${retriesCount} retries`
- )
- }
- return workerNodeKey
- }
-
- /**
- * Removes the worker node key from the worker choice strategy in the context.
- *
- * @param workerNodeKey - The worker node key.
- * @returns `true` if the removal is successful, `false` otherwise.
- */
- public remove (workerNodeKey: number): boolean {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- return this.workerChoiceStrategies
- .get(this.workerChoiceStrategy)!
- .remove(workerNodeKey)
- }
-
- /**
- * Sets the worker choice strategies in the context options.
- *
- * @param opts - The worker choice strategy options.
- */
- public setOptions (opts: WorkerChoiceStrategyOptions | undefined): void {
- for (const workerChoiceStrategy of this.workerChoiceStrategies.values()) {
- workerChoiceStrategy.setOptions(opts)
- }
- }
-}
import cluster, { Worker as ClusterWorker } from 'node:cluster'
import { existsSync } from 'node:fs'
-import { cpus } from 'node:os'
import { env } from 'node:process'
import {
SHARE_ENV,
import type { MessageValue, Task } from '../utility-types.js'
import { average, isPlainObject, max, median, min } from '../utils.js'
-import type { IPool, TasksQueueOptions } from './pool.js'
+import type { TasksQueueOptions } from './pool.js'
import {
type MeasurementStatisticsRequirements,
WorkerChoiceStrategies,
- type WorkerChoiceStrategy,
- type WorkerChoiceStrategyOptions
+ type WorkerChoiceStrategy
} from './selection-strategies/selection-strategies-types.js'
-import type { WorkerChoiceStrategyContext } from './selection-strategies/worker-choice-strategy-context.js'
+import type { WorkerChoiceStrategiesContext } from './selection-strategies/worker-choice-strategies-context.js'
import {
type IWorker,
type IWorkerNode,
}
}
-export const getWorkerChoiceStrategyRetries = <
- Worker extends IWorker,
- Data,
- Response
->(
- pool: IPool<Worker, Data, Response>,
- opts?: WorkerChoiceStrategyOptions
- ): number => {
- return (
- pool.info.maxSize +
- Object.keys(opts?.weights ?? getDefaultWeights(pool.info.maxSize)).length
- )
-}
-
-export const buildWorkerChoiceStrategyOptions = <
- Worker extends IWorker,
- Data,
- Response
->(
- pool: IPool<Worker, Data, Response>,
- opts?: WorkerChoiceStrategyOptions
- ): WorkerChoiceStrategyOptions => {
- opts = clone(opts ?? {})
- opts.weights = opts.weights ?? getDefaultWeights(pool.info.maxSize)
- return {
- ...{
- runTime: { median: false },
- waitTime: { median: false },
- elu: { median: false }
- },
- ...opts
- }
-}
-
-const clone = <T>(object: T): T => {
- return structuredClone<T>(object)
-}
-
-const getDefaultWeights = (
- poolMaxSize: number,
- defaultWorkerWeight?: number
-): Record<number, number> => {
- defaultWorkerWeight = defaultWorkerWeight ?? getDefaultWorkerWeight()
- const weights: Record<number, number> = {}
- for (let workerNodeKey = 0; workerNodeKey < poolMaxSize; workerNodeKey++) {
- weights[workerNodeKey] = defaultWorkerWeight
- }
- return weights
-}
-
-const estimatedCpuSpeed = (): number => {
- const runs = 150000000
- const begin = performance.now()
- // eslint-disable-next-line no-empty
- for (let i = runs; i > 0; i--) {}
- const end = performance.now()
- const duration = end - begin
- return Math.trunc(runs / duration / 1000) // in MHz
-}
-
-const getDefaultWorkerWeight = (): number => {
- const currentCpus = cpus()
- let estCpuSpeed: number | undefined
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (currentCpus.every(cpu => cpu.speed == null || cpu.speed === 0)) {
- estCpuSpeed = estimatedCpuSpeed()
- }
- let cpusCycleTimeWeight = 0
- for (const cpu of currentCpus) {
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (cpu.speed == null || cpu.speed === 0) {
- cpu.speed =
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- currentCpus.find(cpu => cpu.speed != null && cpu.speed !== 0)?.speed ??
- estCpuSpeed ??
- 2000
- }
- // 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 / currentCpus.length)
-}
-
export const checkFilePath = (filePath: string | undefined): void => {
if (filePath == null) {
throw new TypeError('The worker file path must be specified')
}
}
+export const checkValidPriority = (priority: number | undefined): void => {
+ if (priority != null && !Number.isSafeInteger(priority)) {
+ throw new TypeError(`Invalid property 'priority': '${priority}'`)
+ }
+ if (
+ priority != null &&
+ Number.isSafeInteger(priority) &&
+ (priority < -20 || priority > 19)
+ ) {
+ throw new RangeError("Property 'priority' must be between -20 and 19")
+ }
+}
+
export const checkValidWorkerChoiceStrategy = (
workerChoiceStrategy: WorkerChoiceStrategy | undefined
): void => {
Response = unknown
>(
workerChoiceStrategyContext:
- | WorkerChoiceStrategyContext<Worker, Data, Response>
+ | WorkerChoiceStrategiesContext<Worker, Data, Response>
| undefined,
workerUsage: WorkerUsage,
task: Task<Data>
Response = unknown
>(
workerChoiceStrategyContext:
- | WorkerChoiceStrategyContext<Worker, Data, Response>
+ | WorkerChoiceStrategiesContext<Worker, Data, Response>
| undefined,
workerUsage: WorkerUsage,
message: MessageValue<Response>
Response = unknown
>(
workerChoiceStrategyContext:
- | WorkerChoiceStrategyContext<Worker, Data, Response>
+ | WorkerChoiceStrategiesContext<Worker, Data, Response>
| undefined,
workerUsage: WorkerUsage,
message: MessageValue<Response>
/** @inheritdoc */
public getTaskFunctionWorkerUsage (name: string): WorkerUsage | undefined {
- if (!Array.isArray(this.info.taskFunctionNames)) {
+ if (!Array.isArray(this.info.taskFunctionsProperties)) {
throw new Error(
- `Cannot get task function worker usage for task function name '${name}' when task function names list is not yet defined`
+ `Cannot get task function worker usage for task function name '${name}' when task function properties list is not yet defined`
)
}
if (
- Array.isArray(this.info.taskFunctionNames) &&
- this.info.taskFunctionNames.length < 3
+ Array.isArray(this.info.taskFunctionsProperties) &&
+ this.info.taskFunctionsProperties.length < 3
) {
throw new Error(
- `Cannot get task function worker usage for task function name '${name}' when task function names list has less than 3 elements`
+ `Cannot get task function worker usage for task function name '${name}' when task function properties list has less than 3 elements`
)
}
if (name === DEFAULT_TASK_NAME) {
- name = this.info.taskFunctionNames[1]
+ name = this.info.taskFunctionsProperties[1].name
}
if (!this.taskFunctionsUsage.has(name)) {
this.taskFunctionsUsage.set(name, this.initTaskFunctionWorkerUsage(name))
if (
(task.name === DEFAULT_TASK_NAME &&
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- name === this.info.taskFunctionNames![1]) ||
+ name === this.info.taskFunctionsProperties![1].name) ||
(task.name !== DEFAULT_TASK_NAME && name === task.name)
) {
++taskFunctionQueueSize
import type { MessageChannel, WorkerOptions } from 'node:worker_threads'
import type { CircularArray } from '../circular-array.js'
-import type { Task } from '../utility-types.js'
+import type { Task, TaskFunctionProperties } from '../utility-types.js'
/**
* Callback invoked when the worker has started successfully.
*/
stealing: boolean
/**
- * Task function names.
+ * Task functions properties.
*/
- taskFunctionNames?: string[]
+ taskFunctionsProperties?: TaskFunctionProperties[]
}
/**
--- /dev/null
+/**
+ * @internal
+ */
+interface PriorityQueueNode<T> {
+ data: T
+ priority: number
+}
+
+/**
+ * Priority queue.
+ *
+ * @typeParam T - Type of priority queue data.
+ * @internal
+ */
+export class PriorityQueue<T> {
+ private nodeArray!: Array<PriorityQueueNode<T>>
+ /** The size of the priority queue. */
+ public size!: number
+ /** The maximum size of the priority queue. */
+ public maxSize!: number
+
+ public constructor () {
+ this.clear()
+ }
+
+ /**
+ * Enqueue data into the priority queue.
+ *
+ * @param data - Data to enqueue.
+ * @param priority - Priority of the data. Lower values have higher priority.
+ * @returns The new size of the priority queue.
+ */
+ public enqueue (data: T, priority?: number): number {
+ priority = priority ?? 0
+ let inserted = false
+ for (const [index, node] of this.nodeArray.entries()) {
+ if (node.priority > priority) {
+ this.nodeArray.splice(index, 0, { data, priority })
+ inserted = true
+ break
+ }
+ }
+ if (!inserted) {
+ this.nodeArray.push({ data, priority })
+ }
+ return this.incrementSize()
+ }
+
+ /**
+ * Dequeue data from the priority queue.
+ *
+ * @returns The dequeued data or `undefined` if the priority queue is empty.
+ */
+ public dequeue (): T | undefined {
+ if (this.size > 0) {
+ --this.size
+ }
+ return this.nodeArray.shift()?.data
+ }
+
+ /**
+ * Peeks at the first data.
+ * @returns The first data or `undefined` if the priority queue is empty.
+ */
+ public peekFirst (): T | undefined {
+ return this.nodeArray[0]?.data
+ }
+
+ /**
+ * Peeks at the last data.
+ * @returns The last data or `undefined` if the priority queue is empty.
+ */
+ public peekLast (): T | undefined {
+ return this.nodeArray[this.nodeArray.length - 1]?.data
+ }
+
+ /**
+ * Clears the priority queue.
+ */
+ public clear (): void {
+ this.nodeArray = []
+ this.size = 0
+ this.maxSize = 0
+ }
+
+ /**
+ * Increments the size of the deque.
+ *
+ * @returns The new size of the deque.
+ */
+ private incrementSize (): number {
+ ++this.size
+ if (this.size > this.maxSize) {
+ this.maxSize = this.size
+ }
+ return this.size
+ }
+}
import type { EventLoopUtilization } from 'node:perf_hooks'
import type { MessagePort, TransferListItem } from 'node:worker_threads'
+import type { WorkerChoiceStrategy } from './pools/selection-strategies/selection-strategies-types.js'
import type { KillBehavior } from './worker/worker-options.js'
/**
readonly elu: boolean
}
+/**
+ * Task function properties.
+ *
+ * @internal
+ */
+export interface TaskFunctionProperties {
+ /**
+ * Task function name.
+ */
+ name: string
+ /**
+ * Task function priority. Lower values have higher priority.
+ */
+ priority?: number
+ /**
+ * Task function worker choice strategy.
+ */
+ strategy?: WorkerChoiceStrategy
+}
+
/**
* Message object that is passed as a task between main worker and worker.
*
*/
readonly taskFunctionOperationStatus?: boolean
/**
- * Task function serialized to string.
+ * Task function properties.
*/
- readonly taskFunction?: string
+ readonly taskFunctionProperties?: TaskFunctionProperties
/**
- * Task function name.
+ * Task function serialized to string.
*/
- readonly taskFunctionName?: string
+ readonly taskFunction?: string
/**
- * Task function names.
+ * Task functions properties.
*/
- readonly taskFunctionNames?: string[]
+ readonly taskFunctionsProperties?: TaskFunctionProperties[]
/**
* Whether the worker computes the given statistics or not.
*/
import { getRandomValues } from 'node:crypto'
import * as os from 'node:os'
+import type { TaskFunctionProperties } from './utility-types.js'
+import type { TaskFunctionObject } from './worker/task-functions.js'
import type { KillBehavior } from './worker/worker-options.js'
/**
* @internal
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export const once = <A extends any[], R, C>(
+export const once = <A extends any[], R, C extends ThisType<any>>(
fn: (...args: A) => R,
context: C
): ((...args: A) => R) => {
return result
}
}
+
+export const buildTaskFunctionProperties = <Data, Response>(
+ name: string,
+ taskFunctionObject: TaskFunctionObject<Data, Response> | undefined
+): TaskFunctionProperties => {
+ return {
+ name,
+ ...(taskFunctionObject?.priority != null && {
+ priority: taskFunctionObject.priority
+ }),
+ ...(taskFunctionObject?.strategy != null && {
+ strategy: taskFunctionObject.strategy
+ })
+ }
+}
import type {
MessageValue,
Task,
+ TaskFunctionProperties,
TaskPerformance,
WorkerStatistics
} from '../utility-types.js'
import {
+ buildTaskFunctionProperties,
DEFAULT_TASK_NAME,
EMPTY_FUNCTION,
isAsyncFunction,
import type {
TaskAsyncFunction,
TaskFunction,
+ TaskFunctionObject,
TaskFunctionOperationResult,
TaskFunctions,
TaskSyncFunction
} from './task-functions.js'
import {
checkTaskFunctionName,
- checkValidTaskFunctionEntry,
+ checkValidTaskFunctionObjectEntry,
checkValidWorkerOptions
} from './utils.js'
import { KillBehaviors, type WorkerOptions } from './worker-options.js'
*/
protected abstract id: number
/**
- * Task function(s) processed by the worker when the pool's `execution` function is invoked.
+ * Task function object(s) processed by the worker when the pool's `execution` function is invoked.
*/
- protected taskFunctions!: Map<string, TaskFunction<Data, Response>>
+ protected taskFunctions!: Map<string, TaskFunctionObject<Data, Response>>
/**
* Timestamp of the last task processed by this worker.
*/
if (taskFunctions == null) {
throw new Error('taskFunctions parameter is mandatory')
}
- this.taskFunctions = new Map<string, TaskFunction<Data, Response>>()
+ this.taskFunctions = new Map<string, TaskFunctionObject<Data, Response>>()
if (typeof taskFunctions === 'function') {
- const boundFn = taskFunctions.bind(this)
- this.taskFunctions.set(DEFAULT_TASK_NAME, boundFn)
+ const fnObj = { taskFunction: taskFunctions.bind(this) }
+ this.taskFunctions.set(DEFAULT_TASK_NAME, fnObj)
this.taskFunctions.set(
typeof taskFunctions.name === 'string' &&
taskFunctions.name.trim().length > 0
? taskFunctions.name
: 'fn1',
- boundFn
+ fnObj
)
} else if (isPlainObject(taskFunctions)) {
let firstEntry = true
- for (const [name, fn] of Object.entries(taskFunctions)) {
- checkValidTaskFunctionEntry<Data, Response>(name, fn)
- const boundFn = fn.bind(this)
+ for (let [name, fnObj] of Object.entries(taskFunctions)) {
+ if (typeof fnObj === 'function') {
+ fnObj = { taskFunction: fnObj } satisfies TaskFunctionObject<
+ Data,
+ Response
+ >
+ }
+ checkValidTaskFunctionObjectEntry<Data, Response>(name, fnObj)
+ fnObj.taskFunction = fnObj.taskFunction.bind(this)
if (firstEntry) {
- this.taskFunctions.set(DEFAULT_TASK_NAME, boundFn)
+ this.taskFunctions.set(DEFAULT_TASK_NAME, fnObj)
firstEntry = false
}
- this.taskFunctions.set(name, boundFn)
+ this.taskFunctions.set(name, fnObj)
}
if (firstEntry) {
throw new Error('taskFunctions parameter object is empty')
*/
public addTaskFunction (
name: string,
- fn: TaskFunction<Data, Response>
+ fn: TaskFunction<Data, Response> | TaskFunctionObject<Data, Response>
): TaskFunctionOperationResult {
try {
checkTaskFunctionName(name)
'Cannot add a task function with the default reserved name'
)
}
- if (typeof fn !== 'function') {
- throw new TypeError('fn parameter is not a function')
+ if (typeof fn === 'function') {
+ fn = { taskFunction: fn } satisfies TaskFunctionObject<Data, Response>
}
- const boundFn = fn.bind(this)
+ checkValidTaskFunctionObjectEntry<Data, Response>(name, fn)
+ fn.taskFunction = fn.taskFunction.bind(this)
if (
this.taskFunctions.get(name) ===
this.taskFunctions.get(DEFAULT_TASK_NAME)
) {
- this.taskFunctions.set(DEFAULT_TASK_NAME, boundFn)
+ this.taskFunctions.set(DEFAULT_TASK_NAME, fn)
}
- this.taskFunctions.set(name, boundFn)
- this.sendTaskFunctionNamesToMainWorker()
+ this.taskFunctions.set(name, fn)
+ this.sendTaskFunctionsPropertiesToMainWorker()
return { status: true }
} catch (error) {
return { status: false, error: error as Error }
)
}
const deleteStatus = this.taskFunctions.delete(name)
- this.sendTaskFunctionNamesToMainWorker()
+ this.sendTaskFunctionsPropertiesToMainWorker()
return { status: deleteStatus }
} catch (error) {
return { status: false, error: error as Error }
}
/**
- * Lists the names of the worker's task functions.
+ * Lists the properties of the worker's task functions.
*
- * @returns The names of the worker's task functions.
+ * @returns The properties of the worker's task functions.
*/
- public listTaskFunctionNames (): string[] {
- const names = [...this.taskFunctions.keys()]
+ public listTaskFunctionsProperties (): TaskFunctionProperties[] {
let defaultTaskFunctionName = DEFAULT_TASK_NAME
- for (const [name, fn] of this.taskFunctions) {
+ for (const [name, fnObj] of this.taskFunctions) {
if (
name !== DEFAULT_TASK_NAME &&
- fn === this.taskFunctions.get(DEFAULT_TASK_NAME)
+ fnObj === this.taskFunctions.get(DEFAULT_TASK_NAME)
) {
defaultTaskFunctionName = name
break
}
}
+ const taskFunctionsProperties: TaskFunctionProperties[] = []
+ for (const [name, fnObj] of this.taskFunctions) {
+ if (name === DEFAULT_TASK_NAME || name === defaultTaskFunctionName) {
+ continue
+ }
+ taskFunctionsProperties.push(buildTaskFunctionProperties(name, fnObj))
+ }
return [
- names[names.indexOf(DEFAULT_TASK_NAME)],
- defaultTaskFunctionName,
- ...names.filter(
- name => name !== DEFAULT_TASK_NAME && name !== defaultTaskFunctionName
- )
+ buildTaskFunctionProperties(
+ DEFAULT_TASK_NAME,
+ this.taskFunctions.get(DEFAULT_TASK_NAME)
+ ),
+ buildTaskFunctionProperties(
+ defaultTaskFunctionName,
+ this.taskFunctions.get(defaultTaskFunctionName)
+ ),
+ ...taskFunctionsProperties
]
}
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.taskFunctions.set(DEFAULT_TASK_NAME, this.taskFunctions.get(name)!)
- this.sendTaskFunctionNamesToMainWorker()
+ this.sendTaskFunctionsPropertiesToMainWorker()
return { status: true }
} catch (error) {
return { status: false, error: error as Error }
protected handleTaskFunctionOperationMessage (
message: MessageValue<Data>
): void {
- const { taskFunctionOperation, taskFunctionName, taskFunction } = message
- if (taskFunctionName == null) {
+ const { taskFunctionOperation, taskFunctionProperties, taskFunction } =
+ message
+ if (taskFunctionProperties == null) {
throw new Error(
- 'Cannot handle task function operation message without a task function name'
+ 'Cannot handle task function operation message without task function properties'
)
}
let response: TaskFunctionOperationResult
switch (taskFunctionOperation) {
case 'add':
- response = this.addTaskFunction(
- taskFunctionName,
+ response = this.addTaskFunction(taskFunctionProperties.name, {
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func
- new Function(`return ${taskFunction}`)() as TaskFunction<
- Data,
- Response
- >
- )
+ taskFunction: new Function(
+ `return ${taskFunction}`
+ )() as TaskFunction<Data, Response>,
+ ...(taskFunctionProperties.priority != null && {
+ priority: taskFunctionProperties.priority
+ }),
+ ...(taskFunctionProperties.strategy != null && {
+ strategy: taskFunctionProperties.strategy
+ })
+ })
break
case 'remove':
- response = this.removeTaskFunction(taskFunctionName)
+ response = this.removeTaskFunction(taskFunctionProperties.name)
break
case 'default':
- response = this.setDefaultTaskFunction(taskFunctionName)
+ response = this.setDefaultTaskFunction(taskFunctionProperties.name)
break
default:
response = { status: false, error: new Error('Unknown task operation') }
this.sendToMainWorker({
taskFunctionOperation,
taskFunctionOperationStatus: response.status,
- taskFunctionName,
+ taskFunctionProperties,
...(!response.status &&
response.error != null && {
workerError: {
- name: taskFunctionName,
+ name: taskFunctionProperties.name,
message: this.handleError(response.error as Error | string)
}
})
): void
/**
- * Sends task function names to the main worker.
+ * Sends task functions properties to the main worker.
*/
- protected sendTaskFunctionNamesToMainWorker (): void {
+ protected sendTaskFunctionsPropertiesToMainWorker (): void {
this.sendToMainWorker({
- taskFunctionNames: this.listTaskFunctionNames()
+ taskFunctionsProperties: this.listTaskFunctionsProperties()
})
}
})
return
}
- const fn = this.taskFunctions.get(taskFunctionName)
+ const fn = this.taskFunctions.get(taskFunctionName)?.taskFunction
if (isAsyncFunction(fn)) {
this.runAsync(fn as TaskAsyncFunction<Data, Response>, task)
} else {
this.getMainWorker().on('message', this.messageListener.bind(this))
this.sendToMainWorker({
ready: true,
- taskFunctionNames: this.listTaskFunctionNames()
+ taskFunctionsProperties: this.listTaskFunctionsProperties()
})
} catch {
this.sendToMainWorker({
ready: false,
- taskFunctionNames: this.listTaskFunctionNames()
+ taskFunctionsProperties: this.listTaskFunctionsProperties()
})
}
}
+import type { WorkerChoiceStrategy } from '../pools/selection-strategies/selection-strategies-types.js'
+
/**
* Task synchronous function that can be executed.
*
| TaskSyncFunction<Data, Response>
| TaskAsyncFunction<Data, Response>
+/**
+ * Task function object.
+ *
+ * @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.
+ */
+export interface TaskFunctionObject<Data = unknown, Response = unknown> {
+ /**
+ * Task function.
+ */
+ taskFunction: TaskFunction<Data, Response>
+ /**
+ * Task function priority. Lower values have higher priority.
+ */
+ priority?: number
+ /**
+ * Task function worker choice strategy.
+ */
+ strategy?: WorkerChoiceStrategy
+}
+
/**
* Tasks functions that can be executed.
- * This object can contain synchronous or asynchronous functions.
- * The key is the name of the function.
- * The value is the function itself.
+ * The key is the name of the task function or task function object.
+ * The value is the function or task function object.
*
* @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.
*/
export type TaskFunctions<Data = unknown, Response = unknown> = Record<
string,
-TaskFunction<Data, Response>
+TaskFunction<Data, Response> | TaskFunctionObject<Data, Response>
>
/**
this.port.on('message', this.messageListener.bind(this))
this.sendToMainWorker({
ready: true,
- taskFunctionNames: this.listTaskFunctionNames()
+ taskFunctionsProperties: this.listTaskFunctionsProperties()
})
} catch {
this.sendToMainWorker({
ready: false,
- taskFunctionNames: this.listTaskFunctionNames()
+ taskFunctionsProperties: this.listTaskFunctionsProperties()
})
}
}
+import {
+ checkValidPriority,
+ checkValidWorkerChoiceStrategy
+} from '../pools/utils.js'
import { isPlainObject } from '../utils.js'
-import type { TaskFunction } from './task-functions.js'
+import type { TaskFunctionObject } from './task-functions.js'
import { KillBehaviors, type WorkerOptions } from './worker-options.js'
export const checkValidWorkerOptions = (
}
}
-export const checkValidTaskFunctionEntry = <Data = unknown, Response = unknown>(
- name: string,
- fn: TaskFunction<Data, Response>
-): void => {
+export const checkValidTaskFunctionObjectEntry = <
+ Data = unknown,
+ Response = unknown
+>(
+ name: string,
+ fnObj: TaskFunctionObject<Data, Response>
+ ): void => {
if (typeof name !== 'string') {
throw new TypeError('A taskFunctions parameter object key is not a string')
}
'A taskFunctions parameter object key is an empty string'
)
}
- if (typeof fn !== 'function') {
+ if (typeof fnObj.taskFunction !== 'function') {
throw new TypeError(
- 'A taskFunctions parameter object value is not a function'
+ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
+ `taskFunction object 'taskFunction' property '${fnObj.taskFunction}' is not a function`
)
}
+ checkValidPriority(fnObj.priority)
+ checkValidWorkerChoiceStrategy(fnObj.strategy)
}
export const checkTaskFunctionName = (name: string): void => {
enableTasksQueue: false,
workerChoiceStrategy: WorkerChoiceStrategies.ROUND_ROBIN
})
- for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
+ for (const [, workerChoiceStrategy] of pool.workerChoiceStrategiesContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
runTime: { median: false },
errorHandler: testHandler,
exitHandler: testHandler
})
- for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
+ for (const [, workerChoiceStrategy] of pool.workerChoiceStrategiesContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
runTime: { median: true },
{ workerChoiceStrategy: WorkerChoiceStrategies.FAIR_SHARE }
)
expect(pool.opts.workerChoiceStrategyOptions).toBeUndefined()
- for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
+ for (const [, workerChoiceStrategy] of pool.workerChoiceStrategiesContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
runTime: { median: false },
})
}
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
runTime: { median: true },
elu: { median: true }
})
- for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
+ for (const [, workerChoiceStrategy] of pool.workerChoiceStrategiesContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
runTime: { median: true },
})
}
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
runTime: { median: false },
elu: { median: false }
})
- for (const [, workerChoiceStrategy] of pool.workerChoiceStrategyContext
+ for (const [, workerChoiceStrategy] of pool.workerChoiceStrategiesContext
.workerChoiceStrategies) {
expect(workerChoiceStrategy.opts).toStrictEqual({
runTime: { median: false },
})
}
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
worker: WorkerTypes.thread,
started: true,
ready: true,
- strategy: WorkerChoiceStrategies.ROUND_ROBIN,
+ defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN,
strategyRetries: 0,
minSize: numberOfWorkers,
maxSize: numberOfWorkers,
worker: WorkerTypes.cluster,
started: true,
ready: true,
- strategy: WorkerChoiceStrategies.ROUND_ROBIN,
+ defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN,
strategyRetries: 0,
minSize: Math.floor(numberOfWorkers / 2),
maxSize: numberOfWorkers,
await pool.destroy()
})
- it('Verify that pool worker tasks usage are reset at worker choice strategy change', async () => {
+ it("Verify that pool worker tasks usage aren't reset at worker choice strategy change", async () => {
const pool = new DynamicThreadPool(
Math.floor(numberOfWorkers / 2),
numberOfWorkers,
for (const workerNode of pool.workerNodes) {
expect(workerNode.usage).toStrictEqual({
tasks: {
- executed: 0,
+ executed: expect.any(Number),
executing: 0,
queued: 0,
maxQueued: 0,
}
}
})
+ expect(workerNode.usage.tasks.executed).toBeGreaterThan(0)
+ expect(workerNode.usage.tasks.executed).toBeLessThanOrEqual(
+ numberOfWorkers * maxMultiplier
+ )
expect(workerNode.usage.runTime.history.length).toBe(0)
expect(workerNode.usage.waitTime.history.length).toBe(0)
expect(workerNode.usage.elu.idle.history.length).toBe(0)
worker: WorkerTypes.cluster,
started: true,
ready: true,
- strategy: WorkerChoiceStrategies.ROUND_ROBIN,
+ defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN,
strategyRetries: expect.any(Number),
minSize: expect.any(Number),
maxSize: expect.any(Number),
worker: WorkerTypes.thread,
started: true,
ready: true,
- strategy: WorkerChoiceStrategies.ROUND_ROBIN,
+ defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN,
strategyRetries: expect.any(Number),
minSize: expect.any(Number),
maxSize: expect.any(Number),
worker: WorkerTypes.thread,
started: true,
ready: true,
- strategy: WorkerChoiceStrategies.ROUND_ROBIN,
+ defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN,
strategyRetries: expect.any(Number),
minSize: expect.any(Number),
maxSize: expect.any(Number),
worker: WorkerTypes.thread,
started: true,
ready: true,
- strategy: WorkerChoiceStrategies.ROUND_ROBIN,
+ defaultStrategy: WorkerChoiceStrategies.ROUND_ROBIN,
strategyRetries: expect.any(Number),
minSize: expect.any(Number),
maxSize: expect.any(Number),
new TypeError('name argument must not be an empty string')
)
await expect(dynamicThreadPool.addTaskFunction('test', 0)).rejects.toThrow(
- new TypeError('fn argument must be a function')
+ new TypeError('taskFunction property must be a function')
)
await expect(dynamicThreadPool.addTaskFunction('test', '')).rejects.toThrow(
- new TypeError('fn argument must be a function')
+ new TypeError('taskFunction property must be a function')
)
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'test'
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' }
])
+ expect([
+ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys()
+ ]).toStrictEqual([WorkerChoiceStrategies.ROUND_ROBIN])
const echoTaskFunction = data => {
return data
}
await expect(
- dynamicThreadPool.addTaskFunction('echo', echoTaskFunction)
+ dynamicThreadPool.addTaskFunction('echo', {
+ taskFunction: echoTaskFunction,
+ strategy: WorkerChoiceStrategies.LEAST_ELU
+ })
).resolves.toBe(true)
expect(dynamicThreadPool.taskFunctions.size).toBe(1)
- expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual(
- echoTaskFunction
- )
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'test',
- 'echo'
+ expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual({
+ taskFunction: echoTaskFunction,
+ strategy: WorkerChoiceStrategies.LEAST_ELU
+ })
+ expect([
+ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys()
+ ]).toStrictEqual([
+ WorkerChoiceStrategies.ROUND_ROBIN,
+ WorkerChoiceStrategies.LEAST_ELU
+ ])
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' },
+ { name: 'echo', strategy: WorkerChoiceStrategies.LEAST_ELU }
])
const taskFunctionData = { test: 'test' }
const echoResult = await dynamicThreadPool.execute(taskFunctionData, 'echo')
},
elu: {
idle: {
+ aggregate: 0,
+ maximum: 0,
+ minimum: 0,
history: new CircularArray()
},
active: {
+ aggregate: 0,
+ maximum: 0,
+ minimum: 0,
history: new CircularArray()
}
}
'./tests/worker-files/thread/testWorker.mjs'
)
await waitPoolEvents(dynamicThreadPool, PoolEvents.ready, 1)
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'test'
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' }
])
await expect(dynamicThreadPool.removeTaskFunction('test')).rejects.toThrow(
new Error('Cannot remove a task function not handled on the pool side')
const echoTaskFunction = data => {
return data
}
- await dynamicThreadPool.addTaskFunction('echo', echoTaskFunction)
+ await dynamicThreadPool.addTaskFunction('echo', {
+ taskFunction: echoTaskFunction,
+ strategy: WorkerChoiceStrategies.LEAST_ELU
+ })
expect(dynamicThreadPool.taskFunctions.size).toBe(1)
- expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual(
- echoTaskFunction
- )
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'test',
- 'echo'
+ expect(dynamicThreadPool.taskFunctions.get('echo')).toStrictEqual({
+ taskFunction: echoTaskFunction,
+ strategy: WorkerChoiceStrategies.LEAST_ELU
+ })
+ expect([
+ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys()
+ ]).toStrictEqual([
+ WorkerChoiceStrategies.ROUND_ROBIN,
+ WorkerChoiceStrategies.LEAST_ELU
+ ])
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' },
+ { name: 'echo', strategy: WorkerChoiceStrategies.LEAST_ELU }
])
await expect(dynamicThreadPool.removeTaskFunction('echo')).resolves.toBe(
true
)
expect(dynamicThreadPool.taskFunctions.size).toBe(0)
expect(dynamicThreadPool.taskFunctions.get('echo')).toBeUndefined()
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'test'
+ expect([
+ ...dynamicThreadPool.workerChoiceStrategiesContext.workerChoiceStrategies.keys()
+ ]).toStrictEqual([WorkerChoiceStrategies.ROUND_ROBIN])
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' }
])
await dynamicThreadPool.destroy()
})
- it('Verify that listTaskFunctionNames() is working', async () => {
+ it('Verify that listTaskFunctionsProperties() is working', async () => {
const dynamicThreadPool = new DynamicThreadPool(
Math.floor(numberOfWorkers / 2),
numberOfWorkers,
'./tests/worker-files/thread/testMultipleTaskFunctionsWorker.mjs'
)
await waitPoolEvents(dynamicThreadPool, PoolEvents.ready, 1)
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'jsonIntegerSerialization',
- 'factorial',
- 'fibonacci'
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'jsonIntegerSerialization' },
+ { name: 'factorial' },
+ { name: 'fibonacci' }
])
await dynamicThreadPool.destroy()
const fixedClusterPool = new FixedClusterPool(
'./tests/worker-files/cluster/testMultipleTaskFunctionsWorker.cjs'
)
await waitPoolEvents(fixedClusterPool, PoolEvents.ready, 1)
- expect(fixedClusterPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'jsonIntegerSerialization',
- 'factorial',
- 'fibonacci'
+ expect(fixedClusterPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'jsonIntegerSerialization' },
+ { name: 'factorial' },
+ { name: 'fibonacci' }
])
await fixedClusterPool.destroy()
})
`Task function operation 'default' failed on worker ${workerId} with error: 'Error: Cannot set the default task function to a non-existing task function'`
)
)
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'jsonIntegerSerialization',
- 'factorial',
- 'fibonacci'
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'jsonIntegerSerialization' },
+ { name: 'factorial' },
+ { name: 'fibonacci' }
])
await expect(
dynamicThreadPool.setDefaultTaskFunction('factorial')
).resolves.toBe(true)
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'factorial',
- 'jsonIntegerSerialization',
- 'fibonacci'
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'factorial' },
+ { name: 'jsonIntegerSerialization' },
+ { name: 'fibonacci' }
])
await expect(
dynamicThreadPool.setDefaultTaskFunction('fibonacci')
).resolves.toBe(true)
- expect(dynamicThreadPool.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'fibonacci',
- 'jsonIntegerSerialization',
- 'factorial'
+ expect(dynamicThreadPool.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'fibonacci' },
+ { name: 'jsonIntegerSerialization' },
+ { name: 'factorial' }
])
await dynamicThreadPool.destroy()
})
expect(pool.info.executingTasks).toBe(0)
expect(pool.info.executedTasks).toBe(4)
for (const workerNode of pool.workerNodes) {
- expect(workerNode.info.taskFunctionNames).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'jsonIntegerSerialization',
- 'factorial',
- 'fibonacci'
+ expect(workerNode.info.taskFunctionsProperties).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'jsonIntegerSerialization' },
+ { name: 'factorial' },
+ { name: 'fibonacci' }
])
expect(workerNode.taskFunctionsUsage.size).toBe(3)
- for (const name of pool.listTaskFunctionNames()) {
- expect(workerNode.getTaskFunctionWorkerUsage(name)).toStrictEqual({
+ for (const taskFunctionProperties of pool.listTaskFunctionsProperties()) {
+ expect(
+ workerNode.getTaskFunctionWorkerUsage(taskFunctionProperties.name)
+ ).toStrictEqual({
tasks: {
executed: expect.any(Number),
executing: 0,
}
})
expect(
- workerNode.getTaskFunctionWorkerUsage(name).tasks.executed
+ workerNode.getTaskFunctionWorkerUsage(taskFunctionProperties.name)
+ .tasks.executed
).toBeGreaterThan(0)
}
expect(
workerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME)
).toStrictEqual(
workerNode.getTaskFunctionWorkerUsage(
- workerNode.info.taskFunctionNames[1]
+ workerNode.info.taskFunctionsProperties[1].name
)
)
}
await expect(
pool.sendTaskFunctionOperationToWorker(workerNodeKey, {
taskFunctionOperation: 'add',
- taskFunctionName: 'empty',
+ taskFunctionProperties: { name: 'empty' },
taskFunction: (() => {}).toString()
})
).resolves.toBe(true)
expect(
- pool.workerNodes[workerNodeKey].info.taskFunctionNames
- ).toStrictEqual([DEFAULT_TASK_NAME, 'test', 'empty'])
+ pool.workerNodes[workerNodeKey].info.taskFunctionsProperties
+ ).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' },
+ { name: 'empty' }
+ ])
await pool.destroy()
})
await expect(
pool.sendTaskFunctionOperationToWorkers({
taskFunctionOperation: 'add',
- taskFunctionName: 'empty',
+ taskFunctionProperties: { name: 'empty' },
taskFunction: (() => {}).toString()
})
).resolves.toBe(true)
for (const workerNode of pool.workerNodes) {
- expect(workerNode.info.taskFunctionNames).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'test',
- 'empty'
+ expect(workerNode.info.taskFunctionsProperties).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'test' },
+ { name: 'empty' }
])
}
await pool.destroy()
await waitWorkerEvents(longRunningPool, 'exit', max - min)
expect(longRunningPool.workerNodes.length).toBe(min)
expect(
- longRunningPool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- longRunningPool.workerChoiceStrategyContext.workerChoiceStrategy
+ longRunningPool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ longRunningPool.workerChoiceStrategiesContext
+ .defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBeLessThan(longRunningPool.workerNodes.length)
// We need to clean up the resources after our test
--- /dev/null
+import { expect } from 'expect'
+
+import { FixedClusterPool, FixedThreadPool } from '../../../lib/index.cjs'
+import {
+ buildWorkerChoiceStrategyOptions,
+ getWorkerChoiceStrategiesRetries
+} from '../../../lib/pools/selection-strategies/selection-strategies-utils.cjs'
+
+describe('Selection strategies utils test suite', () => {
+ it('Verify buildWorkerChoiceStrategyOptions() behavior', async () => {
+ const numberOfWorkers = 4
+ const pool = new FixedClusterPool(
+ numberOfWorkers,
+ './tests/worker-files/cluster/testWorker.cjs'
+ )
+ expect(buildWorkerChoiceStrategyOptions(pool)).toStrictEqual({
+ runTime: { median: false },
+ waitTime: { median: false },
+ elu: { median: false },
+ weights: expect.objectContaining({
+ 0: expect.any(Number),
+ [pool.info.maxSize - 1]: expect.any(Number)
+ })
+ })
+ const workerChoiceStrategyOptions = {
+ runTime: { median: true },
+ waitTime: { median: true },
+ elu: { median: true },
+ weights: {
+ 0: 100,
+ 1: 100
+ }
+ }
+ expect(
+ buildWorkerChoiceStrategyOptions(pool, workerChoiceStrategyOptions)
+ ).toStrictEqual(workerChoiceStrategyOptions)
+ await pool.destroy()
+ })
+
+ it('Verify getWorkerChoiceStrategyRetries() behavior', async () => {
+ const numberOfThreads = 4
+ const pool = new FixedThreadPool(
+ numberOfThreads,
+ './tests/worker-files/thread/testWorker.mjs'
+ )
+ expect(getWorkerChoiceStrategiesRetries(pool)).toBe(pool.info.maxSize * 2)
+ const workerChoiceStrategyOptions = {
+ runTime: { median: true },
+ waitTime: { median: true },
+ elu: { median: true },
+ weights: {
+ 0: 100,
+ 1: 100
+ }
+ }
+ expect(
+ getWorkerChoiceStrategiesRetries(pool, workerChoiceStrategyOptions)
+ ).toBe(
+ pool.info.maxSize +
+ Object.keys(workerChoiceStrategyOptions.weights).length
+ )
+ await pool.destroy()
+ })
+})
+import { randomInt } from 'node:crypto'
+
import { expect } from 'expect'
import { CircularArray } from '../../../lib/circular-array.cjs'
expect(pool.opts.workerChoiceStrategy).toBe(
WorkerChoiceStrategies.ROUND_ROBIN
)
+ expect(pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
// We need to clean up the resources after our test
await pool.destroy()
})
{ workerChoiceStrategy }
)
expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
- expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
+ expect(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).toBe(workerChoiceStrategy)
await pool.destroy()
}
})
)
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
- expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
+ expect(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).toBe(workerChoiceStrategy)
await pool.destroy()
}
for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
)
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(pool.opts.workerChoiceStrategy).toBe(workerChoiceStrategy)
- expect(pool.workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
+ expect(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).toBe(workerChoiceStrategy)
await pool.destroy()
}
})
it('Verify available strategies default internals at pool creation', async () => {
- const pool = new FixedThreadPool(
- max,
- './tests/worker-files/thread/testWorker.mjs'
- )
for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
+ const pool = new FixedThreadPool(
+ max,
+ './tests/worker-files/thread/testWorker.mjs',
+ { workerChoiceStrategy }
+ )
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).nextWorkerNodeKey
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).previousWorkerNodeKey
).toBe(0)
workerChoiceStrategy === WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
) {
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).workerNodeVirtualTaskRunTime
).toBe(0)
WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
) {
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).workerNodeVirtualTaskRunTime
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).roundId
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).workerNodeId
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).roundWeights.length
).toBe(1)
expect(
Number.isSafeInteger(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
workerChoiceStrategy
).roundWeights[0]
)
).toBe(true)
}
+ await pool.destroy()
}
- await pool.destroy()
})
it('Verify ROUND_ROBIN strategy default policy', async () => {
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: false,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: false,
})
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toBe(pool.workerNodes.length - 1)
// We need to clean up the resources after our test
)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toBe(pool.workerNodes.length - 1)
// We need to clean up the resources after our test
await pool.destroy()
})
- it('Verify ROUND_ROBIN strategy internals are resets after setting it', async () => {
+ it("Verify ROUND_ROBIN strategy internals aren't reset after setting it", async () => {
const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
let pool = new FixedThreadPool(
max,
'./tests/worker-files/thread/testWorker.mjs',
- { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
+ { workerChoiceStrategy }
)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).nextWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).previousWorkerNodeKey
- ).toBeDefined()
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).nextWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).previousWorkerNodeKey = randomInt(1, max - 1)
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
await pool.destroy()
pool = new DynamicThreadPool(
min,
max,
'./tests/worker-files/thread/testWorker.mjs',
- { workerChoiceStrategy: WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN }
+ { workerChoiceStrategy }
)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).nextWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
- ).previousWorkerNodeKey
- ).toBeDefined()
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).nextWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).previousWorkerNodeKey = randomInt(1, max - 1)
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
// We need to clean up the resources after our test
await pool.destroy()
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: false,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: false,
)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: false,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: false,
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeGreaterThan(0)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeGreaterThan(0)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeGreaterThan(0)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(expect.any(Number))
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(expect.any(Number))
// We need to clean up the resources after our test
await pool.destroy()
})
- it('Verify FAIR_SHARE strategy internals are resets after setting it', async () => {
+ it("Verify FAIR_SHARE strategy internals aren't reset after setting it", async () => {
const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
let pool = new FixedThreadPool(
max,
}
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
for (const workerNode of pool.workerNodes) {
- expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeUndefined()
+ expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeGreaterThan(0)
}
await pool.destroy()
pool = new DynamicThreadPool(
}
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
for (const workerNode of pool.workerNodes) {
- expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeUndefined()
+ expect(workerNode.strategyData.virtualTaskEndTimestamp).toBeGreaterThan(0)
}
// We need to clean up the resources after our test
await pool.destroy()
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeVirtualTaskRunTime
).toBeGreaterThanOrEqual(0)
// We need to clean up the resources after our test
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeVirtualTaskRunTime
).toBeGreaterThanOrEqual(0)
// We need to clean up the resources after our test
}
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeVirtualTaskRunTime
).toBeGreaterThanOrEqual(0)
// We need to clean up the resources after our test
await pool.destroy()
})
- it('Verify WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
+ it("Verify WEIGHTED_ROUND_ROBIN strategy internals aren't reset after setting it", async () => {
const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
let pool = new FixedThreadPool(
max,
- './tests/worker-files/thread/testWorker.mjs'
+ './tests/worker-files/thread/testWorker.mjs',
+ { workerChoiceStrategy }
)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).nextWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).previousWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).workerNodeVirtualTaskRunTime
- ).toBeDefined()
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).nextWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).previousWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).workerNodeVirtualTaskRunTime = randomInt(100, 1000)
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeVirtualTaskRunTime
- ).toBe(0)
+ ).toBeGreaterThan(99)
await pool.destroy()
pool = new DynamicThreadPool(
min,
max,
- './tests/worker-files/thread/testWorker.mjs'
+ './tests/worker-files/thread/testWorker.mjs',
+ { workerChoiceStrategy }
)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).nextWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).previousWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).workerNodeVirtualTaskRunTime
- ).toBeDefined()
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).nextWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).previousWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).workerNodeVirtualTaskRunTime = randomInt(100, 1000)
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeVirtualTaskRunTime
- ).toBe(0)
+ ).toBeGreaterThan(99)
// We need to clean up the resources after our test
await pool.destroy()
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
'./tests/worker-files/thread/testWorker.mjs',
{ workerChoiceStrategy }
)
- expect(pool.workerChoiceStrategyContext.getStrategyPolicy()).toStrictEqual({
+ expect(pool.workerChoiceStrategiesContext.getPolicy()).toStrictEqual({
dynamicWorkerUsage: false,
dynamicWorkerReady: true
})
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
{ workerChoiceStrategy }
)
expect(
- pool.workerChoiceStrategyContext.getTaskStatisticsRequirements()
+ pool.workerChoiceStrategiesContext.getTaskStatisticsRequirements()
).toStrictEqual({
runTime: {
aggregate: true,
)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundId
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeId
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights.length
).toBe(1)
expect(
Number.isSafeInteger(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights[0]
)
).toBe(true)
)
}
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundId
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeId
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBe(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
).toEqual(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights.length
).toBe(1)
expect(
Number.isSafeInteger(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights[0]
)
).toBe(true)
await pool.destroy()
})
- it('Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals are resets after setting it', async () => {
+ it("Verify INTERLEAVED_WEIGHTED_ROUND_ROBIN strategy internals aren't resets after setting it", async () => {
const workerChoiceStrategy =
WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
let pool = new FixedThreadPool(
max,
- './tests/worker-files/thread/testWorker.mjs'
+ './tests/worker-files/thread/testWorker.mjs',
+ { workerChoiceStrategy }
)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).roundId
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).workerNodeId
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).nextWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).previousWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).roundWeights
- ).toBeDefined()
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).roundId = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).workerNodeId = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).nextWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).previousWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).roundWeights = [randomInt(1, max - 1), randomInt(1, max - 1)]
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundId
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeId
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights.length
- ).toBe(1)
+ ).toBeGreaterThan(1)
expect(
Number.isSafeInteger(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights[0]
)
).toBe(true)
pool = new DynamicThreadPool(
min,
max,
- './tests/worker-files/thread/testWorker.mjs'
+ './tests/worker-files/thread/testWorker.mjs',
+ { workerChoiceStrategy }
)
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).roundId
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).workerNodeId
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).nextWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).previousWorkerNodeKey
- ).toBeDefined()
- expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- ).roundWeights
- ).toBeDefined()
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).roundId = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).workerNodeId = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).nextWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).previousWorkerNodeKey = randomInt(1, max - 1)
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategy
+ ).roundWeights = [randomInt(1, max - 1), randomInt(1, max - 1)]
pool.setWorkerChoiceStrategy(workerChoiceStrategy)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundId
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).workerNodeId
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).nextWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).previousWorkerNodeKey
- ).toBe(0)
+ ).toBeGreaterThan(0)
expect(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights.length
- ).toBe(1)
+ ).toBeGreaterThan(1)
expect(
Number.isSafeInteger(
- pool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- pool.workerChoiceStrategyContext.workerChoiceStrategy
+ pool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ pool.workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
).roundWeights[0]
)
).toBe(true)
--- /dev/null
+import { expect } from 'expect'
+import { createStubInstance, restore, stub } from 'sinon'
+
+import {
+ DynamicThreadPool,
+ FixedThreadPool,
+ WorkerChoiceStrategies
+} from '../../../lib/index.cjs'
+import { FairShareWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy.cjs'
+import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.cjs'
+import { LeastBusyWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-busy-worker-choice-strategy.cjs'
+import { LeastEluWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-elu-worker-choice-strategy.cjs'
+import { LeastUsedWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-used-worker-choice-strategy.cjs'
+import { RoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy.cjs'
+import { WeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.cjs'
+import { WorkerChoiceStrategiesContext } from '../../../lib/pools/selection-strategies/worker-choice-strategies-context.cjs'
+
+describe('Worker choice strategies context test suite', () => {
+ const min = 1
+ const max = 3
+ let fixedPool, dynamicPool
+
+ before(() => {
+ fixedPool = new FixedThreadPool(
+ max,
+ './tests/worker-files/thread/testWorker.mjs'
+ )
+ dynamicPool = new DynamicThreadPool(
+ min,
+ max,
+ './tests/worker-files/thread/testWorker.mjs'
+ )
+ })
+
+ afterEach(() => {
+ restore()
+ })
+
+ after(async () => {
+ await fixedPool.destroy()
+ await dynamicPool.destroy()
+ })
+
+ it('Verify that constructor() initializes the context with the default choice strategy', () => {
+ let workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ expect(workerChoiceStrategiesContext.workerChoiceStrategies.size).toBe(1)
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
+ workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ expect(workerChoiceStrategiesContext.workerChoiceStrategies.size).toBe(1)
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that constructor() initializes the context with retries attribute properly set', () => {
+ let workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ expect(workerChoiceStrategiesContext.retries).toBe(
+ fixedPool.info.maxSize * 2
+ )
+ workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ expect(workerChoiceStrategiesContext.retries).toBe(
+ dynamicPool.info.maxSize * 2
+ )
+ })
+
+ it('Verify that execute() throws error if null or undefined is returned after retries', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
+ const workerChoiceStrategyUndefinedStub = createStubInstance(
+ RoundRobinWorkerChoiceStrategy,
+ {
+ choose: stub().returns(undefined)
+ }
+ )
+ workerChoiceStrategiesContext.workerChoiceStrategies.set(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
+ workerChoiceStrategyUndefinedStub
+ )
+ expect(() => workerChoiceStrategiesContext.execute()).toThrow(
+ new Error(
+ `Worker node key chosen is null or undefined after ${workerChoiceStrategiesContext.retries} retries`
+ )
+ )
+ const workerChoiceStrategyNullStub = createStubInstance(
+ RoundRobinWorkerChoiceStrategy,
+ {
+ choose: stub().returns(null)
+ }
+ )
+ workerChoiceStrategiesContext.workerChoiceStrategies.set(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
+ workerChoiceStrategyNullStub
+ )
+ expect(() => workerChoiceStrategiesContext.execute()).toThrow(
+ new Error(
+ `Worker node key chosen is null or undefined after ${workerChoiceStrategiesContext.retries} retries`
+ )
+ )
+ })
+
+ it('Verify that execute() retry until a worker node is chosen', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ const workerChoiceStrategyStub = createStubInstance(
+ RoundRobinWorkerChoiceStrategy,
+ {
+ choose: stub()
+ .onCall(0)
+ .returns(undefined)
+ .onCall(1)
+ .returns(undefined)
+ .onCall(2)
+ .returns(undefined)
+ .onCall(3)
+ .returns(undefined)
+ .onCall(4)
+ .returns(undefined)
+ .returns(1)
+ }
+ )
+ expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
+ workerChoiceStrategiesContext.workerChoiceStrategies.set(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
+ workerChoiceStrategyStub
+ )
+ const chosenWorkerKey = workerChoiceStrategiesContext.execute()
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).choose.callCount
+ ).toBe(6)
+ expect(chosenWorkerKey).toBe(1)
+ })
+
+ it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ const workerChoiceStrategyStub = createStubInstance(
+ RoundRobinWorkerChoiceStrategy,
+ {
+ choose: stub().returns(0)
+ }
+ )
+ expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
+ workerChoiceStrategiesContext.workerChoiceStrategies.set(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
+ workerChoiceStrategyStub
+ )
+ const chosenWorkerKey = workerChoiceStrategiesContext.execute()
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).choose.calledOnce
+ ).toBe(true)
+ expect(chosenWorkerKey).toBe(0)
+ })
+
+ it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ const workerChoiceStrategyStub = createStubInstance(
+ RoundRobinWorkerChoiceStrategy,
+ {
+ choose: stub().returns(0)
+ }
+ )
+ expect(workerChoiceStrategiesContext.defaultWorkerChoiceStrategy).toBe(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
+ workerChoiceStrategiesContext.workerChoiceStrategies.set(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy,
+ workerChoiceStrategyStub
+ )
+ const chosenWorkerKey = workerChoiceStrategiesContext.execute()
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ ).choose.calledOnce
+ ).toBe(true)
+ expect(chosenWorkerKey).toBe(0)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.ROUND_ROBIN
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.LEAST_USED
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.LEAST_USED
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.LEAST_BUSY
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.LEAST_BUSY
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.LEAST_ELU
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.LEAST_ELU
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.FAIR_SHARE
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.FAIR_SHARE
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that setDefaultWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
+ const workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool
+ )
+ workerChoiceStrategiesContext.setDefaultWorkerChoiceStrategy(
+ WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
+ )
+ expect(
+ workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ workerChoiceStrategiesContext.defaultWorkerChoiceStrategy
+ )
+ ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
+ })
+
+ it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
+ const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
+ let workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool,
+ [wwrWorkerChoiceStrategy],
+ {
+ runTime: { median: true }
+ }
+ )
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .average
+ ).toBe(false)
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .median
+ ).toBe(true)
+ workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool,
+ [wwrWorkerChoiceStrategy],
+ {
+ runTime: { median: true }
+ }
+ )
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .average
+ ).toBe(false)
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .median
+ ).toBe(true)
+ const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
+ workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ fixedPool,
+ [fsWorkerChoiceStrategy],
+ {
+ runTime: { median: true }
+ }
+ )
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .average
+ ).toBe(false)
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .median
+ ).toBe(true)
+ workerChoiceStrategiesContext = new WorkerChoiceStrategiesContext(
+ dynamicPool,
+ [fsWorkerChoiceStrategy],
+ {
+ runTime: { median: true }
+ }
+ )
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .average
+ ).toBe(false)
+ expect(
+ workerChoiceStrategiesContext.getTaskStatisticsRequirements().runTime
+ .median
+ ).toBe(true)
+ })
+})
+++ /dev/null
-import { expect } from 'expect'
-import { createStubInstance, restore, stub } from 'sinon'
-
-import {
- DynamicThreadPool,
- FixedThreadPool,
- WorkerChoiceStrategies
-} from '../../../lib/index.cjs'
-import { FairShareWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/fair-share-worker-choice-strategy.cjs'
-import { InterleavedWeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/interleaved-weighted-round-robin-worker-choice-strategy.cjs'
-import { LeastBusyWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-busy-worker-choice-strategy.cjs'
-import { LeastEluWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-elu-worker-choice-strategy.cjs'
-import { LeastUsedWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/least-used-worker-choice-strategy.cjs'
-import { RoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/round-robin-worker-choice-strategy.cjs'
-import { WeightedRoundRobinWorkerChoiceStrategy } from '../../../lib/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.cjs'
-import { WorkerChoiceStrategyContext } from '../../../lib/pools/selection-strategies/worker-choice-strategy-context.cjs'
-
-describe('Worker choice strategy context test suite', () => {
- const min = 1
- const max = 3
- let fixedPool, dynamicPool
-
- before(() => {
- fixedPool = new FixedThreadPool(
- max,
- './tests/worker-files/thread/testWorker.mjs'
- )
- dynamicPool = new DynamicThreadPool(
- min,
- max,
- './tests/worker-files/thread/testWorker.mjs'
- )
- })
-
- afterEach(() => {
- restore()
- })
-
- after(async () => {
- await fixedPool.destroy()
- await dynamicPool.destroy()
- })
-
- it('Verify that constructor() initializes the context with all the available worker choice strategies', () => {
- let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(fixedPool)
- expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
- Object.keys(WorkerChoiceStrategies).length
- )
- workerChoiceStrategyContext = new WorkerChoiceStrategyContext(dynamicPool)
- expect(workerChoiceStrategyContext.workerChoiceStrategies.size).toBe(
- Object.keys(WorkerChoiceStrategies).length
- )
- })
-
- it('Verify that constructor() initializes the context with retries attribute properly set', () => {
- let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(fixedPool)
- expect(workerChoiceStrategyContext.retries).toBe(fixedPool.info.maxSize * 2)
- workerChoiceStrategyContext = new WorkerChoiceStrategyContext(dynamicPool)
- expect(workerChoiceStrategyContext.retries).toBe(
- dynamicPool.info.maxSize * 2
- )
- })
-
- it('Verify that execute() throws error if null or undefined is returned after retries', () => {
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- WorkerChoiceStrategies.ROUND_ROBIN
- )
- const workerChoiceStrategyUndefinedStub = createStubInstance(
- RoundRobinWorkerChoiceStrategy,
- {
- choose: stub().returns(undefined)
- }
- )
- workerChoiceStrategyContext.workerChoiceStrategies.set(
- workerChoiceStrategyContext.workerChoiceStrategy,
- workerChoiceStrategyUndefinedStub
- )
- expect(() => workerChoiceStrategyContext.execute()).toThrow(
- new Error(
- `Worker node key chosen is null or undefined after ${workerChoiceStrategyContext.retries} retries`
- )
- )
- const workerChoiceStrategyNullStub = createStubInstance(
- RoundRobinWorkerChoiceStrategy,
- {
- choose: stub().returns(null)
- }
- )
- workerChoiceStrategyContext.workerChoiceStrategies.set(
- workerChoiceStrategyContext.workerChoiceStrategy,
- workerChoiceStrategyNullStub
- )
- expect(() => workerChoiceStrategyContext.execute()).toThrow(
- new Error(
- `Worker node key chosen is null or undefined after ${workerChoiceStrategyContext.retries} retries`
- )
- )
- })
-
- it('Verify that execute() retry until a worker node is chosen', () => {
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- const workerChoiceStrategyStub = createStubInstance(
- RoundRobinWorkerChoiceStrategy,
- {
- choose: stub()
- .onCall(0)
- .returns(undefined)
- .onCall(1)
- .returns(undefined)
- .onCall(2)
- .returns(undefined)
- .onCall(3)
- .returns(undefined)
- .onCall(4)
- .returns(undefined)
- .returns(1)
- }
- )
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- WorkerChoiceStrategies.ROUND_ROBIN
- )
- workerChoiceStrategyContext.workerChoiceStrategies.set(
- workerChoiceStrategyContext.workerChoiceStrategy,
- workerChoiceStrategyStub
- )
- const chosenWorkerKey = workerChoiceStrategyContext.execute()
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategyContext.workerChoiceStrategy
- ).choose.callCount
- ).toBe(6)
- expect(chosenWorkerKey).toBe(1)
- })
-
- it('Verify that execute() return the worker node key chosen by the strategy with fixed pool', () => {
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- const workerChoiceStrategyStub = createStubInstance(
- RoundRobinWorkerChoiceStrategy,
- {
- choose: stub().returns(0)
- }
- )
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- WorkerChoiceStrategies.ROUND_ROBIN
- )
- workerChoiceStrategyContext.workerChoiceStrategies.set(
- workerChoiceStrategyContext.workerChoiceStrategy,
- workerChoiceStrategyStub
- )
- const chosenWorkerKey = workerChoiceStrategyContext.execute()
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategyContext.workerChoiceStrategy
- ).choose.calledOnce
- ).toBe(true)
- expect(chosenWorkerKey).toBe(0)
- })
-
- it('Verify that execute() return the worker node key chosen by the strategy with dynamic pool', () => {
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- const workerChoiceStrategyStub = createStubInstance(
- RoundRobinWorkerChoiceStrategy,
- {
- choose: stub().returns(0)
- }
- )
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- WorkerChoiceStrategies.ROUND_ROBIN
- )
- workerChoiceStrategyContext.workerChoiceStrategies.set(
- workerChoiceStrategyContext.workerChoiceStrategy,
- workerChoiceStrategyStub
- )
- const chosenWorkerKey = workerChoiceStrategyContext.execute()
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategyContext.workerChoiceStrategy
- ).choose.calledOnce
- ).toBe(true)
- expect(chosenWorkerKey).toBe(0)
- })
-
- it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and fixed pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with ROUND_ROBIN and dynamic pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(RoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and fixed pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with LEAST_USED and dynamic pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_USED
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(LeastUsedWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and fixed pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with LEAST_BUSY and dynamic pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_BUSY
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(LeastBusyWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and fixed pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with LEAST_ELU and dynamic pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.LEAST_ELU
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(LeastEluWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and fixed pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with FAIR_SHARE and dynamic pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(FairShareWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and fixed pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
- const workerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(WeightedRoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and fixed pool', () => {
- const workerChoiceStrategy =
- WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that setWorkerChoiceStrategy() works with INTERLEAVED_WEIGHTED_ROUND_ROBIN and dynamic pool', () => {
- const workerChoiceStrategy =
- WorkerChoiceStrategies.INTERLEAVED_WEIGHTED_ROUND_ROBIN
- const workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool
- )
- workerChoiceStrategyContext.setWorkerChoiceStrategy(workerChoiceStrategy)
- expect(
- workerChoiceStrategyContext.workerChoiceStrategies.get(
- workerChoiceStrategy
- )
- ).toBeInstanceOf(InterleavedWeightedRoundRobinWorkerChoiceStrategy)
- expect(workerChoiceStrategyContext.workerChoiceStrategy).toBe(
- workerChoiceStrategy
- )
- })
-
- it('Verify that worker choice strategy options enable median runtime pool statistics', () => {
- const wwrWorkerChoiceStrategy = WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN
- let workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool,
- wwrWorkerChoiceStrategy,
- {
- runTime: { median: true }
- }
- )
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
- .average
- ).toBe(false)
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
- ).toBe(true)
- workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool,
- wwrWorkerChoiceStrategy,
- {
- runTime: { median: true }
- }
- )
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
- .average
- ).toBe(false)
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
- ).toBe(true)
- const fsWorkerChoiceStrategy = WorkerChoiceStrategies.FAIR_SHARE
- workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- fixedPool,
- fsWorkerChoiceStrategy,
- {
- runTime: { median: true }
- }
- )
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
- .average
- ).toBe(false)
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
- ).toBe(true)
- workerChoiceStrategyContext = new WorkerChoiceStrategyContext(
- dynamicPool,
- fsWorkerChoiceStrategy,
- {
- runTime: { median: true }
- }
- )
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime
- .average
- ).toBe(false)
- expect(
- workerChoiceStrategyContext.getTaskStatisticsRequirements().runTime.median
- ).toBe(true)
- })
-})
await waitWorkerEvents(longRunningPool, 'exit', max - min)
expect(longRunningPool.workerNodes.length).toBe(min)
expect(
- longRunningPool.workerChoiceStrategyContext.workerChoiceStrategies.get(
- longRunningPool.workerChoiceStrategyContext.workerChoiceStrategy
+ longRunningPool.workerChoiceStrategiesContext.workerChoiceStrategies.get(
+ longRunningPool.workerChoiceStrategiesContext
+ .defaultWorkerChoiceStrategy
).nextWorkerNodeKey
).toBeLessThan(longRunningPool.workerNodes.length)
// We need to clean up the resources after our test
CircularArray,
DEFAULT_CIRCULAR_ARRAY_SIZE
} from '../../lib/circular-array.cjs'
+import { WorkerTypes } from '../../lib/index.cjs'
import {
- FixedClusterPool,
- FixedThreadPool,
- WorkerTypes
-} from '../../lib/index.cjs'
-import {
- buildWorkerChoiceStrategyOptions,
createWorker,
DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS,
getDefaultTasksQueueOptions,
- getWorkerChoiceStrategyRetries,
getWorkerId,
getWorkerType,
updateMeasurementStatistics
})
})
- it('Verify getWorkerChoiceStrategyRetries() behavior', async () => {
- const numberOfThreads = 4
- const pool = new FixedThreadPool(
- numberOfThreads,
- './tests/worker-files/thread/testWorker.mjs'
- )
- expect(getWorkerChoiceStrategyRetries(pool)).toBe(pool.info.maxSize * 2)
- const workerChoiceStrategyOptions = {
- runTime: { median: true },
- waitTime: { median: true },
- elu: { median: true },
- weights: {
- 0: 100,
- 1: 100
- }
- }
- expect(
- getWorkerChoiceStrategyRetries(pool, workerChoiceStrategyOptions)
- ).toBe(
- pool.info.maxSize +
- Object.keys(workerChoiceStrategyOptions.weights).length
- )
- await pool.destroy()
- })
-
- it('Verify buildWorkerChoiceStrategyOptions() behavior', async () => {
- const numberOfWorkers = 4
- const pool = new FixedClusterPool(
- numberOfWorkers,
- './tests/worker-files/cluster/testWorker.cjs'
- )
- expect(buildWorkerChoiceStrategyOptions(pool)).toStrictEqual({
- runTime: { median: false },
- waitTime: { median: false },
- elu: { median: false },
- weights: expect.objectContaining({
- 0: expect.any(Number),
- [pool.info.maxSize - 1]: expect.any(Number)
- })
- })
- const workerChoiceStrategyOptions = {
- runTime: { median: true },
- waitTime: { median: true },
- elu: { median: true },
- weights: {
- 0: 100,
- 1: 100
- }
- }
- expect(
- buildWorkerChoiceStrategyOptions(pool, workerChoiceStrategyOptions)
- ).toStrictEqual(workerChoiceStrategyOptions)
- await pool.destroy()
- })
-
it('Verify updateMeasurementStatistics() behavior', () => {
const measurementStatistics = {
history: new CircularArray()
threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
).toThrow(
new TypeError(
- "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list is not yet defined"
+ "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list is not yet defined"
)
)
- threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1']
+ threadWorkerNode.info.taskFunctionsProperties = [
+ { name: DEFAULT_TASK_NAME },
+ { name: 'fn1' }
+ ]
expect(() =>
threadWorkerNode.getTaskFunctionWorkerUsage('invalidTaskFunction')
).toThrow(
new TypeError(
- "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function names list has less than 3 elements"
+ "Cannot get task function worker usage for task function name 'invalidTaskFunction' when task function properties list has less than 3 elements"
)
)
- threadWorkerNode.info.taskFunctionNames = [DEFAULT_TASK_NAME, 'fn1', 'fn2']
+ threadWorkerNode.info.taskFunctionsProperties = [
+ { name: DEFAULT_TASK_NAME },
+ { name: 'fn1' },
+ { name: 'fn2' }
+ ]
expect(
threadWorkerNode.getTaskFunctionWorkerUsage(DEFAULT_TASK_NAME)
).toStrictEqual({
})
it('Worker node deleteTaskFunctionWorkerUsage()', () => {
- expect(threadWorkerNode.info.taskFunctionNames).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'fn1',
- 'fn2'
+ expect(threadWorkerNode.info.taskFunctionsProperties).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'fn1' },
+ { name: 'fn2' }
])
expect(threadWorkerNode.taskFunctionsUsage.size).toBe(2)
expect(
import { expect } from 'expect'
import { restore, stub } from 'sinon'
-import { ClusterWorker, KillBehaviors, ThreadWorker } from '../../lib/index.cjs'
+import {
+ ClusterWorker,
+ KillBehaviors,
+ ThreadWorker,
+ WorkerChoiceStrategies
+} from '../../lib/index.cjs'
import { DEFAULT_TASK_NAME, EMPTY_FUNCTION } from '../../lib/utils.cjs'
describe('Abstract worker test suite', () => {
})
it('Verify that taskFunctions parameter with unique function is taken', () => {
- const worker = new ThreadWorker(() => {})
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
+ const worker = new ThreadWorker(EMPTY_FUNCTION)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.size).toBe(2)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
new TypeError('A taskFunctions parameter object key is an empty string')
)
expect(() => new ThreadWorker({ fn1, fn2 })).toThrow(
- new TypeError('A taskFunctions parameter object value is not a function')
+ new TypeError(
+ "taskFunction object 'taskFunction' property 'undefined' is not a function"
+ )
+ )
+ expect(() => new ThreadWorker({ fn1: { fn1 } })).toThrow(
+ new TypeError(
+ "taskFunction object 'taskFunction' property 'undefined' is not a function"
+ )
+ )
+ expect(() => new ThreadWorker({ fn2: { taskFunction: fn2 } })).toThrow(
+ new TypeError(
+ "taskFunction object 'taskFunction' property '' is not a function"
+ )
+ )
+ expect(
+ () => new ThreadWorker({ fn1: { taskFunction: fn1, priority: '' } })
+ ).toThrow(new TypeError("Invalid property 'priority': ''"))
+ expect(
+ () => new ThreadWorker({ fn1: { taskFunction: fn1, priority: -21 } })
+ ).toThrow(new TypeError("Property 'priority' must be between -20 and 19"))
+ expect(
+ () => new ThreadWorker({ fn1: { taskFunction: fn1, priority: 20 } })
+ ).toThrow(new RangeError("Property 'priority' must be between -20 and 19"))
+ expect(
+ () =>
+ new ThreadWorker({
+ fn1: { taskFunction: fn1, strategy: 'invalidStrategy' }
+ })
+ ).toThrow(
+ new RangeError("Invalid worker choice strategy 'invalidStrategy'")
)
})
return 2
}
const worker = new ClusterWorker({ fn1, fn2 })
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.size).toBe(3)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
+ worker.taskFunctions.get('fn1')
+ )
+ })
+
+ it('Verify that taskFunctions parameter with multiple task functions object is taken', () => {
+ const fn1Obj = {
+ taskFunction: () => {
+ return 1
+ },
+ priority: 5
+ }
+ const fn2Obj = {
+ taskFunction: () => {
+ return 2
+ },
+ priority: 6,
+ strategy: WorkerChoiceStrategies.LESS_BUSY
+ }
+ const worker = new ThreadWorker({
+ fn1: fn1Obj,
+ fn2: fn2Obj
+ })
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(fn1Obj)
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual(fn1Obj)
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual(fn2Obj)
expect(worker.taskFunctions.size).toBe(3)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
})
expect(worker.addTaskFunction('fn3', '')).toStrictEqual({
status: false,
- error: new TypeError('fn parameter is not a function')
+ error: new TypeError(
+ "taskFunction object 'taskFunction' property 'undefined' is not a function"
+ )
+ })
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
})
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
expect(worker.taskFunctions.size).toBe(2)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
)
})
worker.addTaskFunction('fn2', fn2)
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.size).toBe(3)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
)
worker.addTaskFunction('fn1', fn1Replacement)
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.size).toBe(3)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
)
})
- it('Verify that listTaskFunctionNames() is working', () => {
+ it('Verify that listTaskFunctionsProperties() is working', () => {
const fn1 = () => {
return 1
}
return 2
}
const worker = new ClusterWorker({ fn1, fn2 })
- expect(worker.listTaskFunctionNames()).toStrictEqual([
- DEFAULT_TASK_NAME,
- 'fn1',
- 'fn2'
+ expect(worker.listTaskFunctionsProperties()).toStrictEqual([
+ { name: DEFAULT_TASK_NAME },
+ { name: 'fn1' },
+ { name: 'fn2' }
])
})
status: false,
error: new TypeError('name parameter is an empty string')
})
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.size).toBe(3)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
status: false,
error: new TypeError('name parameter is an empty string')
})
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.size).toBe(3)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
)
})
worker.removeTaskFunction('fn2')
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.get('fn2')).toBeUndefined()
expect(worker.taskFunctions.size).toBe(2)
expect(worker.getMainWorker.calledTwice).toBe(true)
status: false,
error: new TypeError('name parameter is an empty string')
})
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn2')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn2')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.size).toBe(3)
expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual(
worker.taskFunctions.get('fn1')
)
})
worker.removeTaskFunction('fn2')
- expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toBeInstanceOf(Function)
- expect(worker.taskFunctions.get('fn1')).toBeInstanceOf(Function)
+ expect(worker.taskFunctions.get(DEFAULT_TASK_NAME)).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
+ expect(worker.taskFunctions.get('fn1')).toStrictEqual({
+ taskFunction: expect.any(Function)
+ })
expect(worker.taskFunctions.get('fn2')).toBeUndefined()
expect(worker.taskFunctions.size).toBe(2)
expect(worker.port.postMessage.calledOnce).toBe(true)