import { performance } from 'node:perf_hooks'
import type { TransferListItem } from 'node:worker_threads'
import { EventEmitterAsyncResource } from 'node:events'
+import { AsyncResource } from 'node:async_hooks'
import type {
MessageValue,
PromiseResponseWrapper,
0
),
busyWorkerNodes: this.workerNodes.reduce(
- (accumulator, workerNode) =>
- workerNode.usage.tasks.executing > 0 ? accumulator + 1 : accumulator,
+ (accumulator, _workerNode, workerNodeKey) =>
+ this.isWorkerNodeBusy(workerNodeKey) ? accumulator + 1 : accumulator,
0
),
executedTasks: this.workerNodes.reduce(
this.buildTasksQueueOptions(tasksQueueOptions)
this.setTasksQueueSize(this.opts.tasksQueueOptions.size as number)
if (this.opts.tasksQueueOptions.taskStealing === true) {
+ this.unsetTaskStealing()
this.setTaskStealing()
} else {
this.unsetTaskStealing()
}
if (this.opts.tasksQueueOptions.tasksStealingOnBackPressure === true) {
+ this.unsetTasksStealingOnBackPressure()
this.setTasksStealingOnBackPressure()
} else {
this.unsetTasksStealingOnBackPressure()
)
}
+ private isWorkerNodeBusy (workerNodeKey: number): boolean {
+ if (this.opts.enableTasksQueue === true) {
+ return (
+ this.workerNodes[workerNodeKey].usage.tasks.executing >=
+ (this.opts.tasksQueueOptions?.concurrency as number)
+ )
+ }
+ return this.workerNodes[workerNodeKey].usage.tasks.executing > 0
+ }
+
private async sendTaskFunctionOperationToWorker (
workerNodeKey: number,
message: MessageValue<Data>
this.promiseResponseMap.set(task.taskId as string, {
resolve,
reject,
- workerNodeKey
+ workerNodeKey,
+ ...(this.emitter != null && {
+ asyncResource: new AsyncResource('poolifier:task', {
+ triggerAsyncId: this.emitter.asyncId,
+ requireManualDestroy: true
+ })
+ })
})
if (
this.opts.enableTasksQueue === false ||
}
this.destroying = true
await Promise.all(
- this.workerNodes.map(async (_, workerNodeKey) => {
+ this.workerNodes.map(async (_workerNode, workerNodeKey) => {
await this.destroyWorkerNode(workerNodeKey)
})
)
// Listen to worker messages.
this.registerWorkerMessageListener(
workerNodeKey,
- this.workerMessageListener.bind(this)
+ this.workerMessageListener
)
// Send the startup message to worker.
this.sendStartupMessageToWorker(workerNodeKey)
})
}
+ private handleTask (workerNodeKey: number, task: Task<Data>): void {
+ if (this.shallExecuteTask(workerNodeKey)) {
+ this.executeTask(workerNodeKey, task)
+ } else {
+ this.enqueueTask(workerNodeKey, task)
+ }
+ }
+
private redistributeQueuedTasks (workerNodeKey: number): void {
+ if (this.workerNodes.length <= 1) {
+ return
+ }
while (this.tasksQueueSize(workerNodeKey) > 0) {
const destinationWorkerNodeKey = this.workerNodes.reduce(
(minWorkerNodeKey, workerNode, workerNodeKey, workerNodes) => {
},
0
)
- const task = this.dequeueTask(workerNodeKey) as Task<Data>
- if (this.shallExecuteTask(destinationWorkerNodeKey)) {
- this.executeTask(destinationWorkerNodeKey, task)
- } else {
- this.enqueueTask(destinationWorkerNodeKey, task)
- }
+ this.handleTask(
+ destinationWorkerNodeKey,
+ this.dequeueTask(workerNodeKey) as Task<Data>
+ )
}
}
eventDetail: WorkerNodeEventDetail,
previousStolenTask?: Task<Data>
): void => {
+ if (this.workerNodes.length <= 1) {
+ return
+ }
const { workerNodeKey } = eventDetail
if (workerNodeKey == null) {
throw new Error(
)
if (sourceWorkerNode != null) {
const task = sourceWorkerNode.popTask() as Task<Data>
- if (this.shallExecuteTask(workerNodeKey)) {
- this.executeTask(workerNodeKey, task)
- } else {
- this.enqueueTask(workerNodeKey, task)
- }
+ this.handleTask(workerNodeKey, task)
this.updateTaskSequentiallyStolenStatisticsWorkerUsage(workerNodeKey)
this.updateTaskStolenStatisticsWorkerUsage(
workerNodeKey,
private readonly handleBackPressureEvent = (
eventDetail: WorkerNodeEventDetail
): void => {
+ if (this.workerNodes.length <= 1) {
+ return
+ }
const { workerId } = eventDetail
const sizeOffset = 1
if ((this.opts.tasksQueueOptions?.size as number) <= sizeOffset) {
(this.opts.tasksQueueOptions?.size as number) - sizeOffset
) {
const task = sourceWorkerNode.popTask() as Task<Data>
- if (this.shallExecuteTask(workerNodeKey)) {
- this.executeTask(workerNodeKey, task)
- } else {
- this.enqueueTask(workerNodeKey, task)
- }
+ this.handleTask(workerNodeKey, task)
this.updateTaskStolenStatisticsWorkerUsage(
workerNodeKey,
task.name as string
/**
* This method is the message listener registered on each worker.
*/
- protected workerMessageListener (message: MessageValue<Response>): void {
+ protected readonly workerMessageListener = (
+ message: MessageValue<Response>
+ ): void => {
this.checkMessageWorkerId(message)
const { workerId, ready, taskId, taskFunctionNames } = message
if (ready != null && taskFunctionNames != null) {
const { workerId, taskId, workerError, data } = message
const promiseResponse = this.promiseResponseMap.get(taskId as string)
if (promiseResponse != null) {
- const { resolve, reject, workerNodeKey } = promiseResponse
+ const { resolve, reject, workerNodeKey, asyncResource } = promiseResponse
if (workerError != null) {
this.emitter?.emit(PoolEvents.taskError, workerError)
- reject(workerError.message)
+ asyncResource != null
+ ? asyncResource.runInAsyncScope(
+ reject,
+ this.emitter,
+ workerError.message
+ )
+ : reject(workerError.message)
} else {
- resolve(data as Response)
+ asyncResource != null
+ ? asyncResource.runInAsyncScope(resolve, this.emitter, data)
+ : resolve(data as Response)
}
+ asyncResource?.emitDestroy()
this.afterTaskExecutionHook(workerNodeKey, message)
this.workerChoiceStrategyContext.update(workerNodeKey)
this.promiseResponseMap.delete(taskId as string)