diff --git a/api/src/main/java/io/grpc/LoadBalancer.java b/api/src/main/java/io/grpc/LoadBalancer.java
index ebe30e7d6d..8d74216b36 100644
--- a/api/src/main/java/io/grpc/LoadBalancer.java
+++ b/api/src/main/java/io/grpc/LoadBalancer.java
@@ -124,46 +124,39 @@ public abstract class LoadBalancer {
*
*
Implementations should not modify the given {@code servers}.
*
- * @param resolvedAddresses the resolved server addresses, attributes, and config.
- * @since 1.21.0
+ * @param servers the resolved server addresses, never empty.
+ * @param attributes extra information from naming system.
+ * @deprecated override {@link #handleResolvedAddresses(ResolvedAddresses) instead}
+ * @since 1.2.0
*/
- public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ @Deprecated
+ public void handleResolvedAddressGroups(
+ List servers,
+ @NameResolver.ResolutionResultAttr Attributes attributes) {
if (recursionCount++ == 0) {
- // Note that the information about the addresses actually being accepted will be lost
- // if you rely on this method for backward compatibility.
- acceptResolvedAddresses(resolvedAddresses);
+ handleResolvedAddresses(
+ ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(attributes).build());
}
recursionCount = 0;
}
/**
- * Accepts newly resolved addresses from the name resolution system. The {@link
- * EquivalentAddressGroup} addresses should be considered equivalent but may be flattened into a
- * single list if needed.
+ * Handles newly resolved server groups and metadata attributes from name resolution system.
+ * {@code servers} contained in {@link EquivalentAddressGroup} should be considered equivalent
+ * but may be flattened into a single list if needed.
*
- * Implementations can choose to reject the given addresses by returning {@code false}.
- *
- *
Implementations should not modify the given {@code addresses}.
+ *
Implementations should not modify the given {@code servers}.
*
* @param resolvedAddresses the resolved server addresses, attributes, and config.
- * @return {@code true} if the resolved addresses were accepted. {@code false} if rejected.
- * @since 1.49.0
+ * @since 1.21.0
*/
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
- if (resolvedAddresses.getAddresses().isEmpty()
- && !canHandleEmptyAddressListFromNameResolution()) {
- handleNameResolutionError(Status.UNAVAILABLE.withDescription(
- "NameResolver returned no usable address. addrs=" + resolvedAddresses.getAddresses()
- + ", attrs=" + resolvedAddresses.getAttributes()));
- return false;
- } else {
- if (recursionCount++ == 0) {
- handleResolvedAddresses(resolvedAddresses);
- }
- recursionCount = 0;
-
- return true;
+ @SuppressWarnings("deprecation")
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ if (recursionCount++ == 0) {
+ handleResolvedAddressGroups(
+ resolvedAddresses.getAddresses(), resolvedAddresses.getAttributes());
}
+ recursionCount = 0;
}
/**
diff --git a/api/src/test/java/io/grpc/LoadBalancerTest.java b/api/src/test/java/io/grpc/LoadBalancerTest.java
index beaf3335e2..be3d10ba2a 100644
--- a/api/src/test/java/io/grpc/LoadBalancerTest.java
+++ b/api/src/test/java/io/grpc/LoadBalancerTest.java
@@ -235,14 +235,13 @@ public class LoadBalancerTest {
@Deprecated
@Test
- public void handleResolvedAddresses_delegatesToAcceptResolvedAddresses() {
+ public void handleResolvedAddressGroups_delegatesToHandleResolvedAddresses() {
final AtomicReference resultCapture = new AtomicReference<>();
LoadBalancer balancer = new LoadBalancer() {
@Override
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
resultCapture.set(resolvedAddresses);
- return true;
}
@Override
@@ -261,22 +260,23 @@ public class LoadBalancerTest {
List servers = Arrays.asList(
new EquivalentAddressGroup(new SocketAddress(){}),
new EquivalentAddressGroup(new SocketAddress(){}));
- ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers)
- .setAttributes(attrs).build();
- balancer.handleResolvedAddresses(addresses);
+ balancer.handleResolvedAddressGroups(servers, attrs);
assertThat(resultCapture.get()).isEqualTo(
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(attrs).build());
}
@Deprecated
@Test
- public void acceptResolvedAddresses_delegatesToHandleResolvedAddressGroups() {
- final AtomicReference addressesCapture = new AtomicReference<>();
+ public void handleResolvedAddresses_delegatesToHandleResolvedAddressGroups() {
+ final AtomicReference> serversCapture = new AtomicReference<>();
+ final AtomicReference attrsCapture = new AtomicReference<>();
LoadBalancer balancer = new LoadBalancer() {
@Override
- public void handleResolvedAddresses(ResolvedAddresses addresses) {
- addressesCapture.set(addresses);
+ public void handleResolvedAddressGroups(
+ List servers, Attributes attrs) {
+ serversCapture.set(servers);
+ attrsCapture.set(attrs);
}
@Override
@@ -295,23 +295,25 @@ public class LoadBalancerTest {
List servers = Arrays.asList(
new EquivalentAddressGroup(new SocketAddress(){}),
new EquivalentAddressGroup(new SocketAddress(){}));
- ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers)
- .setAttributes(attrs).build();
- balancer.handleResolvedAddresses(addresses);
- assertThat(addressesCapture.get().getAddresses()).isEqualTo(servers);
- assertThat(addressesCapture.get().getAttributes()).isEqualTo(attrs);
+ balancer.handleResolvedAddresses(
+ ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(attrs).build());
+ assertThat(serversCapture.get()).isEqualTo(servers);
+ assertThat(attrsCapture.get()).isEqualTo(attrs);
}
@Deprecated
@Test
- public void acceptResolvedAddresses_noInfiniteLoop() {
- final List addressesCapture = new ArrayList<>();
+ public void handleResolvedAddresses_noInfiniteLoop() {
+ final List> serversCapture = new ArrayList<>();
+ final List attrsCapture = new ArrayList<>();
LoadBalancer balancer = new LoadBalancer() {
@Override
- public void handleResolvedAddresses(ResolvedAddresses addresses) {
- addressesCapture.add(addresses);
- super.handleResolvedAddresses(addresses);
+ public void handleResolvedAddressGroups(
+ List servers, Attributes attrs) {
+ serversCapture.add(servers);
+ attrsCapture.add(attrs);
+ super.handleResolvedAddressGroups(servers, attrs);
}
@Override
@@ -326,12 +328,12 @@ public class LoadBalancerTest {
List servers = Arrays.asList(
new EquivalentAddressGroup(new SocketAddress(){}),
new EquivalentAddressGroup(new SocketAddress(){}));
- ResolvedAddresses addresses = ResolvedAddresses.newBuilder().setAddresses(servers)
- .setAttributes(attrs).build();
- balancer.handleResolvedAddresses(addresses);
- assertThat(addressesCapture).hasSize(1);
- assertThat(addressesCapture.get(0).getAddresses()).isEqualTo(servers);
- assertThat(addressesCapture.get(0).getAttributes()).isEqualTo(attrs);
+ balancer.handleResolvedAddresses(
+ ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(attrs).build());
+ assertThat(serversCapture).hasSize(1);
+ assertThat(attrsCapture).hasSize(1);
+ assertThat(serversCapture.get(0)).isEqualTo(servers);
+ assertThat(attrsCapture.get(0)).isEqualTo(attrs);
}
private static class NoopHelper extends LoadBalancer.Helper {
diff --git a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
index b8c9cab745..01c48b9efc 100644
--- a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
+++ b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
@@ -20,9 +20,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
+import io.grpc.Attributes;
import io.grpc.ChannelLogger.ChannelLogLevel;
import io.grpc.ConnectivityState;
import io.grpc.ConnectivityStateInfo;
+import io.grpc.EquivalentAddressGroup;
import io.grpc.LoadBalancer;
import io.grpc.LoadBalancer.Helper;
import io.grpc.LoadBalancer.PickResult;
@@ -65,14 +67,10 @@ public final class AutoConfiguredLoadBalancerFactory {
@Override
@Deprecated
- @SuppressWarnings("InlineMeSuggester")
- public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
- }
+ public void handleResolvedAddressGroups(List s, Attributes a) {}
@Override
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
- return true;
- }
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {}
@Override
public void handleNameResolutionError(Status error) {}
@@ -99,10 +97,14 @@ public final class AutoConfiguredLoadBalancerFactory {
}
/**
- * Returns non-OK status if the delegate rejects the resolvedAddresses (e.g. if it does not
- * support an empty list).
+ * Returns non-OK status if resolvedAddresses is empty and delegate lb requires address ({@link
+ * LoadBalancer#canHandleEmptyAddressListFromNameResolution()} returns {@code false}). {@code
+ * AutoConfiguredLoadBalancer} doesn't expose {@code
+ * canHandleEmptyAddressListFromNameResolution} because it depends on the delegated LB.
*/
- boolean tryAcceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ Status tryHandleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ List servers = resolvedAddresses.getAddresses();
+ Attributes attributes = resolvedAddresses.getAttributes();
PolicySelection policySelection =
(PolicySelection) resolvedAddresses.getLoadBalancingPolicyConfig();
@@ -116,7 +118,7 @@ public final class AutoConfiguredLoadBalancerFactory {
delegate.shutdown();
delegateProvider = null;
delegate = new NoopLoadBalancer();
- return true;
+ return Status.OK;
}
policySelection =
new PolicySelection(defaultProvider, /* config= */ null);
@@ -139,12 +141,20 @@ public final class AutoConfiguredLoadBalancerFactory {
ChannelLogLevel.DEBUG, "Load-balancing config: {0}", policySelection.config);
}
- return getDelegate().acceptResolvedAddresses(
- ResolvedAddresses.newBuilder()
- .setAddresses(resolvedAddresses.getAddresses())
- .setAttributes(resolvedAddresses.getAttributes())
- .setLoadBalancingPolicyConfig(lbConfig)
- .build());
+ LoadBalancer delegate = getDelegate();
+ if (resolvedAddresses.getAddresses().isEmpty()
+ && !delegate.canHandleEmptyAddressListFromNameResolution()) {
+ return Status.UNAVAILABLE.withDescription(
+ "NameResolver returned no usable address. addrs=" + servers + ", attrs=" + attributes);
+ } else {
+ delegate.handleResolvedAddresses(
+ ResolvedAddresses.newBuilder()
+ .setAddresses(resolvedAddresses.getAddresses())
+ .setAttributes(attributes)
+ .setLoadBalancingPolicyConfig(lbConfig)
+ .build());
+ return Status.OK;
+ }
}
void handleNameResolutionError(Status error) {
diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
index acf0147ff0..3e2e3ec17f 100644
--- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
+++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
@@ -1860,17 +1860,16 @@ final class ManagedChannelImpl extends ManagedChannel implements
.set(LoadBalancer.ATTR_HEALTH_CHECKING_CONFIG, healthCheckingConfig)
.build();
}
- Attributes attributes = attrBuilder.build();
- boolean addressesAccepted = helper.lb.tryAcceptResolvedAddresses(
+ Status handleResult = helper.lb.tryHandleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(servers)
- .setAttributes(attributes)
+ .setAttributes(attrBuilder.build())
.setLoadBalancingPolicyConfig(effectiveServiceConfig.getLoadBalancingConfig())
.build());
- if (!addressesAccepted) {
- scheduleExponentialBackOffInSyncContext();
+ if (!handleResult.isOk()) {
+ handleErrorInSyncContext(handleResult.augmentDescription(resolver + " was used"));
}
}
}
diff --git a/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java b/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java
index b105a9ed0c..d5f74db54a 100644
--- a/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java
+++ b/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java
@@ -45,7 +45,7 @@ final class PickFirstLoadBalancer extends LoadBalancer {
}
@Override
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
List servers = resolvedAddresses.getAddresses();
if (subchannel == null) {
final Subchannel subchannel = helper.createSubchannel(
@@ -67,8 +67,6 @@ final class PickFirstLoadBalancer extends LoadBalancer {
} else {
subchannel.updateAddresses(servers);
}
-
- return true;
}
@Override
diff --git a/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java b/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java
index 65588b1181..628eda3b71 100644
--- a/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java
+++ b/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java
@@ -17,10 +17,14 @@
package io.grpc.util;
import com.google.common.base.MoreObjects;
+import io.grpc.Attributes;
import io.grpc.ConnectivityStateInfo;
+import io.grpc.EquivalentAddressGroup;
import io.grpc.ExperimentalApi;
import io.grpc.LoadBalancer;
+import io.grpc.NameResolver;
import io.grpc.Status;
+import java.util.List;
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1771")
public abstract class ForwardingLoadBalancer extends LoadBalancer {
@@ -31,13 +35,15 @@ public abstract class ForwardingLoadBalancer extends LoadBalancer {
@Override
@Deprecated
- public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
- delegate().handleResolvedAddresses(resolvedAddresses);
+ public void handleResolvedAddressGroups(
+ List servers,
+ @NameResolver.ResolutionResultAttr Attributes attributes) {
+ delegate().handleResolvedAddressGroups(servers, attributes);
}
@Override
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
- return delegate().acceptResolvedAddresses(resolvedAddresses);
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ delegate().handleResolvedAddresses(resolvedAddresses);
}
@Override
diff --git a/core/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java b/core/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java
index ca6d77642e..7cba3098ca 100644
--- a/core/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java
+++ b/core/src/main/java/io/grpc/util/GracefulSwitchLoadBalancer.java
@@ -35,7 +35,7 @@ import javax.annotation.concurrent.NotThreadSafe;
* will keep using the old policy until the new policy reports READY or the old policy exits READY.
*
* The balancer must {@link #switchTo(LoadBalancer.Factory) switch to} a policy prior to {@link
- * LoadBalancer#acceptResolvedAddresses(ResolvedAddresses) handling resolved addresses} for the
+ * LoadBalancer#handleResolvedAddresses(ResolvedAddresses) handling resolved addresses} for the
* first time.
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/5999")
@@ -43,7 +43,7 @@ import javax.annotation.concurrent.NotThreadSafe;
public final class GracefulSwitchLoadBalancer extends ForwardingLoadBalancer {
private final LoadBalancer defaultBalancer = new LoadBalancer() {
@Override
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
// Most LB policies using this class will receive child policy configuration within the
// service config, so they are naturally calling switchTo() just before
// handleResolvedAddresses(), within their own handleResolvedAddresses(). If switchTo() is
diff --git a/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java b/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java
index 905832d44d..4a6a1ff561 100644
--- a/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java
+++ b/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java
@@ -69,7 +69,7 @@ final class RoundRobinLoadBalancer extends LoadBalancer {
}
@Override
- public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) {
+ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
List servers = resolvedAddresses.getAddresses();
Set currentAddrs = subchannels.keySet();
Map latestAddrs = stripAttrs(servers);
@@ -126,8 +126,6 @@ final class RoundRobinLoadBalancer extends LoadBalancer {
for (Subchannel removedSubchannel : removedSubchannels) {
shutdownSubchannel(removedSubchannel);
}
-
- return !resolvedAddresses.getAddresses().isEmpty();
}
@Override
diff --git a/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java b/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java
index ad886c3114..bf7c63818c 100644
--- a/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java
+++ b/core/src/test/java/io/grpc/internal/AutoConfiguredLoadBalancerFactoryTest.java
@@ -21,8 +21,8 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.AdditionalAnswers.delegatesTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -97,8 +97,9 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@Before
public void setUp() {
- when(testLbBalancer.acceptResolvedAddresses(isA(ResolvedAddresses.class))).thenReturn(true);
- when(testLbBalancer2.acceptResolvedAddresses(isA(ResolvedAddresses.class))).thenReturn(true);
+ when(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).thenCallRealMethod();
+ assertThat(testLbBalancer.canHandleEmptyAddressListFromNameResolution()).isFalse();
+ when(testLbBalancer2.canHandleEmptyAddressListFromNameResolution()).thenReturn(true);
defaultRegistry.register(testLbBalancerProvider);
defaultRegistry.register(testLbBalancerProvider2);
}
@@ -170,7 +171,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
}
@Test
- public void acceptResolvedAddresses_keepOldBalancer() {
+ public void handleResolvedAddressGroups_keepOldBalancer() {
final List servers =
Collections.singletonList(new EquivalentAddressGroup(new SocketAddress(){}));
Helper helper = new TestHelper() {
@@ -183,19 +184,19 @@ public class AutoConfiguredLoadBalancerFactoryTest {
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
LoadBalancer oldDelegate = lb.getDelegate();
- boolean addressesAccepted = lb.tryAcceptResolvedAddresses(
+ Status handleResult = lb.tryHandleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(servers)
.setAttributes(Attributes.EMPTY)
.setLoadBalancingPolicyConfig(null)
.build());
- assertThat(addressesAccepted).isTrue();
+ assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
assertThat(lb.getDelegate()).isSameInstanceAs(oldDelegate);
}
@Test
- public void acceptResolvedAddresses_shutsDownOldBalancer() throws Exception {
+ public void handleResolvedAddressGroups_shutsDownOldBalancer() throws Exception {
Map serviceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"round_robin\": { } } ] }");
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(serviceConfig);
@@ -225,13 +226,13 @@ public class AutoConfiguredLoadBalancerFactoryTest {
};
lb.setDelegate(testlb);
- boolean addressesAccepted = lb.tryAcceptResolvedAddresses(
+ Status handleResult = lb.tryHandleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(servers)
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
.build());
- assertThat(addressesAccepted).isTrue();
+ assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
assertThat(lb.getDelegateProvider().getClass().getName()).isEqualTo(
"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider");
assertTrue(shutdown.get());
@@ -239,7 +240,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
@Test
@SuppressWarnings("unchecked")
- public void acceptResolvedAddresses_propagateLbConfigToDelegate() throws Exception {
+ public void handleResolvedAddressGroups_propagateLbConfigToDelegate() throws Exception {
Map rawServiceConfig =
parseConfig("{\"loadBalancingConfig\": [ {\"test_lb\": { \"setting1\": \"high\" } } ] }");
ConfigOrError lbConfigs = lbf.parseLoadBalancerPolicy(rawServiceConfig);
@@ -250,19 +251,20 @@ public class AutoConfiguredLoadBalancerFactoryTest {
Helper helper = new TestHelper();
AutoConfiguredLoadBalancer lb = lbf.newLoadBalancer(helper);
- boolean addressesAccepted = lb.tryAcceptResolvedAddresses(
+ Status handleResult = lb.tryHandleResolvedAddresses(
ResolvedAddresses.newBuilder()
.setAddresses(servers)
.setLoadBalancingPolicyConfig(lbConfigs.getConfig())
.build());
verify(testLbBalancerProvider).newLoadBalancer(same(helper));
- assertThat(addressesAccepted).isTrue();
+ assertThat(handleResult.getCode()).isEqualTo(Status.Code.OK);
assertThat(lb.getDelegate()).isSameInstanceAs(testLbBalancer);
ArgumentCaptor resultCaptor =
ArgumentCaptor.forClass(ResolvedAddresses.class);
- verify(testLbBalancer).acceptResolvedAddresses(resultCaptor.capture());
+ verify(testLbBalancer).handleResolvedAddresses(resultCaptor.capture());
assertThat(resultCaptor.getValue().getAddresses()).containsExactlyElementsIn(servers).inOrder();
+ verify(testLbBalancer, atLeast(0)).canHandleEmptyAddressListFromNameResolution();
ArgumentCaptor