Skip to content

Commit 429a66d

Browse files
committed
grpc-js: round_robin: always have children reconnect immediately
1 parent 1b753af commit 429a66d

File tree

5 files changed

+45
-5
lines changed

5 files changed

+45
-5
lines changed

packages/grpc-js-xds/test/backend.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*/
1717

18-
import { loadPackageDefinition, sendUnaryData, Server, ServerCredentials, ServerUnaryCall, UntypedServiceImplementation } from "@grpc/grpc-js";
18+
import { loadPackageDefinition, sendUnaryData, Server, ServerCredentials, ServerOptions, ServerUnaryCall, UntypedServiceImplementation } from "@grpc/grpc-js";
1919
import { loadSync } from "@grpc/proto-loader";
2020
import { ProtoGrpcType } from "./generated/echo";
2121
import { EchoRequest__Output } from "./generated/grpc/testing/EchoRequest";
@@ -43,7 +43,7 @@ export class Backend {
4343
private receivedCallCount = 0;
4444
private callListeners: (() => void)[] = [];
4545
private port: number | null = null;
46-
constructor() {
46+
constructor(private serverOptions?: ServerOptions) {
4747
}
4848
Echo(call: ServerUnaryCall<EchoRequest__Output, EchoResponse>, callback: sendUnaryData<EchoResponse>) {
4949
// call.request.params is currently ignored
@@ -76,13 +76,12 @@ export class Backend {
7676
if (this.server) {
7777
throw new Error("Backend already running");
7878
}
79-
this.server = new Server();
79+
this.server = new Server(this.serverOptions);
8080
this.server.addService(loadedProtos.grpc.testing.EchoTestService.service, this as unknown as UntypedServiceImplementation);
8181
const boundPort = this.port ?? 0;
8282
this.server.bindAsync(`localhost:${boundPort}`, ServerCredentials.createInsecure(), (error, port) => {
8383
if (!error) {
8484
this.port = port;
85-
this.server!.start();
8685
}
8786
callback(error, port);
8887
})

packages/grpc-js-xds/test/test-core.ts

+28
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,32 @@ describe('core xDS functionality', () => {
9191
});
9292
}, reason => done(reason));
9393
});
94+
it('should handle connections aging out', function(done) {
95+
this.timeout(5000);
96+
const cluster = new FakeEdsCluster('cluster1', 'endpoint1', [{backends: [new Backend({'grpc.max_connection_age_ms': 1000})], locality:{region: 'region1'}}]);
97+
const routeGroup = new FakeRouteGroup('listener1', 'route1', [{cluster: cluster}]);
98+
routeGroup.startAllBackends().then(() => {
99+
xdsServer.setEdsResource(cluster.getEndpointConfig());
100+
xdsServer.setCdsResource(cluster.getClusterConfig());
101+
xdsServer.setRdsResource(routeGroup.getRouteConfiguration());
102+
xdsServer.setLdsResource(routeGroup.getListener());
103+
xdsServer.addResponseListener((typeUrl, responseState) => {
104+
if (responseState.state === 'NACKED') {
105+
client.stopCalls();
106+
assert.fail(`Client NACKED ${typeUrl} resource with message ${responseState.errorMessage}`);
107+
}
108+
})
109+
client = XdsTestClient.createFromServer('listener1', xdsServer);
110+
client.sendOneCall(error => {
111+
assert.ifError(error);
112+
// Make another call after the max_connection_age_ms expires
113+
setTimeout(() => {
114+
client.sendOneCall(error => {
115+
done(error);
116+
})
117+
}, 1100);
118+
});
119+
}, reason => done(reason));
120+
121+
})
94122
});

packages/grpc-js/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@grpc/grpc-js",
3-
"version": "1.10.0",
3+
"version": "1.10.1",
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

+4
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,10 @@ export class LeafLoadBalancer {
605605
return this.endpoint;
606606
}
607607

608+
exitIdle() {
609+
this.pickFirstBalancer.exitIdle();
610+
}
611+
608612
destroy() {
609613
this.pickFirstBalancer.destroy();
610614
}

packages/grpc-js/src/load-balancer-round-robin.ts

+9
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ export class RoundRobinLoadBalancer implements LoadBalancer {
161161
} else {
162162
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
163163
}
164+
/* round_robin should keep all children connected, this is how we do that.
165+
* We can't do this more efficiently in the individual child's updateState
166+
* callback because that doesn't have a reference to which child the state
167+
* change is associated with. */
168+
for (const child of this.children) {
169+
if (child.getConnectivityState() === ConnectivityState.IDLE) {
170+
child.exitIdle();
171+
}
172+
}
164173
}
165174

166175
private updateState(newState: ConnectivityState, picker: Picker) {

0 commit comments

Comments
 (0)