Skip to content

Commit c8b9a45

Browse files
committed
grpc-js-xds: Fix behavior when channel goes IDLE
1 parent c8cbffa commit c8b9a45

File tree

4 files changed

+49
-12
lines changed

4 files changed

+49
-12
lines changed

packages/grpc-js-xds/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-xds",
3-
"version": "1.9.0",
3+
"version": "1.9.1",
44
"description": "Plugin for @grpc/grpc-js. Adds the xds:// URL scheme and associated features.",
55
"main": "build/src/index.js",
66
"scripts": {

packages/grpc-js-xds/src/resolver-xds.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ function getPredicateForMatcher(routeMatch: RouteMatch__Output): Matcher {
213213
* the ServiceConfig definition. The difference is that the protobuf message
214214
* defines seconds as a long, which is represented as a string in JavaScript,
215215
* and the one used in the service config defines it as a number.
216-
* @param duration
216+
* @param duration
217217
*/
218218
function protoDurationToDuration(duration: Duration__Output): Duration {
219219
return {
@@ -235,7 +235,7 @@ function getDefaultRetryMaxInterval(baseInterval: string): string {
235235
/**
236236
* Encode a text string as a valid path of a URI, as specified in RFC-3986 section 3.3
237237
* @param uriPath A value representing an unencoded URI path
238-
* @returns
238+
* @returns
239239
*/
240240
function encodeURIPath(uriPath: string): string {
241241
return uriPath.replace(/[^A-Za-z0-9._~!$&^()*+,;=/-]/g, substring => encodeURIComponent(substring));
@@ -447,7 +447,7 @@ class XdsResolver implements Resolver {
447447
}
448448
}
449449
}
450-
let retryPolicy: RetryPolicy | undefined = undefined;
450+
let retryPolicy: RetryPolicy | undefined = undefined;
451451
if (EXPERIMENTAL_RETRY) {
452452
const retryConfig = route.route!.retry_policy ?? virtualHost.retry_policy;
453453
if (retryConfig) {
@@ -458,10 +458,10 @@ class XdsResolver implements Resolver {
458458
}
459459
}
460460
if (retryableStatusCodes.length > 0) {
461-
const baseInterval = retryConfig.retry_back_off?.base_interval ?
462-
protoDurationToSecondsString(retryConfig.retry_back_off.base_interval) :
461+
const baseInterval = retryConfig.retry_back_off?.base_interval ?
462+
protoDurationToSecondsString(retryConfig.retry_back_off.base_interval) :
463463
DEFAULT_RETRY_BASE_INTERVAL;
464-
const maxInterval = retryConfig.retry_back_off?.max_interval ?
464+
const maxInterval = retryConfig.retry_back_off?.max_interval ?
465465
protoDurationToSecondsString(retryConfig.retry_back_off.max_interval) :
466466
getDefaultRetryMaxInterval(baseInterval);
467467
retryPolicy = {
@@ -664,9 +664,11 @@ class XdsResolver implements Resolver {
664664
destroy() {
665665
if (this.listenerResourceName) {
666666
ListenerResourceType.cancelWatch(this.xdsClient, this.listenerResourceName, this.ldsWatcher);
667+
this.isLdsWatcherActive = false;
667668
}
668669
if (this.latestRouteConfigName) {
669670
RouteConfigurationResourceType.cancelWatch(this.xdsClient, this.latestRouteConfigName, this.rdsWatcher);
671+
this.latestRouteConfigName = null;
670672
}
671673
}
672674

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*/
1717

18-
import { credentials, loadPackageDefinition, ServiceError } from "@grpc/grpc-js";
18+
import { ChannelOptions, credentials, loadPackageDefinition, ServiceError } from "@grpc/grpc-js";
1919
import { loadSync } from "@grpc/proto-loader";
2020
import { ProtoGrpcType } from "./generated/echo";
2121
import { EchoTestServiceClient } from "./generated/grpc/testing/EchoTestService";
@@ -44,14 +44,14 @@ export class XdsTestClient {
4444
private client: EchoTestServiceClient;
4545
private callInterval: NodeJS.Timer;
4646

47-
constructor(target: string, bootstrapInfo: string) {
48-
this.client = new loadedProtos.grpc.testing.EchoTestService(target, credentials.createInsecure(), {[BOOTSTRAP_CONFIG_KEY]: bootstrapInfo});
47+
constructor(target: string, bootstrapInfo: string, options?: ChannelOptions) {
48+
this.client = new loadedProtos.grpc.testing.EchoTestService(target, credentials.createInsecure(), {...options, [BOOTSTRAP_CONFIG_KEY]: bootstrapInfo});
4949
this.callInterval = setInterval(() => {}, 0);
5050
clearInterval(this.callInterval);
5151
}
5252

53-
static createFromServer(targetName: string, xdsServer: XdsServer) {
54-
return new XdsTestClient(`xds:///${targetName}`, xdsServer.getBootstrapInfoString());
53+
static createFromServer(targetName: string, xdsServer: XdsServer, options?: ChannelOptions) {
54+
return new XdsTestClient(`xds:///${targetName}`, xdsServer.getBootstrapInfoString(), options);
5555
}
5656

5757
startCalls(interval: number) {
@@ -98,4 +98,8 @@ export class XdsTestClient {
9898
}
9999
sendInner(count, callback);
100100
}
101+
102+
getConnectivityState() {
103+
return this.client.getChannel().getConnectivityState(false);
104+
}
101105
}

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { XdsServer } from "./xds-server";
2222

2323
import { register } from "../src";
2424
import assert = require("assert");
25+
import { connectivityState } from "@grpc/grpc-js";
2526

2627
register();
2728

@@ -60,4 +61,34 @@ describe('core xDS functionality', () => {
6061
}, reason => done(reason));
6162
}, reason => done(reason));
6263
});
64+
it('should be able to enter and exit idle', function(done) {
65+
this.timeout(5000);
66+
const cluster = new FakeEdsCluster('cluster1', 'endpoint1', [{backends: [new Backend()], locality:{region: 'region1'}}]);
67+
const routeGroup = new FakeRouteGroup('listener1', 'route1', [{cluster: cluster}]);
68+
routeGroup.startAllBackends().then(() => {
69+
xdsServer.setEdsResource(cluster.getEndpointConfig());
70+
xdsServer.setCdsResource(cluster.getClusterConfig());
71+
xdsServer.setRdsResource(routeGroup.getRouteConfiguration());
72+
xdsServer.setLdsResource(routeGroup.getListener());
73+
xdsServer.addResponseListener((typeUrl, responseState) => {
74+
if (responseState.state === 'NACKED') {
75+
client.stopCalls();
76+
assert.fail(`Client NACKED ${typeUrl} resource with message ${responseState.errorMessage}`);
77+
}
78+
})
79+
client = XdsTestClient.createFromServer('listener1', xdsServer, {
80+
'grpc.client_idle_timeout_ms': 1000,
81+
});
82+
client.sendOneCall(error => {
83+
assert.ifError(error);
84+
assert.strictEqual(client.getConnectivityState(), connectivityState.READY);
85+
setTimeout(() => {
86+
assert.strictEqual(client.getConnectivityState(), connectivityState.IDLE);
87+
client.sendOneCall(error => {
88+
done(error);
89+
})
90+
}, 1100);
91+
});
92+
}, reason => done(reason));
93+
});
6394
});

0 commit comments

Comments
 (0)