diff --git a/packages/grpc-js/src/subchannel.ts b/packages/grpc-js/src/subchannel.ts index ab7b176c..f56a31d4 100644 --- a/packages/grpc-js/src/subchannel.ts +++ b/packages/grpc-js/src/subchannel.ts @@ -231,21 +231,25 @@ export class Subchannel { maxDelay: options['grpc.max_reconnect_backoff_ms'], }; this.backoffTimeout = new BackoffTimeout(() => { - if (this.continueConnecting) { - this.transitionToState( - [ConnectivityState.TRANSIENT_FAILURE], - ConnectivityState.CONNECTING - ); - } else { - this.transitionToState( - [ConnectivityState.TRANSIENT_FAILURE], - ConnectivityState.IDLE - ); - } + this.handleBackoffTimer(); }, backoffOptions); this.subchannelAddressString = subchannelAddressToString(subchannelAddress); } + private handleBackoffTimer() { + if (this.continueConnecting) { + this.transitionToState( + [ConnectivityState.TRANSIENT_FAILURE], + ConnectivityState.CONNECTING + ); + } else { + this.transitionToState( + [ConnectivityState.TRANSIENT_FAILURE], + ConnectivityState.IDLE + ); + } + } + /** * Start a backoff timer with the current nextBackoff timeout */ @@ -505,6 +509,14 @@ export class Subchannel { } this.session = null; this.stopKeepalivePings(); + /* If the backoff timer has already ended by the time we get to the + * TRANSIENT_FAILURE state, we want to immediately transition out of + * TRANSIENT_FAILURE as though the backoff timer is ending right now */ + if (!this.backoffTimeout.isRunning()) { + process.nextTick(() => { + this.handleBackoffTimer(); + }); + } break; case ConnectivityState.IDLE: /* Stopping the backoff timer here is probably redundant because we