mirror of https://github.com/grpc/grpc-java.git
core: add LoadBalancer.canHandleEmptyAddressListFromNameResolution() (#5148)
Currently ManagedChannelImpl will interpret empty address list from NameResolver as an error, and LoadBalancer will NEVER receive an empty list from handleResolvedAddressGroups(). There is a case in the request-routing design where a LoadBalancer only receives service config with which it constructs children NameResolver/LoadBalancer pairs, thus no address is expected at this level. canHandleEmptyAddressListFromNameResolution() is a signal to Channel (and to the parent LoadBalancer in case of hierachical LoadBalancers) about whether it accepts empty address lists. The default is false, which is the current behavior. The logic is currently duplicated in ManagedChannelImpl and AutoConfiguredLoadBalancer. The one in ManagedChannelImpl will be removed once we delete ManagedChannelBuilder.loadBalancerFactory() as AutoConfiguredLoadBalancer will always be the top-level LoadBalancer by then.
This commit is contained in:
parent
01f79bb909
commit
c0175e4cbe
|
@ -168,6 +168,20 @@ public abstract class LoadBalancer {
|
|||
*/
|
||||
public abstract void shutdown();
|
||||
|
||||
/**
|
||||
* Whether this LoadBalancer can handle empty address group list to be passed to {@link
|
||||
* #handleResolvedAddressGroups}. The default implementation returns {@code false}, meaning that
|
||||
* if the NameResolver returns an empty list, the Channel will turn that into an error and call
|
||||
* {@link #handleNameResolutionError}. LoadBalancers that want to accept empty lists should
|
||||
* override this method and return {@code true}.
|
||||
*
|
||||
* <p>This method should always return a constant value. It's not specified when this will be
|
||||
* called.
|
||||
*/
|
||||
public boolean canHandleEmptyAddressListFromNameResolution() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main balancing logic. It <strong>must be thread-safe</strong>. Typically it should only
|
||||
* synchronize on its own state, and avoid synchronizing with the LoadBalancer's state.
|
||||
|
|
|
@ -137,7 +137,17 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
|
|||
attributes =
|
||||
attributes.toBuilder().set(ATTR_LOAD_BALANCING_CONFIG, selection.config).build();
|
||||
}
|
||||
getDelegate().handleResolvedAddressGroups(selection.serverList, attributes);
|
||||
|
||||
LoadBalancer delegate = getDelegate();
|
||||
if (selection.serverList.isEmpty()
|
||||
&& !delegate.canHandleEmptyAddressListFromNameResolution()) {
|
||||
delegate.handleNameResolutionError(
|
||||
Status.UNAVAILABLE.withDescription(
|
||||
"Name resolver returned no usable address. addrs="
|
||||
+ servers + ", attrs=" + attributes));
|
||||
} else {
|
||||
delegate.handleResolvedAddressGroups(selection.serverList, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,6 +160,11 @@ public final class AutoConfiguredLoadBalancerFactory extends LoadBalancer.Factor
|
|||
getDelegate().handleSubchannelState(subchannel, stateInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandleEmptyAddressListFromNameResolution() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
delegate.shutdown();
|
||||
|
|
|
@ -1257,11 +1257,6 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
|
||||
@Override
|
||||
public void onAddresses(final List<EquivalentAddressGroup> servers, final Attributes config) {
|
||||
if (servers.isEmpty()) {
|
||||
onError(Status.UNAVAILABLE.withDescription(
|
||||
"Name resolver " + resolver + " returned an empty list"));
|
||||
return;
|
||||
}
|
||||
channelLogger.log(
|
||||
ChannelLogLevel.DEBUG, "Resolved address: {0}, config={1}", servers, config);
|
||||
|
||||
|
@ -1300,7 +1295,12 @@ final class ManagedChannelImpl extends ManagedChannel implements
|
|||
}
|
||||
}
|
||||
|
||||
helper.lb.handleResolvedAddressGroups(servers, config);
|
||||
if (servers.isEmpty() && !helper.lb.canHandleEmptyAddressListFromNameResolution()) {
|
||||
onError(Status.UNAVAILABLE.withDescription(
|
||||
"Name resolver " + resolver + " returned an empty list"));
|
||||
} else {
|
||||
helper.lb.handleResolvedAddressGroups(servers, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,6 +57,11 @@ public abstract class ForwardingLoadBalancer extends LoadBalancer {
|
|||
delegate().shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandleEmptyAddressListFromNameResolution() {
|
||||
return delegate().canHandleEmptyAddressListFromNameResolution();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this).add("delegate", delegate()).toString();
|
||||
|
|
|
@ -26,11 +26,13 @@ import static org.mockito.Matchers.eq;
|
|||
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.atLeast;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import io.grpc.Attributes;
|
||||
import io.grpc.ChannelLogger;
|
||||
|
@ -46,6 +48,7 @@ import io.grpc.LoadBalancerProvider;
|
|||
import io.grpc.LoadBalancerRegistry;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Status;
|
||||
import io.grpc.Status.Code;
|
||||
import io.grpc.SynchronizationContext;
|
||||
import io.grpc.grpclb.GrpclbLoadBalancerProvider;
|
||||
import io.grpc.internal.AutoConfiguredLoadBalancerFactory.AutoConfiguredLoadBalancer;
|
||||
|
@ -74,22 +77,34 @@ import org.mockito.ArgumentCaptor;
|
|||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class AutoConfiguredLoadBalancerFactoryTest {
|
||||
private static final LoadBalancerRegistry defaultRegistry =
|
||||
LoadBalancerRegistry.getDefaultRegistry();
|
||||
private final AutoConfiguredLoadBalancerFactory lbf =
|
||||
new AutoConfiguredLoadBalancerFactory(GrpcUtil.DEFAULT_LB_POLICY);
|
||||
|
||||
private final ChannelLogger channelLogger = mock(ChannelLogger.class);
|
||||
private final LoadBalancer testLbBalancer = mock(LoadBalancer.class);
|
||||
private final LoadBalancer testLbBalancer2 = mock(LoadBalancer.class);
|
||||
private final LoadBalancerProvider testLbBalancerProvider =
|
||||
mock(LoadBalancerProvider.class, delegatesTo(new TestLbLoadBalancerProvider()));
|
||||
mock(LoadBalancerProvider.class,
|
||||
delegatesTo(new FakeLoadBalancerProvider("test_lb", testLbBalancer)));
|
||||
private final LoadBalancerProvider testLbBalancerProvider2 =
|
||||
mock(LoadBalancerProvider.class,
|
||||
delegatesTo(new FakeLoadBalancerProvider("test_lb2", testLbBalancer2)));
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
LoadBalancerRegistry.getDefaultRegistry().register(testLbBalancerProvider);
|
||||
when(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).thenCallRealMethod();
|
||||
assertThat(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).isFalse();
|
||||
when(testLbBalancer2.canHandleEmptyAddressListFromNameResolution()).thenReturn(true);
|
||||
defaultRegistry.register(testLbBalancerProvider);
|
||||
defaultRegistry.register(testLbBalancerProvider2);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
LoadBalancerRegistry.getDefaultRegistry().deregister(testLbBalancerProvider);
|
||||
defaultRegistry.deregister(testLbBalancerProvider);
|
||||
defaultRegistry.deregister(testLbBalancerProvider2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -164,11 +179,6 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
assertThat(addrs).isEqualTo(servers);
|
||||
return new TestSubchannel(addrs, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
|
||||
// noop
|
||||
}
|
||||
};
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(helper);
|
||||
|
@ -195,11 +205,6 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
assertThat(addrs).isEqualTo(servers);
|
||||
return new TestSubchannel(addrs, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
|
||||
// noop
|
||||
}
|
||||
};
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(helper);
|
||||
|
@ -240,12 +245,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
.build();
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
Helper helper = new TestHelper() {
|
||||
@Override
|
||||
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
|
||||
// noop
|
||||
}
|
||||
};
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(helper);
|
||||
|
||||
|
@ -257,6 +257,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
verify(testLbBalancer).handleResolvedAddressGroups(eq(servers), attrsCaptor.capture());
|
||||
assertThat(attrsCaptor.getValue().get(ATTR_LOAD_BALANCING_CONFIG))
|
||||
.isEqualTo(Collections.singletonMap("setting1", "high"));
|
||||
verify(testLbBalancer, atLeast(0)).canHandleEmptyAddressListFromNameResolution();
|
||||
verifyNoMoreInteractions(testLbBalancer);
|
||||
|
||||
serviceConfig =
|
||||
|
@ -282,7 +283,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
// 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());
|
||||
registry.register(new FakeLoadBalancerProvider("round_robin", testLbBalancer));
|
||||
|
||||
final List<EquivalentAddressGroup> servers =
|
||||
Arrays.asList(
|
||||
|
@ -290,12 +291,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
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
|
||||
}
|
||||
};
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) new AutoConfiguredLoadBalancerFactory(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(helper);
|
||||
|
@ -307,6 +303,55 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
eq(Collections.singletonList(servers.get(0))), any(Attributes.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_delegateDoNotAcceptEmptyAddressList_nothing()
|
||||
throws Exception {
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(helper);
|
||||
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
lb.handleResolvedAddressGroups(
|
||||
Collections.<EquivalentAddressGroup>emptyList(),
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build());
|
||||
|
||||
assertThat(lb.getDelegate()).isSameAs(testLbBalancer);
|
||||
assertThat(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).isFalse();
|
||||
ArgumentCaptor<Status> statusCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(testLbBalancer).handleNameResolutionError(statusCaptor.capture());
|
||||
Status error = statusCaptor.getValue();
|
||||
assertThat(error.getCode()).isEqualTo(Code.UNAVAILABLE);
|
||||
assertThat(error.getDescription()).startsWith("Name resolver returned no usable address");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleResolvedAddressGroups_delegateAcceptsEmptyAddressList()
|
||||
throws Exception {
|
||||
Helper helper = new TestHelper();
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) lbf.newLoadBalancer(helper);
|
||||
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb2\": { \"setting1\": \"high\" } } ] }");
|
||||
lb.handleResolvedAddressGroups(
|
||||
Collections.<EquivalentAddressGroup>emptyList(),
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig).build());
|
||||
|
||||
assertThat(lb.getDelegate()).isSameAs(testLbBalancer2);
|
||||
assertThat(testLbBalancer2.canHandleEmptyAddressListFromNameResolution()).isTrue();
|
||||
ArgumentCaptor<Attributes> attrsCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(testLbBalancer2).handleResolvedAddressGroups(
|
||||
eq(Collections.<EquivalentAddressGroup>emptyList()), attrsCaptor.capture());
|
||||
Map<String, Object> lbConfig =
|
||||
attrsCaptor.getValue().get(LoadBalancer.ATTR_LOAD_BALANCING_CONFIG);
|
||||
assertThat(lbConfig).isEqualTo(Collections.<String, Object>singletonMap("setting1", "high"));
|
||||
assertThat(attrsCaptor.getValue().get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG))
|
||||
.isSameAs(serviceConfig);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decideLoadBalancerProvider_noBalancerAddresses_noServiceConfig_pickFirst()
|
||||
throws Exception {
|
||||
|
@ -402,7 +447,9 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
throws Exception {
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
registry.register(new FakeRoundRobinLoadBalancerProvider());
|
||||
LoadBalancerProvider fakeRondRobinProvider =
|
||||
new FakeLoadBalancerProvider("round_robin", testLbBalancer);
|
||||
registry.register(fakeRondRobinProvider);
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) new AutoConfiguredLoadBalancerFactory(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(new TestHelper());
|
||||
|
@ -416,7 +463,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
new EquivalentAddressGroup(new SocketAddress(){}));
|
||||
PolicySelection selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(FakeRoundRobinLoadBalancerProvider.class);
|
||||
assertThat(selection.provider).isSameAs(fakeRondRobinProvider);
|
||||
assertThat(selection.config).isNull();
|
||||
verify(channelLogger).log(
|
||||
eq(ChannelLogLevel.ERROR),
|
||||
|
@ -425,7 +472,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
// Called for the second time, the warning is only logged once
|
||||
selection = lb.decideLoadBalancerProvider(servers, serviceConfig);
|
||||
|
||||
assertThat(selection.provider).isInstanceOf(FakeRoundRobinLoadBalancerProvider.class);
|
||||
assertThat(selection.provider).isSameAs(fakeRondRobinProvider);
|
||||
// Balancer addresses are filtered out in the server list passed to round_robin
|
||||
assertThat(selection.serverList).containsExactly(servers.get(1));
|
||||
assertThat(selection.config).isNull();
|
||||
|
@ -437,7 +484,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
throws Exception {
|
||||
LoadBalancerRegistry registry = new LoadBalancerRegistry();
|
||||
registry.register(new PickFirstLoadBalancerProvider());
|
||||
registry.register(new FakeRoundRobinLoadBalancerProvider());
|
||||
registry.register(new FakeLoadBalancerProvider("round_robin", testLbBalancer));
|
||||
AutoConfiguredLoadBalancer lb =
|
||||
(AutoConfiguredLoadBalancer) new AutoConfiguredLoadBalancerFactory(
|
||||
registry, GrpcUtil.DEFAULT_LB_POLICY).newLoadBalancer(new TestHelper());
|
||||
|
@ -582,11 +629,6 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
return "fake_authority";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public SynchronizationContext getSynchronizationContext() {
|
||||
return new SynchronizationContext(
|
||||
|
@ -703,6 +745,11 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
public ChannelLogger getChannelLogger() {
|
||||
return channelLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBalancingState(ConnectivityState newState, SubchannelPicker newPicker) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestSubchannel extends Subchannel {
|
||||
|
@ -733,8 +780,14 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
private final class TestLbLoadBalancerProvider extends LoadBalancerProvider {
|
||||
static final String POLICY_NAME = "test_lb";
|
||||
private static final class FakeLoadBalancerProvider extends LoadBalancerProvider {
|
||||
private final String policyName;
|
||||
private final LoadBalancer balancer;
|
||||
|
||||
FakeLoadBalancerProvider(String policyName, LoadBalancer balancer) {
|
||||
this.policyName = policyName;
|
||||
this.balancer = balancer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
|
@ -748,36 +801,12 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
|||
|
||||
@Override
|
||||
public String getPolicyName() {
|
||||
return POLICY_NAME;
|
||||
return policyName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadBalancer newLoadBalancer(Helper helper) {
|
||||
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;
|
||||
return balancer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -280,6 +280,7 @@ public class ManagedChannelImplTest {
|
|||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mockLoadBalancer.canHandleEmptyAddressListFromNameResolution()).thenCallRealMethod();
|
||||
LoadBalancerRegistry.getDefaultRegistry().register(mockLoadBalancerProvider);
|
||||
expectedUri = new URI(TARGET);
|
||||
transports = TestUtils.captureTransports(mockTransportFactory);
|
||||
|
@ -825,10 +826,82 @@ public class ManagedChannelImplTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void nameResolverReturnsEmptySubLists() {
|
||||
public void nameResolverReturnsEmptySubLists_becomeErrorByDefault() throws Exception {
|
||||
String errorDescription = "Name resolver returned no usable address";
|
||||
|
||||
// Pass a FakeNameResolverFactory with an empty list and LB config
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).build();
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
createChannel();
|
||||
|
||||
// LoadBalancer received the error
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
verify(mockLoadBalancer).handleNameResolutionError(statusCaptor.capture());
|
||||
Status status = statusCaptor.getValue();
|
||||
assertSame(Status.Code.UNAVAILABLE, status.getCode());
|
||||
Truth.assertThat(status.getDescription()).startsWith(errorDescription);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nameResolverReturnsEmptySubLists_optionallyAllowed() throws Exception {
|
||||
when(mockLoadBalancer.canHandleEmptyAddressListFromNameResolution()).thenReturn(true);
|
||||
|
||||
// Pass a FakeNameResolverFactory with an empty list and LB config
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).build();
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
createChannel();
|
||||
|
||||
// LoadBalancer received the empty list and the LB config
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
ArgumentCaptor<Attributes> attrsCaptor = ArgumentCaptor.forClass(null);
|
||||
verify(mockLoadBalancer).handleResolvedAddressGroups(
|
||||
eq(ImmutableList.<EquivalentAddressGroup>of()), attrsCaptor.capture());
|
||||
Map<String, Object> lbConfig =
|
||||
attrsCaptor.getValue().get(LoadBalancer.ATTR_LOAD_BALANCING_CONFIG);
|
||||
assertEquals(ImmutableMap.<String, Object>of("setting1", "high"), lbConfig);
|
||||
assertSame(
|
||||
serviceConfig, attrsCaptor.getValue().get(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
public void nameResolverReturnsEmptySubLists_becomeErrorByDefault_lbFactorySetDirectly()
|
||||
throws Exception {
|
||||
String errorDescription = "returned an empty list";
|
||||
|
||||
// Pass a FakeNameResolverFactory with an empty list
|
||||
// Pass a FakeNameResolverFactory with an empty list and LB config
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).build();
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
|
||||
// Pass a LoadBalancerFactory directly to the builder, bypassing
|
||||
// AutoConfiguredLoadBalancerFactory. The empty-list check is done in ManagedChannelImpl rather
|
||||
// than AutoConfiguredLoadBalancerFactory
|
||||
channelBuilder.loadBalancerFactory(mockLoadBalancerProvider);
|
||||
|
||||
createChannel();
|
||||
|
||||
// LoadBalancer received the error
|
||||
|
@ -839,6 +912,37 @@ public class ManagedChannelImplTest {
|
|||
Truth.assertThat(status.getDescription()).contains(errorDescription);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
public void nameResolverReturnsEmptySubLists_optionallyAllowed_lbFactorySetDirectly()
|
||||
throws Exception {
|
||||
when(mockLoadBalancer.canHandleEmptyAddressListFromNameResolution()).thenReturn(true);
|
||||
|
||||
// Pass a FakeNameResolverFactory with an empty list and LB config
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).build();
|
||||
Map<String, Object> serviceConfig =
|
||||
parseConfig("{\"loadBalancingConfig\": [ {\"mock_lb\": { \"setting1\": \"high\" } } ] }");
|
||||
Attributes serviceConfigAttrs =
|
||||
Attributes.newBuilder()
|
||||
.set(GrpcAttributes.NAME_RESOLVER_SERVICE_CONFIG, serviceConfig)
|
||||
.build();
|
||||
nameResolverFactory.nextResolvedAttributes.set(serviceConfigAttrs);
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
|
||||
// Pass a LoadBalancerFactory directly to the builder, bypassing
|
||||
// AutoConfiguredLoadBalancerFactory. The empty-list check is done in ManagedChannelImpl rather
|
||||
// than AutoConfiguredLoadBalancerFactory
|
||||
channelBuilder.loadBalancerFactory(mockLoadBalancerProvider);
|
||||
|
||||
createChannel();
|
||||
|
||||
// LoadBalancer received the empty list and the LB config
|
||||
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
|
||||
verify(mockLoadBalancer).handleResolvedAddressGroups(
|
||||
eq(ImmutableList.<EquivalentAddressGroup>of()), same(serviceConfigAttrs));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadBalancerThrowsInHandleResolvedAddresses() {
|
||||
RuntimeException ex = new RuntimeException("simulated");
|
||||
|
@ -2366,11 +2470,15 @@ public class ManagedChannelImplTest {
|
|||
public void channelTracing_nameResolvingErrorEvent() throws Exception {
|
||||
timer.forwardNanos(1234);
|
||||
channelBuilder.maxTraceEvents(10);
|
||||
|
||||
Status error = Status.UNAVAILABLE.withDescription("simulated error");
|
||||
FakeNameResolverFactory nameResolverFactory =
|
||||
new FakeNameResolverFactory.Builder(expectedUri).setError(error).build();
|
||||
channelBuilder.nameResolverFactory(nameResolverFactory);
|
||||
createChannel();
|
||||
|
||||
assertThat(getStats(channel).channelTrace.events).contains(new ChannelTrace.Event.Builder()
|
||||
.setDescription("Failed to resolve name:"
|
||||
+ " Status{code=UNAVAILABLE, description=Name resolver FakeNameResolver"
|
||||
+ " returned an empty list, cause=null}")
|
||||
.setDescription("Failed to resolve name: " + error)
|
||||
.setSeverity(ChannelTrace.Event.Severity.CT_WARNING)
|
||||
.setTimestampNanos(timer.getTicker().read())
|
||||
.build());
|
||||
|
@ -3288,4 +3396,9 @@ public class ManagedChannelImplTest {
|
|||
});
|
||||
return resultCapture.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> parseConfig(String json) throws Exception {
|
||||
return (Map<String, Object>) JsonParser.parse(json);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue