Skip to content

Commit bfebbe7

Browse files
authored
Merge pull request #2881 from murgatroid99/grpc-js-xds_dependency_manager_logging
grpc-js-xds: Add more detailed xDS dependency manager logging
2 parents 5b0956a + 2a6ce92 commit bfebbe7

File tree

2 files changed

+88
-11
lines changed

2 files changed

+88
-11
lines changed

packages/grpc-js-xds/src/xds-dependency-manager.ts

+55-10
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ interface ClusterGraph {
136136
[name: string]: ClusterEntry;
137137
}
138138

139-
function isClusterTreeFullyUpdated(tree: ClusterGraph, roots: string[]): boolean {
139+
type ClusterTreeUpdatedResult = {result: true} | {result: false, reason: string};
140+
141+
function isClusterTreeFullyUpdated(tree: ClusterGraph, roots: string[]): ClusterTreeUpdatedResult {
140142
const toCheck: string[] = [...roots];
141143
const visited = new Set<string>();
142144
while (toCheck.length > 0) {
@@ -145,19 +147,31 @@ function isClusterTreeFullyUpdated(tree: ClusterGraph, roots: string[]): boolean
145147
continue;
146148
}
147149
visited.add(next);
148-
if (!tree[next] || !tree[next].latestUpdate) {
149-
return false;
150+
if (!tree[next]) {
151+
return {
152+
result: false,
153+
reason: 'Missing expected cluster entry ' + next
154+
};
155+
}
156+
if (!tree[next].latestUpdate) {
157+
return {
158+
result: false,
159+
reason: 'Cluster entry ' + next + ' not updated'
160+
};
150161
}
151162
if (tree[next].latestUpdate.success) {
152163
if (tree[next].latestUpdate.value.type !== 'AGGREGATE') {
153-
if (!(tree[next].latestUpdate.value.latestUpdate || tree[next].latestUpdate.value.latestUpdate)) {
154-
return false;
164+
if (!(tree[next].latestUpdate.value.latestUpdate)) {
165+
return {
166+
result: false,
167+
reason: 'Cluster entry ' + next + ' endpoint not updated'
168+
};
155169
}
156170
}
157171
}
158172
toCheck.push(...tree[next].children);
159173
}
160-
return true;
174+
return {result: true};
161175
}
162176

163177
// Better match type has smaller value.
@@ -353,18 +367,21 @@ export class XdsDependencyManager {
353367
const routeConfigName = httpConnectionManager.rds!.route_config_name;
354368
if (this.latestRouteConfigName !== routeConfigName) {
355369
if (this.latestRouteConfigName !== null) {
370+
this.trace('RDS.cancelWatch(' + this.latestRouteConfigName + '): Route config name changed');
356371
RouteConfigurationResourceType.cancelWatch(this.xdsClient, this.latestRouteConfigName, this.rdsWatcher);
357372
this.latestRouteConfiguration = null;
358373
this.clusterRoots = [];
359374
this.pruneOrphanClusters();
360375
}
376+
this.trace('RDS.startWatch(' + routeConfigName + '): New route config name');
361377
RouteConfigurationResourceType.startWatch(this.xdsClient, routeConfigName, this.rdsWatcher);
362378
this.latestRouteConfigName = routeConfigName;
363379
}
364380
break;
365381
}
366382
case 'route_config':
367383
if (this.latestRouteConfigName) {
384+
this.trace('RDS.cancelWatch(' + this.latestRouteConfigName + '): Listener switched to embedded route config');
368385
RouteConfigurationResourceType.cancelWatch(this.xdsClient, this.latestRouteConfigName, this.rdsWatcher);
369386
this.latestRouteConfigName = null;
370387
}
@@ -378,13 +395,14 @@ export class XdsDependencyManager {
378395
/* A transient error only needs to bubble up as a failure if we have
379396
* not already provided a ServiceConfig for the upper layer to use */
380397
if (!this.latestListener) {
381-
trace('Resolution error for target ' + listenerResourceName + ' due to xDS client transient error ' + error.details);
398+
this.trace('Resolution error due to xDS client transient error ' + error.details);
382399
this.watcher.onError(`Listener ${listenerResourceName}`, error);
383400
}
384401
},
385402
onResourceDoesNotExist: () => {
386-
trace('Resolution error for target ' + listenerResourceName + ': LDS resource does not exist');
403+
this.trace('Resolution error: LDS resource does not exist');
387404
if (this.latestRouteConfigName) {
405+
this.trace('RDS.cancelWatch(' + this.latestRouteConfigName + '): LDS resource does not exist');
388406
RouteConfigurationResourceType.cancelWatch(this.xdsClient, this.latestRouteConfigName, this.rdsWatcher);
389407
this.latestRouteConfigName = null;
390408
this.latestRouteConfiguration = null;
@@ -409,11 +427,26 @@ export class XdsDependencyManager {
409427
this.pruneOrphanClusters();
410428
}
411429
});
430+
this.trace('LDS.startWatch(' + listenerResourceName + '): Startup');
412431
ListenerResourceType.startWatch(this.xdsClient, listenerResourceName, this.ldsWatcher);
413432
}
414433

434+
private trace(text: string) {
435+
trace('[' + this.listenerResourceName + '] ' + text);
436+
}
437+
415438
private maybeSendUpdate() {
416-
if (!(this.latestListener && this.latestRouteConfiguration && isClusterTreeFullyUpdated(this.clusterForest, this.clusterRoots))) {
439+
if (!this.latestListener) {
440+
this.trace('Not sending update: no Listener update received');
441+
return;
442+
}
443+
if (!this.latestRouteConfiguration) {
444+
this.trace('Not sending update: no RouteConfiguration update received');
445+
return;
446+
}
447+
const clusterTreeUpdated = isClusterTreeFullyUpdated(this.clusterForest, this.clusterRoots);
448+
if (!clusterTreeUpdated.result) {
449+
this.trace('Not sending update: ' + clusterTreeUpdated.reason);
417450
return;
418451
}
419452
const update: XdsConfig = {
@@ -424,6 +457,7 @@ export class XdsDependencyManager {
424457
};
425458
for (const [clusterName, entry] of Object.entries(this.clusterForest)) {
426459
if (!entry.latestUpdate) {
460+
this.trace('Not sending update: Cluster entry ' + clusterName + ' not updated (not caught by isClusterTreeFullyUpdated)');
427461
return;
428462
}
429463
if (entry.latestUpdate.success) {
@@ -471,6 +505,7 @@ export class XdsDependencyManager {
471505
case 'AGGREGATE':
472506
break;
473507
case 'EDS':
508+
this.trace('EDS.cancelWatch(' + entry.latestUpdate.value.edsServiceName + '): Cluster switched to aggregate');
474509
EndpointResourceType.cancelWatch(this.xdsClient, entry.latestUpdate.value.edsServiceName, entry.latestUpdate.value.watcher);
475510
break;
476511
case 'LOGICAL_DNS':
@@ -503,7 +538,9 @@ export class XdsDependencyManager {
503538
case 'EDS':
504539
// If the names are the same, keep the watch
505540
if (entry.latestUpdate.value.edsServiceName !== edsServiceName) {
541+
this.trace('EDS.cancelWatch(' + entry.latestUpdate.value.edsServiceName + '): EDS service name changed');
506542
EndpointResourceType.cancelWatch(this.xdsClient, entry.latestUpdate.value.edsServiceName, entry.latestUpdate.value.watcher);
543+
this.trace('EDS.startWatch(' + edsServiceName + '): EDS service name changed');
507544
EndpointResourceType.startWatch(this.xdsClient, edsServiceName, entry.latestUpdate.value.watcher);
508545
entry.latestUpdate.value.edsServiceName = edsServiceName;
509546
entry.latestUpdate.value.latestUpdate = undefined;
@@ -550,6 +587,7 @@ export class XdsDependencyManager {
550587
watcher: edsWatcher
551588
}
552589
};
590+
this.trace('EDS.startWatch(' + edsServiceName + '): New EDS service name');
553591
EndpointResourceType.startWatch(this.xdsClient, edsServiceName, edsWatcher);
554592
this.maybeSendUpdate();
555593
break;
@@ -561,6 +599,7 @@ export class XdsDependencyManager {
561599
this.pruneOrphanClusters();
562600
break;
563601
case 'EDS':
602+
this.trace('EDS.cancelWatch(' + entry.latestUpdate.value.edsServiceName + '): Cluster switched to DNS');
564603
EndpointResourceType.cancelWatch(this.xdsClient, entry.latestUpdate.value.edsServiceName, entry.latestUpdate.value.watcher);
565604
break;
566605
case 'LOGICAL_DNS':
@@ -571,7 +610,7 @@ export class XdsDependencyManager {
571610
}
572611
}
573612
}
574-
trace('Creating DNS resolver');
613+
this.trace('Creating DNS resolver for hostname ' + update.dnsHostname!);
575614
const resolver = createResolver({scheme: 'dns', path: update.dnsHostname!}, {
576615
onSuccessfulResolution: endpointList => {
577616
if (entry.latestUpdate?.success && entry.latestUpdate.value.type === 'LOGICAL_DNS') {
@@ -616,6 +655,7 @@ export class XdsDependencyManager {
616655
if (entry.latestUpdate?.success) {
617656
switch (entry.latestUpdate.value.type) {
618657
case 'EDS':
658+
this.trace('EDS.cancelWatch(' + entry.latestUpdate.value.edsServiceName + '): CDS resource does not exist');
619659
EndpointResourceType.cancelWatch(this.xdsClient, entry.latestUpdate.value.edsServiceName, entry.latestUpdate.value.watcher);
620660
break;
621661
case 'LOGICAL_DNS':
@@ -639,6 +679,7 @@ export class XdsDependencyManager {
639679
children: []
640680
}
641681
this.clusterForest[clusterName] = entry;
682+
this.trace('CDS.startWatch(' + clusterName + '): Cluster added');
642683
ClusterResourceType.startWatch(this.xdsClient, clusterName, entry.watcher);
643684
}
644685

@@ -670,6 +711,7 @@ export class XdsDependencyManager {
670711
if (entry.latestUpdate?.success) {
671712
switch (entry.latestUpdate.value.type) {
672713
case 'EDS':
714+
this.trace('EDS.cancelWatch(' + entry.latestUpdate.value.edsServiceName + '): Cluster ' + clusterName + ' removed');
673715
EndpointResourceType.cancelWatch(this.xdsClient, entry.latestUpdate.value.edsServiceName, entry.latestUpdate.value.watcher);
674716
break;
675717
case 'LOGICAL_DNS':
@@ -679,6 +721,7 @@ export class XdsDependencyManager {
679721
break;
680722
}
681723
}
724+
this.trace('CDS.cancelWatch(' + clusterName + '): Cluster removed');
682725
ClusterResourceType.cancelWatch(this.xdsClient, clusterName, entry.watcher);
683726
delete this.clusterForest[clusterName];
684727
}
@@ -761,8 +804,10 @@ export class XdsDependencyManager {
761804
}
762805

763806
destroy() {
807+
this.trace('LDS.cancelWatch(' + this.listenerResourceName + '): destroy');
764808
ListenerResourceType.cancelWatch(this.xdsClient, this.listenerResourceName, this.ldsWatcher);
765809
if (this.latestRouteConfigName) {
810+
this.trace('RDS.cancelWatch(' + this.latestRouteConfigName + '): destroy');
766811
RouteConfigurationResourceType.cancelWatch(this.xdsClient, this.latestRouteConfigName, this.rdsWatcher);
767812
}
768813
this.clusterRoots = [];

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

+33-1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,38 @@ describe('core xDS functionality', () => {
164164
xdsServer.setEdsResource(cluster2.getEndpointConfig());
165165
await cluster2.waitForAllBackendsToReceiveTraffic();
166166
client.stopCalls();
167-
167+
});
168+
it('should handle switching to a different cluster', async () => {
169+
const [backend1, backend2] = await createBackends(2);
170+
const serverRoute1 = new FakeServerRoute(backend1.getPort(), 'serverRoute');
171+
const serverRoute2 = new FakeServerRoute(backend2.getPort(), 'serverRoute2');
172+
xdsServer.setRdsResource(serverRoute1.getRouteConfiguration());
173+
xdsServer.setLdsResource(serverRoute1.getListener());
174+
xdsServer.setRdsResource(serverRoute2.getRouteConfiguration());
175+
xdsServer.setLdsResource(serverRoute2.getListener());
176+
xdsServer.addResponseListener((typeUrl, responseState) => {
177+
if (responseState.state === 'NACKED') {
178+
client?.stopCalls();
179+
assert.fail(`Client NACKED ${typeUrl} resource with message ${responseState.errorMessage}`);
180+
}
181+
});
182+
const cluster1 = new FakeEdsCluster('cluster1', 'endpoint1', [{backends: [backend1], locality:{region: 'region1'}}]);
183+
const routeGroup1 = new FakeRouteGroup('listener1', 'route1', [{cluster: cluster1}]);
184+
await routeGroup1.startAllBackends(xdsServer);
185+
xdsServer.setEdsResource(cluster1.getEndpointConfig());
186+
xdsServer.setCdsResource(cluster1.getClusterConfig());
187+
xdsServer.setRdsResource(routeGroup1.getRouteConfiguration());
188+
xdsServer.setLdsResource(routeGroup1.getListener());
189+
client = XdsTestClient.createFromServer('listener1', xdsServer);
190+
client.startCalls(100);
191+
await cluster1.waitForAllBackendsToReceiveTraffic();
192+
const cluster2 = new FakeEdsCluster('cluster2', 'endpoint2', [{backends: [backend2], locality:{region: 'region2'}}]);
193+
const routeGroup2 = new FakeRouteGroup('listener1', 'route1', [{cluster: cluster2}]);
194+
await cluster2.startAllBackends(xdsServer);
195+
xdsServer.setEdsResource(cluster2.getEndpointConfig());
196+
xdsServer.setCdsResource(cluster2.getClusterConfig());
197+
xdsServer.setRdsResource(routeGroup2.getRouteConfiguration());
198+
await cluster2.waitForAllBackendsToReceiveTraffic();
199+
client.stopCalls();
168200
})
169201
});

0 commit comments

Comments
 (0)