@@ -26,12 +26,18 @@ export async function handler (args: ArgumentsCamelCase) {
2626 assertNumber ( timeout , "timeout must be a number in ms" ) ;
2727 const interval = args . interval ;
2828 assertNumber ( interval , "interval must be a number in ms" ) ;
29+ const stableChecks = args . stableChecks ;
30+ assertNumber ( stableChecks , "stableChecks must be a number" ) ;
2931
3032 const dockerode = new Docker ( ) ;
3133
3234 console . log ( `Awaiting task reconciliation for ${ timeout } ms` ) ;
3335
3436 let services : Service [ ] , tasks : Task [ ] , timedout , bail , serviceStateMap ;
37+ let stableStreak = 0 ;
38+ const seenFailedTaskIds = new Set < string > ( ) ;
39+ const restartCooldown = new Map < string , number > ( ) ;
40+ let firstCheck = true ;
3541 const start = Date . now ( ) ;
3642 do {
3743 // To prevent high cpu usage
@@ -55,17 +61,40 @@ export async function handler (args: ArgumentsCamelCase) {
5561 }
5662 }
5763
64+ for ( const t of tasks ) {
65+ if ( ! [ "failed" , "rejected" ] . includes ( t . Status . State ) || seenFailedTaskIds . has ( t . ID ) ) {
66+ continue ;
67+ }
68+ seenFailedTaskIds . add ( t . ID ) ;
69+ if ( ! firstCheck ) {
70+ restartCooldown . set ( t . ServiceID , stableChecks ) ;
71+ }
72+ }
73+ firstCheck = false ;
74+
75+ for ( const [ serviceId , remaining ] of restartCooldown ) {
76+ serviceStateMap . set ( serviceId , "restarting" ) ;
77+ restartCooldown . set ( serviceId , remaining - 1 ) ;
78+ if ( remaining - 1 <= 0 ) {
79+ restartCooldown . delete ( serviceId ) ;
80+ }
81+ }
82+
5883 const servicesUpdating = [ ...serviceStateMap . entries ( ) ] ;
5984
60- bail = servicesUpdating . length === 0 ;
61- if ( ! bail ) {
85+ if ( servicesUpdating . length === 0 ) {
86+ stableStreak ++ ;
87+ } else {
88+ stableStreak = 0 ;
6289 for ( const [ serviceId , state ] of servicesUpdating ) {
6390 const serviceName = services . find ( ( s ) => s . ID === serviceId ) ?. Spec ?. Name ;
6491 assert ( serviceName != null , "serviceName must be a string" ) ;
6592 const errMsg = tasks . find ( ( t ) => t . ServiceID === serviceId && t . Status . Err ) ?. Status . Err ;
6693 console . log ( `${ serviceName } is in ${ state } state${ errMsg ? ", error: '" + errMsg + "'" : "" } ` ) ;
6794 }
6895 }
96+
97+ bail = stableStreak >= stableChecks ;
6998 } while ( ! timedout && ! bail ) ;
7099
71100 if ( timedout ) {
@@ -88,6 +117,11 @@ export function builder (yargs: Argv) {
88117 description : "How often reconciliation should run" ,
89118 default : 5000 ,
90119 } ) ;
120+ yargs . positional ( "stableChecks" , {
121+ type : "number" ,
122+ description : "Consecutive settled checks required before success" ,
123+ default : 3 ,
124+ } ) ;
91125 yargs . hide ( "help" ) ;
92126 yargs . hide ( "version" ) ;
93127 return yargs ;
0 commit comments