11'use strict'
22
3- import { Worker , BroadcastChannel } from 'worker_threads'
4- import { EventEmitter } from 'node:stream'
3+ import {
4+ Worker ,
5+ BroadcastChannel ,
6+ captureRejectionSymbol as workerCapture
7+ } from 'node:worker_threads'
8+ import {
9+ EventEmitter ,
10+ captureRejectionSymbol as eventCapture
11+ } from 'node:events'
512import { AsyncResource } from 'node:async_hooks'
13+ import { strict as assert } from 'node:assert'
614import ModelFactory , { totalServices , requestContext } from '.'
715import EventBrokerFactory from './event-broker'
816import domainEvents from './domain-events'
9- import assert from 'assert'
1017import path from 'path'
1118import os from 'os'
12- import { setServers } from 'dns/promises'
1319
1420const { poolOpen, poolClose, poolDrain, poolAbort } = domainEvents
1521const broker = EventBrokerFactory . getInstance ( )
@@ -160,14 +166,20 @@ export class ThreadPool extends EventEmitter {
160166 this . aborting = false
161167 this . jobsRequested = this . jobsQueued = 0
162168 this . broadcastChannel = options . broadcast
163- this . updateLocks ( )
169+ this . checkoutOpen = true
164170
165171 if ( options ?. preload ) {
166172 console . info ( 'preload enabled for' , this . name )
167173 this . startThreads ( )
168174 }
169175 }
170176
177+ [ eventCapture ] ( err , event , ...args ) {
178+ const reason = `rejection happened for ${ event } with ${ err } ${ args } `
179+ console . error ( reason )
180+ this . abort ( reason )
181+ }
182+
171183 /**
172184 * Connect event subchannel to {@link EventBroker}
173185 * @param {Worker } worker worker thread
@@ -201,6 +213,12 @@ export class ThreadPool extends EventEmitter {
201213 const eventChannel = new MessageChannel ( )
202214 const worker = new Worker ( file , { workerData } )
203215
216+ worker [ workerCapture ] = function ( err , event , ...args ) {
217+ const reason = `rejection happened for ${ event } with ${ err } ${ args } `
218+ console . error ( reason )
219+ pool . abort ( reason )
220+ }
221+
204222 /**
205223 * @type {Thread }
206224 */
@@ -440,11 +458,14 @@ export class ThreadPool extends EventEmitter {
440458
441459 incrementJobsQueued ( ) {
442460 this . jobsQueued ++
461+ //if (this.jobsQueued % 10 === 0) this.jobQueueRate()
443462 return this
444463 }
445464
446465 jobQueueRate ( ) {
447- return Math . round ( ( this . jobsQueued / this . jobsRequested ) * 100 )
466+ const rate = Math . round ( ( this . jobsQueued / this . jobsRequested ) * 100 )
467+ //if (rate > this.jobQueueThreshold()) this.updateLocks()
468+ return rate
448469 }
449470
450471 jobQueueThreshold ( ) {
@@ -536,8 +557,13 @@ export class ThreadPool extends EventEmitter {
536557 /**
537558 * Spin up a new thread if needed and available.
538559 */
539- async allocate ( ) {
540- if ( this . poolCanGrow ( ) ) return this . startThread ( )
560+ async allocate ( cb ) {
561+ const yes = this . poolCanGrow ( )
562+ if ( yes ) {
563+ const thread = this . startThread ( )
564+ cb ( thread )
565+ return this . startThread ( )
566+ }
541567 }
542568
543569 threadsInUse ( ) {
@@ -546,33 +572,35 @@ export class ThreadPool extends EventEmitter {
546572 return diff
547573 }
548574
549- updateLocks ( ) {
550- this . locks = [ ]
551- this . threads . forEach ( t => this . locks . push ( t . id ) )
552- }
553-
554- canCheckout ( ) {
555- return this . threads . some ( t => t . id === this . locks . pop ( ) )
556- }
557-
558575 checkout ( ) {
559- if ( ! this . canCheckout ( ) ) return
560-
561576 try {
562- if ( this . freeThreads . length > 0 ) {
577+ if ( this . checkoutOpen ) {
563578 const thread = this . freeThreads . shift ( )
579+ if ( ! thread ) {
580+ if ( this . workers . length > 1 ) this . checkoutOpen = false
581+ ThreadPoolFactory . pauseMonitoring ( this )
582+ this . allocate ( ( ) => {
583+ if ( this . poolCanGrow ( ) )
584+ setTimeout ( ( ) => {
585+ if ( this . poolCanGrow ( ) ) {
586+ this . checkoutOpen = true
587+ ThreadPoolFactory . resumeMonitoring ( this )
588+ }
589+ } , 1000 )
590+ } )
591+ }
564592 console . debug (
565593 `thread checked out, total in use now ${ this . threadsInUse ( ) } `
566594 )
567595 return thread
568596 }
569- this . allocate ( )
570597 } catch ( err ) { }
571598 }
572599
573600 checkin ( thread ) {
574601 if ( thread ) {
575602 this . freeThreads . push ( thread )
603+ this . checkoutOpen = true
576604 console . debug (
577605 `thread checked in, total in use now ${ this . threadsInUse ( ) } `
578606 )
@@ -754,6 +782,7 @@ export class ThreadPool extends EventEmitter {
754782const ThreadPoolFactory = ( ( ) => {
755783 /** @type {Map<string, ThreadPool> } */
756784 const threadPools = new Map ( )
785+ const monitoredPools = new Map ( )
757786
758787 /** @type {Map<string, BroadcastChannel> } */
759788 const broadcastChannels = new Map ( )
@@ -826,6 +855,7 @@ const ThreadPoolFactory = (() => {
826855 } )
827856
828857 threadPools . set ( poolName , pool )
858+ monitoredPools . set ( poolName , pool )
829859 return pool
830860 } catch ( error ) {
831861 console . error ( { fn : createThreadPool . name , error } )
@@ -991,9 +1021,16 @@ const ThreadPoolFactory = (() => {
9911021 /**
9921022 * Monitor pools for stuck threads and restart them
9931023 */
994- function monitorPools ( ) {
1024+ function monitorPools ( pool = null ) {
1025+ if ( pool && ! monitoredPools . has ( pool ) ) {
1026+ monitoredPools . set ( pool . name , pool )
1027+ return
1028+ }
1029+
1030+ if ( monitorIntervalId ) return
1031+
9951032 monitorIntervalId = setInterval ( ( ) => {
996- threadPools . forEach ( pool => {
1033+ monitorPools . forEach ( pool => {
9971034 if ( pool . aborting ) return
9981035
9991036 const workRequested = pool . totalJobsRequested ( )
@@ -1040,8 +1077,13 @@ const ThreadPoolFactory = (() => {
10401077 } , poolMaxAbortTime ( ) )
10411078 }
10421079
1043- function pauseMonitoring ( ) {
1044- clearInterval ( monitorIntervalId )
1080+ function pauseMonitoring ( pool = null ) {
1081+ if ( ! pool ) {
1082+ clearInterval ( monitorIntervalId )
1083+ monitorIntervalId = null
1084+ return
1085+ }
1086+ monitoredPools . delete ( pool . name )
10451087 }
10461088
10471089 function resumeMonitoring ( ) {
0 commit comments