Skip to content

Commit a6575c3

Browse files
committed
grpc-js: Simplify pick_first behavior
1 parent f5ea6ce commit a6575c3

File tree

1 file changed

+28
-38
lines changed

1 file changed

+28
-38
lines changed

packages/grpc-js/src/load-balancer-pick-first.ts

+28-38
Original file line numberDiff line numberDiff line change
@@ -320,21 +320,16 @@ export class PickFirstLoadBalancer implements LoadBalancer {
320320

321321
private removeCurrentPick() {
322322
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);
323+
this.currentPick.removeConnectivityStateListener(this.subchannelStateListener);
330324
this.channelControlHelper.removeChannelzChild(
331-
currentPick.getChannelzRef()
325+
this.currentPick.getChannelzRef()
332326
);
333-
if (this.reportHealthStatus) {
334-
currentPick.removeHealthStateWatcher(
335-
this.pickedSubchannelHealthListener
336-
);
337-
}
327+
this.currentPick.removeHealthStateWatcher(
328+
this.pickedSubchannelHealthListener
329+
);
330+
// Unref last, to avoid triggering listeners
331+
this.currentPick.unref();
332+
this.currentPick = null;
338333
}
339334
}
340335

@@ -418,20 +413,25 @@ export class PickFirstLoadBalancer implements LoadBalancer {
418413
this.connectionDelayTimeout.unref?.();
419414
}
420415

416+
/**
417+
* Declare that the specified subchannel should be used to make requests.
418+
* This functions the same independent of whether subchannel is a member of
419+
* this.children and whether it is equal to this.currentPick.
420+
* Prerequisite: subchannel.getConnectivityState() === READY.
421+
* @param subchannel
422+
*/
421423
private pickSubchannel(subchannel: SubchannelInterface) {
422-
if (this.currentPick && subchannel.realSubchannelEquals(this.currentPick)) {
423-
return;
424-
}
425424
trace('Pick subchannel with address ' + subchannel.getAddress());
426425
this.stickyTransientFailureMode = false;
427-
this.removeCurrentPick();
428-
this.currentPick = subchannel;
426+
/* Ref before removeCurrentPick and resetSubchannelList to avoid the
427+
* refcount dropping to 0 during this process. */
429428
subchannel.ref();
430-
if (this.reportHealthStatus) {
431-
subchannel.addHealthStateWatcher(this.pickedSubchannelHealthListener);
432-
}
433429
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
430+
this.removeCurrentPick();
434431
this.resetSubchannelList();
432+
subchannel.addConnectivityStateListener(this.subchannelStateListener);
433+
subchannel.addHealthStateWatcher(this.pickedSubchannelHealthListener);
434+
this.currentPick = subchannel;
435435
clearTimeout(this.connectionDelayTimeout);
436436
this.calculateAndReportNewState();
437437
}
@@ -448,20 +448,11 @@ export class PickFirstLoadBalancer implements LoadBalancer {
448448

449449
private resetSubchannelList() {
450450
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-
}
451+
/* Always remoev the connectivity state listener. If the subchannel is
452+
getting picked, it will be re-added then. */
453+
child.subchannel.removeConnectivityStateListener(
454+
this.subchannelStateListener
455+
);
465456
/* Refs are counted independently for the children list and the
466457
* currentPick, so we call unref whether or not the child is the
467458
* currentPick. Channelz child references are also refcounted, so
@@ -478,15 +469,13 @@ export class PickFirstLoadBalancer implements LoadBalancer {
478469
}
479470

480471
private connectToAddressList(addressList: SubchannelAddress[]) {
472+
trace('connectToAddressList([' + addressList.map(address => subchannelAddressToString(address)) + '])');
481473
const newChildrenList = addressList.map(address => ({
482474
subchannel: this.channelControlHelper.createSubchannel(address, {}),
483475
hasReportedTransientFailure: false,
484476
}));
485-
trace('connectToAddressList([' + addressList.map(address => subchannelAddressToString(address)) + '])');
486477
for (const { subchannel } of newChildrenList) {
487478
if (subchannel.getConnectivityState() === ConnectivityState.READY) {
488-
this.channelControlHelper.addChannelzChild(subchannel.getChannelzRef());
489-
subchannel.addConnectivityStateListener(this.subchannelStateListener);
490479
this.pickSubchannel(subchannel);
491480
return;
492481
}
@@ -522,6 +511,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
522511
if (!(lbConfig instanceof PickFirstLoadBalancingConfig)) {
523512
return;
524513
}
514+
this.requestedResolutionSinceLastUpdate = false;
525515
/* Previously, an update would be discarded if it was identical to the
526516
* previous update, to minimize churn. Now the DNS resolver is
527517
* rate-limited, so that is less of a concern. */

0 commit comments

Comments
 (0)