mirror of https://github.com/grpc/grpc-java.git
core:Change address deduping to be across EAGs (#11345)
* Change dedup to be cross EAG, not just within an EAG
This commit is contained in:
parent
9e2145970a
commit
0b68eff120
|
|
@ -91,9 +91,6 @@ final class PickFirstLeafLoadBalancer extends LoadBalancer {
|
||||||
handleNameResolutionError(unavailableStatus);
|
handleNameResolutionError(unavailableStatus);
|
||||||
return unavailableStatus;
|
return unavailableStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<EquivalentAddressGroup> cleanServers = new ArrayList<>();
|
|
||||||
|
|
||||||
for (EquivalentAddressGroup eag : servers) {
|
for (EquivalentAddressGroup eag : servers) {
|
||||||
if (eag == null) {
|
if (eag == null) {
|
||||||
Status unavailableStatus = Status.UNAVAILABLE.withDescription(
|
Status unavailableStatus = Status.UNAVAILABLE.withDescription(
|
||||||
|
|
@ -103,12 +100,13 @@ final class PickFirstLeafLoadBalancer extends LoadBalancer {
|
||||||
handleNameResolutionError(unavailableStatus);
|
handleNameResolutionError(unavailableStatus);
|
||||||
return unavailableStatus;
|
return unavailableStatus;
|
||||||
}
|
}
|
||||||
cleanServers.add(removeDuplicateAddresses(eag));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since we have a new set of addresses, we are again at first pass
|
// Since we have a new set of addresses, we are again at first pass
|
||||||
firstPass = true;
|
firstPass = true;
|
||||||
|
|
||||||
|
List<EquivalentAddressGroup> cleanServers = deDupAddresses(servers);
|
||||||
|
|
||||||
// We can optionally be configured to shuffle the address list. This can help better distribute
|
// We can optionally be configured to shuffle the address list. This can help better distribute
|
||||||
// the load.
|
// the load.
|
||||||
if (resolvedAddresses.getLoadBalancingPolicyConfig()
|
if (resolvedAddresses.getLoadBalancingPolicyConfig()
|
||||||
|
|
@ -121,7 +119,6 @@ final class PickFirstLeafLoadBalancer extends LoadBalancer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we're storing our own list rather than what was passed in
|
|
||||||
final ImmutableList<EquivalentAddressGroup> newImmutableAddressGroups =
|
final ImmutableList<EquivalentAddressGroup> newImmutableAddressGroups =
|
||||||
ImmutableList.<EquivalentAddressGroup>builder().addAll(cleanServers).build();
|
ImmutableList.<EquivalentAddressGroup>builder().addAll(cleanServers).build();
|
||||||
|
|
||||||
|
|
@ -181,21 +178,23 @@ final class PickFirstLeafLoadBalancer extends LoadBalancer {
|
||||||
return Status.OK;
|
return Status.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static EquivalentAddressGroup removeDuplicateAddresses(EquivalentAddressGroup eag) {
|
private static List<EquivalentAddressGroup> deDupAddresses(List<EquivalentAddressGroup> groups) {
|
||||||
Set<SocketAddress> addressSet = new HashSet<>();
|
Set<SocketAddress> seenAddresses = new HashSet<>();
|
||||||
ArrayList<SocketAddress> addrs = new ArrayList<>(); // maintains order
|
List<EquivalentAddressGroup> newGroups = new ArrayList<>();
|
||||||
|
|
||||||
for (SocketAddress address : eag.getAddresses()) {
|
for (EquivalentAddressGroup group : groups) {
|
||||||
if (addressSet.add(address)) {
|
List<SocketAddress> addrs = new ArrayList<>();
|
||||||
addrs.add(address);
|
for (SocketAddress addr : group.getAddresses()) {
|
||||||
|
if (seenAddresses.add(addr)) {
|
||||||
|
addrs.add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!addrs.isEmpty()) {
|
||||||
|
newGroups.add(new EquivalentAddressGroup(addrs, group.getAttributes()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addressSet.size() == eag.getAddresses().size()) {
|
return newGroups;
|
||||||
return eag;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new EquivalentAddressGroup(addrs, eag.getAttributes());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ public final class PickFirstLoadBalancerProvider extends LoadBalancerProvider {
|
||||||
public static final String GRPC_PF_USE_HAPPY_EYEBALLS = "GRPC_PF_USE_HAPPY_EYEBALLS";
|
public static final String GRPC_PF_USE_HAPPY_EYEBALLS = "GRPC_PF_USE_HAPPY_EYEBALLS";
|
||||||
private static final String SHUFFLE_ADDRESS_LIST_KEY = "shuffleAddressList";
|
private static final String SHUFFLE_ADDRESS_LIST_KEY = "shuffleAddressList";
|
||||||
|
|
||||||
static boolean enableNewPickFirst =
|
private static boolean enableNewPickFirst =
|
||||||
GrpcUtil.getFlag("GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST", false);
|
GrpcUtil.getFlag("GRPC_EXPERIMENTAL_ENABLE_NEW_PICK_FIRST", false);
|
||||||
|
|
||||||
public static boolean isEnabledHappyEyeballs() {
|
public static boolean isEnabledHappyEyeballs() {
|
||||||
|
|
@ -44,11 +44,6 @@ public final class PickFirstLoadBalancerProvider extends LoadBalancerProvider {
|
||||||
return GrpcUtil.getFlag(GRPC_PF_USE_HAPPY_EYEBALLS, false);
|
return GrpcUtil.getFlag(GRPC_PF_USE_HAPPY_EYEBALLS, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public static boolean isEnableNewPickFirst() {
|
|
||||||
return enableNewPickFirst;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ public class AutoConfiguredLoadBalancerFactoryTest {
|
||||||
new FakeLoadBalancerProvider("test_lb2", testLbBalancer2, nextParsedConfigOrError2)));
|
new FakeLoadBalancerProvider("test_lb2", testLbBalancer2, nextParsedConfigOrError2)));
|
||||||
|
|
||||||
private final Class<? extends LoadBalancer> pfLbClass =
|
private final Class<? extends LoadBalancer> pfLbClass =
|
||||||
PickFirstLoadBalancerProvider.enableNewPickFirst
|
PickFirstLoadBalancerProvider.isEnabledNewPickFirst()
|
||||||
? PickFirstLeafLoadBalancer.class
|
? PickFirstLeafLoadBalancer.class
|
||||||
: PickFirstLoadBalancer.class;
|
: PickFirstLoadBalancer.class;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -565,7 +565,7 @@ public class PickFirstLeafLoadBalancerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pickAWithDupAddressesUpDownUp() {
|
public void pickWithDupAddressesUpDownUp() {
|
||||||
InOrder inOrder = inOrder(mockHelper);
|
InOrder inOrder = inOrder(mockHelper);
|
||||||
SocketAddress socketAddress = servers.get(0).getAddresses().get(0);
|
SocketAddress socketAddress = servers.get(0).getAddresses().get(0);
|
||||||
EquivalentAddressGroup badEag = new EquivalentAddressGroup(
|
EquivalentAddressGroup badEag = new EquivalentAddressGroup(
|
||||||
|
|
@ -599,6 +599,38 @@ public class PickFirstLeafLoadBalancerTest {
|
||||||
assertEquals(Status.OK, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
|
assertEquals(Status.OK, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pickWithDupEagsUpDownUp() {
|
||||||
|
InOrder inOrder = inOrder(mockHelper);
|
||||||
|
List<EquivalentAddressGroup> newServers = Lists.newArrayList(servers.get(0), servers.get(0));
|
||||||
|
|
||||||
|
loadBalancer.acceptResolvedAddresses(
|
||||||
|
ResolvedAddresses.newBuilder().setAddresses(newServers).setAttributes(affinity).build());
|
||||||
|
verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
|
||||||
|
verify(mockHelper).createSubchannel(createArgsCaptor.capture());
|
||||||
|
verify(mockSubchannel1).start(stateListenerCaptor.capture());
|
||||||
|
SubchannelStateListener stateListener = stateListenerCaptor.getValue();
|
||||||
|
|
||||||
|
reset(mockHelper);
|
||||||
|
|
||||||
|
// An error has happened.
|
||||||
|
Status error = Status.UNAVAILABLE.withDescription("boom!");
|
||||||
|
stateListener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
|
||||||
|
inOrder.verify(mockHelper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
|
||||||
|
inOrder.verify(mockHelper).refreshNameResolution();
|
||||||
|
assertEquals(error, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
|
||||||
|
|
||||||
|
// Transition from TRANSIENT_ERROR to CONNECTING should also be ignored.
|
||||||
|
stateListener.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING));
|
||||||
|
verifyNoMoreInteractions(mockHelper);
|
||||||
|
assertEquals(error, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
|
||||||
|
|
||||||
|
// Transition from CONNECTING to READY .
|
||||||
|
stateListener.onSubchannelState(ConnectivityStateInfo.forNonError(READY));
|
||||||
|
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
|
||||||
|
assertEquals(Status.OK, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nameResolutionError() {
|
public void nameResolutionError() {
|
||||||
Status error = Status.NOT_FOUND.withDescription("nameResolutionError");
|
Status error = Status.NOT_FOUND.withDescription("nameResolutionError");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue