@@ -8,6 +8,7 @@ import { AnyQueueItem, SimpleQueue } from "./queue.js";
88import { nanoid } from "nanoid" ;
99import pLimit from "p-limit" ;
1010import { createRedisClient } from "@internal/redis" ;
11+ import { shutdownManager } from "@trigger.dev/core/v3/serverOnly" ;
1112
1213export type WorkerCatalog = {
1314 [ key : string ] : {
@@ -44,6 +45,7 @@ type WorkerOptions<TCatalog extends WorkerCatalog> = {
4445 concurrency ?: WorkerConcurrencyOptions ;
4546 pollIntervalMs ?: number ;
4647 immediatePollIntervalMs ?: number ;
48+ shutdownTimeoutMs ?: number ;
4749 logger ?: Logger ;
4850 tracer ?: Tracer ;
4951} ;
@@ -69,6 +71,7 @@ class Worker<TCatalog extends WorkerCatalog> {
6971 private workerLoops : Promise < void > [ ] = [ ] ;
7072 private isShuttingDown = false ;
7173 private concurrency : Required < NonNullable < WorkerOptions < TCatalog > [ "concurrency" ] > > ;
74+ private shutdownTimeoutMs : number ;
7275
7376 // The p-limit limiter to control overall concurrency.
7477 private limiter : ReturnType < typeof pLimit > ;
@@ -77,6 +80,8 @@ class Worker<TCatalog extends WorkerCatalog> {
7780 this . logger = options . logger ?? new Logger ( "Worker" , "debug" ) ;
7881 this . tracer = options . tracer ?? trace . getTracer ( options . name ) ;
7982
83+ this . shutdownTimeoutMs = options . shutdownTimeoutMs ?? 60_000 ;
84+
8085 const schema : QueueCatalogFromWorkerCatalog < TCatalog > = Object . fromEntries (
8186 Object . entries ( this . options . catalog ) . map ( ( [ key , value ] ) => [ key , value . schema ] )
8287 ) as QueueCatalogFromWorkerCatalog < TCatalog > ;
@@ -386,22 +391,33 @@ class Worker<TCatalog extends WorkerCatalog> {
386391 }
387392
388393 private setupShutdownHandlers ( ) {
389- process . on ( "SIGTERM" , this . shutdown . bind ( this ) ) ;
390- process . on ( "SIGINT" , this . shutdown . bind ( this ) ) ;
394+ shutdownManager . register ( "redis-worker" , this . shutdown . bind ( this ) ) ;
391395 }
392396
393- private async shutdown ( ) {
394- if ( this . isShuttingDown ) return ;
397+ private async shutdown ( signal ?: NodeJS . Signals ) {
398+ if ( this . isShuttingDown ) {
399+ this . logger . log ( "Worker already shutting down" , { signal } ) ;
400+ return ;
401+ }
402+
395403 this . isShuttingDown = true ;
396- this . logger . log ( "Shutting down worker loops..." ) ;
404+ this . logger . log ( "Shutting down worker loops..." , { signal } ) ;
397405
398406 // Wait for all worker loops to finish.
399- await Promise . all ( this . workerLoops ) ;
407+ await Promise . race ( [
408+ Promise . all ( this . workerLoops ) ,
409+ Worker . delay ( this . shutdownTimeoutMs ) . then ( ( ) => {
410+ this . logger . error ( "Worker shutdown timed out" , {
411+ signal,
412+ shutdownTimeoutMs : this . shutdownTimeoutMs ,
413+ } ) ;
414+ } ) ,
415+ ] ) ;
400416
401417 await this . subscriber ?. unsubscribe ( ) ;
402418 await this . subscriber ?. quit ( ) ;
403419 await this . queue . close ( ) ;
404- this . logger . log ( "All workers and subscribers shut down." ) ;
420+ this . logger . log ( "All workers and subscribers shut down." , { signal } ) ;
405421 }
406422
407423 public async stop ( ) {
0 commit comments