@@ -10,6 +10,7 @@ import {
1010 WorkerMessage ,
1111 WorkerStatus ,
1212} from '@livekit/protocol' ;
13+ import type { Throws } from '@livekit/throws-transformer/throws' ;
1314import type { ParticipantInfo } from 'livekit-server-sdk' ;
1415import { AccessToken , RoomServiceClient } from 'livekit-server-sdk' ;
1516import { EventEmitter } from 'node:events' ;
@@ -22,7 +23,7 @@ import { ProcPool } from './ipc/proc_pool.js';
2223import type { JobAcceptArguments , JobProcess , RunningJobInfo } from './job.js' ;
2324import { JobRequest } from './job.js' ;
2425import { log } from './log.js' ;
25- import { Future } from './utils.js' ;
26+ import { Future , rejectOnAbort } from './utils.js' ;
2627import { version } from './version.js' ;
2728
2829const MAX_RECONNECT_ATTEMPTS = 10 ;
@@ -428,7 +429,7 @@ export class AgentServer {
428429 }
429430
430431 /** @throws {@link WorkerError } if worker did not drain in time */
431- async drain ( timeout ?: number ) {
432+ async drain ( timeout ?: number ) : Promise < Throws < void , WorkerError > > {
432433 if ( this . #draining) {
433434 return ;
434435 }
@@ -450,7 +451,7 @@ export class AgentServer {
450451
451452 const joinJobs = async ( ) => {
452453 return Promise . all (
453- this . #procPool. processes . map ( ( proc ) => {
454+ this . #procPool. processes . map ( ( proc ) : Promise < Throws < void , Error > > => {
454455 if ( ! proc . runningJob ) {
455456 proc . close ( ) ;
456457 }
@@ -459,17 +460,16 @@ export class AgentServer {
459460 ) ;
460461 } ;
461462
462- let timer : NodeJS . Timeout | undefined ;
463+ const promises = [ joinJobs ( ) ] ;
464+
463465 if ( timeout ) {
464- timer = setTimeout ( ( ) => {
465- throw new WorkerError ( 'timed out draining' ) ;
466- } , timeout ) ;
466+ promises . push (
467+ rejectOnAbort ( AbortSignal . timeout ( timeout ) ) . catch ( ( ) => {
468+ throw new WorkerError ( 'timed out draining' ) ;
469+ } ) ,
470+ ) ;
467471 }
468- await joinJobs ( ) . then ( ( ) => {
469- if ( timeout ) {
470- clearTimeout ( timer ) ;
471- }
472- } ) ;
472+ await Promise . race ( promises ) ;
473473 }
474474
475475 async simulateJob ( roomName : string , participantIdentity ?: string ) {
@@ -629,6 +629,25 @@ export class AgentServer {
629629 const loadMonitor = setInterval ( ( ) => {
630630 if ( closingWS ) clearInterval ( loadMonitor ) ;
631631
632+ if ( this . #draining) {
633+ if ( currentStatus !== WorkerStatus . WS_FULL ) {
634+ currentStatus = WorkerStatus . WS_FULL ;
635+ this . event . emit (
636+ 'worker_msg' ,
637+ new WorkerMessage ( {
638+ message : {
639+ case : 'updateWorker' ,
640+ value : {
641+ load : 1 ,
642+ status : WorkerStatus . WS_FULL ,
643+ } ,
644+ } ,
645+ } ) ,
646+ ) ;
647+ }
648+ return ;
649+ }
650+
632651 const oldStatus = currentStatus ;
633652 this . #opts
634653 . loadFunc ( this )
@@ -708,6 +727,7 @@ export class AgentServer {
708727 ) ;
709728
710729 this . #pending[ req . id ] = new PendingAssignment ( ) ;
730+
711731 const timer = setTimeout ( ( ) => {
712732 this . #logger. child ( { req } ) . warn ( `assignment for job ${ req . id } timed out` ) ;
713733 return ;
@@ -718,13 +738,17 @@ export class AgentServer {
718738 } ) ;
719739
720740 if ( asgn ) {
721- await this . #procPool. launchJob ( {
722- acceptArguments : args ,
723- job : msg . job ! ,
724- url : asgn . url || this . #opts. wsURL ,
725- token : asgn . token ,
726- workerId : this . id ,
727- } ) ;
741+ try {
742+ await this . #procPool. launchJob ( {
743+ acceptArguments : args ,
744+ job : msg . job ! ,
745+ url : asgn . url || this . #opts. wsURL ,
746+ token : asgn . token ,
747+ workerId : this . id ,
748+ } ) ;
749+ } catch ( e ) {
750+ this . #logger. child ( { requestId : req . id } ) . error ( e , 'error launching job' ) ;
751+ }
728752 } else {
729753 this . #logger. child ( { requestId : req . id } ) . warn ( 'pending assignment not found' ) ;
730754 }
@@ -735,6 +759,14 @@ export class AgentServer {
735759 . child ( { jobId : msg . job ?. id , resuming : msg . resuming , agentName : this . #opts. agentName } )
736760 . info ( 'received job request' ) ;
737761
762+ if ( this . #draining) {
763+ this . #logger
764+ . child ( { jobId : msg . job ?. id , resuming : msg . resuming , agentName : this . #opts. agentName } )
765+ . info ( 'Worker is draining and no longer available, rejecting job' ) ;
766+ await req . reject ( ) ;
767+ return ;
768+ }
769+
738770 const jobRequestTask = async ( ) => {
739771 try {
740772 await this . #opts. requestFunc ( req ) ;
@@ -770,7 +802,7 @@ export class AgentServer {
770802 // safe to ignore
771803 return ;
772804 }
773- await proc . close ( ) ;
805+ await proc . close ( ) . catch ( ( e ) => this . #logger . error ( e , 'Error terminating job' ) ) ;
774806 }
775807
776808 async close ( ) {
0 commit comments