diff --git a/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java b/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java index dec1581b47..d871b12a53 100644 --- a/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/ClusterResolverLoadBalancer.java @@ -38,6 +38,7 @@ import io.grpc.internal.BackoffPolicy; import io.grpc.internal.ExponentialBackoffPolicy; import io.grpc.internal.ObjectPool; import io.grpc.internal.ServiceConfigUtil.PolicySelection; +import io.grpc.util.ForwardingLoadBalancerHelper; import io.grpc.util.GracefulSwitchLoadBalancer; import io.grpc.xds.ClusterImplLoadBalancerProvider.ClusterImplConfig; import io.grpc.xds.ClusterResolverLoadBalancerProvider.ClusterResolverConfig; @@ -49,6 +50,7 @@ import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import io.grpc.xds.LrsLoadBalancerProvider.LrsConfig; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedPolicySelection; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedTargetConfig; import io.grpc.xds.XdsClient.EdsResourceWatcher; @@ -166,7 +168,7 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { private LoadBalancer childLb; ClusterResolverLbState(Helper helper) { - this.helper = checkNotNull(helper, "helper"); + this.helper = new RefreshableHelper(checkNotNull(helper, "helper")); logger.log(XdsLogLevel.DEBUG, "New ClusterResolverLbState"); } @@ -214,7 +216,7 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { private void handleEndpointResourceUpdate() { List addresses = new ArrayList<>(); - Map priorityLbPolicies = new HashMap<>(); + Map priorityChildConfigs = new HashMap<>(); List priorities = new ArrayList<>(); // totally ordered priority list boolean allResolved = true; for (String cluster : clusters) { @@ -225,7 +227,7 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { } if (state.result != null) { addresses.addAll(state.result.addresses); - priorityLbPolicies.putAll(state.result.priorityLbPolicies); + priorityChildConfigs.putAll(state.result.priorityChildConfigs); priorities.addAll(state.result.priorities); } } @@ -243,7 +245,7 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { return; } PriorityLbConfig childConfig = - new PriorityLbConfig(Collections.unmodifiableMap(priorityLbPolicies), + new PriorityLbConfig(Collections.unmodifiableMap(priorityChildConfigs), Collections.unmodifiableList(priorities)); if (childLb == null) { childLb = lbRegistry.getProvider(PRIORITY_POLICY_NAME).newLoadBalancer(helper); @@ -273,6 +275,31 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { } } + /** + * Wires re-resolution requests from downstream LB policies with DNS resolver. + */ + private final class RefreshableHelper extends ForwardingLoadBalancerHelper { + private final Helper delegate; + + private RefreshableHelper(Helper delegate) { + this.delegate = checkNotNull(delegate, "delegate"); + } + + @Override + public void refreshNameResolution() { + for (ClusterState state : clusterStates.values()) { + if (state instanceof LogicalDnsClusterState) { + ((LogicalDnsClusterState) state).refresh(); + } + } + } + + @Override + protected Helper delegate() { + return delegate; + } + } + /** * Resolution state of an underlying cluster. */ @@ -390,13 +417,13 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { } List priorities = new ArrayList<>(prioritizedLocalityWeights.keySet()); Collections.sort(priorities); - Map priorityLbPolicies = - generateClusterPriorityLbPolicies(name, edsServiceName, lrsServerName, - maxConcurrentRequests, tlsContext, localityPickingPolicy, - endpointPickingPolicy, lbRegistry, prioritizedLocalityWeights, dropOverloads); + Map priorityChildConfigs = generatePriorityChildConfigs( + name, edsServiceName, lrsServerName, maxConcurrentRequests, tlsContext, + localityPickingPolicy, endpointPickingPolicy, true, lbRegistry, + prioritizedLocalityWeights, dropOverloads); status = Status.OK; resolved = true; - result = new ClusterResolutionResult(addresses, priorityLbPolicies, priorities); + result = new ClusterResolutionResult(addresses, priorityChildConfigs, priorities); handleEndpointResourceUpdate(); } } @@ -463,12 +490,23 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { resolver.start(new NameResolverListener()); } + void refresh() { + cancelBackoff(); + resolver.refresh(); + } + @Override void shutdown() { super.shutdown(); resolver.shutdown(); + cancelBackoff(); + } + + private void cancelBackoff() { if (scheduledRefresh != null) { scheduledRefresh.cancel(); + scheduledRefresh = null; + backoffPolicy = null; } } @@ -505,13 +543,13 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { lbRegistry.getProvider("pick_first"); PolicySelection endpointPickingPolicy = new PolicySelection(endpointPickingLbProvider, null); - PolicySelection priorityLbPolicy = - generateClusterPriorityLbPolicy(name, edsServiceName, lrsServerName, - maxConcurrentRequests, tlsContext, endpointPickingPolicy, lbRegistry, - LOGICAL_DNS_CLUSTER_LOCALITY, Collections.emptyList()); + PriorityChildConfig priorityChildConfig = generatePriorityChildConfig( + name, edsServiceName, lrsServerName, maxConcurrentRequests, tlsContext, + endpointPickingPolicy, false, lbRegistry, LOGICAL_DNS_CLUSTER_LOCALITY, + Collections.emptyList()); status = Status.OK; resolved = true; - result = new ClusterResolutionResult(addresses, priorityName, priorityLbPolicy); + result = new ClusterResolutionResult(addresses, priorityName, priorityChildConfig); handleEndpointResourceUpdate(); } } @@ -555,35 +593,35 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { private static class ClusterResolutionResult { // Endpoint addresses. private final List addresses; - // Load balancing policy (with config) for each priority in the cluster. - private final Map priorityLbPolicies; + // Config (include load balancing policy/config) for each priority in the cluster. + private final Map priorityChildConfigs; // List of priority names ordered in descending priorities. private final List priorities; ClusterResolutionResult(List addresses, String priority, - PolicySelection priorityLbPolicy) { - this(addresses, Collections.singletonMap(priority, priorityLbPolicy), + PriorityChildConfig config) { + this(addresses, Collections.singletonMap(priority, config), Collections.singletonList(priority)); } ClusterResolutionResult(List addresses, - Map priorityLbPolicies, List priorities) { + Map configs, List priorities) { this.addresses = addresses; - this.priorityLbPolicies = priorityLbPolicies; + this.priorityChildConfigs = configs; this.priorities = priorities; } } /** - * Generates the intra-priority LB policy for a single priority with the single given locality. + * Generates the config to be used in the priority LB policy for a single priority. * *

priority LB -> cluster_impl LB -> (lrs LB) -> pick_first */ - private PolicySelection generateClusterPriorityLbPolicy( + private static PriorityChildConfig generatePriorityChildConfig( String cluster, @Nullable String edsServiceName, @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext, - PolicySelection endpointPickingPolicy, LoadBalancerRegistry lbRegistry, - Locality locality, List dropOverloads) { + PolicySelection endpointPickingPolicy, boolean ignoreReresolution, + LoadBalancerRegistry lbRegistry, Locality locality, List dropOverloads) { PolicySelection localityLbPolicy = generateLocalityLbConfig(locality, cluster, edsServiceName, lrsServerName, endpointPickingPolicy, lbRegistry); @@ -592,23 +630,25 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { dropOverloads, localityLbPolicy, tlsContext); LoadBalancerProvider clusterImplLbProvider = lbRegistry.getProvider(XdsLbPolicies.CLUSTER_IMPL_POLICY_NAME); - return new PolicySelection(clusterImplLbProvider, clusterImplConfig); + PolicySelection clusterImplPolicy = + new PolicySelection(clusterImplLbProvider, clusterImplConfig); + return new PriorityChildConfig(clusterImplPolicy, ignoreReresolution); } /** - * Generates intra-priority LB policies (with config) for priorities in the cluster. + * Generates configs to be used in the priority LB policy for priorities in the cluster. * *

priority LB -> cluster_impl LB (one per priority) -> weighted_target LB * -> (lrs LB (one per locality)) -> round_robin */ - private static Map generateClusterPriorityLbPolicies( + private static Map generatePriorityChildConfigs( String cluster, @Nullable String edsServiceName, @Nullable String lrsServerName, @Nullable Long maxConcurrentRequests, @Nullable UpstreamTlsContext tlsContext, PolicySelection localityPickingPolicy, PolicySelection endpointPickingPolicy, - LoadBalancerRegistry lbRegistry, + boolean ignoreReresolution, LoadBalancerRegistry lbRegistry, Map> prioritizedLocalityWeights, List dropOverloads) { - Map policies = new HashMap<>(); + Map configs = new HashMap<>(); for (String priority : prioritizedLocalityWeights.keySet()) { WeightedTargetConfig localityPickingLbConfig = generateLocalityPickingLbConfig(cluster, edsServiceName, lrsServerName, @@ -622,9 +662,9 @@ final class ClusterResolverLoadBalancer extends LoadBalancer { lbRegistry.getProvider(XdsLbPolicies.CLUSTER_IMPL_POLICY_NAME); PolicySelection clusterImplPolicy = new PolicySelection(clusterImplLbProvider, clusterImplConfig); - policies.put(priority, clusterImplPolicy); + configs.put(priority, new PriorityChildConfig(clusterImplPolicy, ignoreReresolution)); } - return policies; + return configs; } private static WeightedTargetConfig generateLocalityPickingLbConfig( diff --git a/xds/src/main/java/io/grpc/xds/EdsLoadBalancer2.java b/xds/src/main/java/io/grpc/xds/EdsLoadBalancer2.java index 44243c2c90..ffcef06154 100644 --- a/xds/src/main/java/io/grpc/xds/EdsLoadBalancer2.java +++ b/xds/src/main/java/io/grpc/xds/EdsLoadBalancer2.java @@ -40,6 +40,7 @@ import io.grpc.xds.EnvoyProtoData.Locality; import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints; import io.grpc.xds.LrsLoadBalancerProvider.LrsConfig; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedPolicySelection; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedTargetConfig; import io.grpc.xds.XdsClient.EdsResourceWatcher; @@ -293,8 +294,8 @@ final class EdsLoadBalancer2 extends LoadBalancer { private void handleResourceUpdate() { // Populate configurations used by downstream LB policies from the freshest result. EdsConfig config = (EdsConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); - // Load balancing policy for each priority. - Map priorityChildPolicies = new HashMap<>(); + // Config for each priority. + Map priorityChildConfigs = new HashMap<>(); List priorities = new ArrayList<>(); for (Integer priority : prioritizedLocalityWeights.keySet()) { WeightedTargetConfig weightedTargetConfig = @@ -312,12 +313,12 @@ final class EdsLoadBalancer2 extends LoadBalancer { PolicySelection clusterImplPolicy = new PolicySelection(clusterImplLbProvider, clusterImplConfig); String priorityName = priorityName(priority); - priorityChildPolicies.put(priorityName, clusterImplPolicy); + priorityChildConfigs.put(priorityName, new PriorityChildConfig(clusterImplPolicy, true)); priorities.add(priorityName); } Collections.sort(priorities); PriorityLbConfig priorityLbConfig = - new PriorityLbConfig(Collections.unmodifiableMap(priorityChildPolicies), + new PriorityLbConfig(Collections.unmodifiableMap(priorityChildConfigs), Collections.unmodifiableList(priorities)); lb.handleResolvedAddresses( resolvedAddresses.toBuilder() diff --git a/xds/src/main/java/io/grpc/xds/PriorityLoadBalancer.java b/xds/src/main/java/io/grpc/xds/PriorityLoadBalancer.java index 6925836b1a..c8f63c4ad2 100644 --- a/xds/src/main/java/io/grpc/xds/PriorityLoadBalancer.java +++ b/xds/src/main/java/io/grpc/xds/PriorityLoadBalancer.java @@ -34,17 +34,22 @@ import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.util.ForwardingLoadBalancerHelper; import io.grpc.util.GracefulSwitchLoadBalancer; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import io.grpc.xds.XdsLogger.XdsLogLevel; import io.grpc.xds.XdsSubchannelPickers.ErrorPicker; -import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; -/** Load balancer for priority policy. */ +/** + * Load balancer for priority policy. A priority represents a logical entity within a + * cluster for load balancing purposes. + */ final class PriorityLoadBalancer extends LoadBalancer { private final Helper helper; private final SynchronizationContext syncContext; @@ -57,8 +62,10 @@ final class PriorityLoadBalancer extends LoadBalancer { // Following fields are only null initially. private ResolvedAddresses resolvedAddresses; + // List of priority names in order. private List priorityNames; - private Map priorityNameToIndex; + // Config for each priority. + private Map priorityConfigs; private ConnectivityState currentConnectivityState; private SubchannelPicker currentPicker; @@ -78,13 +85,10 @@ final class PriorityLoadBalancer extends LoadBalancer { PriorityLbConfig config = (PriorityLbConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); checkNotNull(config, "missing priority lb config"); priorityNames = config.priorities; - Map pToI = new HashMap<>(); - for (int i = 0; i < priorityNames.size(); i++) { - pToI.put(priorityNames.get(i), i); - } - priorityNameToIndex = Collections.unmodifiableMap(pToI); + priorityConfigs = config.childConfigs; + Set prioritySet = new HashSet<>(config.priorities); for (String priority : children.keySet()) { - if (!priorityNameToIndex.containsKey(priority)) { + if (!prioritySet.contains(priority)) { children.get(priority).deactivate(); } } @@ -125,7 +129,8 @@ final class PriorityLoadBalancer extends LoadBalancer { for (int i = 0; i < priorityNames.size(); i++) { String priority = priorityNames.get(i); if (!children.containsKey(priority)) { - ChildLbState child = new ChildLbState(priority); + ChildLbState child = + new ChildLbState(priority, priorityConfigs.get(priority).ignoreReresolution); children.put(priority, child); child.updateResolvedAddresses(); updateOverallState(CONNECTING, BUFFER_PICKER); @@ -180,9 +185,9 @@ final class PriorityLoadBalancer extends LoadBalancer { ConnectivityState connectivityState = CONNECTING; SubchannelPicker picker = BUFFER_PICKER; - ChildLbState(final String priority) { + ChildLbState(final String priority, boolean ignoreReresolution) { this.priority = priority; - childHelper = new ChildHelper(); + childHelper = new ChildHelper(ignoreReresolution); lb = new GracefulSwitchLoadBalancer(childHelper); class FailOverTask implements Runnable { @@ -253,7 +258,7 @@ final class PriorityLoadBalancer extends LoadBalancer { void updateResolvedAddresses() { PriorityLbConfig config = (PriorityLbConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); - PolicySelection childPolicySelection = config.childConfigs.get(priority); + PolicySelection childPolicySelection = config.childConfigs.get(priority).policySelection; LoadBalancerProvider lbProvider = childPolicySelection.getProvider(); String newPolicy = lbProvider.getPolicyName(); if (!newPolicy.equals(policy)) { @@ -268,6 +273,19 @@ final class PriorityLoadBalancer extends LoadBalancer { } final class ChildHelper extends ForwardingLoadBalancerHelper { + private final boolean ignoreReresolution; + + ChildHelper(boolean ignoreReresolution) { + this.ignoreReresolution = ignoreReresolution; + } + + @Override + public void refreshNameResolution() { + if (!ignoreReresolution) { + delegate().refreshNameResolution(); + } + } + @Override public void updateBalancingState(final ConnectivityState newState, final SubchannelPicker newPicker) { diff --git a/xds/src/main/java/io/grpc/xds/PriorityLoadBalancerProvider.java b/xds/src/main/java/io/grpc/xds/PriorityLoadBalancerProvider.java index 88297108de..dff1e77840 100644 --- a/xds/src/main/java/io/grpc/xds/PriorityLoadBalancerProvider.java +++ b/xds/src/main/java/io/grpc/xds/PriorityLoadBalancerProvider.java @@ -61,10 +61,10 @@ public final class PriorityLoadBalancerProvider extends LoadBalancerProvider { } static final class PriorityLbConfig { - final Map childConfigs; + final Map childConfigs; final List priorities; - PriorityLbConfig(Map childConfigs, List priorities) { + PriorityLbConfig(Map childConfigs, List priorities) { this.childConfigs = Collections.unmodifiableMap(checkNotNull(childConfigs, "childConfigs")); this.priorities = Collections.unmodifiableList(checkNotNull(priorities, "priorities")); checkArgument(!priorities.isEmpty(), "priority list is empty"); @@ -86,5 +86,15 @@ public final class PriorityLoadBalancerProvider extends LoadBalancerProvider { .add("priorities", priorities) .toString(); } + + static final class PriorityChildConfig { + final PolicySelection policySelection; + final boolean ignoreReresolution; + + PriorityChildConfig(PolicySelection policySelection, boolean ignoreReresolution) { + this.policySelection = checkNotNull(policySelection, "policySelection"); + this.ignoreReresolution = ignoreReresolution; + } + } } } diff --git a/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java index 875c1818c8..f094159996 100644 --- a/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/ClusterResolverLoadBalancerTest.java @@ -53,6 +53,7 @@ import io.grpc.Status.Code; import io.grpc.SynchronizationContext; import io.grpc.internal.BackoffPolicy; import io.grpc.internal.FakeClock; +import io.grpc.internal.FakeClock.ScheduledTask; import io.grpc.internal.GrpcUtil; import io.grpc.internal.ObjectPool; import io.grpc.internal.ServiceConfigUtil.PolicySelection; @@ -66,6 +67,7 @@ import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import io.grpc.xds.LrsLoadBalancerProvider.LrsConfig; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedPolicySelection; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedTargetConfig; import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; @@ -151,7 +153,9 @@ public class ClusterResolverLoadBalancerTest { @Mock private BackoffPolicy.Provider backoffPolicyProvider; @Mock - private BackoffPolicy backoffPolicy; + private BackoffPolicy backoffPolicy1; + @Mock + private BackoffPolicy backoffPolicy2; @Captor private ArgumentCaptor pickerCaptor; private int xdsClientRefs; @@ -181,9 +185,11 @@ public class ClusterResolverLoadBalancerTest { when(helper.getSynchronizationContext()).thenReturn(syncContext); when(helper.getScheduledExecutorService()).thenReturn(fakeClock.getScheduledExecutorService()); when(helper.getAuthority()).thenReturn(AUTHORITY); - when(backoffPolicyProvider.get()).thenReturn(backoffPolicy); - when(backoffPolicy.nextBackoffNanos()) + when(backoffPolicyProvider.get()).thenReturn(backoffPolicy1, backoffPolicy2); + when(backoffPolicy1.nextBackoffNanos()) .thenReturn(TimeUnit.SECONDS.toNanos(1L), TimeUnit.SECONDS.toNanos(10L)); + when(backoffPolicy2.nextBackoffNanos()) + .thenReturn(TimeUnit.SECONDS.toNanos(5L), TimeUnit.SECONDS.toNanos(50L)); loadBalancer = new ClusterResolverLoadBalancer(helper, lbRegistry, backoffPolicyProvider); } @@ -193,6 +199,7 @@ public class ClusterResolverLoadBalancerTest { assertThat(resolvers).isEmpty(); assertThat(xdsClient.watchers).isEmpty(); assertThat(xdsClientRefs).isEqualTo(0); + assertThat(fakeClock.getPendingTasks()).isEmpty(); } @Test @@ -224,10 +231,12 @@ public class ClusterResolverLoadBalancerTest { assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME); PriorityLbConfig priorityLbConfig = (PriorityLbConfig) childBalancer.config; assertThat(priorityLbConfig.priorities).containsExactly(priority1, priority2).inOrder(); - PolicySelection priorityChildPolicy = priorityLbConfig.childConfigs.get(priority1); - assertThat(priorityChildPolicy.getProvider().getPolicyName()) + PriorityChildConfig priorityChildConfig = priorityLbConfig.childConfigs.get(priority1); + assertThat(priorityChildConfig.ignoreReresolution).isTrue(); + assertThat(priorityChildConfig.policySelection.getProvider().getPolicyName()) .isEqualTo(CLUSTER_IMPL_POLICY_NAME); - ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildPolicy.getConfig(); + ClusterImplConfig clusterImplConfig = + (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); assertClusterImplConfig(clusterImplConfig, CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_NAME, 200L, tlsContext, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); WeightedTargetConfig weightedTargetConfig = @@ -239,10 +248,11 @@ public class ClusterResolverLoadBalancerTest { assertLrsConfig((LrsConfig) target.policySelection.getConfig(), CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_NAME, locality1, "round_robin"); - priorityChildPolicy = priorityLbConfig.childConfigs.get(priority2); - assertThat(priorityChildPolicy.getProvider().getPolicyName()) + priorityChildConfig = priorityLbConfig.childConfigs.get(priority2); + assertThat(priorityChildConfig.ignoreReresolution).isTrue(); + assertThat(priorityChildConfig.policySelection.getProvider().getPolicyName()) .isEqualTo(CLUSTER_IMPL_POLICY_NAME); - clusterImplConfig = (ClusterImplConfig) priorityChildPolicy.getConfig(); + clusterImplConfig = (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); assertClusterImplConfig(clusterImplConfig, CLUSTER2, EDS_SERVICE_NAME2, LRS_SERVER_NAME, 200L, tlsContext, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); weightedTargetConfig = (WeightedTargetConfig) clusterImplConfig.childPolicy.getConfig(); @@ -269,10 +279,11 @@ public class ClusterResolverLoadBalancerTest { assertThat(priorityLbConfig.priorities) .containsExactly(priority3, priority1, priority2).inOrder(); - priorityChildPolicy = priorityLbConfig.childConfigs.get(priority3); - assertThat(priorityChildPolicy.getProvider().getPolicyName()) + priorityChildConfig = priorityLbConfig.childConfigs.get(priority3); + assertThat(priorityChildConfig.ignoreReresolution).isTrue(); + assertThat(priorityChildConfig.policySelection.getProvider().getPolicyName()) .isEqualTo(CLUSTER_IMPL_POLICY_NAME); - clusterImplConfig = (ClusterImplConfig) priorityChildPolicy.getConfig(); + clusterImplConfig = (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); assertClusterImplConfig(clusterImplConfig, CLUSTER1, EDS_SERVICE_NAME1, LRS_SERVER_NAME, 100L, tlsContext, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); weightedTargetConfig = (WeightedTargetConfig) clusterImplConfig.childPolicy.getConfig(); @@ -374,8 +385,9 @@ public class ClusterResolverLoadBalancerTest { FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); PriorityLbConfig priorityLbConfig = (PriorityLbConfig) childBalancer.config; - PolicySelection priorityChildPolicy = priorityLbConfig.childConfigs.get(priority); - ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildPolicy.getConfig(); + PriorityChildConfig priorityChildConfig = priorityLbConfig.childConfigs.get(priority); + ClusterImplConfig clusterImplConfig = + (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); WeightedTargetConfig weightedTargetConfig = (WeightedTargetConfig) clusterImplConfig.childPolicy.getConfig(); assertThat(weightedTargetConfig.targets.keySet()).containsExactly(locality2.toString()); @@ -439,10 +451,12 @@ public class ClusterResolverLoadBalancerTest { assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME); PriorityLbConfig priorityLbConfig = (PriorityLbConfig) childBalancer.config; String priority = Iterables.getOnlyElement(priorityLbConfig.priorities); - PolicySelection priorityChildPolicy = priorityLbConfig.childConfigs.get(priority); - assertThat(priorityChildPolicy.getProvider().getPolicyName()) + PriorityChildConfig priorityChildConfig = priorityLbConfig.childConfigs.get(priority); + assertThat(priorityChildConfig.ignoreReresolution).isFalse(); + assertThat(priorityChildConfig.policySelection.getProvider().getPolicyName()) .isEqualTo(CLUSTER_IMPL_POLICY_NAME); - ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildPolicy.getConfig(); + ClusterImplConfig clusterImplConfig = + (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); assertClusterImplConfig(clusterImplConfig, CLUSTER_DNS, null, LRS_SERVER_NAME, 100L, null, Collections.emptyList(), LRS_POLICY_NAME); LrsConfig lrsConfig = (LrsConfig) clusterImplConfig.childPolicy.getConfig(); @@ -451,9 +465,23 @@ public class ClusterResolverLoadBalancerTest { assertAddressesEqual(Arrays.asList(endpoint1, endpoint2), childBalancer.addresses); } + @Test + public void onlyLogicalDnsCluster_handleRefreshNameResolution() { + deliverConfigWithSingleLogicalDnsCluster(); + EquivalentAddressGroup endpoint1 = makeAddress("endpoint-addr-1"); + EquivalentAddressGroup endpoint2 = makeAddress("endpoint-addr-2"); + FakeNameResolver resolver = Iterables.getOnlyElement(resolvers); + resolver.deliverEndpointAddresses(Arrays.asList(endpoint1, endpoint2)); + assertThat(resolver.refreshCount).isEqualTo(0); + FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); + childBalancer.helper.refreshNameResolution(); + assertThat(resolver.refreshCount).isEqualTo(1); + } + @Test public void onlyLogicalDnsCluster_resolutionError_backoffAndRefresh() { - InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider, backoffPolicy); + InOrder inOrder = Mockito.inOrder(helper, backoffPolicyProvider, + backoffPolicy1, backoffPolicy2); deliverConfigWithSingleLogicalDnsCluster(); FakeNameResolver resolver = Iterables.getOnlyElement(resolvers); Status error = Status.UNAVAILABLE.withDescription("cannot reach DNS server"); @@ -463,7 +491,7 @@ public class ClusterResolverLoadBalancerTest { assertPicker(pickerCaptor.getValue(), error, null); assertThat(resolver.refreshCount).isEqualTo(0); inOrder.verify(backoffPolicyProvider).get(); - inOrder.verify(backoffPolicy).nextBackoffNanos(); + inOrder.verify(backoffPolicy1).nextBackoffNanos(); assertThat(fakeClock.getPendingTasks()).hasSize(1); assertThat(Iterables.getOnlyElement(fakeClock.getPendingTasks()).getDelay(TimeUnit.SECONDS)) .isEqualTo(1L); @@ -474,7 +502,7 @@ public class ClusterResolverLoadBalancerTest { resolver.deliverError(error); inOrder.verify(helper).updateBalancingState( eq(ConnectivityState.TRANSIENT_FAILURE), pickerCaptor.capture()); - inOrder.verify(backoffPolicy).nextBackoffNanos(); + inOrder.verify(backoffPolicy1).nextBackoffNanos(); assertPicker(pickerCaptor.getValue(), error, null); assertThat(fakeClock.getPendingTasks()).hasSize(1); assertThat(Iterables.getOnlyElement(fakeClock.getPendingTasks()).getDelay(TimeUnit.SECONDS)) @@ -494,6 +522,42 @@ public class ClusterResolverLoadBalancerTest { inOrder.verifyNoMoreInteractions(); } + @Test + public void onlyLogicalDnsCluster_refreshNameResolutionRaceWithResolutionError() { + InOrder inOrder = Mockito.inOrder(backoffPolicyProvider, backoffPolicy1, backoffPolicy2); + deliverConfigWithSingleLogicalDnsCluster(); + EquivalentAddressGroup endpoint = makeAddress("endpoint-addr"); + FakeNameResolver resolver = Iterables.getOnlyElement(resolvers); + resolver.deliverEndpointAddresses(Collections.singletonList(endpoint)); + FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); + assertAddressesEqual(Collections.singletonList(endpoint), childBalancer.addresses); + assertThat(resolver.refreshCount).isEqualTo(0); + + childBalancer.helper.refreshNameResolution(); + assertThat(resolver.refreshCount).isEqualTo(1); + resolver.deliverError(Status.UNAVAILABLE.withDescription("I am lost")); + inOrder.verify(backoffPolicyProvider).get(); + inOrder.verify(backoffPolicy1).nextBackoffNanos(); + assertThat(fakeClock.getPendingTasks()).hasSize(1); + ScheduledTask task = Iterables.getOnlyElement(fakeClock.getPendingTasks()); + assertThat(task.getDelay(TimeUnit.SECONDS)).isEqualTo(1L); + + fakeClock.forwardTime( 100L, TimeUnit.MILLISECONDS); + childBalancer.helper.refreshNameResolution(); + assertThat(resolver.refreshCount).isEqualTo(2); + assertThat(task.isCancelled()).isTrue(); + assertThat(fakeClock.getPendingTasks()).isEmpty(); + resolver.deliverError(Status.UNAVAILABLE.withDescription("I am still lost")); + inOrder.verify(backoffPolicyProvider).get(); // active refresh resets backoff sequence + inOrder.verify(backoffPolicy2).nextBackoffNanos(); + task = Iterables.getOnlyElement(fakeClock.getPendingTasks()); + assertThat(task.getDelay(TimeUnit.SECONDS)).isEqualTo(5L); + + fakeClock.forwardTime(5L, TimeUnit.SECONDS); + assertThat(resolver.refreshCount).isEqualTo(3); + inOrder.verifyNoMoreInteractions(); + } + private void deliverConfigWithSingleLogicalDnsCluster() { DiscoveryMechanism instance = DiscoveryMechanism.forLogicalDns(CLUSTER_DNS, LRS_SERVER_NAME, 100L, null); diff --git a/xds/src/test/java/io/grpc/xds/EdsLoadBalancer2Test.java b/xds/src/test/java/io/grpc/xds/EdsLoadBalancer2Test.java index 5ab7a7a8e1..defc6cec43 100644 --- a/xds/src/test/java/io/grpc/xds/EdsLoadBalancer2Test.java +++ b/xds/src/test/java/io/grpc/xds/EdsLoadBalancer2Test.java @@ -55,6 +55,7 @@ import io.grpc.xds.EnvoyProtoData.LocalityLbEndpoints; import io.grpc.xds.EnvoyServerProtoData.UpstreamTlsContext; import io.grpc.xds.LrsLoadBalancerProvider.LrsConfig; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedPolicySelection; import io.grpc.xds.WeightedTargetLoadBalancerProvider.WeightedTargetConfig; import io.grpc.xds.internal.sds.CommonTlsContextTestsUtil; @@ -170,9 +171,12 @@ public class EdsLoadBalancer2Test { assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME); PriorityLbConfig config = (PriorityLbConfig) childBalancer.config; assertThat(config.priorities).containsExactly("priority1", "priority2"); - PolicySelection priorityChild1 = config.childConfigs.get("priority1"); - assertThat(priorityChild1.getProvider().getPolicyName()).isEqualTo(CLUSTER_IMPL_POLICY_NAME); - ClusterImplConfig clusterImplConfig1 = (ClusterImplConfig) priorityChild1.getConfig(); + PriorityChildConfig priorityChild1 = config.childConfigs.get("priority1"); + assertThat(priorityChild1.ignoreReresolution).isTrue(); + assertThat(priorityChild1.policySelection.getProvider().getPolicyName()) + .isEqualTo(CLUSTER_IMPL_POLICY_NAME); + ClusterImplConfig clusterImplConfig1 = + (ClusterImplConfig) priorityChild1.policySelection.getConfig(); assertClusterImplConfig(clusterImplConfig1, CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, null, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); PolicySelection weightedTargetPolicy1 = clusterImplConfig1.childPolicy; @@ -193,9 +197,12 @@ public class EdsLoadBalancer2Test { assertLrsConfig((LrsConfig) target2.policySelection.getConfig(), CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, locality2, "round_robin"); - PolicySelection priorityChild2 = config.childConfigs.get("priority2"); - assertThat(priorityChild2.getProvider().getPolicyName()).isEqualTo(CLUSTER_IMPL_POLICY_NAME); - ClusterImplConfig clusterImplConfig2 = (ClusterImplConfig) priorityChild2.getConfig(); + PriorityChildConfig priorityChild2 = config.childConfigs.get("priority2"); + assertThat(priorityChild2.ignoreReresolution).isTrue(); + assertThat(priorityChild2.policySelection.getProvider().getPolicyName()) + .isEqualTo(CLUSTER_IMPL_POLICY_NAME); + ClusterImplConfig clusterImplConfig2 = + (ClusterImplConfig) priorityChild2.policySelection.getConfig(); assertClusterImplConfig(clusterImplConfig2, CLUSTER, EDS_SERVICE_NAME, LRS_SERVER_NAME, null, Collections.emptyList(), WEIGHTED_TARGET_POLICY_NAME); PolicySelection weightedTargetPolicy2 = clusterImplConfig2.childPolicy; @@ -244,9 +251,12 @@ public class EdsLoadBalancer2Test { assertThat(childBalancer.name).isEqualTo(PRIORITY_POLICY_NAME); PriorityLbConfig config = (PriorityLbConfig) childBalancer.config; assertThat(config.priorities).containsExactly("priority1"); - PolicySelection priorityChild = config.childConfigs.get("priority1"); - assertThat(priorityChild.getProvider().getPolicyName()).isEqualTo(CLUSTER_IMPL_POLICY_NAME); - ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChild.getConfig(); + PriorityChildConfig priorityChild = config.childConfigs.get("priority1"); + assertThat(priorityChild.ignoreReresolution).isTrue(); + assertThat(priorityChild.policySelection.getProvider().getPolicyName()) + .isEqualTo(CLUSTER_IMPL_POLICY_NAME); + ClusterImplConfig clusterImplConfig = + (ClusterImplConfig) priorityChild.policySelection.getConfig(); PolicySelection weightedTargetPolicy = clusterImplConfig.childPolicy; assertThat(weightedTargetPolicy.getProvider().getPolicyName()) .isEqualTo(WEIGHTED_TARGET_POLICY_NAME); @@ -276,8 +286,10 @@ public class EdsLoadBalancer2Test { config = (PriorityLbConfig) childBalancer.config; assertThat(config.priorities).containsExactly("priority1"); priorityChild = config.childConfigs.get("priority1"); - assertThat(priorityChild.getProvider().getPolicyName()).isEqualTo(CLUSTER_IMPL_POLICY_NAME); - clusterImplConfig = (ClusterImplConfig) priorityChild.getConfig(); + assertThat(priorityChild.ignoreReresolution).isTrue(); + assertThat(priorityChild.policySelection.getProvider().getPolicyName()) + .isEqualTo(CLUSTER_IMPL_POLICY_NAME); + clusterImplConfig = (ClusterImplConfig) priorityChild.policySelection.getConfig(); weightedTargetPolicy = clusterImplConfig.childPolicy; assertThat(weightedTargetPolicy.getProvider().getPolicyName()) .isEqualTo(WEIGHTED_TARGET_POLICY_NAME); @@ -355,8 +367,9 @@ public class EdsLoadBalancer2Test { FakeLoadBalancer childBalancer = Iterables.getOnlyElement(childBalancers); PriorityLbConfig config = (PriorityLbConfig) childBalancer.config; - PolicySelection priorityChildPolicy = config.childConfigs.get("priority1"); - ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildPolicy.getConfig(); + PriorityChildConfig priorityChildConfig = config.childConfigs.get("priority1"); + ClusterImplConfig clusterImplConfig = + (ClusterImplConfig) priorityChildConfig.policySelection.getConfig(); WeightedTargetConfig weightedTargetConfig = (WeightedTargetConfig) clusterImplConfig.childPolicy.getConfig(); assertThat(weightedTargetConfig.targets.keySet()).containsExactly(locality2.toString()); @@ -484,7 +497,7 @@ public class EdsLoadBalancer2Test { } private Long populateMaxConcurrentRequests(PriorityLbConfig config, String priority) { - PolicySelection priorityChildConfig = config.childConfigs.get(priority); + PolicySelection priorityChildConfig = config.childConfigs.get(priority).policySelection; ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildConfig.getConfig(); return clusterImplConfig.maxConcurrentRequests; } @@ -510,7 +523,7 @@ public class EdsLoadBalancer2Test { } private UpstreamTlsContext populateTlsContext(PriorityLbConfig config, String priority) { - PolicySelection priorityChildConfig = config.childConfigs.get(priority); + PolicySelection priorityChildConfig = config.childConfigs.get(priority).policySelection; ClusterImplConfig clusterImplConfig = (ClusterImplConfig) priorityChildConfig.getConfig(); return clusterImplConfig.tlsContext; } diff --git a/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerProviderTest.java b/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerProviderTest.java index a7abcf822e..5d96ed8794 100644 --- a/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerProviderTest.java +++ b/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerProviderTest.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap; import io.grpc.LoadBalancerProvider; import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import java.util.List; import java.util.Map; import org.junit.Rule; @@ -40,8 +41,11 @@ public class PriorityLoadBalancerProviderTest { @SuppressWarnings("ExpectedExceptionChecker") @Test public void priorityLbConfig_emptyPriorities() { - Map childConfigs = - ImmutableMap.of("p0", new PolicySelection(mock(LoadBalancerProvider.class), null)); + Map childConfigs = + ImmutableMap.of( + "p0", + new PriorityChildConfig( + new PolicySelection(mock(LoadBalancerProvider.class), null), true)); List priorities = ImmutableList.of(); thrown.expect(IllegalArgumentException.class); @@ -51,8 +55,11 @@ public class PriorityLoadBalancerProviderTest { @SuppressWarnings("ExpectedExceptionChecker") @Test public void priorityLbConfig_missingChildConfig() { - Map childConfigs = - ImmutableMap.of("p1", new PolicySelection(mock(LoadBalancerProvider.class), null)); + Map childConfigs = + ImmutableMap.of( + "p1", + new PriorityChildConfig( + new PolicySelection(mock(LoadBalancerProvider.class), null), true)); List priorities = ImmutableList.of("p0", "p1"); thrown.expect(IllegalArgumentException.class); diff --git a/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerTest.java b/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerTest.java index fb522193ae..2a030e3d30 100644 --- a/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerTest.java +++ b/xds/src/test/java/io/grpc/xds/PriorityLoadBalancerTest.java @@ -47,6 +47,7 @@ import io.grpc.internal.FakeClock; import io.grpc.internal.ServiceConfigUtil.PolicySelection; import io.grpc.internal.TestUtils.StandardLoadBalancerProvider; import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig; +import io.grpc.xds.PriorityLoadBalancerProvider.PriorityLbConfig.PriorityChildConfig; import io.grpc.xds.XdsSubchannelPickers.ErrorPicker; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -137,14 +138,18 @@ public class PriorityLoadBalancerTest { Attributes attributes = Attributes.newBuilder().set(Attributes.Key.create("fakeKey"), "fakeValue").build(); Object fooConfig0 = new Object(); - PolicySelection fooPolicy0 = new PolicySelection(fooLbProvider, fooConfig0); + PriorityChildConfig priorityChildConfig0 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, fooConfig0), true); Object barConfig0 = new Object(); - PolicySelection barPolicy0 = new PolicySelection(barLbProvider, barConfig0); + PriorityChildConfig priorityChildConfig1 = + new PriorityChildConfig(new PolicySelection(barLbProvider, barConfig0), true); Object fooConfig1 = new Object(); - PolicySelection fooPolicy1 = new PolicySelection(fooLbProvider, fooConfig1); + PriorityChildConfig priorityChildConfig2 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, fooConfig1), true); PriorityLbConfig priorityLbConfig = new PriorityLbConfig( - ImmutableMap.of("p0", fooPolicy0, "p1", barPolicy0, "p2", fooPolicy1), + ImmutableMap.of("p0", priorityChildConfig0, "p1", priorityChildConfig1, + "p2", priorityChildConfig2), ImmutableList.of("p0", "p1", "p2")); priorityLb.handleResolvedAddresses( ResolvedAddresses.newBuilder() @@ -190,9 +195,11 @@ public class PriorityLoadBalancerTest { newEag = AddressFilter.setPathFilter(newEag, ImmutableList.of("p1")); List newAddresses = ImmutableList.of(newEag); Object newBarConfig = new Object(); - PolicySelection newBarPolicy = new PolicySelection(barLbProvider, newBarConfig); PriorityLbConfig newPriorityLbConfig = - new PriorityLbConfig(ImmutableMap.of("p1", newBarPolicy), ImmutableList.of("p1")); + new PriorityLbConfig( + ImmutableMap.of("p1", + new PriorityChildConfig(new PolicySelection(barLbProvider, newBarConfig), true)), + ImmutableList.of("p1")); priorityLb.handleResolvedAddresses( ResolvedAddresses.newBuilder() .setAddresses(newAddresses) @@ -217,12 +224,14 @@ public class PriorityLoadBalancerTest { @Test public void handleNameResolutionError() { Object fooConfig0 = new Object(); - PolicySelection fooPolicy0 = new PolicySelection(fooLbProvider, fooConfig0); + PriorityChildConfig priorityChildConfig0 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, fooConfig0), true); Object fooConfig1 = new Object(); - PolicySelection fooPolicy1 = new PolicySelection(fooLbProvider, fooConfig1); + PriorityChildConfig priorityChildConfig1 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, fooConfig1), true); PriorityLbConfig priorityLbConfig = - new PriorityLbConfig(ImmutableMap.of("p0", fooPolicy0), ImmutableList.of("p0")); + new PriorityLbConfig(ImmutableMap.of("p0", priorityChildConfig0), ImmutableList.of("p0")); priorityLb.handleResolvedAddresses( ResolvedAddresses.newBuilder() .setAddresses(ImmutableList.of()) @@ -234,7 +243,7 @@ public class PriorityLoadBalancerTest { verify(fooLb0).handleNameResolutionError(status); priorityLbConfig = - new PriorityLbConfig(ImmutableMap.of("p1", fooPolicy1), ImmutableList.of("p1")); + new PriorityLbConfig(ImmutableMap.of("p1", priorityChildConfig1), ImmutableList.of("p1")); priorityLb.handleResolvedAddresses( ResolvedAddresses.newBuilder() .setAddresses(ImmutableList.of()) @@ -253,13 +262,18 @@ public class PriorityLoadBalancerTest { @Test public void typicalPriorityFailOverFlow() { - PolicySelection policy0 = new PolicySelection(fooLbProvider, new Object()); - PolicySelection policy1 = new PolicySelection(fooLbProvider, new Object()); - PolicySelection policy2 = new PolicySelection(fooLbProvider, new Object()); - PolicySelection policy3 = new PolicySelection(fooLbProvider, new Object()); + PriorityChildConfig priorityChildConfig0 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, new Object()), true); + PriorityChildConfig priorityChildConfig1 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, new Object()), true); + PriorityChildConfig priorityChildConfig2 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, new Object()), true); + PriorityChildConfig priorityChildConfig3 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, new Object()), true); PriorityLbConfig priorityLbConfig = new PriorityLbConfig( - ImmutableMap.of("p0", policy0, "p1", policy1, "p2", policy2, "p3", policy3), + ImmutableMap.of("p0", priorityChildConfig0, "p1", priorityChildConfig1, + "p2", priorityChildConfig2, "p3", priorityChildConfig3), ImmutableList.of("p0", "p1", "p2", "p3")); priorityLb.handleResolvedAddresses( ResolvedAddresses.newBuilder() @@ -379,6 +393,33 @@ public class PriorityLoadBalancerTest { verify(balancer3).shutdown(); } + @Test + public void bypassReresolutionRequestsIfConfiged() { + PriorityChildConfig priorityChildConfig0 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, new Object()), true); + PriorityChildConfig priorityChildConfig1 = + new PriorityChildConfig(new PolicySelection(fooLbProvider, new Object()), false); + PriorityLbConfig priorityLbConfig = + new PriorityLbConfig( + ImmutableMap.of("p0", priorityChildConfig0, "p1", priorityChildConfig1), + ImmutableList.of("p0", "p1")); + priorityLb.handleResolvedAddresses( + ResolvedAddresses.newBuilder() + .setAddresses(ImmutableList.of()) + .setLoadBalancingPolicyConfig(priorityLbConfig) + .build()); + Helper priorityHelper0 = Iterables.getOnlyElement(fooHelpers); // priority p0 + priorityHelper0.refreshNameResolution(); + verify(helper, never()).refreshNameResolution(); + + // Simulate fallback to priority p1. + priorityHelper0.updateBalancingState(TRANSIENT_FAILURE, new ErrorPicker(Status.UNAVAILABLE)); + assertThat(fooHelpers).hasSize(2); + Helper priorityHelper1 = Iterables.getLast(fooHelpers); + priorityHelper1.refreshNameResolution(); + verify(helper).refreshNameResolution(); + } + private void assertLatestConnectivityState(ConnectivityState expectedState) { verify(helper, atLeastOnce()) .updateBalancingState(connectivityStateCaptor.capture(), pickerCaptor.capture());