@@ -213,8 +213,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
213
213
*/
214
214
private connectionDelayTimeout : NodeJS . Timeout ;
215
215
216
- private triedAllSubchannels = false ;
217
-
218
216
/**
219
217
* The LB policy enters sticky TRANSIENT_FAILURE mode when all
220
218
* subchannels have failed to connect at least once, and it stays in that
@@ -225,12 +223,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
225
223
226
224
private reportHealthStatus : boolean ;
227
225
228
- /**
229
- * Indicates whether we called channelControlHelper.requestReresolution since
230
- * the last call to updateAddressList
231
- */
232
- private requestedResolutionSinceLastUpdate = false ;
233
-
234
226
/**
235
227
* The most recent error reported by any subchannel as it transitioned to
236
228
* TRANSIENT_FAILURE.
@@ -259,6 +251,10 @@ export class PickFirstLoadBalancer implements LoadBalancer {
259
251
return this . children . every ( child => child . hasReportedTransientFailure ) ;
260
252
}
261
253
254
+ private resetChildrenReportedTF ( ) {
255
+ this . children . every ( child => child . hasReportedTransientFailure = false ) ;
256
+ }
257
+
262
258
private calculateAndReportNewState ( ) {
263
259
if ( this . currentPick ) {
264
260
if ( this . reportHealthStatus && ! this . currentPick . isHealthy ( ) ) {
@@ -291,23 +287,15 @@ export class PickFirstLoadBalancer implements LoadBalancer {
291
287
}
292
288
293
289
private requestReresolution ( ) {
294
- this . requestedResolutionSinceLastUpdate = true ;
295
290
this . channelControlHelper . requestReresolution ( ) ;
296
291
}
297
292
298
293
private maybeEnterStickyTransientFailureMode ( ) {
299
294
if ( ! this . allChildrenHaveReportedTF ( ) ) {
300
295
return ;
301
296
}
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 ( ) ;
311
299
if ( this . stickyTransientFailureMode ) {
312
300
return ;
313
301
}
@@ -320,21 +308,16 @@ export class PickFirstLoadBalancer implements LoadBalancer {
320
308
321
309
private removeCurrentPick ( ) {
322
310
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 ) ;
330
312
this . channelControlHelper . removeChannelzChild (
331
- currentPick . getChannelzRef ( )
313
+ this . currentPick . getChannelzRef ( )
332
314
) ;
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 ;
338
321
}
339
322
}
340
323
@@ -374,9 +357,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
374
357
375
358
private startNextSubchannelConnecting ( startIndex : number ) {
376
359
clearTimeout ( this . connectionDelayTimeout ) ;
377
- if ( this . triedAllSubchannels ) {
378
- return ;
379
- }
380
360
for ( const [ index , child ] of this . children . entries ( ) ) {
381
361
if ( index >= startIndex ) {
382
362
const subchannelState = child . subchannel . getConnectivityState ( ) ;
@@ -389,7 +369,6 @@ export class PickFirstLoadBalancer implements LoadBalancer {
389
369
}
390
370
}
391
371
}
392
- this . triedAllSubchannels = true ;
393
372
this . maybeEnterStickyTransientFailureMode ( ) ;
394
373
}
395
374
@@ -418,20 +397,25 @@ export class PickFirstLoadBalancer implements LoadBalancer {
418
397
this . connectionDelayTimeout . unref ?.( ) ;
419
398
}
420
399
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
+ */
421
407
private pickSubchannel ( subchannel : SubchannelInterface ) {
422
- if ( this . currentPick && subchannel . realSubchannelEquals ( this . currentPick ) ) {
423
- return ;
424
- }
425
408
trace ( 'Pick subchannel with address ' + subchannel . getAddress ( ) ) ;
426
409
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. */
429
412
subchannel . ref ( ) ;
430
- if ( this . reportHealthStatus ) {
431
- subchannel . addHealthStateWatcher ( this . pickedSubchannelHealthListener ) ;
432
- }
433
413
this . channelControlHelper . addChannelzChild ( subchannel . getChannelzRef ( ) ) ;
414
+ this . removeCurrentPick ( ) ;
434
415
this . resetSubchannelList ( ) ;
416
+ subchannel . addConnectivityStateListener ( this . subchannelStateListener ) ;
417
+ subchannel . addHealthStateWatcher ( this . pickedSubchannelHealthListener ) ;
418
+ this . currentPick = subchannel ;
435
419
clearTimeout ( this . connectionDelayTimeout ) ;
436
420
this . calculateAndReportNewState ( ) ;
437
421
}
@@ -448,20 +432,11 @@ export class PickFirstLoadBalancer implements LoadBalancer {
448
432
449
433
private resetSubchannelList ( ) {
450
434
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
+ ) ;
465
440
/* Refs are counted independently for the children list and the
466
441
* currentPick, so we call unref whether or not the child is the
467
442
* currentPick. Channelz child references are also refcounted, so
@@ -473,20 +448,16 @@ export class PickFirstLoadBalancer implements LoadBalancer {
473
448
}
474
449
this . currentSubchannelIndex = 0 ;
475
450
this . children = [ ] ;
476
- this . triedAllSubchannels = false ;
477
- this . requestedResolutionSinceLastUpdate = false ;
478
451
}
479
452
480
453
private connectToAddressList ( addressList : SubchannelAddress [ ] ) {
454
+ trace ( 'connectToAddressList([' + addressList . map ( address => subchannelAddressToString ( address ) ) + '])' ) ;
481
455
const newChildrenList = addressList . map ( address => ( {
482
456
subchannel : this . channelControlHelper . createSubchannel ( address , { } ) ,
483
457
hasReportedTransientFailure : false ,
484
458
} ) ) ;
485
- trace ( 'connectToAddressList([' + addressList . map ( address => subchannelAddressToString ( address ) ) + '])' ) ;
486
459
for ( const { subchannel } of newChildrenList ) {
487
460
if ( subchannel . getConnectivityState ( ) === ConnectivityState . READY ) {
488
- this . channelControlHelper . addChannelzChild ( subchannel . getChannelzRef ( ) ) ;
489
- subchannel . addConnectivityStateListener ( this . subchannelStateListener ) ;
490
461
this . pickSubchannel ( subchannel ) ;
491
462
return ;
492
463
}
0 commit comments