1
1
import { EventEmitter , CustomEvent } from '@libp2p/interfaces/events' ;
2
+ import { simpleUid } from '@windingtree/contracts' ;
3
+ import { backoffWithJitter } from '../utils/time.js' ;
4
+ import { createLogger } from '../utils/logger.js' ;
5
+
6
+ const logger = createLogger ( 'Queue' ) ;
2
7
3
8
/**
4
9
* Enum to represent the different states a job can be in.
@@ -44,6 +49,8 @@ export interface JobConfig<T extends JobData = JobData> {
44
49
maxRetries ?: number ;
45
50
/** Initial retries value */
46
51
retries ?: number ;
52
+ /** Retries delay */
53
+ retriesDelay ?: number ;
47
54
}
48
55
49
56
/**
@@ -90,6 +97,8 @@ export class Job<T extends JobData = JobData> {
90
97
maxRetries : number ;
91
98
/** The number of times the job has been retried */
92
99
retries : number ;
100
+ /** The period of time between retries */
101
+ retriesDelay : number ;
93
102
/** The history of the job */
94
103
history : JobHistory ;
95
104
@@ -99,7 +108,7 @@ export class Job<T extends JobData = JobData> {
99
108
* @memberof Job
100
109
*/
101
110
constructor ( config : JobConfig < T > ) {
102
- this . id = Date . now ( ) . toString ( ) ;
111
+ this . id = simpleUid ( ) ;
103
112
this . history = new JobHistory ( ) ;
104
113
this . handlerName = config . handlerName ;
105
114
this . data = config . data ;
@@ -110,6 +119,7 @@ export class Job<T extends JobData = JobData> {
110
119
this . maxRecurrences = config . maxRecurrences ?? 0 ;
111
120
this . maxRetries = config . maxRetries ?? 0 ;
112
121
this . retries = config . retries ?? 0 ;
122
+ this . retriesDelay = config . retriesDelay ?? 0 ;
113
123
}
114
124
115
125
/**
@@ -122,6 +132,7 @@ export class Job<T extends JobData = JobData> {
122
132
timestamp : new Date ( ) ,
123
133
status : newStatus ,
124
134
} ) ;
135
+ logger . trace ( `Job #${ this . id } status changed to: ${ this . status } ` ) ;
125
136
}
126
137
127
138
/**
@@ -179,6 +190,7 @@ export class Job<T extends JobData = JobData> {
179
190
* @memberof Job
180
191
*/
181
192
async execute ( handler : JobHandler < T > ) {
193
+ logger . trace ( `Job #${ this . id } executed` ) ;
182
194
return Promise . resolve ( handler ( this . data ) ) ;
183
195
}
184
196
}
@@ -329,8 +341,11 @@ export class Queue extends EventEmitter<QueueEvents> {
329
341
( job ) => job . status === JobStatus . Started ,
330
342
) ;
331
343
const pendingJobs = this . jobs . filter ( ( job ) => job . executable ) ;
344
+ logger . trace ( `Active jobs: ${ activeJobs . length } ` ) ;
345
+ logger . trace ( `Pending jobs: ${ pendingJobs . length } ` ) ;
332
346
333
347
const availableSlots = this . concurrencyLimit - activeJobs . length ;
348
+ logger . trace ( `Available slots: ${ availableSlots } ` ) ;
334
349
335
350
if ( availableSlots <= 0 || pendingJobs . length === 0 ) {
336
351
this . dispatchEvent ( new CustomEvent < void > ( 'stop' ) ) ;
@@ -339,6 +354,9 @@ export class Queue extends EventEmitter<QueueEvents> {
339
354
340
355
// Get the jobs that will be started now
341
356
const jobsToStart = pendingJobs . slice ( 0 , availableSlots ) ;
357
+ logger . trace (
358
+ `Jobs to start: [${ jobsToStart . map ( ( j ) => j . id ) . join ( ', ' ) } ]` ,
359
+ ) ;
342
360
343
361
// Start all the selected jobs concurrently
344
362
const promises = jobsToStart . map ( async ( job ) => {
@@ -347,11 +365,13 @@ export class Queue extends EventEmitter<QueueEvents> {
347
365
348
366
const handler = this . handlers . getHandler ( job . handlerName ) ;
349
367
350
- const shouldRecur = await job . execute ( handler ) ;
368
+ const result = await job . execute ( handler ) ;
369
+ logger . trace ( `Job #${ job . id } execution result: ${ String ( result ) } ` ) ;
351
370
352
- if ( shouldRecur && job . isRecurrent ) {
371
+ if ( result && job . isRecurrent ) {
353
372
// If the job is recurrent and the handler returned true, reschedule the job
354
373
if ( ! job . expired ) {
374
+ logger . trace ( `Job #${ job . id } is done but new one is scheduled` ) ;
355
375
this . changeJobStatus ( job , JobStatus . Done ) ;
356
376
setTimeout ( ( ) => {
357
377
this . add ( {
@@ -366,19 +386,39 @@ export class Queue extends EventEmitter<QueueEvents> {
366
386
} ) ;
367
387
} , job . recurrenceInterval ) ;
368
388
} else {
389
+ logger . trace ( `Job #${ job . id } is expired` ) ;
369
390
this . changeJobStatus ( job , JobStatus . Expired ) ;
370
391
}
371
392
} else {
393
+ logger . trace ( `Job #${ job . id } is done` ) ;
372
394
this . changeJobStatus ( job , JobStatus . Done ) ;
373
395
}
374
396
} catch ( error ) {
397
+ logger . error ( `Job #${ job . id } is errored` , error ) ;
375
398
job . history . errors . push ( error as Error ) ;
376
399
377
400
if ( job . maxRetries > 0 && job . retries < job . maxRetries ) {
378
401
// If the job hasn't reached the maximum number of retries, retry it
379
402
job . retries ++ ;
380
- this . changeJobStatus ( job , JobStatus . Pending ) ;
403
+
404
+ if ( job . retriesDelay > 0 ) {
405
+ logger . trace ( `Job #${ job . id } filed but scheduled for restart` ) ;
406
+ this . changeJobStatus ( job , JobStatus . Failed ) ;
407
+ setTimeout ( ( ) => {
408
+ this . add ( {
409
+ handlerName : job . handlerName ,
410
+ data : job . data ,
411
+ expire : job . expire ,
412
+ maxRetries : job . maxRetries ,
413
+ retries : job . retries + 1 ,
414
+ } ) ;
415
+ } , backoffWithJitter ( job . retriesDelay , job . retries , job . retriesDelay * 10 ) ) ;
416
+ } else {
417
+ logger . trace ( `Job #${ job . id } failed and immediately restarted` ) ;
418
+ this . changeJobStatus ( job , JobStatus . Pending ) ;
419
+ }
381
420
} else {
421
+ logger . trace ( `Job #${ job . id } filed` ) ;
382
422
this . changeJobStatus ( job , JobStatus . Failed ) ;
383
423
}
384
424
}
@@ -387,9 +427,10 @@ export class Queue extends EventEmitter<QueueEvents> {
387
427
await Promise . allSettled ( promises ) ;
388
428
389
429
// After these jobs are done, check if there are any more jobs to process
430
+ logger . trace ( 'Trying to restart queue' ) ;
390
431
void this . start ( ) ;
391
432
} catch ( error ) {
392
- console . error ( error ) ;
433
+ logger . error ( 'Queue start failed' , error ) ;
393
434
}
394
435
}
395
436
@@ -415,6 +456,7 @@ export class Queue extends EventEmitter<QueueEvents> {
415
456
add < T extends JobData = JobData > ( config : JobConfig < T > ) : string {
416
457
const job = new Job < T > ( config ) ;
417
458
this . jobs . push ( job ) ;
459
+ logger . trace ( 'Job added:' , job ) ;
418
460
void this . start ( ) ;
419
461
return job . id ;
420
462
}
@@ -441,10 +483,13 @@ export class Queue extends EventEmitter<QueueEvents> {
441
483
const job = this . jobs . find ( ( job ) => job . id === id ) ;
442
484
443
485
if ( job ) {
486
+ logger . trace ( `Job #${ id } is cancelled` ) ;
444
487
job . status = JobStatus . Cancelled ;
445
488
return true ;
446
489
}
447
490
491
+ logger . trace ( `Job #${ id } has not been cancelled` ) ;
492
+
448
493
return false ;
449
494
}
450
495
@@ -458,6 +503,14 @@ export class Queue extends EventEmitter<QueueEvents> {
458
503
delete ( id : string ) : boolean {
459
504
const size = this . jobs . length ;
460
505
this . jobs = this . jobs . filter ( ( job ) => job . id !== id ) ;
461
- return this . jobs . length < size ;
506
+ const isDeleted = this . jobs . length < size ;
507
+
508
+ if ( isDeleted ) {
509
+ logger . trace ( `Job #${ id } is deleted` ) ;
510
+ } else {
511
+ logger . trace ( `Job #${ id } has not been deleted` ) ;
512
+ }
513
+
514
+ return isDeleted ;
462
515
}
463
516
}
0 commit comments