grpc-js-xds: Fix a couple of bugs with the config tears change

This commit is contained in:
Michael Lumish 2024-11-06 16:24:39 -08:00
parent 8a314311f8
commit eee7030a28
4 changed files with 33 additions and 4 deletions

View File

@ -653,8 +653,9 @@ export class XdsDependencyManager {
this.subscribedClusters[clusterName] -= 1;
if (this.subscribedClusters[clusterName] <= 0) {
delete this.subscribedClusters[clusterName];
this.pruneOrphanClusters();
this.maybeSendUpdate();
if (this.pruneOrphanClusters()) {
this.maybeSendUpdate();
}
}
}
}
@ -682,7 +683,12 @@ export class XdsDependencyManager {
delete this.clusterForest[clusterName];
}
private pruneOrphanClusters() {
/**
* Prune any clusters that are not descendents of any root clusters,
* including subscribed clusters.
* @returns True if any clusters were pruned, false otherwise
*/
private pruneOrphanClusters(): boolean {
const toCheck = [...this.clusterRoots, ...Object.keys(this.subscribedClusters)];
const visited = new Set<string>();
while(toCheck.length > 0) {
@ -695,11 +701,14 @@ export class XdsDependencyManager {
}
visited.add(next);
}
let removedAnyClusters = false;
for (const clusterName of Object.keys(this.clusterForest)) {
if (!visited.has(clusterName)) {
removedAnyClusters = true;
this.removeCluster(clusterName);
}
}
return removedAnyClusters;
}
private handleRouteConfig(routeConfig: RouteConfiguration__Output) {

View File

@ -107,7 +107,7 @@ export class BackoffTimeout {
private runTimer(delay: number) {
this.endTime = this.startTime;
this.endTime.setMilliseconds(
this.endTime.getMilliseconds() + this.nextDelay
this.endTime.getMilliseconds() + delay
);
clearTimeout(this.timerId);
this.timerId = setTimeout(() => {

View File

@ -275,6 +275,13 @@ export class PickFirstLoadBalancer implements LoadBalancer {
new PickFirstPicker(this.currentPick)
);
}
} else if (this.latestAddressList?.length === 0) {
this.updateState(
ConnectivityState.TRANSIENT_FAILURE,
new UnavailablePicker({
details: `No connection established. Last error: ${this.lastError}`,
})
);
} else if (this.children.length === 0) {
this.updateState(ConnectivityState.IDLE, new QueuePicker(this));
} else {

View File

@ -690,6 +690,19 @@ describe('pick_first load balancing policy', () => {
});
});
});
it('Should report TRANSIENT_FAILURE with no addresses', done => {
const channelControlHelper = createChildChannelControlHelper(
baseChannelControlHelper,
{
updateState: updateStateCallBackForExpectedStateSequence(
[ConnectivityState.TRANSIENT_FAILURE],
done
),
}
);
const pickFirst = new PickFirstLoadBalancer(channelControlHelper, creds, {});
pickFirst.updateAddressList([], config);
});
describe('Address list randomization', () => {
const shuffleConfig = new PickFirstLoadBalancingConfig(true);
it('Should pick different subchannels after multiple updates', done => {