@@ -113,7 +113,7 @@ class Worker<TCatalog extends WorkerCatalog> {
113
113
private shutdownTimeoutMs : number ;
114
114
115
115
// The p-limit limiter to control overall concurrency.
116
- private limiter : ReturnType < typeof pLimit > ;
116
+ private limiters : Record < string , ReturnType < typeof pLimit > > = { } ;
117
117
118
118
constructor ( private options : WorkerOptions < TCatalog > ) {
119
119
this . logger = options . logger ?? new Logger ( "Worker" , "debug" ) ;
@@ -138,9 +138,6 @@ class Worker<TCatalog extends WorkerCatalog> {
138
138
const { workers = 1 , tasksPerWorker = 1 , limit = 10 } = options . concurrency ?? { } ;
139
139
this . concurrency = { workers, tasksPerWorker, limit } ;
140
140
141
- // Create a p-limit instance using this limit.
142
- this . limiter = pLimit ( this . concurrency . limit ) ;
143
-
144
141
const masterQueueObservableGauge = this . meter . createObservableGauge ( "redis_worker.queue.size" , {
145
142
description : "The number of items in the queue" ,
146
143
unit : "items" ,
@@ -203,15 +200,21 @@ class Worker<TCatalog extends WorkerCatalog> {
203
200
}
204
201
205
202
async #updateConcurrencyLimitActiveMetric( observableResult : ObservableResult < Attributes > ) {
206
- observableResult . observe ( this . limiter . activeCount , {
207
- worker_name : this . options . name ,
208
- } ) ;
203
+ for ( const [ workerId , limiter ] of Object . entries ( this . limiters ) ) {
204
+ observableResult . observe ( limiter . activeCount , {
205
+ worker_name : this . options . name ,
206
+ worker_id : workerId ,
207
+ } ) ;
208
+ }
209
209
}
210
210
211
211
async #updateConcurrencyLimitPendingMetric( observableResult : ObservableResult < Attributes > ) {
212
- observableResult . observe ( this . limiter . pendingCount , {
213
- worker_name : this . options . name ,
214
- } ) ;
212
+ for ( const [ workerId , limiter ] of Object . entries ( this . limiters ) ) {
213
+ observableResult . observe ( limiter . pendingCount , {
214
+ worker_name : this . options . name ,
215
+ worker_id : workerId ,
216
+ } ) ;
217
+ }
215
218
}
216
219
217
220
public start ( ) {
@@ -417,6 +420,9 @@ class Worker<TCatalog extends WorkerCatalog> {
417
420
workerIndex : number ,
418
421
totalWorkers : number
419
422
) : Promise < void > {
423
+ const limiter = pLimit ( this . concurrency . limit ) ;
424
+ this . limiters [ workerId ] = limiter ;
425
+
420
426
const pollIntervalMs = this . options . pollIntervalMs ?? 1000 ;
421
427
const immediatePollIntervalMs = this . options . immediatePollIntervalMs ?? 100 ;
422
428
@@ -438,35 +444,42 @@ class Worker<TCatalog extends WorkerCatalog> {
438
444
439
445
while ( ! this . isShuttingDown ) {
440
446
// Check overall load. If at capacity, wait a bit before trying to dequeue more.
441
- if ( this . limiter . activeCount + this . limiter . pendingCount >= this . concurrency . limit ) {
447
+ if ( limiter . activeCount + limiter . pendingCount >= this . concurrency . limit ) {
442
448
this . logger . debug ( "Worker at capacity, waiting" , {
443
449
workerId,
444
450
concurrencyOptions : this . concurrency ,
445
- activeCount : this . limiter . activeCount ,
446
- pendingCount : this . limiter . pendingCount ,
451
+ activeCount : limiter . activeCount ,
452
+ pendingCount : limiter . pendingCount ,
447
453
} ) ;
448
454
449
455
await Worker . delay ( pollIntervalMs ) ;
450
456
451
457
continue ;
452
458
}
453
459
460
+ // If taskCount is 10, concurrency limit is 100, and there are 98 active workers, we should dequeue 2 items at most.
461
+ // If taskCount is 10, concurrency limit is 100, and there are 12 active workers, we should dequeue 10 items at most.
462
+ const $taskCount = Math . min (
463
+ taskCount ,
464
+ this . concurrency . limit - limiter . activeCount - limiter . pendingCount
465
+ ) ;
466
+
454
467
try {
455
468
const items = await this . withHistogram (
456
469
this . metrics . dequeueDuration ,
457
- this . queue . dequeue ( taskCount ) ,
470
+ this . queue . dequeue ( $ taskCount) ,
458
471
{
459
472
worker_id : workerId ,
460
- task_count : taskCount ,
473
+ task_count : $ taskCount,
461
474
}
462
475
) ;
463
476
464
477
if ( items . length === 0 ) {
465
478
this . logger . debug ( "No items to dequeue" , {
466
479
workerId,
467
480
concurrencyOptions : this . concurrency ,
468
- activeCount : this . limiter . activeCount ,
469
- pendingCount : this . limiter . pendingCount ,
481
+ activeCount : limiter . activeCount ,
482
+ pendingCount : limiter . pendingCount ,
470
483
} ) ;
471
484
472
485
await Worker . delay ( pollIntervalMs ) ;
@@ -477,17 +490,17 @@ class Worker<TCatalog extends WorkerCatalog> {
477
490
workerId,
478
491
itemCount : items . length ,
479
492
concurrencyOptions : this . concurrency ,
480
- activeCount : this . limiter . activeCount ,
481
- pendingCount : this . limiter . pendingCount ,
493
+ activeCount : limiter . activeCount ,
494
+ pendingCount : limiter . pendingCount ,
482
495
} ) ;
483
496
484
497
// Schedule each item using the limiter.
485
498
for ( const item of items ) {
486
- this . limiter ( ( ) => this . processItem ( item as AnyQueueItem , items . length , workerId ) ) . catch (
487
- ( err ) => {
488
- this . logger . error ( "Unhandled error in processItem:" , { error : err , workerId , item } ) ;
489
- }
490
- ) ;
499
+ limiter ( ( ) =>
500
+ this . processItem ( item as AnyQueueItem , items . length , workerId , limiter )
501
+ ) . catch ( ( err ) => {
502
+ this . logger . error ( "Unhandled error in processItem:" , { error : err , workerId , item } ) ;
503
+ } ) ;
491
504
}
492
505
} catch ( error ) {
493
506
this . logger . error ( "Error dequeuing items:" , { name : this . options . name , error } ) ;
@@ -508,7 +521,8 @@ class Worker<TCatalog extends WorkerCatalog> {
508
521
private async processItem (
509
522
{ id, job, item, visibilityTimeoutMs, attempt, timestamp, deduplicationKey } : AnyQueueItem ,
510
523
batchSize : number ,
511
- workerId : string
524
+ workerId : string ,
525
+ limiter : ReturnType < typeof pLimit >
512
526
) : Promise < void > {
513
527
const catalogItem = this . options . catalog [ job as any ] ;
514
528
const handler = this . jobs [ job as any ] ;
@@ -553,9 +567,9 @@ class Worker<TCatalog extends WorkerCatalog> {
553
567
job_timestamp : timestamp . getTime ( ) ,
554
568
job_age_in_ms : Date . now ( ) - timestamp . getTime ( ) ,
555
569
worker_id : workerId ,
556
- worker_limit_concurrency : this . limiter . concurrency ,
557
- worker_limit_active : this . limiter . activeCount ,
558
- worker_limit_pending : this . limiter . pendingCount ,
570
+ worker_limit_concurrency : limiter . concurrency ,
571
+ worker_limit_active : limiter . activeCount ,
572
+ worker_limit_pending : limiter . pendingCount ,
559
573
worker_name : this . options . name ,
560
574
batch_size : batchSize ,
561
575
} ,
0 commit comments