core: do not fail RPCs if balancer address exists but grpclb is unavailable (#5129)

When service owner turns on grpclb through service config, it
shouldn't break existing clients that don't have grpclb in their
classpath.

Resolves #4602
This commit is contained in:
Kun Zhang 2018-12-06 13:40:30 -08:00 committed by GitHub
parent 8ff92a4a7d
commit 0cc0f2d170
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 263 additions and 121 deletions

View File

@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import io.grpc.Attributes; import io.grpc.Attributes;
import io.grpc.ChannelLogger;
import io.grpc.ChannelLogger.ChannelLogLevel; import io.grpc.ChannelLogger.ChannelLogLevel;
import io.grpc.ConnectivityState; import io.grpc.ConnectivityState;
import io.grpc.ConnectivityStateInfo; import io.grpc.ConnectivityStateInfo;
@ -33,17 +32,31 @@ import io.grpc.LoadBalancer.SubchannelPicker;
import io.grpc.LoadBalancerProvider; import io.grpc.LoadBalancerProvider;
import io.grpc.LoadBalancerRegistry; import io.grpc.LoadBalancerRegistry;
import io.grpc.Status; import io.grpc.Status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@VisibleForTesting @VisibleForTesting
public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factory { public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factory {
private static final String DEFAULT_POLICY = "pick_first"; private static final String DEFAULT_POLICY = "pick_first";
private static final Logger logger =
Logger.getLogger(AutoConfiguredLoadBalancerFactory.class.getName());
private static final LoadBalancerRegistry registry = LoadBalancerRegistry.getDefaultRegistry(); private final LoadBalancerRegistry registry;
public AutoConfiguredLoadBalancerFactory() {
this(LoadBalancerRegistry.getDefaultRegistry());
}
@VisibleForTesting
AutoConfiguredLoadBalancerFactory(LoadBalancerRegistry registry) {
this.registry = checkNotNull(registry, "registry");
}
@Override @Override
public LoadBalancer newLoadBalancer(Helper helper) { public LoadBalancer newLoadBalancer(Helper helper) {
@ -65,12 +78,12 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
public void shutdown() {} public void shutdown() {}
} }
@VisibleForTesting @VisibleForTesting
public static final class AutoConfiguredLoadBalancer extends LoadBalancer { public final class AutoConfiguredLoadBalancer extends LoadBalancer {
private final Helper helper; private final Helper helper;
private LoadBalancer delegate; private LoadBalancer delegate;
private LoadBalancerProvider delegateProvider; private LoadBalancerProvider delegateProvider;
private boolean roundRobinDueToGrpclbDepMissing;
AutoConfiguredLoadBalancer(Helper helper) { AutoConfiguredLoadBalancer(Helper helper) {
this.helper = helper; this.helper = helper;
@ -94,7 +107,7 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
Map<String, Object> configMap = attributes.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG); Map<String, Object> configMap = attributes.get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG);
PolicySelection selection; PolicySelection selection;
try { try {
selection = decideLoadBalancerProvider(servers, configMap, helper.getChannelLogger()); selection = decideLoadBalancerProvider(servers, configMap);
} catch (PolicyException e) { } catch (PolicyException e) {
Status s = Status.INTERNAL.withDescription(e.getMessage()); Status s = Status.INTERNAL.withDescription(e.getMessage());
helper.updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, new FailingPicker(s)); helper.updateBalancingState(ConnectivityState.TRANSIENT_FAILURE, new FailingPicker(s));
@ -122,7 +135,7 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
attributes = attributes =
attributes.toBuilder().set(ATTR_LOAD_BALANCING_CONFIG, selection.config).build(); attributes.toBuilder().set(ATTR_LOAD_BALANCING_CONFIG, selection.config).build();
} }
getDelegate().handleResolvedAddressGroups(servers, attributes); getDelegate().handleResolvedAddressGroups(selection.serverList, attributes);
} }
@Override @Override
@ -155,73 +168,93 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
LoadBalancerProvider getDelegateProvider() { LoadBalancerProvider getDelegateProvider() {
return delegateProvider; return delegateProvider;
} }
}
/** /**
* Picks a load balancer based on given criteria. In order of preference: * Picks a load balancer based on given criteria. In order of preference:
* *
* <ol> * <ol>
* <li>User provided lb on the channel. This is a degenerate case and not handled here.</li> * <li>User provided lb on the channel. This is a degenerate case and not handled here.</li>
* <li>"grpclb" if any gRPC LB balancer addresses are present</li> * <li>"grpclb" if any gRPC LB balancer addresses are present</li>
* <li>The policy picked by the service config</li> * <li>The policy picked by the service config</li>
* <li>"pick_first" if the service config choice does not specify</li> * <li>"pick_first" if the service config choice does not specify</li>
* </ol> * </ol>
* *
* @param servers The list of servers reported * @param servers The list of servers reported
* @param config the service config object * @param config the service config object
* @return the new load balancer factory, never null * @return the new load balancer factory, never null
*/ */
@VisibleForTesting @VisibleForTesting
static PolicySelection decideLoadBalancerProvider( PolicySelection decideLoadBalancerProvider(
List<EquivalentAddressGroup> servers, @Nullable Map<String, Object> config, List<EquivalentAddressGroup> servers, @Nullable Map<String, Object> config)
ChannelLogger channelLogger) throws PolicyException { throws PolicyException {
// Check for balancer addresses // Check for balancer addresses
boolean haveBalancerAddress = false; boolean haveBalancerAddress = false;
for (EquivalentAddressGroup s : servers) { List<EquivalentAddressGroup> backendAddrs = new ArrayList<EquivalentAddressGroup>();
if (s.getAttributes().get(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY) != null) { for (EquivalentAddressGroup s : servers) {
haveBalancerAddress = true; if (s.getAttributes().get(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY) != null) {
break; haveBalancerAddress = true;
} } else {
} backendAddrs.add(s);
if (haveBalancerAddress) {
LoadBalancerProvider provider =
getProviderOrThrow("grpclb", "NameResolver has returned balancer addresses");
return new PolicySelection(provider, null);
}
if (config != null) {
List<Map<String, Object>> lbConfigs =
ServiceConfigUtil.getLoadBalancingConfigsFromServiceConfig(config);
LinkedHashSet<String> policiesTried = new LinkedHashSet<>();
for (Map<String, Object> lbConfig : lbConfigs) {
if (lbConfig.size() != 1) {
throw new PolicyException(
"There are " + lbConfig.size()
+ " load-balancing configs in a list item. Exactly one is expected. Config="
+ lbConfig);
} }
Entry<String, Object> entry = lbConfig.entrySet().iterator().next(); }
String policy = entry.getKey();
LoadBalancerProvider provider = registry.getProvider(policy); if (haveBalancerAddress) {
if (provider != null) { LoadBalancerProvider grpclbProvider = registry.getProvider("grpclb");
if (!policiesTried.isEmpty()) { if (grpclbProvider == null) {
channelLogger.log( if (backendAddrs.isEmpty()) {
ChannelLogLevel.DEBUG, throw new PolicyException(
"{0} specified by Service Config are not available", policiesTried); "Received ONLY balancer addresses but grpclb runtime is missing");
} }
return new PolicySelection(provider, (Map) entry.getValue()); if (!roundRobinDueToGrpclbDepMissing) {
roundRobinDueToGrpclbDepMissing = true;
String errorMsg = "Found balancer addresses but grpclb runtime is missing."
+ " Will use round_robin. Please include grpc-grpclb in your runtime depedencies.";
helper.getChannelLogger().log(ChannelLogLevel.ERROR, errorMsg);
logger.warning(errorMsg);
}
return new PolicySelection(
getProviderOrThrow(
"round_robin", "received balancer addresses but grpclb runtime is missing"),
backendAddrs, null);
} else {
return new PolicySelection(grpclbProvider, servers, null);
} }
policiesTried.add(policy);
} }
throw new PolicyException( roundRobinDueToGrpclbDepMissing = false;
"None of " + policiesTried + " specified by Service Config are available.");
if (config != null) {
List<Map<String, Object>> lbConfigs =
ServiceConfigUtil.getLoadBalancingConfigsFromServiceConfig(config);
LinkedHashSet<String> policiesTried = new LinkedHashSet<>();
for (Map<String, Object> lbConfig : lbConfigs) {
if (lbConfig.size() != 1) {
throw new PolicyException(
"There are " + lbConfig.size()
+ " load-balancing configs in a list item. Exactly one is expected. Config="
+ lbConfig);
}
Entry<String, Object> entry = lbConfig.entrySet().iterator().next();
String policy = entry.getKey();
LoadBalancerProvider provider = registry.getProvider(policy);
if (provider != null) {
if (!policiesTried.isEmpty()) {
helper.getChannelLogger().log(
ChannelLogLevel.DEBUG,
"{0} specified by Service Config are not available", policiesTried);
}
return new PolicySelection(provider, servers, (Map) entry.getValue());
}
policiesTried.add(policy);
}
throw new PolicyException(
"None of " + policiesTried + " specified by Service Config are available.");
}
return new PolicySelection(
getProviderOrThrow(DEFAULT_POLICY, "using default policy"), servers, null);
} }
return new PolicySelection(
getProviderOrThrow(DEFAULT_POLICY, "using default policy"), null);
} }
private static LoadBalancerProvider getProviderOrThrow(String policy, String choiceReason) private LoadBalancerProvider getProviderOrThrow(String policy, String choiceReason)
throws PolicyException { throws PolicyException {
LoadBalancerProvider provider = registry.getProvider(policy); LoadBalancerProvider provider = registry.getProvider(policy);
if (provider == null) { if (provider == null) {
@ -243,11 +276,15 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
@VisibleForTesting @VisibleForTesting
static final class PolicySelection { static final class PolicySelection {
final LoadBalancerProvider provider; final LoadBalancerProvider provider;
final List<EquivalentAddressGroup> serverList;
@Nullable final Map<String, Object> config; @Nullable final Map<String, Object> config;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
PolicySelection(LoadBalancerProvider provider, @Nullable Map<?, ?> config) { PolicySelection(
LoadBalancerProvider provider, List<EquivalentAddressGroup> serverList,
@Nullable Map<?, ?> config) {
this.provider = checkNotNull(provider, "provider"); this.provider = checkNotNull(provider, "provider");
this.serverList = Collections.unmodifiableList(checkNotNull(serverList, "serverList"));
this.config = (Map<String, Object>) config; this.config = (Map<String, Object>) config;
} }
} }

View File

@ -24,6 +24,7 @@ import static org.mockito.AdditionalAnswers.delegatesTo;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same; import static org.mockito.Matchers.same;
import static org.mockito.Matchers.startsWith;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -145,8 +146,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@Test @Test
public void handleResolvedAddressGroups_keepOldBalancer() { public void handleResolvedAddressGroups_keepOldBalancer() {
final List<EquivalentAddressGroup> servers = final List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(new SocketAddress(){}, Attributes.EMPTY));
Helper helper = new TestHelper() { Helper helper = new TestHelper() {
@Override @Override
public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) { public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) {
@ -177,10 +177,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig) .set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
.build(); .build();
final List<EquivalentAddressGroup> servers = final List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(
new SocketAddress(){},
Attributes.EMPTY));
Helper helper = new TestHelper() { Helper helper = new TestHelper() {
@Override @Override
public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) { public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) {
@ -231,10 +228,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig) .set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
.build(); .build();
final List<EquivalentAddressGroup> servers = final List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(
new SocketAddress(){},
Attributes.EMPTY));
Helper helper = new TestHelper() { Helper helper = new TestHelper() {
@Override @Override
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) { public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
@ -249,7 +243,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
verify(testLbBalancerProvider).newLoadBalancer(same(helper)); verify(testLbBalancerProvider).newLoadBalancer(same(helper));
assertThat(lb.getDelegate()).isSameAs(testLbBalancer); assertThat(lb.getDelegate()).isSameAs(testLbBalancer);
ArgumentCaptor<Attributes> attrsCaptor = ArgumentCaptor.forClass(null); ArgumentCaptor<Attributes> attrsCaptor = ArgumentCaptor.forClass(null);
verify(testLbBalancer).handleResolvedAddressGroups(same(servers), attrsCaptor.capture()); verify(testLbBalancer).handleResolvedAddressGroups(eq(servers), attrsCaptor.capture());
assertThat(attrsCaptor.getValue().get(ATTR_LOAD_BALANCING_CONFIG)) assertThat(attrsCaptor.getValue().get(ATTR_LOAD_BALANCING_CONFIG))
.isEqualTo(Collections.singletonMap("setting1", "high")); .isEqualTo(Collections.singletonMap("setting1", "high"));
verifyNoMoreInteractions(testLbBalancer); verifyNoMoreInteractions(testLbBalancer);
@ -263,7 +257,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
lb.handleResolvedAddressGroups(servers, serviceConfigAttrs); lb.handleResolvedAddressGroups(servers, serviceConfigAttrs);
verify(testLbBalancer, times(2)) verify(testLbBalancer, times(2))
.handleResolvedAddressGroups(same(servers), attrsCaptor.capture()); .handleResolvedAddressGroups(eq(servers), attrsCaptor.capture());
// But the balancer config is changed. // But the balancer config is changed.
assertThat(attrsCaptor.getValue().get(ATTR_LOAD_BALANCING_CONFIG)) assertThat(attrsCaptor.getValue().get(ATTR_LOAD_BALANCING_CONFIG))
.isEqualTo(Collections.singletonMap("setting1", "low")); .isEqualTo(Collections.singletonMap("setting1", "low"));
@ -272,40 +266,74 @@ public class AutoConfiguredLoadBalancerFactoryTest {
verify(testLbBalancerProvider).newLoadBalancer(any(Helper.class)); verify(testLbBalancerProvider).newLoadBalancer(any(Helper.class));
} }
@Test
public void handleResolvedAddressGroups_propagateOnlyBackendAddrsToDelegate() throws Exception {
// This case only happens when grpclb is missing. We will use a local registry
LoadBalancerRegistry registry = new LoadBalancerRegistry();
registry.register(new PickFirstLoadBalancerProvider());
registry.register(new FakeRoundRobinLoadBalancerProvider());
final List<EquivalentAddressGroup> servers =
Arrays.asList(
new EquivalentAddressGroup(new SocketAddress(){}),
new EquivalentAddressGroup(
new SocketAddress(){},
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
Helper helper = new TestHelper() {
@Override
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
// noop
}
};
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) new AutoConfiguredLoadBalancerFactory(registry)
.newLoadBalancer(helper);
lb.handleResolvedAddressGroups(servers, Attributes.EMPTY);
assertThat(lb.getDelegate()).isSameAs(testLbBalancer);
verify(testLbBalancer).handleResolvedAddressGroups(
eq(Collections.singletonList(servers.get(0))), any(Attributes.class));
}
@Test @Test
public void decideLoadBalancerProvider_noBalancerAddresses_noServiceConfig_pickFirst() public void decideLoadBalancerProvider_noBalancerAddresses_noServiceConfig_pickFirst()
throws Exception { throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = null; Map<String, Object> serviceConfig = null;
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(new SocketAddress(){}, Attributes.EMPTY)); PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
PolicySelection selection =
AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider(
servers, serviceConfig, channelLogger);
assertThat(selection.provider).isInstanceOf(PickFirstLoadBalancerProvider.class); assertThat(selection.provider).isInstanceOf(PickFirstLoadBalancerProvider.class);
assertThat(selection.serverList).isEqualTo(servers);
assertThat(selection.config).isNull(); assertThat(selection.config).isNull();
verifyZeroInteractions(channelLogger); verifyZeroInteractions(channelLogger);
} }
@Test @Test
public void decideLoadBalancerProvider_oneBalancer_noServiceConfig_grpclb() throws Exception { public void decideLoadBalancerProvider_oneBalancer_noServiceConfig_grpclb() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = null; Map<String, Object> serviceConfig = null;
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(
new EquivalentAddressGroup( new EquivalentAddressGroup(
new SocketAddress(){}, new SocketAddress(){},
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build())); Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
PolicySelection selection = AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider( PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
servers, serviceConfig, channelLogger);
assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class); assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
assertThat(selection.serverList).isEqualTo(servers);
assertThat(selection.config).isNull(); assertThat(selection.config).isNull();
verifyZeroInteractions(channelLogger); verifyZeroInteractions(channelLogger);
} }
@Test @Test
public void decideLoadBalancerProvider_grpclbOverridesServiceConfigLbPolicy() throws Exception { public void decideLoadBalancerProvider_grpclbOverridesServiceConfigLbPolicy() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = new HashMap<String, Object>(); Map<String, Object> serviceConfig = new HashMap<String, Object>();
serviceConfig.put("loadBalancingPolicy", "round_robin"); serviceConfig.put("loadBalancingPolicy", "round_robin");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
@ -313,10 +341,10 @@ public class AutoConfiguredLoadBalancerFactoryTest {
new EquivalentAddressGroup( new EquivalentAddressGroup(
new SocketAddress(){}, new SocketAddress(){},
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build())); Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
PolicySelection selection = AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider( PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
servers, serviceConfig, channelLogger);
assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class); assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
assertThat(selection.serverList).isEqualTo(servers);
assertThat(selection.config).isNull(); assertThat(selection.config).isNull();
verifyZeroInteractions(channelLogger); verifyZeroInteractions(channelLogger);
} }
@ -324,6 +352,8 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Test @Test
public void decideLoadBalancerProvider_grpclbOverridesServiceConfigLbConfig() throws Exception { public void decideLoadBalancerProvider_grpclbOverridesServiceConfigLbConfig() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = Map<String, Object> serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }"); parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
@ -331,25 +361,83 @@ public class AutoConfiguredLoadBalancerFactoryTest {
new EquivalentAddressGroup( new EquivalentAddressGroup(
new SocketAddress(){}, new SocketAddress(){},
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build())); Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
PolicySelection selection = AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider( PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
servers, serviceConfig, channelLogger);
assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class); assertThat(selection.provider).isInstanceOf(GrpclbLoadBalancerProvider.class);
assertThat(selection.serverList).isEqualTo(servers);
assertThat(selection.config).isNull(); assertThat(selection.config).isNull();
verifyZeroInteractions(channelLogger); verifyZeroInteractions(channelLogger);
} }
@Test @Test
public void decideLoadBalancerProvider_serviceConfigLbPolicyOverridesDefault() throws Exception { public void decideLoadBalancerProvider_grpclbProviderNotFound_fallbackToRoundRobin()
Map<String, Object> serviceConfig = new HashMap<String, Object>(); throws Exception {
serviceConfig.put("loadBalancingPolicy", "round_robin"); LoadBalancerRegistry registry = new LoadBalancerRegistry();
registry.register(new PickFirstLoadBalancerProvider());
registry.register(new FakeRoundRobinLoadBalancerProvider());
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) new AutoConfiguredLoadBalancerFactory(registry)
.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
List<EquivalentAddressGroup> servers =
Arrays.asList(
new EquivalentAddressGroup(
new SocketAddress(){},
Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()),
new EquivalentAddressGroup(new SocketAddress(){}));
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
assertThat(selection.provider).isInstanceOf(FakeRoundRobinLoadBalancerProvider.class);
assertThat(selection.config).isNull();
verify(channelLogger).log(
eq(ChannelLogLevel.ERROR),
startsWith("Found balancer addresses but grpclb runtime is missing"));
// Called for the second time, the warning is only logged once
selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
assertThat(selection.provider).isInstanceOf(FakeRoundRobinLoadBalancerProvider.class);
// Balancer addresses are filtered out in the server list passed to round_robin
assertThat(selection.serverList).isEqualTo(Collections.singletonList(servers.get(1)));
assertThat(selection.config).isNull();
verifyNoMoreInteractions(channelLogger);
}
@Test
public void decideLoadBalancerProvider_grpclbProviderNotFound_noBackendAddress()
throws Exception {
LoadBalancerRegistry registry = new LoadBalancerRegistry();
registry.register(new PickFirstLoadBalancerProvider());
registry.register(new FakeRoundRobinLoadBalancerProvider());
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) new AutoConfiguredLoadBalancerFactory(registry)
.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {} } ] }");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(
new EquivalentAddressGroup( new EquivalentAddressGroup(
new SocketAddress(){}, new SocketAddress(){},
Attributes.EMPTY)); Attributes.newBuilder().set(GrpcAttributes.ATTR_LB_ADDR_AUTHORITY, "ok").build()));
PolicySelection selection = AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider( try {
servers, serviceConfig, channelLogger); lb.decideLoadBalancerProvider(servers, serviceConfig);
fail("Should throw");
} catch (PolicyException e) {
assertThat(e.getMessage())
.isEqualTo("Received ONLY balancer addresses but grpclb runtime is missing");
}
}
@Test
public void decideLoadBalancerProvider_serviceConfigLbPolicyOverridesDefault() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = new HashMap<String, Object>();
serviceConfig.put("loadBalancingPolicy", "round_robin");
List<EquivalentAddressGroup> servers =
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
assertThat(selection.provider.getClass().getName()).isEqualTo( assertThat(selection.provider.getClass().getName()).isEqualTo(
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider"); "io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
@ -359,34 +447,31 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@Test @Test
public void decideLoadBalancerProvider_serviceConfigLbConfigOverridesDefault() throws Exception { public void decideLoadBalancerProvider_serviceConfigLbConfigOverridesDefault() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = Map<String, Object> serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {\"setting1\": \"high\"} } ] }"); parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": {\"setting1\": \"high\"} } ] }");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup( PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
new SocketAddress(){},
Attributes.EMPTY));
PolicySelection selection = AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider(
servers, serviceConfig, channelLogger);
assertThat(selection.provider.getClass().getName()).isEqualTo( assertThat(selection.provider.getClass().getName()).isEqualTo(
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider"); "io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
assertThat(selection.serverList).isEqualTo(servers);
assertThat(selection.config).isEqualTo(Collections.singletonMap("setting1", "high")); assertThat(selection.config).isEqualTo(Collections.singletonMap("setting1", "high"));
verifyZeroInteractions(channelLogger); verifyZeroInteractions(channelLogger);
} }
@Test @Test
public void decideLoadBalancerProvider_serviceConfigLbPolicyFailsOnUnknown() { public void decideLoadBalancerProvider_serviceConfigLbPolicyFailsOnUnknown() {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = new HashMap<String, Object>(); Map<String, Object> serviceConfig = new HashMap<String, Object>();
serviceConfig.put("loadBalancingPolicy", "MAGIC_BALANCER"); serviceConfig.put("loadBalancingPolicy", "MAGIC_BALANCER");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(
new SocketAddress(){},
Attributes.EMPTY));
try { try {
AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider( lb.decideLoadBalancerProvider(servers, serviceConfig);
servers, serviceConfig, channelLogger);
fail(); fail();
} catch (PolicyException e) { } catch (PolicyException e) {
assertThat(e.getMessage()).isEqualTo( assertThat(e.getMessage()).isEqualTo(
@ -396,16 +481,14 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@Test @Test
public void decideLoadBalancerProvider_serviceConfigLbConfigFailsOnUnknown() throws Exception { public void decideLoadBalancerProvider_serviceConfigLbConfigFailsOnUnknown() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = Map<String, Object> serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"magic_balancer\": {} } ] }"); parseConfig("{\"loadBalancingConfig\": [ {\"magic_balancer\": {} } ] }");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(
new SocketAddress(){},
Attributes.EMPTY));
try { try {
AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider( lb.decideLoadBalancerProvider(servers, serviceConfig);
servers, serviceConfig, channelLogger);
fail(); fail();
} catch (PolicyException e) { } catch (PolicyException e) {
assertThat(e.getMessage()).isEqualTo( assertThat(e.getMessage()).isEqualTo(
@ -415,19 +498,18 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@Test @Test
public void decideLoadBalancerProvider_serviceConfigLbConfigSkipUnknown() throws Exception { public void decideLoadBalancerProvider_serviceConfigLbConfigSkipUnknown() throws Exception {
AutoConfiguredLoadBalancer lb =
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(new TestHelper());
Map<String, Object> serviceConfig = Map<String, Object> serviceConfig =
parseConfig( parseConfig(
"{\"loadBalancingConfig\": [ {\"magic_balancer\": {} }, {\"round_robin\": {} } ] }"); "{\"loadBalancingConfig\": [ {\"magic_balancer\": {} }, {\"round_robin\": {} } ] }");
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup( PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
new SocketAddress(){},
Attributes.EMPTY));
PolicySelection selection = AutoConfiguredLoadBalancerFactory.decideLoadBalancerProvider(
servers, serviceConfig, channelLogger);
assertThat(selection.provider.getClass().getName()).isEqualTo( assertThat(selection.provider.getClass().getName()).isEqualTo(
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider"); "io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
assertThat(selection.serverList).isEqualTo(servers);
assertThat(selection.config).isEqualTo(Collections.emptyMap()); assertThat(selection.config).isEqualTo(Collections.emptyMap());
verify(channelLogger).log( verify(channelLogger).log(
eq(ChannelLogLevel.DEBUG), eq(ChannelLogLevel.DEBUG),
@ -439,8 +521,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
public void channelTracing_lbPolicyChanged() { public void channelTracing_lbPolicyChanged() {
final FakeClock clock = new FakeClock(); final FakeClock clock = new FakeClock();
List<EquivalentAddressGroup> servers = List<EquivalentAddressGroup> servers =
Collections.singletonList( Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
new EquivalentAddressGroup(new SocketAddress(){}, Attributes.EMPTY));
Helper helper = new TestHelper() { Helper helper = new TestHelper() {
@Override @Override
public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) { public Subchannel createSubchannel(List<EquivalentAddressGroup> addrs, Attributes attrs) {
@ -630,4 +711,28 @@ public class AutoConfiguredLoadBalancerFactoryTest {
return testLbBalancer; return testLbBalancer;
} }
} }
private final class FakeRoundRobinLoadBalancerProvider extends LoadBalancerProvider {
static final String POLICY_NAME = "round_robin";
@Override
public boolean isAvailable() {
return true;
}
@Override
public int getPriority() {
return 5;
}
@Override
public String getPolicyName() {
return POLICY_NAME;
}
@Override
public LoadBalancer newLoadBalancer(Helper helper) {
return testLbBalancer;
}
}
} }