Skip to content

Commit 1d5aff5

Browse files
authored
xds: Allow child of cluster_impl LB to change (#10091) (#10103)
Under normal conditions the child LB of `ClusterImplLoadBalancer` does not fluctuate, based on the field used to configure load balancing in the xDS `Cluster` proto it is either: 1. `WrrLocalityLoadBalancer` if the newer `load_balancing_policy` field is used 2. `WeightedTargetLoadBalancer` if the legacy `lb_policy` field is used `ClusterImplLoadBalancer` currently assumes that this child does not change and so does not change the child LB when the name resolver sends an update. If the control plane does switch to using a different field for LB config, that update will have an LB config meant for the other child LB type. This will result in a ClassCastException and a channel panic. To address this, `ClusterImplLoadBalancer` will now use `GracefulSwitchLoadBalancer` and makes sure if the child policy changes the correct LB implementation is switched to.
1 parent b256818 commit 1d5aff5

File tree

2 files changed

+45
-7
lines changed

2 files changed

+45
-7
lines changed

xds/src/main/java/io/grpc/xds/ClusterImplLoadBalancer.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import io.grpc.internal.ObjectPool;
3636
import io.grpc.util.ForwardingLoadBalancerHelper;
3737
import io.grpc.util.ForwardingSubchannel;
38+
import io.grpc.util.GracefulSwitchLoadBalancer;
3839
import io.grpc.xds.Bootstrapper.ServerInfo;
3940
import io.grpc.xds.ClusterImplLoadBalancerProvider.ClusterImplConfig;
4041
import io.grpc.xds.Endpoints.DropOverload;
@@ -87,7 +88,7 @@ final class ClusterImplLoadBalancer extends LoadBalancer {
8788
private CallCounterProvider callCounterProvider;
8889
private ClusterDropStats dropStats;
8990
private ClusterImplLbHelper childLbHelper;
90-
private LoadBalancer childLb;
91+
private GracefulSwitchLoadBalancer childSwitchLb;
9192

9293
ClusterImplLoadBalancer(Helper helper) {
9394
this(helper, ThreadSafeRandomImpl.instance);
@@ -120,7 +121,7 @@ public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
120121
childLbHelper = new ClusterImplLbHelper(
121122
callCounterProvider.getOrCreate(config.cluster, config.edsServiceName),
122123
config.lrsServerInfo);
123-
childLb = config.childPolicy.getProvider().newLoadBalancer(childLbHelper);
124+
childSwitchLb = new GracefulSwitchLoadBalancer(childLbHelper);
124125
// Assume load report server does not change throughout cluster lifetime.
125126
if (config.lrsServerInfo != null) {
126127
dropStats = xdsClient.addClusterDropStats(config.lrsServerInfo, cluster, edsServiceName);
@@ -129,7 +130,9 @@ public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
129130
childLbHelper.updateDropPolicies(config.dropCategories);
130131
childLbHelper.updateMaxConcurrentRequests(config.maxConcurrentRequests);
131132
childLbHelper.updateSslContextProviderSupplier(config.tlsContext);
132-
childLb.handleResolvedAddresses(
133+
134+
childSwitchLb.switchTo(config.childPolicy.getProvider());
135+
childSwitchLb.handleResolvedAddresses(
133136
resolvedAddresses.toBuilder()
134137
.setAttributes(attributes)
135138
.setLoadBalancingPolicyConfig(config.childPolicy.getConfig())
@@ -139,8 +142,8 @@ public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
139142

140143
@Override
141144
public void handleNameResolutionError(Status error) {
142-
if (childLb != null) {
143-
childLb.handleNameResolutionError(error);
145+
if (childSwitchLb != null) {
146+
childSwitchLb.handleNameResolutionError(error);
144147
} else {
145148
helper.updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, new ErrorPicker(error));
146149
}
@@ -151,8 +154,8 @@ public void shutdown() {
151154
if (dropStats != null) {
152155
dropStats.release();
153156
}
154-
if (childLb != null) {
155-
childLb.shutdown();
157+
if (childSwitchLb != null) {
158+
childSwitchLb.shutdown();
156159
if (childLbHelper != null) {
157160
childLbHelper.updateSslContextProviderSupplier(null);
158161
childLbHelper = null;

xds/src/test/java/io/grpc/xds/ClusterImplLoadBalancerTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,41 @@ public void handleResolvedAddresses_propagateToChildPolicy() {
165165
.isSameInstanceAs(xdsClientPool);
166166
}
167167

168+
/**
169+
* If the control plane switches from using the legacy lb_policy field in the xDS Cluster proto
170+
* to the newer load_balancing_policy then the child policy can switch from weighted_target to
171+
* xds_wrr_locality (this could happen the opposite way as well). This test assures that this
172+
* results in the child LB changing if this were to happen. If this is not done correctly the new
173+
* configuration would be given to the old LB implementation which would cause a channel panic.
174+
*/
175+
@Test
176+
public void handleResolvedAddresses_childPolicyChanges() {
177+
FakeLoadBalancerProvider weightedTargetProvider =
178+
new FakeLoadBalancerProvider(XdsLbPolicies.WEIGHTED_TARGET_POLICY_NAME);
179+
Object weightedTargetConfig = new Object();
180+
ClusterImplConfig configWithWeightedTarget = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME,
181+
LRS_SERVER_INFO,
182+
null, Collections.<DropOverload>emptyList(),
183+
new PolicySelection(weightedTargetProvider, weightedTargetConfig), null);
184+
EquivalentAddressGroup endpoint = makeAddress("endpoint-addr", locality);
185+
deliverAddressesAndConfig(Collections.singletonList(endpoint), configWithWeightedTarget);
186+
FakeLoadBalancer childBalancer = Iterables.getOnlyElement(downstreamBalancers);
187+
assertThat(childBalancer.name).isEqualTo(XdsLbPolicies.WEIGHTED_TARGET_POLICY_NAME);
188+
assertThat(childBalancer.config).isSameInstanceAs(weightedTargetConfig);
189+
190+
FakeLoadBalancerProvider wrrLocalityProvider =
191+
new FakeLoadBalancerProvider(XdsLbPolicies.WRR_LOCALITY_POLICY_NAME);
192+
Object wrrLocalityConfig = new Object();
193+
ClusterImplConfig configWithWrrLocality = new ClusterImplConfig(CLUSTER, EDS_SERVICE_NAME,
194+
LRS_SERVER_INFO,
195+
null, Collections.<DropOverload>emptyList(),
196+
new PolicySelection(wrrLocalityProvider, wrrLocalityConfig), null);
197+
deliverAddressesAndConfig(Collections.singletonList(endpoint), configWithWrrLocality);
198+
childBalancer = Iterables.getOnlyElement(downstreamBalancers);
199+
assertThat(childBalancer.name).isEqualTo(XdsLbPolicies.WRR_LOCALITY_POLICY_NAME);
200+
assertThat(childBalancer.config).isSameInstanceAs(wrrLocalityConfig);
201+
}
202+
168203
@Test
169204
public void nameResolutionError_beforeChildPolicyInstantiated_returnErrorPickerToUpstream() {
170205
loadBalancer.handleNameResolutionError(Status.UNIMPLEMENTED.withDescription("not found"));

0 commit comments

Comments
 (0)