+ * Creates a new, completely set up worker node.
+ *
+ * @returns New, completely set up worker node key.
+ */
+ protected createAndSetupWorkerNode (): number {
+ const workerNode = this.createWorkerNode()
+ workerNode.registerWorkerEventHandler(
+ 'online',
+ this.opts.onlineHandler ?? EMPTY_FUNCTION
+ )
+ workerNode.registerWorkerEventHandler(
+ 'message',
+ this.opts.messageHandler ?? EMPTY_FUNCTION
+ )
+ workerNode.registerWorkerEventHandler(
+ 'error',
+ this.opts.errorHandler ?? EMPTY_FUNCTION
+ )
+ workerNode.registerWorkerEventHandler('error', (error: Error) => {
+ workerNode.info.ready = false
+ this.emitter?.emit(PoolEvents.error, error)
+ if (
+ this.started &&
+ !this.destroying &&
+ this.opts.restartWorkerOnError === true
+ ) {
+ if (workerNode.info.dynamic) {
+ this.createAndSetupDynamicWorkerNode()
+ } else {
+ this.createAndSetupWorkerNode()
+ }
+ }
+ if (
+ this.started &&
+ !this.destroying &&
+ this.opts.enableTasksQueue === true
+ ) {
+ this.redistributeQueuedTasks(this.workerNodes.indexOf(workerNode))
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ workerNode?.terminate().catch(error => {
+ this.emitter?.emit(PoolEvents.error, error)
+ })
+ })
+ workerNode.registerWorkerEventHandler(
+ 'exit',
+ this.opts.exitHandler ?? EMPTY_FUNCTION
+ )
+ workerNode.registerOnceWorkerEventHandler('exit', () => {
+ this.removeWorkerNode(workerNode)
+ })
+ const workerNodeKey = this.addWorkerNode(workerNode)
+ this.afterWorkerNodeSetup(workerNodeKey)
+ return workerNodeKey
+ }
+
+ /**
+ * Creates a new, completely set up dynamic worker node.
+ *
+ * @returns New, completely set up dynamic worker node key.
+ */
+ protected createAndSetupDynamicWorkerNode (): number {
+ const workerNodeKey = this.createAndSetupWorkerNode()
+ this.registerWorkerMessageListener(workerNodeKey, message => {
+ this.checkMessageWorkerId(message)
+ const localWorkerNodeKey = this.getWorkerNodeKeyByWorkerId(
+ message.workerId
+ )
+ const workerUsage = this.workerNodes[localWorkerNodeKey]?.usage
+ // Kill message received from worker
+ if (
+ isKillBehavior(KillBehaviors.HARD, message.kill) ||
+ (isKillBehavior(KillBehaviors.SOFT, message.kill) &&
+ ((this.opts.enableTasksQueue === false &&
+ workerUsage.tasks.executing === 0) ||
+ (this.opts.enableTasksQueue === true &&
+ workerUsage.tasks.executing === 0 &&
+ this.tasksQueueSize(localWorkerNodeKey) === 0)))
+ ) {
+ // Flag the worker node as not ready immediately
+ this.flagWorkerNodeAsNotReady(localWorkerNodeKey)
+ this.destroyWorkerNode(localWorkerNodeKey).catch(error => {
+ this.emitter?.emit(PoolEvents.error, error)
+ })
+ }
+ })
+ this.sendToWorker(workerNodeKey, {
+ checkActive: true
+ })
+ if (this.taskFunctions.size > 0) {
+ for (const [taskFunctionName, taskFunction] of this.taskFunctions) {
+ this.sendTaskFunctionOperationToWorker(workerNodeKey, {
+ taskFunctionOperation: 'add',
+ taskFunctionName,
+ taskFunction: taskFunction.toString()
+ }).catch(error => {
+ 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
+ ) {
+ workerNode.info.ready = true
+ }
+ this.checkAndEmitDynamicWorkerCreationEvents()
+ return workerNodeKey
+ }
+
+ /**
+ * Registers a listener callback on the worker given its worker node key.