Skip to content

Commit 3dbb472

Browse files
committed
Make admin client reconnect retry configurable, and delay exponential
1 parent dabf0a8 commit 3dbb472

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

src/client/admin-client.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ export interface AdminClientOptions {
9191
*/
9292
adminServerUrl?: string;
9393

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+
94102
/**
95103
* Options to include on all client requests.
96104
*/
@@ -192,7 +200,9 @@ export async function resetAdminServer(options: AdminClientOptions = {}): Promis
192200
*/
193201
export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any> }> extends EventEmitter {
194202

195-
private adminClientOptions: RequireProps<AdminClientOptions, 'adminServerUrl'>;
203+
private adminClientOptions: RequireProps<AdminClientOptions,
204+
'adminServerUrl' | 'adminStreamReconnectAttempts'
205+
>;
196206

197207
private adminSessionBaseUrl: string | undefined;
198208
private adminServerStream: Duplex | undefined;
@@ -212,7 +222,8 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
212222
super();
213223
this.debug = !!options.debug;
214224
this.adminClientOptions = _.defaults(options, {
215-
adminServerUrl: `http://localhost:${DEFAULT_ADMIN_SERVER_PORT}`
225+
adminServerUrl: `http://localhost:${DEFAULT_ADMIN_SERVER_PORT}`,
226+
adminStreamReconnectAttempts: 5
216227
});
217228
}
218229

@@ -246,7 +257,14 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
246257
targetStream.emit('server-shutdown');
247258
} else if (streamConnected && (await this.running) === true) {
248259
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+
}
250268
}
251269
// If never connected successfully, we do nothing.
252270
});
@@ -266,7 +284,11 @@ export class AdminClient<Plugins extends { [key: string]: AdminPlugin<any, any>
266284
* different to normal connection setup, as it assumes the target stream is otherwise already
267285
* set up and active.
268286
*/
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+
) {
270292
this.emit('stream-reconnecting');
271293

272294
// 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>
283305
if (retries > 0) {
284306
// We delay re-retrying briefly - this helps to handle cases like the computer going
285307
// 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+
287312
return this.tryToReconnectStream(adminSessionBaseUrl, targetStream, retries - 1);
288313
}
289314

0 commit comments

Comments
 (0)