@@ -91,6 +91,14 @@ export interface AdminClientOptions {
91
91
*/
92
92
adminServerUrl ?: string ;
93
93
94
+ /**
95
+ * If the admin stream disconnects, how many times should we try to
96
+ * reconnect? Increasing this can be useful in unstable environments, such
97
+ * as desktop app use case, while fewer retries will provide faster shutdown
98
+ * in environments where you may be killing processes intentionally.
99
+ */
100
+ adminStreamReconnectAttempts ?: number ;
101
+
94
102
/**
95
103
* Options to include on all client requests.
96
104
*/
@@ -192,7 +200,9 @@ export async function resetAdminServer(options: AdminClientOptions = {}): Promis
192
200
*/
193
201
export class AdminClient < Plugins extends { [ key : string ] : AdminPlugin < any , any > } > extends EventEmitter {
194
202
195
- private adminClientOptions : RequireProps < AdminClientOptions , 'adminServerUrl' > ;
203
+ private adminClientOptions : RequireProps < AdminClientOptions ,
204
+ 'adminServerUrl' | 'adminStreamReconnectAttempts'
205
+ > ;
196
206
197
207
private adminSessionBaseUrl : string | undefined ;
198
208
private adminServerStream : Duplex | undefined ;
@@ -212,7 +222,8 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
212
222
super ( ) ;
213
223
this . debug = ! ! options . debug ;
214
224
this . adminClientOptions = _ . defaults ( options , {
215
- adminServerUrl : `http://localhost:${ DEFAULT_ADMIN_SERVER_PORT } `
225
+ adminServerUrl : `http://localhost:${ DEFAULT_ADMIN_SERVER_PORT } ` ,
226
+ adminStreamReconnectAttempts : 5
216
227
} ) ;
217
228
}
218
229
@@ -246,7 +257,14 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
246
257
targetStream . emit ( 'server-shutdown' ) ;
247
258
} else if ( streamConnected && ( await this . running ) === true ) {
248
259
console . warn ( 'Admin client stream unexpectedly disconnected' , closeEvent ) ;
249
- this . tryToReconnectStream ( adminSessionBaseUrl , targetStream ) ;
260
+
261
+ if ( this . adminClientOptions . adminStreamReconnectAttempts > 0 ) {
262
+ this . tryToReconnectStream ( adminSessionBaseUrl , targetStream ) ;
263
+ } else {
264
+ // If retries are disabled, shut down immediately:
265
+ console . log ( 'Admin client stream reconnect disabled, shutting down' ) ;
266
+ targetStream . emit ( 'server-shutdown' ) ;
267
+ }
250
268
}
251
269
// If never connected successfully, we do nothing.
252
270
} ) ;
@@ -266,7 +284,11 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
266
284
* different to normal connection setup, as it assumes the target stream is otherwise already
267
285
* set up and active.
268
286
*/
269
- private async tryToReconnectStream ( adminSessionBaseUrl : string , targetStream : Duplex , retries = 3 ) {
287
+ private async tryToReconnectStream (
288
+ adminSessionBaseUrl : string ,
289
+ targetStream : Duplex ,
290
+ retries = this . adminClientOptions . adminStreamReconnectAttempts
291
+ ) {
270
292
this . emit ( 'stream-reconnecting' ) ;
271
293
272
294
// Unclean shutdown means something has gone wrong somewhere. Try to reconnect.
@@ -283,7 +305,10 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
283
305
if ( retries > 0 ) {
284
306
// We delay re-retrying briefly - this helps to handle cases like the computer going
285
307
// to sleep (where the server & client pause in parallel, but race to do so).
286
- await delay ( 50 ) ;
308
+ // The delay increases exponentially with retry attempts (10ms, 50, 250, 1250, 6250)
309
+ const retryAttempt = this . adminClientOptions . adminStreamReconnectAttempts - retries ;
310
+ await delay ( 10 * Math . pow ( 5 , retryAttempt ) ) ;
311
+
287
312
return this . tryToReconnectStream ( adminSessionBaseUrl , targetStream , retries - 1 ) ;
288
313
}
289
314
0 commit comments