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:
Larry Safran 2024-06-28 14:58:50 -07:00 committed by GitHub
parent 9e2145970a
commit 0b68eff120
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 24 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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");