Xds: Aggregate cluster fixes (A75) (#12186)

Instead of representing an aggregate cluster as a single cluster whose
priorities come from different underlying clusters, represent an aggregate cluster as an instance of a priority LB policy where each child is a cds LB policy for the underlying
cluster.
This commit is contained in:
Kannan J 2025-07-29 12:36:39 +00:00 committed by GitHub
parent c3ef1ab034
commit 7e982e48a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 329 additions and 406 deletions

View File

@ -18,10 +18,10 @@ package io.grpc.xds;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.grpc.ConnectivityState.TRANSIENT_FAILURE;
import static io.grpc.xds.XdsLbPolicies.CDS_POLICY_NAME;
import static io.grpc.xds.XdsLbPolicies.CLUSTER_RESOLVER_POLICY_NAME;
import static io.grpc.xds.XdsLbPolicies.PRIORITY_POLICY_NAME;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CheckReturnValue;
import io.grpc.InternalLogId;
import io.grpc.LoadBalancer;
@ -33,6 +33,7 @@ import io.grpc.util.GracefulSwitchLoadBalancer;
import io.grpc.xds.CdsLoadBalancerProvider.CdsConfig;
import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig;
import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig.DiscoveryMechanism;
import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig;
import io.grpc.xds.XdsClusterResource.CdsUpdate;
import io.grpc.xds.XdsClusterResource.CdsUpdate.ClusterType;
import io.grpc.xds.XdsConfig.Subscription;
@ -41,10 +42,11 @@ import io.grpc.xds.XdsConfig.XdsClusterConfig.AggregateConfig;
import io.grpc.xds.XdsConfig.XdsClusterConfig.EndpointConfig;
import io.grpc.xds.client.XdsLogger;
import io.grpc.xds.client.XdsLogger.XdsLogLevel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Load balancer for cds_experimental LB policy. One instance per top-level cluster.
@ -55,19 +57,15 @@ final class CdsLoadBalancer2 extends LoadBalancer {
private final XdsLogger logger;
private final Helper helper;
private final LoadBalancerRegistry lbRegistry;
private GracefulSwitchLoadBalancer delegate;
// Following fields are effectively final.
private String clusterName;
private Subscription clusterSubscription;
private LoadBalancer childLb;
CdsLoadBalancer2(Helper helper) {
this(helper, LoadBalancerRegistry.getDefaultRegistry());
}
@VisibleForTesting
CdsLoadBalancer2(Helper helper, LoadBalancerRegistry lbRegistry) {
this.helper = checkNotNull(helper, "helper");
this.lbRegistry = checkNotNull(lbRegistry, "lbRegistry");
this.delegate = new GracefulSwitchLoadBalancer(helper);
logger = XdsLogger.withLogId(InternalLogId.allocate("cds-lb", helper.getAuthority()));
logger.log(XdsLogLevel.INFO, "Created");
}
@ -91,7 +89,7 @@ final class CdsLoadBalancer2 extends LoadBalancer {
if (clusterSubscription == null) {
// Should be impossible, because XdsDependencyManager wouldn't have generated this
return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unable to find non-dynamic root cluster"));
errorPrefix() + "Unable to find non-dynamic cluster"));
}
// The dynamic cluster must not have loaded yet
return Status.OK;
@ -100,42 +98,25 @@ final class CdsLoadBalancer2 extends LoadBalancer {
return fail(clusterConfigOr.getStatus());
}
XdsClusterConfig clusterConfig = clusterConfigOr.getValue();
List<String> leafNames;
if (clusterConfig.getChildren() instanceof AggregateConfig) {
leafNames = ((AggregateConfig) clusterConfig.getChildren()).getLeafNames();
} else if (clusterConfig.getChildren() instanceof EndpointConfig) {
leafNames = ImmutableList.of(clusterName);
} else {
return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unexpected cluster children type: "
+ clusterConfig.getChildren().getClass()));
}
if (leafNames.isEmpty()) {
// Should be impossible, because XdsClusterResource validated this
return fail(Status.UNAVAILABLE.withDescription(
errorPrefix() + "Zero leaf clusters for root cluster " + clusterName));
}
Status noneFoundError = Status.INTERNAL
.withDescription(errorPrefix() + "No leaves and no error; this is a bug");
List<DiscoveryMechanism> instances = new ArrayList<>();
for (String leafName : leafNames) {
StatusOr<XdsClusterConfig> leafConfigOr = xdsConfig.getClusters().get(leafName);
if (!leafConfigOr.hasValue()) {
noneFoundError = leafConfigOr.getStatus();
continue;
NameResolver.ConfigOrError configOrError;
Object gracefulConfig;
if (clusterConfig.getChildren() instanceof EndpointConfig) {
// The LB policy config is provided in service_config.proto/JSON format.
configOrError =
GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
Arrays.asList(clusterConfig.getClusterResource().lbPolicyConfig()),
lbRegistry);
if (configOrError.getError() != null) {
// Should be impossible, because XdsClusterResource validated this
return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unable to parse the LB config: " + configOrError.getError()));
}
if (!(leafConfigOr.getValue().getChildren() instanceof EndpointConfig)) {
noneFoundError = Status.INTERNAL.withDescription(
errorPrefix() + "Unexpected child " + leafName + " cluster children type: "
+ leafConfigOr.getValue().getChildren().getClass());
continue;
}
CdsUpdate result = leafConfigOr.getValue().getClusterResource();
CdsUpdate result = clusterConfig.getClusterResource();
DiscoveryMechanism instance;
if (result.clusterType() == ClusterType.EDS) {
instance = DiscoveryMechanism.forEds(
leafName,
clusterName,
result.edsServiceName(),
result.lrsServerInfo(),
result.maxConcurrentRequests(),
@ -144,45 +125,49 @@ final class CdsLoadBalancer2 extends LoadBalancer {
result.outlierDetection());
} else {
instance = DiscoveryMechanism.forLogicalDns(
leafName,
clusterName,
result.dnsHostName(),
result.lrsServerInfo(),
result.maxConcurrentRequests(),
result.upstreamTlsContext(),
result.filterMetadata());
}
instances.add(instance);
}
if (instances.isEmpty()) {
return fail(noneFoundError);
}
// The LB policy config is provided in service_config.proto/JSON format.
NameResolver.ConfigOrError configOrError =
GracefulSwitchLoadBalancer.parseLoadBalancingPolicyConfig(
Arrays.asList(clusterConfig.getClusterResource().lbPolicyConfig()), lbRegistry);
if (configOrError.getError() != null) {
// Should be impossible, because XdsClusterResource validated this
gracefulConfig = GracefulSwitchLoadBalancer.createLoadBalancingPolicyConfig(
lbRegistry.getProvider(CLUSTER_RESOLVER_POLICY_NAME),
new ClusterResolverConfig(
instance,
configOrError.getConfig(),
clusterConfig.getClusterResource().isHttp11ProxyAvailable()));
} else if (clusterConfig.getChildren() instanceof AggregateConfig) {
Map<String, PriorityChildConfig> priorityChildConfigs = new HashMap<>();
List<String> leafClusters = ((AggregateConfig) clusterConfig.getChildren()).getLeafNames();
for (String childCluster: leafClusters) {
priorityChildConfigs.put(childCluster,
new PriorityChildConfig(
GracefulSwitchLoadBalancer.createLoadBalancingPolicyConfig(
lbRegistry.getProvider(CDS_POLICY_NAME),
new CdsConfig(childCluster)),
false));
}
gracefulConfig = GracefulSwitchLoadBalancer.createLoadBalancingPolicyConfig(
lbRegistry.getProvider(PRIORITY_POLICY_NAME),
new PriorityLoadBalancerProvider.PriorityLbConfig(
Collections.unmodifiableMap(priorityChildConfigs), leafClusters));
} else {
return fail(Status.INTERNAL.withDescription(
errorPrefix() + "Unable to parse the LB config: " + configOrError.getError()));
errorPrefix() + "Unexpected cluster children type: "
+ clusterConfig.getChildren().getClass()));
}
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.unmodifiableList(instances),
configOrError.getConfig(),
clusterConfig.getClusterResource().isHttp11ProxyAvailable());
if (childLb == null) {
childLb = lbRegistry.getProvider(CLUSTER_RESOLVER_POLICY_NAME).newLoadBalancer(helper);
}
return childLb.acceptResolvedAddresses(
resolvedAddresses.toBuilder().setLoadBalancingPolicyConfig(config).build());
return delegate.acceptResolvedAddresses(
resolvedAddresses.toBuilder().setLoadBalancingPolicyConfig(gracefulConfig).build());
}
@Override
public void handleNameResolutionError(Status error) {
logger.log(XdsLogLevel.WARNING, "Received name resolution error: {0}", error);
if (childLb != null) {
childLb.handleNameResolutionError(error);
if (delegate != null) {
delegate.handleNameResolutionError(error);
} else {
helper.updateBalancingState(
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error)));
@ -192,10 +177,8 @@ final class CdsLoadBalancer2 extends LoadBalancer {
@Override
public void shutdown() {
logger.log(XdsLogLevel.INFO, "Shutdown");
if (childLb != null) {
childLb.shutdown();
childLb = null;
}
delegate.shutdown();
delegate = new GracefulSwitchLoadBalancer(helper);
if (clusterSubscription != null) {
clusterSubscription.close();
clusterSubscription = null;
@ -204,10 +187,7 @@ final class CdsLoadBalancer2 extends LoadBalancer {
@CheckReturnValue // don't forget to return up the stack after the fail call
private Status fail(Status error) {
if (childLb != null) {
childLb.shutdown();
childLb = null;
}
delegate.shutdown();
helper.updateBalancingState(
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error)));
return Status.OK; // XdsNameResolver isn't a polling NR, so this value doesn't matter

View File

@ -23,6 +23,7 @@ import io.grpc.Internal;
import io.grpc.LoadBalancer;
import io.grpc.LoadBalancer.Helper;
import io.grpc.LoadBalancerProvider;
import io.grpc.LoadBalancerRegistry;
import io.grpc.NameResolver.ConfigOrError;
import io.grpc.Status;
import io.grpc.internal.JsonUtil;
@ -51,9 +52,24 @@ public class CdsLoadBalancerProvider extends LoadBalancerProvider {
return XdsLbPolicies.CDS_POLICY_NAME;
}
private final LoadBalancerRegistry loadBalancerRegistry;
public CdsLoadBalancerProvider() {
this.loadBalancerRegistry = null;
}
public CdsLoadBalancerProvider(LoadBalancerRegistry loadBalancerRegistry) {
this.loadBalancerRegistry = loadBalancerRegistry;
}
@Override
public LoadBalancer newLoadBalancer(Helper helper) {
return new CdsLoadBalancer2(helper);
LoadBalancerRegistry loadBalancerRegistry = this.loadBalancerRegistry;
if (loadBalancerRegistry == null) {
loadBalancerRegistry = LoadBalancerRegistry.getDefaultRegistry();
}
return new CdsLoadBalancer2(helper, loadBalancerRegistry);
}
@Override

View File

@ -168,8 +168,8 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
*/
private final class ClusterResolverLbState extends LoadBalancer {
private final Helper helper;
private final List<String> clusters = new ArrayList<>();
private final Map<String, ClusterState> clusterStates = new HashMap<>();
private ClusterState clusterState;
private String cluster;
private Object endpointLbConfig;
private ResolvedAddresses resolvedAddresses;
private LoadBalancer childLb;
@ -185,21 +185,18 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
ClusterResolverConfig config =
(ClusterResolverConfig) resolvedAddresses.getLoadBalancingPolicyConfig();
endpointLbConfig = config.lbConfig;
for (DiscoveryMechanism instance : config.discoveryMechanisms) {
clusters.add(instance.cluster);
ClusterState state;
if (instance.type == DiscoveryMechanism.Type.EDS) {
state = new EdsClusterState(instance.cluster, instance.edsServiceName,
instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext,
instance.filterMetadata, instance.outlierDetection);
} else { // logical DNS
state = new LogicalDnsClusterState(instance.cluster, instance.dnsHostName,
instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext,
instance.filterMetadata);
}
clusterStates.put(instance.cluster, state);
state.start();
DiscoveryMechanism instance = config.discoveryMechanism;
cluster = instance.cluster;
if (instance.type == DiscoveryMechanism.Type.EDS) {
clusterState = new EdsClusterState(instance.cluster, instance.edsServiceName,
instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext,
instance.filterMetadata, instance.outlierDetection);
} else { // logical DNS
clusterState = new LogicalDnsClusterState(instance.cluster, instance.dnsHostName,
instance.lrsServerInfo, instance.maxConcurrentRequests, instance.tlsContext,
instance.filterMetadata);
}
clusterState.start();
return Status.OK;
}
@ -215,9 +212,7 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
@Override
public void shutdown() {
for (ClusterState state : clusterStates.values()) {
state.shutdown();
}
clusterState.shutdown();
if (childLb != null) {
childLb.shutdown();
}
@ -229,24 +224,21 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
List<String> priorities = new ArrayList<>(); // totally ordered priority list
Status endpointNotFound = Status.OK;
for (String cluster : clusters) {
ClusterState state = clusterStates.get(cluster);
// Propagate endpoints to the child LB policy only after all clusters have been resolved.
if (!state.resolved && state.status.isOk()) {
return;
}
if (state.result != null) {
addresses.addAll(state.result.addresses);
priorityChildConfigs.putAll(state.result.priorityChildConfigs);
priorities.addAll(state.result.priorities);
} else {
endpointNotFound = state.status;
}
// Propagate endpoints to the child LB policy only after all clusters have been resolved.
if (!clusterState.resolved && clusterState.status.isOk()) {
return;
}
if (clusterState.result != null) {
addresses.addAll(clusterState.result.addresses);
priorityChildConfigs.putAll(clusterState.result.priorityChildConfigs);
priorities.addAll(clusterState.result.priorities);
} else {
endpointNotFound = clusterState.status;
}
if (addresses.isEmpty()) {
if (endpointNotFound.isOk()) {
endpointNotFound = Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + clusters);
"No usable endpoint from cluster: " + cluster);
} else {
endpointNotFound =
Status.UNAVAILABLE.withCause(endpointNotFound.getCause())
@ -274,22 +266,12 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
}
private void handleEndpointResolutionError() {
boolean allInError = true;
Status error = null;
for (String cluster : clusters) {
ClusterState state = clusterStates.get(cluster);
if (state.status.isOk()) {
allInError = false;
} else {
error = state.status;
}
}
if (allInError) {
if (!clusterState.status.isOk()) {
if (childLb != null) {
childLb.handleNameResolutionError(error);
childLb.handleNameResolutionError(clusterState.status);
} else {
helper.updateBalancingState(
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(error)));
TRANSIENT_FAILURE, new FixedResultPicker(PickResult.withError(clusterState.status)));
}
}
}
@ -306,10 +288,8 @@ final class ClusterResolverLoadBalancer extends LoadBalancer {
@Override
public void refreshNameResolution() {
for (ClusterState state : clusterStates.values()) {
if (state instanceof LogicalDnsClusterState) {
((LogicalDnsClusterState) state).refresh();
}
if (clusterState instanceof LogicalDnsClusterState) {
((LogicalDnsClusterState) clusterState).refresh();
}
}

View File

@ -30,7 +30,6 @@ import io.grpc.Status;
import io.grpc.xds.EnvoyServerProtoData.OutlierDetection;
import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext;
import io.grpc.xds.client.Bootstrapper.ServerInfo;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nullable;
@ -70,15 +69,15 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
}
static final class ClusterResolverConfig {
// Ordered list of clusters to be resolved.
final List<DiscoveryMechanism> discoveryMechanisms;
// Cluster to be resolved.
final DiscoveryMechanism discoveryMechanism;
// GracefulSwitch configuration
final Object lbConfig;
private final boolean isHttp11ProxyAvailable;
ClusterResolverConfig(List<DiscoveryMechanism> discoveryMechanisms, Object lbConfig,
ClusterResolverConfig(DiscoveryMechanism discoveryMechanism, Object lbConfig,
boolean isHttp11ProxyAvailable) {
this.discoveryMechanisms = checkNotNull(discoveryMechanisms, "discoveryMechanisms");
this.discoveryMechanism = checkNotNull(discoveryMechanism, "discoveryMechanism");
this.lbConfig = checkNotNull(lbConfig, "lbConfig");
this.isHttp11ProxyAvailable = isHttp11ProxyAvailable;
}
@ -89,7 +88,7 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
@Override
public int hashCode() {
return Objects.hash(discoveryMechanisms, lbConfig, isHttp11ProxyAvailable);
return Objects.hash(discoveryMechanism, lbConfig, isHttp11ProxyAvailable);
}
@Override
@ -101,7 +100,7 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
return false;
}
ClusterResolverConfig that = (ClusterResolverConfig) o;
return discoveryMechanisms.equals(that.discoveryMechanisms)
return discoveryMechanism.equals(that.discoveryMechanism)
&& lbConfig.equals(that.lbConfig)
&& isHttp11ProxyAvailable == that.isHttp11ProxyAvailable;
}
@ -109,7 +108,7 @@ public final class ClusterResolverLoadBalancerProvider extends LoadBalancerProvi
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("discoveryMechanisms", discoveryMechanisms)
.add("discoveryMechanism", discoveryMechanism)
.add("lbConfig", lbConfig)
.add("isHttp11ProxyAvailable", isHttp11ProxyAvailable)
.toString();

View File

@ -18,6 +18,7 @@ package io.grpc.xds;
import static com.google.common.truth.Truth.assertThat;
import static io.grpc.xds.XdsLbPolicies.CLUSTER_RESOLVER_POLICY_NAME;
import static io.grpc.xds.XdsLbPolicies.PRIORITY_POLICY_NAME;
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_CDS;
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_EDS;
import static io.grpc.xds.XdsTestControlPlaneService.ADS_TYPE_URL_LDS;
@ -27,6 +28,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.github.xds.type.v3.TypedStruct;
import com.google.common.collect.ImmutableMap;
@ -157,7 +159,9 @@ public class CdsLoadBalancer2Test {
new LeastRequestLoadBalancerProvider()));
lbRegistry.register(new FakeLoadBalancerProvider("wrr_locality_experimental",
new WrrLocalityLoadBalancerProvider()));
loadBalancer = new CdsLoadBalancer2(helper, lbRegistry);
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
cleanupRule.register(InProcessServerBuilder
.forName("control-plane.example.com")
@ -169,6 +173,8 @@ public class CdsLoadBalancer2Test {
SynchronizationContext syncContext = new SynchronizationContext((t, e) -> {
throw new AssertionError(e);
});
when(helper.getSynchronizationContext()).thenReturn(syncContext);
when(helper.getScheduledExecutorService()).thenReturn(fakeClock.getScheduledExecutorService());
NameResolver.Args nameResolverArgs = NameResolver.Args.newBuilder()
.setDefaultPort(8080)
@ -246,13 +252,12 @@ public class CdsLoadBalancer2Test {
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, lrsServerInfo, 100L, upstreamTlsContext,
Collections.emptyMap(), io.grpc.xds.EnvoyServerProtoData.OutlierDetection.create(
null, null, null, null, SuccessRateEjection.create(null, null, null, null),
FailurePercentageEjection.create(null, null, null, null)))));
assertThat(childLbConfig.discoveryMechanism).isEqualTo(
DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, lrsServerInfo, 100L, upstreamTlsContext,
Collections.emptyMap(), io.grpc.xds.EnvoyServerProtoData.OutlierDetection.create(
null, null, null, null, SuccessRateEjection.create(null, null, null, null),
FailurePercentageEjection.create(null, null, null, null))));
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName())
.isEqualTo("wrr_locality_experimental");
@ -296,11 +301,10 @@ public class CdsLoadBalancer2Test {
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forLogicalDns(
CLUSTER, "dns.example.com:1111", lrsServerInfo, 100L, upstreamTlsContext,
Collections.emptyMap())));
assertThat(childLbConfig.discoveryMechanism).isEqualTo(
DiscoveryMechanism.forLogicalDns(
CLUSTER, "dns.example.com:1111", lrsServerInfo, 100L, upstreamTlsContext,
Collections.emptyMap()));
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName())
.isEqualTo("wrr_locality_experimental");
@ -332,10 +336,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
assertThat(childLbConfig.discoveryMechanism).isEqualTo(
DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, null, 100L, null, Collections.emptyMap(), null)));
CLUSTER, EDS_SERVICE_NAME, null, 100L, null, Collections.emptyMap(), null));
cluster = EDS_CLUSTER.toBuilder()
.setCircuitBreakers(CircuitBreakers.newBuilder()
@ -348,10 +351,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1);
childBalancer = Iterables.getOnlyElement(childBalancers);
childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
assertThat(childLbConfig.discoveryMechanism).isEqualTo(
DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, null, 200L, null, Collections.emptyMap(), null)));
CLUSTER, EDS_SERVICE_NAME, null, 200L, null, Collections.emptyMap(), null));
}
@Test
@ -363,10 +365,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
assertThat(childLbConfig.discoveryMechanism).isEqualTo(
DiscoveryMechanism.forEds(
CLUSTER, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null)));
CLUSTER, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null));
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of());
@ -395,10 +396,9 @@ public class CdsLoadBalancer2Test {
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
assertThat(childLbConfig.discoveryMechanism).isEqualTo(
DiscoveryMechanism.forEds(
clusterName, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null)));
clusterName, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null));
assertThat(this.lastXdsConfig.getClusters()).containsKey(clusterName);
shutdownLoadBalancer();
@ -406,7 +406,12 @@ public class CdsLoadBalancer2Test {
}
@Test
public void discoverAggregateCluster() {
public void discoverAggregateCluster_createsPriorityLbPolicy() {
lbRegistry.register(new FakeLoadBalancerProvider(PRIORITY_POLICY_NAME));
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
String cluster1 = "cluster-01.googleapis.com";
String cluster2 = "cluster-02.googleapis.com";
String cluster3 = "cluster-03.googleapis.com";
@ -459,20 +464,77 @@ public class CdsLoadBalancer2Test {
verify(helper, never()).updateBalancingState(eq(ConnectivityState.TRANSIENT_FAILURE), any());
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(childBalancer.name).isEqualTo(CLUSTER_RESOLVER_POLICY_NAME);
ClusterResolverConfig childLbConfig = (ClusterResolverConfig) childBalancer.config;
// Clusters are resolved recursively, later duplicates removed: [cluster3, cluster4, cluster2]
assertThat(childLbConfig.discoveryMechanisms).isEqualTo(
Arrays.asList(
DiscoveryMechanism.forEds(
cluster3, EDS_SERVICE_NAME, null, 100L, null, Collections.emptyMap(), null),
DiscoveryMechanism.forEds(
cluster4, EDS_SERVICE_NAME, null, null, null, Collections.emptyMap(), null),
DiscoveryMechanism.forLogicalDns(
cluster2, "dns.example.com:1111", null, null, null, Collections.emptyMap())));
assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME);
PriorityLoadBalancerProvider.PriorityLbConfig childLbConfig =
(PriorityLoadBalancerProvider.PriorityLbConfig) childBalancer.config;
assertThat(childLbConfig.priorities).hasSize(3);
assertThat(childLbConfig.priorities.get(0)).isEqualTo(cluster3);
assertThat(childLbConfig.priorities.get(1)).isEqualTo(cluster4);
assertThat(childLbConfig.priorities.get(2)).isEqualTo(cluster2);
assertThat(childLbConfig.childConfigs).hasSize(3);
PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig childConfig3 =
childLbConfig.childConfigs.get(cluster3);
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childLbConfig.lbConfig).getPolicyName())
.isEqualTo("ring_hash_experimental"); // dominated by top-level cluster's config
GracefulSwitchLoadBalancerAccessor.getChildProvider(childConfig3.childConfig)
.getPolicyName())
.isEqualTo("cds_experimental");
PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig childConfig4 =
childLbConfig.childConfigs.get(cluster4);
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childConfig4.childConfig)
.getPolicyName())
.isEqualTo("cds_experimental");
PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig childConfig2 =
childLbConfig.childConfigs.get(cluster2);
assertThat(
GracefulSwitchLoadBalancerAccessor.getChildProvider(childConfig2.childConfig)
.getPolicyName())
.isEqualTo("cds_experimental");
}
@Test
// Both priorities will get tried using real priority LB policy.
public void discoverAggregateCluster_testChildCdsLbPolicyParsing() {
lbRegistry.register(new PriorityLoadBalancerProvider());
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
String cluster1 = "cluster-01.googleapis.com";
String cluster2 = "cluster-02.googleapis.com";
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of(
// CLUSTER (aggr.) -> [cluster1 (EDS), cluster2 (EDS)]
CLUSTER, Cluster.newBuilder()
.setName(CLUSTER)
.setClusterType(Cluster.CustomClusterType.newBuilder()
.setName("envoy.clusters.aggregate")
.setTypedConfig(Any.pack(ClusterConfig.newBuilder()
.addClusters(cluster1)
.addClusters(cluster2)
.build())))
.build(),
cluster1, EDS_CLUSTER.toBuilder().setName(cluster1).build(),
cluster2, EDS_CLUSTER.toBuilder().setName(cluster2).build()));
startXdsDepManager();
verify(helper, never()).updateBalancingState(eq(ConnectivityState.TRANSIENT_FAILURE), any());
assertThat(childBalancers).hasSize(2);
ClusterResolverConfig cluster1ResolverConfig =
(ClusterResolverConfig) childBalancers.get(0).config;
assertThat(cluster1ResolverConfig.discoveryMechanism.cluster)
.isEqualTo("cluster-01.googleapis.com");
assertThat(cluster1ResolverConfig.discoveryMechanism.type)
.isEqualTo(DiscoveryMechanism.Type.EDS);
assertThat(cluster1ResolverConfig.discoveryMechanism.edsServiceName)
.isEqualTo("backend-service-1.googleapis.com");
ClusterResolverConfig cluster2ResolverConfig =
(ClusterResolverConfig) childBalancers.get(1).config;
assertThat(cluster2ResolverConfig.discoveryMechanism.cluster)
.isEqualTo("cluster-02.googleapis.com");
assertThat(cluster2ResolverConfig.discoveryMechanism.type)
.isEqualTo(DiscoveryMechanism.Type.EDS);
assertThat(cluster2ResolverConfig.discoveryMechanism.edsServiceName)
.isEqualTo("backend-service-1.googleapis.com");
}
@Test
@ -500,6 +562,11 @@ public class CdsLoadBalancer2Test {
@Test
public void aggregateCluster_noNonAggregateClusterExits_returnErrorPicker() {
lbRegistry.register(new PriorityLoadBalancerProvider());
CdsLoadBalancerProvider cdsLoadBalancerProvider = new CdsLoadBalancerProvider(lbRegistry);
lbRegistry.register(cdsLoadBalancerProvider);
loadBalancer = (CdsLoadBalancer2) cdsLoadBalancerProvider.newLoadBalancer(helper);
String cluster1 = "cluster-01.googleapis.com";
controlPlaneService.setXdsConfig(ADS_TYPE_URL_CDS, ImmutableMap.of(
// CLUSTER (aggr.) -> [cluster1 (missing)]
@ -550,8 +617,8 @@ public class CdsLoadBalancer2Test {
loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable"));
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("unreachable");
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class));
verify(helper).updateBalancingState(
eq(ConnectivityState.CONNECTING), any(SubchannelPicker.class));
}
@Test

View File

@ -245,7 +245,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void edsClustersWithRingHashEndpointLbPolicy() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), ringHash, false);
edsDiscoveryMechanism1, ringHash, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
@ -311,7 +311,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void edsClustersWithLeastRequestEndpointLbPolicy() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, false);
edsDiscoveryMechanism1, leastRequest, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
@ -358,7 +358,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void edsClustersEndpointHostname_addedToAddressAttribute() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), leastRequest, false);
edsDiscoveryMechanismWithOutlierDetection, leastRequest, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
@ -385,7 +385,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void endpointAddressRewritten_whenProxyMetadataIsInEndpointMetadata() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), leastRequest, true);
edsDiscoveryMechanismWithOutlierDetection, leastRequest, true);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
@ -404,7 +404,7 @@ public class ClusterResolverLoadBalancerTest {
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.create(
Arrays.asList(
LbEndpoint.create(endpoint, 0 /* loadBalancingWeight */, true,
"hostname1", endpointMetadata)),
"hostname1", endpointMetadata)),
100 /* localityWeight */, 1 /* priority */, localityMetadata);
xdsClient.deliverClusterLoadAssignment(
@ -432,7 +432,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void endpointAddressRewritten_whenProxyMetadataIsInLocalityMetadata() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection), leastRequest, true);
edsDiscoveryMechanismWithOutlierDetection, leastRequest, true);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
@ -451,7 +451,7 @@ public class ClusterResolverLoadBalancerTest {
LocalityLbEndpoints localityLbEndpoints = LocalityLbEndpoints.create(
Arrays.asList(
LbEndpoint.create(endpoint, 0 /* loadBalancingWeight */, true,
"hostname2", endpointMetadata)),
"hostname2", endpointMetadata)),
100 /* localityWeight */, 1 /* priority */, localityMetadata);
xdsClient.deliverClusterLoadAssignment(
@ -479,48 +479,36 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void onlyEdsClusters_receivedEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), roundRobin, false);
edsDiscoveryMechanism2, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1, EDS_SERVICE_NAME2);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME2);
assertThat(childBalancers).isEmpty();
// CLUSTER1 has priority 1 (priority3), which has locality 2, which has endpoint3.
// CLUSTER2 has priority 1 (priority1) and 2 (priority2); priority1 has locality1,
// which has endpoint1 and endpoint2; priority2 has locality3, which has endpoint4.
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
EquivalentAddressGroup endpoint3 = makeAddress("endpoint-addr-3");
EquivalentAddressGroup endpoint4 = makeAddress("endpoint-addr-4");
LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create(
Arrays.asList(
LbEndpoint.create(endpoint1, 100,
true, "hostname1", ImmutableMap.of()),
LbEndpoint.create(endpoint2, 100,
true, "hostname1", ImmutableMap.of())),
70 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint3, 100, true,
"hostname2", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints.create(
Arrays.asList(
LbEndpoint.create(endpoint1, 100,
true, "hostname1", ImmutableMap.of()),
LbEndpoint.create(endpoint2, 100,
true, "hostname1", ImmutableMap.of())),
70 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints3 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint4, 100, true,
"hostname3", ImmutableMap.of())),
"hostname3", ImmutableMap.of())),
20 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
String priority1 = CLUSTER2 + "[child1]";
String priority2 = CLUSTER2 + "[child2]";
String priority3 = CLUSTER1 + "[child1]";
// CLUSTER2: locality1 with priority 1 and locality3 with priority 2.
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME2,
ImmutableMap.of(locality1, localityLbEndpoints1, locality3, localityLbEndpoints3));
assertThat(childBalancers).isEmpty(); // not created until all clusters resolved
// CLUSTER1: locality2 with priority 1.
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality2, localityLbEndpoints2));
EDS_SERVICE_NAME2,
ImmutableMap.of(locality1, localityLbEndpoints1, locality3, localityLbEndpoints3));
// Endpoints of all clusters have been resolved.
assertThat(childBalancers).hasSize(1);
@ -528,12 +516,12 @@ public class ClusterResolverLoadBalancerTest {
assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME);
PriorityLbConfig priorityLbConfig = (PriorityLbConfig) childBalancer.config;
assertThat(priorityLbConfig.priorities)
.containsExactly(priority3, priority1, priority2).inOrder();
.containsExactly(priority1, priority2).inOrder();
PriorityChildConfig priorityChildConfig1 = priorityLbConfig.childConfigs.get(priority1);
assertThat(priorityChildConfig1.ignoreReresolution).isTrue();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig1.childConfig)
.getPolicyName())
.getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig1 = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig1.childConfig);
@ -548,7 +536,7 @@ public class ClusterResolverLoadBalancerTest {
PriorityChildConfig priorityChildConfig2 = priorityLbConfig.childConfigs.get(priority2);
assertThat(priorityChildConfig2.ignoreReresolution).isTrue();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig2.childConfig)
.getPolicyName())
.getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig2 = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig2.childConfig);
@ -560,15 +548,6 @@ public class ClusterResolverLoadBalancerTest {
GracefulSwitchLoadBalancerAccessor.getChildProvider(wrrLocalityConfig2.childConfig);
assertThat(childProvider2.getPolicyName()).isEqualTo("round_robin");
PriorityChildConfig priorityChildConfig3 = priorityLbConfig.childConfigs.get(priority3);
assertThat(priorityChildConfig3.ignoreReresolution).isTrue();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig3.childConfig)
.getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig3 = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig3.childConfig);
assertClusterImplConfig(clusterImplConfig3, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_INFO, 100L,
tlsContext, Collections.<DropOverload>emptyList(), WRR_LOCALITY_POLICY_NAME);
WrrLocalityConfig wrrLocalityConfig3 = (WrrLocalityConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(clusterImplConfig1.childConfig);
LoadBalancerProvider childProvider3 =
@ -595,7 +574,7 @@ public class ClusterResolverLoadBalancerTest {
private void verifyEdsPriorityNames(List<String> want,
Map<Locality, LocalityLbEndpoints>... updates) {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism2), roundRobin, false);
edsDiscoveryMechanism2, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME2);
assertThat(childBalancers).isEmpty();
@ -617,7 +596,7 @@ public class ClusterResolverLoadBalancerTest {
public void edsUpdatePriorityName_twoPriorities() {
verifyEdsPriorityNames(Arrays.asList(CLUSTER2 + "[child1]", CLUSTER2 + "[child2]"),
ImmutableMap.of(locality1, createEndpoints(1),
locality2, createEndpoints(2)
locality2, createEndpoints(2)
));
}
@ -653,8 +632,8 @@ public class ClusterResolverLoadBalancerTest {
locality3, createEndpoints(3),
locality2, createEndpoints(2)),
ImmutableMap.of(locality1, createEndpoints(2),
locality3, createEndpoints(1),
locality2, createEndpoints(1)
locality3, createEndpoints(1),
locality2, createEndpoints(1)
));
}
@ -662,76 +641,64 @@ public class ClusterResolverLoadBalancerTest {
return LocalityLbEndpoints.create(
Arrays.asList(
LbEndpoint.create(makeAddress("endpoint-addr-1"), 100,
true, "hostname1", ImmutableMap.of()),
true, "hostname1", ImmutableMap.of()),
LbEndpoint.create(makeAddress("endpoint-addr-2"), 100,
true, "hostname2", ImmutableMap.of())),
true, "hostname2", ImmutableMap.of())),
70 /* localityWeight */, priority /* priority */, ImmutableMap.of());
}
@Test
public void onlyEdsClusters_resourceNeverExist_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1, EDS_SERVICE_NAME2);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
reset(helper);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class)); // wait for CLUSTER2's results
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME2);
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(
pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + Arrays.asList(CLUSTER1, CLUSTER2)),
"No usable endpoint from cluster: " + CLUSTER1),
null);
}
@Test
public void onlyEdsClusters_allResourcesRevoked_shutDownChildLbPolicy() {
public void edsCluster_resourcesRevoked_shutDownChildLbPolicy() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1, EDS_SERVICE_NAME2);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, true,
"hostname1", ImmutableMap.of())),
"hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint2, 100, true,
"hostname2", ImmutableMap.of())),
20 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints1));
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME2, Collections.singletonMap(locality2, localityLbEndpoints2));
assertThat(childBalancers).hasSize(1); // child LB policy created
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities).hasSize(2);
assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses);
assertThat(((PriorityLbConfig) childBalancer.config).priorities).hasSize(1);
assertAddressesEqual(Arrays.asList(endpoint1), childBalancer.addresses);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME2);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
Status expectedError = Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + Arrays.asList(CLUSTER1, CLUSTER2));
"No usable endpoint from cluster: " + CLUSTER1);
assertPicker(pickerCaptor.getValue(), expectedError, null);
}
@Test
public void handleEdsResource_ignoreUnhealthyEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
@ -739,9 +706,9 @@ public class ClusterResolverLoadBalancerTest {
LocalityLbEndpoints.create(
Arrays.asList(
LbEndpoint.create(endpoint1, 100, false /* isHealthy */,
"hostname1", ImmutableMap.of()),
"hostname1", ImmutableMap.of()),
LbEndpoint.create(endpoint2, 100, true /* isHealthy */,
"hostname2", ImmutableMap.of())),
"hostname2", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
@ -753,19 +720,19 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void handleEdsResource_ignoreLocalitiesWithNoHealthyEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, false /* isHealthy */,
"hostname1", ImmutableMap.of())),
"hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint2, 100, true /* isHealthy */,
"hostname2", ImmutableMap.of())),
"hostname2", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1,
@ -780,20 +747,20 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void handleEdsResource_ignorePrioritiesWithNoHealthyEndpoints() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints1 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, false /* isHealthy */,
"hostname1", ImmutableMap.of())),
"hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
LocalityLbEndpoints localityLbEndpoints2 =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint2, 200, true /* isHealthy */,
"hostname2", ImmutableMap.of())),
10 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
"hostname2", ImmutableMap.of())),
10 /* localityWeight */, 2 /* priority */, ImmutableMap.of());
String priority2 = CLUSTER1 + "[child2]";
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1,
@ -806,7 +773,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void handleEdsResource_noHealthyEndpoint() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1");
LocalityLbEndpoints localityLbEndpoints =
@ -823,7 +790,7 @@ public class ClusterResolverLoadBalancerTest {
assertPicker(
pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription(
"No usable endpoint from cluster(s): " + Collections.singleton(CLUSTER1)),
"No usable endpoint from cluster: " + CLUSTER1),
null);
}
@ -841,7 +808,7 @@ public class ClusterResolverLoadBalancerTest {
void do_onlyLogicalDnsCluster_endpointsResolved() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false);
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
@ -857,7 +824,7 @@ public class ClusterResolverLoadBalancerTest {
PriorityChildConfig priorityChildConfig = priorityLbConfig.childConfigs.get(priority);
assertThat(priorityChildConfig.ignoreReresolution).isFalse();
assertThat(GracefulSwitchLoadBalancerAccessor.getChildProvider(priorityChildConfig.childConfig)
.getPolicyName())
.getPolicyName())
.isEqualTo(CLUSTER_IMPL_POLICY_NAME);
ClusterImplConfig clusterImplConfig = (ClusterImplConfig)
GracefulSwitchLoadBalancerAccessor.getChildConfig(priorityChildConfig.childConfig);
@ -873,7 +840,7 @@ public class ClusterResolverLoadBalancerTest {
@Test
public void onlyLogicalDnsCluster_handleRefreshNameResolution() {
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false);
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
@ -900,9 +867,9 @@ public class ClusterResolverLoadBalancerTest {
void do_onlyLogicalDnsCluster_resolutionError_backoffAndRefresh() {
InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider,
backoffPolicy1, backoffPolicy2);
backoffPolicy1, backoffPolicy2);
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false);
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
@ -948,7 +915,7 @@ public class ClusterResolverLoadBalancerTest {
public void onlyLogicalDnsCluster_refreshNameResolutionRaceWithResolutionError() {
InOrder inOrder = Mockito.inOrder(backoffPolicyProvider, backoffPolicy1, backoffPolicy2);
ClusterResolverConfig config = new ClusterResolverConfig(
Collections.singletonList(logicalDnsDiscoveryMechanism), roundRobin, false);
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
@ -984,119 +951,22 @@ public class ClusterResolverLoadBalancerTest {
}
@Test
public void edsClustersAndLogicalDnsCluster_receivedEndpoints() {
public void resolutionErrorAfterChildLbCreated_propagateError() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); // DNS endpoint
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); // DNS endpoint
EquivalentAddressGroup endpoint3 = makeAddress("endpoint-addr-3"); // EDS endpoint
resolver.deliverEndpointAddresses(Arrays.asList(endpoint1, endpoint2));
LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint3, 100, true,
"hostname3", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]", CLUSTER_DNS + "[child0]").inOrder();
assertAddressesEqual(Arrays.asList(endpoint3, endpoint1, endpoint2),
childBalancer.addresses); // ordered by cluster then addresses
assertAddressesEqual(AddressFilter.filter(AddressFilter.filter(
childBalancer.addresses, CLUSTER1 + "[child1]"),
"{region=\"test-region-1\", zone=\"test-zone-1\", sub_zone=\"test-subzone-1\"}"),
Collections.singletonList(endpoint3));
assertAddressesEqual(AddressFilter.filter(AddressFilter.filter(
childBalancer.addresses, CLUSTER_DNS + "[child0]"),
"{region=\"\", zone=\"\", sub_zone=\"\"}"),
Arrays.asList(endpoint1, endpoint2));
}
@Test
public void noEdsResourceExists_useDnsResolutionResults() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class)); // wait for DNS results
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
resolver.deliverEndpointAddresses(Arrays.asList(endpoint1, endpoint2));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
String priority = Iterables.getOnlyElement(
((PriorityLbConfig) childBalancer.config).priorities);
assertThat(priority).isEqualTo(CLUSTER_DNS + "[child0]");
assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses);
}
@Test
public void edsResourceRevoked_dnsResolutionError_shutDownChildLbPolicyAndReturnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1");
LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint, 100, true,
"hostname1", ImmutableMap.of())),
"hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
resolver.deliverError(Status.UNKNOWN.withDescription("I am lost"));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]");
assertAddressesEqual(Collections.singletonList(endpoint), childBalancer.addresses);
assertThat(childBalancer.shutdown).isFalse();
xdsClient.deliverResourceNotFound(EDS_SERVICE_NAME1);
assertThat(childBalancer.shutdown).isTrue();
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription("I am lost"), null);
}
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
@Test
public void resolutionErrorAfterChildLbCreated_propagateErrorIfAllClustersEncounterError() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr-1");
LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint, 100, true,
"hostname1", ImmutableMap.of())),
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
assertThat(childBalancers).isEmpty(); // not created until all clusters resolved.
resolver.deliverError(Status.UNKNOWN.withDescription("I am lost"));
// DNS resolution failed, but there are EDS endpoints can be used.
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); // child LB created
assertThat(childBalancer.upstreamError).isNull(); // should not propagate error to child LB
@ -1104,40 +974,19 @@ public class ClusterResolverLoadBalancerTest {
xdsClient.deliverError(Status.RESOURCE_EXHAUSTED.withDescription("out of memory"));
assertThat(childBalancer.upstreamError).isNotNull(); // last cluster's (DNS) error propagated
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNKNOWN);
assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("I am lost");
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(childBalancer.upstreamError.getDescription())
.isEqualTo("Unable to load EDS backend-service-foo.googleapis.com. xDS server returned: "
+ "RESOURCE_EXHAUSTED: out of memory");
assertThat(childBalancer.shutdown).isFalse();
verify(helper, never()).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), any(SubchannelPicker.class));
}
@Test
public void resolutionErrorBeforeChildLbCreated_returnErrorPickerIfAllClustersEncounterError() {
public void resolutionErrorBeforeChildLbCreated_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
xdsClient.deliverError(Status.UNIMPLEMENTED.withDescription("not found"));
assertThat(childBalancers).isEmpty();
verify(helper, never()).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), any(SubchannelPicker.class)); // wait for DNS
Status dnsError = Status.UNKNOWN.withDescription("I am lost");
resolver.deliverError(dnsError);
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(
pickerCaptor.getValue(),
Status.UNAVAILABLE.withDescription(dnsError.getDescription()),
null);
}
@Test
public void resolutionErrorBeforeChildLbCreated_edsOnly_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
@ -1153,11 +1002,25 @@ public class ClusterResolverLoadBalancerTest {
}
@Test
public void handleNameResolutionErrorFromUpstream_beforeChildLbCreated_returnErrorPicker() {
public void handleNameResolutionErrorFromUpstream_eds_beforeChildLbCreated_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
assertThat(childBalancers).isEmpty();
reset(helper);
Status upstreamError = Status.UNAVAILABLE.withDescription("unreachable");
loadBalancer.handleNameResolutionError(upstreamError);
verify(helper).updateBalancingState(
eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture());
assertPicker(pickerCaptor.getValue(), upstreamError, null);
}
@Test
public void handleNameResolutionErrorFromUpstream_lDns_beforeChildLbCreated_returnErrorPicker() {
ClusterResolverConfig config = new ClusterResolverConfig(
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
@ -1169,16 +1032,14 @@ public class ClusterResolverLoadBalancerTest {
}
@Test
public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_fallThrough() {
public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_eds_fallThrough() {
ClusterResolverConfig config = new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, logicalDnsDiscoveryMechanism), roundRobin, false);
edsDiscoveryMechanism1, roundRobin, false);
deliverLbConfig(config);
assertThat(xdsClient.watchers.keySet()).containsExactly(EDS_SERVICE_NAME1);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1");
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
LocalityLbEndpoints localityLbEndpoints =
LocalityLbEndpoints.create(
Collections.singletonList(LbEndpoint.create(endpoint1, 100, true,
@ -1186,12 +1047,34 @@ public class ClusterResolverLoadBalancerTest {
10 /* localityWeight */, 1 /* priority */, ImmutableMap.of());
xdsClient.deliverClusterLoadAssignment(
EDS_SERVICE_NAME1, Collections.singletonMap(locality1, localityLbEndpoints));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]");
assertAddressesEqual(Arrays.asList(endpoint1), childBalancer.addresses);
loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable"));
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
assertThat(childBalancer.upstreamError.getDescription()).isEqualTo("unreachable");
verify(helper, never()).updateBalancingState(
any(ConnectivityState.class), any(SubchannelPicker.class));
}
@Test
public void handleNameResolutionErrorFromUpstream_afterChildLbCreated_logicalDns_fallThrough() {
ClusterResolverConfig config = new ClusterResolverConfig(
logicalDnsDiscoveryMechanism, roundRobin, false);
deliverLbConfig(config);
FakeNameResolver resolver = assertResolverCreated("/" + DNS_HOST_NAME);
assertThat(childBalancers).isEmpty();
reset(helper);
EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2");
resolver.deliverEndpointAddresses(Collections.singletonList(endpoint2));
assertThat(childBalancers).hasSize(1);
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers);
assertThat(((PriorityLbConfig) childBalancer.config).priorities)
.containsExactly(CLUSTER1 + "[child1]", CLUSTER_DNS + "[child0]");
assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses);
.containsExactly(CLUSTER_DNS + "[child0]");
assertAddressesEqual(Arrays.asList(endpoint2), childBalancer.addresses);
loadBalancer.handleNameResolutionError(Status.UNAVAILABLE.withDescription("unreachable"));
assertThat(childBalancer.upstreamError.getCode()).isEqualTo(Code.UNAVAILABLE);
@ -1205,19 +1088,17 @@ public class ClusterResolverLoadBalancerTest {
new EqualsTester()
.addEqualityGroup(
new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, false),
edsDiscoveryMechanism1, leastRequest, false),
new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, false))
edsDiscoveryMechanism1, leastRequest, false))
.addEqualityGroup(new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), roundRobin, false))
edsDiscoveryMechanism1, roundRobin, false))
.addEqualityGroup(new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanism1), leastRequest, true))
edsDiscoveryMechanism1, leastRequest, true))
.addEqualityGroup(new ClusterResolverConfig(
Collections.singletonList(edsDiscoveryMechanismWithOutlierDetection),
edsDiscoveryMechanismWithOutlierDetection,
leastRequest,
false))
.addEqualityGroup(new ClusterResolverConfig(
Arrays.asList(edsDiscoveryMechanism1, edsDiscoveryMechanism2), leastRequest, false))
.testEquals();
}