Skip to content

Commit 85bc3c2

Browse files
authored
Merge pull request #2619 from murgatroid99/grpc-js_idle_loop_fix
grpc-js: Make pick_first use exitIdle
2 parents 667bae6 + 8843706 commit 85bc3c2

File tree

3 files changed

+52
-17
lines changed

3 files changed

+52
-17
lines changed

packages/grpc-js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@grpc/grpc-js",
3-
"version": "1.9.10",
3+
"version": "1.9.11",
44
"description": "gRPC Library for Node - pure JS implementation",
55
"homepage": "https://grpc.io/",
66
"repository": "https://github.com/grpc/grpc-node/tree/master/packages/grpc-js",

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

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ export class PickFirstLoadBalancer implements LoadBalancer {
186186
*/
187187
private lastError: string | null = null;
188188

189+
private latestAddressList: SubchannelAddress[] | null = null;
190+
189191
/**
190192
* Load balancer that attempts to connect to each backend in the address list
191193
* in order, and picks the first one that connects, using it for every
@@ -404,19 +406,7 @@ export class PickFirstLoadBalancer implements LoadBalancer {
404406
this.requestedResolutionSinceLastUpdate = false;
405407
}
406408

407-
updateAddressList(
408-
addressList: SubchannelAddress[],
409-
lbConfig: LoadBalancingConfig
410-
): void {
411-
if (!(lbConfig instanceof PickFirstLoadBalancingConfig)) {
412-
return;
413-
}
414-
/* Previously, an update would be discarded if it was identical to the
415-
* previous update, to minimize churn. Now the DNS resolver is
416-
* rate-limited, so that is less of a concern. */
417-
if (lbConfig.getShuffleAddressList()) {
418-
addressList = shuffled(addressList);
419-
}
409+
private connectToAddressList(addressList: SubchannelAddress[]) {
420410
const newChildrenList = addressList.map(address => ({
421411
subchannel: this.channelControlHelper.createSubchannel(address, {}),
422412
hasReportedTransientFailure: false,
@@ -449,10 +439,27 @@ export class PickFirstLoadBalancer implements LoadBalancer {
449439
this.calculateAndReportNewState();
450440
}
451441

442+
updateAddressList(
443+
addressList: SubchannelAddress[],
444+
lbConfig: LoadBalancingConfig
445+
): void {
446+
if (!(lbConfig instanceof PickFirstLoadBalancingConfig)) {
447+
return;
448+
}
449+
/* Previously, an update would be discarded if it was identical to the
450+
* previous update, to minimize churn. Now the DNS resolver is
451+
* rate-limited, so that is less of a concern. */
452+
if (lbConfig.getShuffleAddressList()) {
453+
addressList = shuffled(addressList);
454+
}
455+
this.latestAddressList = addressList;
456+
this.connectToAddressList(addressList);
457+
}
458+
452459
exitIdle() {
453-
/* The pick_first LB policy is only in the IDLE state if it has no
454-
* addresses to try to connect to and it has no picked subchannel.
455-
* In that case, there is no meaningful action that can be taken here. */
460+
if (this.currentState === ConnectivityState.IDLE && this.latestAddressList) {
461+
this.connectToAddressList(this.latestAddressList);
462+
}
456463
}
457464

458465
resetBackoff() {

packages/grpc-js/test/test-pick-first.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,34 @@ describe('pick_first load balancing policy', () => {
550550
});
551551
});
552552
});
553+
it('Should reconnect to the same address list if exitIdle is called', done => {
554+
const currentStartState = ConnectivityState.READY;
555+
const channelControlHelper = createChildChannelControlHelper(
556+
baseChannelControlHelper,
557+
{
558+
createSubchannel: (subchannelAddress, subchannelArgs) => {
559+
const subchannel = new MockSubchannel(
560+
subchannelAddressToString(subchannelAddress),
561+
currentStartState
562+
);
563+
subchannels.push(subchannel);
564+
return subchannel;
565+
},
566+
updateState: updateStateCallBackForExpectedStateSequence(
567+
[ConnectivityState.READY, ConnectivityState.IDLE, ConnectivityState.READY],
568+
done
569+
),
570+
}
571+
);
572+
const pickFirst = new PickFirstLoadBalancer(channelControlHelper);
573+
pickFirst.updateAddressList([{ host: 'localhost', port: 1 }], config);
574+
process.nextTick(() => {
575+
subchannels[0].transitionToState(ConnectivityState.IDLE);
576+
process.nextTick(() => {
577+
pickFirst.exitIdle();
578+
});
579+
});
580+
});
553581
describe('Address list randomization', () => {
554582
const shuffleConfig = new PickFirstLoadBalancingConfig(true);
555583
it('Should pick different subchannels after multiple updates', done => {

0 commit comments

Comments
 (0)