@@ -213,8 +213,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
213213 */
214214 private connectionDelayTimeout : NodeJS . Timeout ;
215215
216- private triedAllSubchannels = false ;
217-
218216 /**
219217 * The LB policy enters sticky TRANSIENT_FAILURE mode when all
220218 * subchannels have failed to connect at least once, and it stays in that
@@ -225,12 +223,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
225223
226224 private reportHealthStatus : boolean ;
227225
228- /**
229- * Indicates whether we called channelControlHelper.requestReresolution since
230- * the last call to updateAddressList
231- */
232- private requestedResolutionSinceLastUpdate = false ;
233-
234226 /**
235227 * The most recent error reported by any subchannel as it transitioned to
236228 * TRANSIENT_FAILURE.
@@ -259,6 +251,10 @@ export class PickFirstLoadBalancer implements LoadBalancer {
259251 return this . children . every ( child => child . hasReportedTransientFailure ) ;
260252 }
261253
254+ private resetChildrenReportedTF ( ) {
255+ this . children . every ( child => child . hasReportedTransientFailure = false ) ;
256+ }
257+
262258 private calculateAndReportNewState ( ) {
263259 if ( this . currentPick ) {
264260 if ( this . reportHealthStatus && ! this . currentPick . isHealthy ( ) ) {
@@ -291,23 +287,15 @@ export class PickFirstLoadBalancer implements LoadBalancer {
291287 }
292288
293289 private requestReresolution ( ) {
294- this . requestedResolutionSinceLastUpdate = true ;
295290 this . channelControlHelper . requestReresolution ( ) ;
296291 }
297292
298293 private maybeEnterStickyTransientFailureMode ( ) {
299294 if ( ! this . allChildrenHaveReportedTF ( ) ) {
300295 return ;
301296 }
302- if ( ! this . requestedResolutionSinceLastUpdate ) {
303- /* Each time we get an update we reset each subchannel's
304- * hasReportedTransientFailure flag, so the next time we get to this
305- * point after that, each subchannel has reported TRANSIENT_FAILURE
306- * at least once since then. That is the trigger for requesting
307- * reresolution, whether or not the LB policy is already in sticky TF
308- * mode. */
309- this . requestReresolution ( ) ;
310- }
297+ this . requestReresolution ( ) ;
298+ this . resetChildrenReportedTF ( ) ;
311299 if ( this . stickyTransientFailureMode ) {
312300 return ;
313301 }
@@ -320,21 +308,16 @@ export class PickFirstLoadBalancer implements LoadBalancer {
320308
321309 private removeCurrentPick ( ) {
322310 if ( this . currentPick !== null ) {
323- /* Unref can cause a state change, which can cause a change in the value
324- * of this.currentPick, so we hold a local reference to make sure that
325- * does not impact this function. */
326- const currentPick = this . currentPick ;
327- this . currentPick = null ;
328- currentPick . unref ( ) ;
329- currentPick . removeConnectivityStateListener ( this . subchannelStateListener ) ;
311+ this . currentPick . removeConnectivityStateListener ( this . subchannelStateListener ) ;
330312 this . channelControlHelper . removeChannelzChild (
331- currentPick . getChannelzRef ( )
313+ this . currentPick . getChannelzRef ( )
332314 ) ;
333- if ( this . reportHealthStatus ) {
334- currentPick . removeHealthStateWatcher (
335- this . pickedSubchannelHealthListener
336- ) ;
337- }
315+ this . currentPick . removeHealthStateWatcher (
316+ this . pickedSubchannelHealthListener
317+ ) ;
318+ // Unref last, to avoid triggering listeners
319+ this . currentPick . unref ( ) ;
320+ this . currentPick = null ;
338321 }
339322 }
340323
@@ -374,9 +357,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
374357
375358 private startNextSubchannelConnecting ( startIndex : number ) {
376359 clearTimeout ( this . connectionDelayTimeout ) ;
377- if ( this . triedAllSubchannels ) {
378- return ;
379- }
380360 for ( const [ index , child ] of this . children . entries ( ) ) {
381361 if ( index >= startIndex ) {
382362 const subchannelState = child . subchannel . getConnectivityState ( ) ;
@@ -389,7 +369,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
389369 }
390370 }
391371 }
392- this . triedAllSubchannels = true ;
393372 this . maybeEnterStickyTransientFailureMode ( ) ;
394373 }
395374
@@ -418,20 +397,25 @@ export class PickFirstLoadBalancer implements LoadBalancer {
418397 this . connectionDelayTimeout . unref ?.( ) ;
419398 }
420399
400+ /**
401+ * Declare that the specified subchannel should be used to make requests.
402+ * This functions the same independent of whether subchannel is a member of
403+ * this.children and whether it is equal to this.currentPick.
404+ * Prerequisite: subchannel.getConnectivityState() === READY.
405+ * @param subchannel
406+ */
421407 private pickSubchannel ( subchannel : SubchannelInterface ) {
422- if ( this . currentPick && subchannel . realSubchannelEquals ( this . currentPick ) ) {
423- return ;
424- }
425408 trace ( 'Pick subchannel with address ' + subchannel . getAddress ( ) ) ;
426409 this . stickyTransientFailureMode = false ;
427- this . removeCurrentPick ( ) ;
428- this . currentPick = subchannel ;
410+ /* Ref before removeCurrentPick and resetSubchannelList to avoid the
411+ * refcount dropping to 0 during this process. */
429412 subchannel . ref ( ) ;
430- if ( this . reportHealthStatus ) {
431- subchannel . addHealthStateWatcher ( this . pickedSubchannelHealthListener ) ;
432- }
433413 this . channelControlHelper . addChannelzChild ( subchannel . getChannelzRef ( ) ) ;
414+ this . removeCurrentPick ( ) ;
434415 this . resetSubchannelList ( ) ;
416+ subchannel . addConnectivityStateListener ( this . subchannelStateListener ) ;
417+ subchannel . addHealthStateWatcher ( this . pickedSubchannelHealthListener ) ;
418+ this . currentPick = subchannel ;
435419 clearTimeout ( this . connectionDelayTimeout ) ;
436420 this . calculateAndReportNewState ( ) ;
437421 }
@@ -448,20 +432,11 @@ export class PickFirstLoadBalancer implements LoadBalancer {
448432
449433 private resetSubchannelList ( ) {
450434 for ( const child of this . children ) {
451- if (
452- ! (
453- this . currentPick &&
454- child . subchannel . realSubchannelEquals ( this . currentPick )
455- )
456- ) {
457- /* The connectivity state listener is the same whether the subchannel
458- * is in the list of children or it is the currentPick, so if it is in
459- * both, removing it here would cause problems. In particular, that
460- * always happens immediately after the subchannel is picked. */
461- child . subchannel . removeConnectivityStateListener (
462- this . subchannelStateListener
463- ) ;
464- }
435+ /* Always remoev the connectivity state listener. If the subchannel is
436+ getting picked, it will be re-added then. */
437+ child . subchannel . removeConnectivityStateListener (
438+ this . subchannelStateListener
439+ ) ;
465440 /* Refs are counted independently for the children list and the
466441 * currentPick, so we call unref whether or not the child is the
467442 * currentPick. Channelz child references are also refcounted, so
@@ -473,20 +448,16 @@ export class PickFirstLoadBalancer implements LoadBalancer {
473448 }
474449 this . currentSubchannelIndex = 0 ;
475450 this . children = [ ] ;
476- this . triedAllSubchannels = false ;
477- this . requestedResolutionSinceLastUpdate = false ;
478451 }
479452
480453 private connectToAddressList ( addressList : SubchannelAddress [ ] ) {
454+ trace ( 'connectToAddressList([' + addressList . map ( address => subchannelAddressToString ( address ) ) + '])' ) ;
481455 const newChildrenList = addressList . map ( address => ( {
482456 subchannel : this . channelControlHelper . createSubchannel ( address , { } ) ,
483457 hasReportedTransientFailure : false ,
484458 } ) ) ;
485- trace ( 'connectToAddressList([' + addressList . map ( address => subchannelAddressToString ( address ) ) + '])' ) ;
486459 for ( const { subchannel } of newChildrenList ) {
487460 if ( subchannel . getConnectivityState ( ) === ConnectivityState . READY ) {
488- this . channelControlHelper . addChannelzChild ( subchannel . getChannelzRef ( ) ) ;
489- subchannel . addConnectivityStateListener ( this . subchannelStateListener ) ;
490461 this . pickSubchannel ( subchannel ) ;
491462 return ;
492463 }
0 commit comments