core: Don't reuse channels in PickFirstLeafLB test

PickFirstLeafLB uses channel reference equality to see if it has
re-created subchannels. The tests reusing channels breaks the expected
invariant.
This commit is contained in:
Eric Anderson 2024-08-02 11:40:02 -07:00 committed by GitHub
parent c29763d886
commit e567b4427a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 99 additions and 65 deletions

View File

@ -123,16 +123,14 @@ public class PickFirstLeafLoadBalancerTest {
private ArgumentCaptor<CreateSubchannelArgs> createArgsCaptor;
@Captor
private ArgumentCaptor<SubchannelStateListener> stateListenerCaptor;
private final Helper mockHelper = mock(Helper.class, delegatesTo(new MockHelperImpl()));
@Mock
private Helper mockHelper;
private FakeSubchannel mockSubchannel1;
@Mock
private FakeSubchannel mockSubchannel1n2;
private FakeSubchannel mockSubchannel2;
@Mock
private FakeSubchannel mockSubchannel2n2;
private FakeSubchannel mockSubchannel3;
@Mock
private FakeSubchannel mockSubchannel3n2;
private FakeSubchannel mockSubchannel4;
@Mock
private FakeSubchannel mockSubchannel5;
@Mock // This LoadBalancer doesn't use any of the arg fields, as verified in tearDown().
private PickSubchannelArgs mockArgs;
@ -150,23 +148,28 @@ public class PickFirstLeafLoadBalancerTest {
SocketAddress addr = new FakeSocketAddress("server" + i);
servers.add(new EquivalentAddressGroup(addr));
}
mockSubchannel1 = mock(FakeSubchannel.class);
mockSubchannel2 = mock(FakeSubchannel.class);
mockSubchannel3 = mock(FakeSubchannel.class);
mockSubchannel4 = mock(FakeSubchannel.class);
mockSubchannel5 = mock(FakeSubchannel.class);
when(mockSubchannel1.getAttributes()).thenReturn(Attributes.EMPTY);
when(mockSubchannel2.getAttributes()).thenReturn(Attributes.EMPTY);
when(mockSubchannel3.getAttributes()).thenReturn(Attributes.EMPTY);
when(mockSubchannel4.getAttributes()).thenReturn(Attributes.EMPTY);
when(mockSubchannel5.getAttributes()).thenReturn(Attributes.EMPTY);
when(mockSubchannel1.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(0)));
when(mockSubchannel2.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(1)));
when(mockSubchannel3.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(2)));
when(mockSubchannel4.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(3)));
when(mockSubchannel5.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(4)));
mockSubchannel1 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(0)), Attributes.EMPTY)));
mockSubchannel1n2 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(0)), Attributes.EMPTY)));
mockSubchannel2 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(1)), Attributes.EMPTY)));
mockSubchannel2n2 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(1)), Attributes.EMPTY)));
mockSubchannel3 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(2)), Attributes.EMPTY)));
mockSubchannel3n2 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(2)), Attributes.EMPTY)));
mockSubchannel4 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(3)), Attributes.EMPTY)));
mockSubchannel5 = mock(FakeSubchannel.class, delegatesTo(
new FakeSubchannel(Arrays.asList(servers.get(4)), Attributes.EMPTY)));
mockHelper = mock(Helper.class, delegatesTo(new MockHelperImpl(Arrays.asList(
mockSubchannel1, mockSubchannel1n2,
mockSubchannel2, mockSubchannel2n2,
mockSubchannel3, mockSubchannel3n2,
mockSubchannel4, mockSubchannel5))));
loadBalancer = new PickFirstLeafLoadBalancer(mockHelper);
}
@ -251,14 +254,14 @@ public class PickFirstLeafLoadBalancerTest {
PickResult pick2 = pickerCaptor.getValue().pickSubchannel(mockArgs);
assertEquals(pick1, pick2);
verifyNoMoreInteractions(mockHelper);
assertThat(pick1.toString()).contains("subchannel=null");
assertThat(pick1.getSubchannel()).isNull();
stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY));
verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
PickResult pick3 = pickerCaptor.getValue().pickSubchannel(mockArgs);
PickResult pick4 = pickerCaptor.getValue().pickSubchannel(mockArgs);
assertEquals(pick3, pick4);
assertThat(pick3.toString()).contains("subchannel=Mock");
assertThat(pick3.getSubchannel()).isEqualTo(mockSubchannel2);
}
@Test
@ -569,7 +572,7 @@ public class PickFirstLeafLoadBalancerTest {
InOrder inOrder = inOrder(mockHelper);
SocketAddress socketAddress = servers.get(0).getAddresses().get(0);
EquivalentAddressGroup badEag = new EquivalentAddressGroup(
Lists.newArrayList(socketAddress, socketAddress), affinity);
Lists.newArrayList(socketAddress, socketAddress));
List<EquivalentAddressGroup> newServers = Lists.newArrayList(badEag);
loadBalancer.acceptResolvedAddresses(
@ -727,7 +730,7 @@ public class PickFirstLeafLoadBalancerTest {
@Test
public void nameResolutionTemporaryError() {
List<EquivalentAddressGroup> newServers = Lists.newArrayList(servers.get(0));
InOrder inOrder = inOrder(mockHelper, mockSubchannel1);
InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel1n2);
loadBalancer.acceptResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(newServers).setAttributes(affinity).build());
inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture());
@ -744,14 +747,15 @@ public class PickFirstLeafLoadBalancerTest {
loadBalancer.acceptResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(affinity).build());
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture());
inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture());
SubchannelStateListener stateListener2 = stateListenerCaptor.getValue();
assertNull(pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel());
stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY));
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
assertEquals(mockSubchannel1, pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel());
assertEquals(mockSubchannel1n2,
pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel());
}
@ -1027,7 +1031,7 @@ public class PickFirstLeafLoadBalancerTest {
@Test
public void updateAddresses_disjoint_ready_twice() {
InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2,
mockSubchannel3, mockSubchannel4);
mockSubchannel3, mockSubchannel4, mockSubchannel1n2, mockSubchannel2n2);
// Creating first set of endpoints/addresses
List<EquivalentAddressGroup> oldServers = Lists.newArrayList(servers.get(0), servers.get(1));
SubchannelStateListener stateListener2 = null;
@ -1126,7 +1130,7 @@ public class PickFirstLeafLoadBalancerTest {
ResolvedAddresses.newBuilder().setAddresses(newestServers).setAttributes(affinity).build());
inOrder.verify(mockSubchannel3).shutdown();
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture());
inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture());
stateListener = stateListenerCaptor.getValue();
assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState());
picker = pickerCaptor.getValue();
@ -1135,7 +1139,7 @@ public class PickFirstLeafLoadBalancerTest {
assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs));
// But the picker calls requestConnection() only once
inOrder.verify(mockSubchannel1).requestConnection();
inOrder.verify(mockSubchannel1n2).requestConnection();
assertEquals(PickResult.withNoResult(), pickerCaptor.getValue().pickSubchannel(mockArgs));
assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState());
@ -1150,23 +1154,24 @@ public class PickFirstLeafLoadBalancerTest {
stateListener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(CONNECTION_ERROR));
// Starting connection attempt to address 2
if (!enableHappyEyeballs) {
inOrder.verify(mockSubchannel2).start(stateListenerCaptor.capture());
FakeSubchannel mockSubchannel2Attempt =
enableHappyEyeballs ? mockSubchannel2n2 : mockSubchannel2;
inOrder.verify(mockSubchannel2Attempt).start(stateListenerCaptor.capture());
stateListener2 = stateListenerCaptor.getValue();
}
inOrder.verify(mockSubchannel2).requestConnection();
inOrder.verify(mockSubchannel2Attempt).requestConnection();
// Connection attempt to address 2 is successful
stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY));
assertEquals(READY, loadBalancer.getConcludedConnectivityState());
inOrder.verify(mockSubchannel1).shutdown();
inOrder.verify(mockSubchannel1n2).shutdown();
// Successful connection shuts down other subchannel
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
picker = pickerCaptor.getValue();
// Verify that picker still returns correct subchannel
assertEquals(PickResult.withSubchannel(mockSubchannel2), picker.pickSubchannel(mockArgs));
assertEquals(
PickResult.withSubchannel(mockSubchannel2Attempt), picker.pickSubchannel(mockArgs));
}
@Test
@ -2048,7 +2053,7 @@ public class PickFirstLeafLoadBalancerTest {
// Starting first connection attempt
InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2,
mockSubchannel3, mockSubchannel4); // captor: captures
mockSubchannel3, mockSubchannel4, mockSubchannel1n2); // captor: captures
// Creating first set of endpoints/addresses
List<EquivalentAddressGroup> addrs =
@ -2084,9 +2089,9 @@ public class PickFirstLeafLoadBalancerTest {
// Calling pickSubchannel() requests a connection.
assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs));
inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture());
inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture());
SubchannelStateListener stateListener3 = stateListenerCaptor.getValue();
inOrder.verify(mockSubchannel1).requestConnection();
inOrder.verify(mockSubchannel1n2).requestConnection();
when(mockSubchannel1.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(0)));
// gives the same result when called twice
@ -2101,7 +2106,7 @@ public class PickFirstLeafLoadBalancerTest {
// second subchannel connection attempt succeeds
inOrder.verify(mockSubchannel2).requestConnection();
stateListener2.onSubchannelState(ConnectivityStateInfo.forNonError(READY));
inOrder.verify(mockSubchannel1).shutdown();
inOrder.verify(mockSubchannel1n2).shutdown();
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
assertEquals(READY, loadBalancer.getConcludedConnectivityState());
@ -2146,7 +2151,7 @@ public class PickFirstLeafLoadBalancerTest {
public void ready_then_transient_failure_again() {
// Starting first connection attempt
InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2,
mockSubchannel3, mockSubchannel4); // captor: captures
mockSubchannel3, mockSubchannel4, mockSubchannel1n2); // captor: captures
// Creating first set of endpoints/addresses
List<EquivalentAddressGroup> addrs =
@ -2183,9 +2188,9 @@ public class PickFirstLeafLoadBalancerTest {
// Calling pickSubchannel() requests a connection, gives the same result when called twice.
assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs));
inOrder.verify(mockSubchannel1).start(stateListenerCaptor.capture());
inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture());
SubchannelStateListener stateListener3 = stateListenerCaptor.getValue();
inOrder.verify(mockSubchannel1).requestConnection();
inOrder.verify(mockSubchannel1n2).requestConnection();
when(mockSubchannel3.getAllAddresses()).thenReturn(Lists.newArrayList(servers.get(0)));
stateListener3.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING));
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
@ -2201,7 +2206,7 @@ public class PickFirstLeafLoadBalancerTest {
assertEquals(READY, loadBalancer.getConcludedConnectivityState());
// verify that picker returns correct subchannel
inOrder.verify(mockSubchannel1).shutdown();
inOrder.verify(mockSubchannel1n2).shutdown();
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
picker = pickerCaptor.getValue();
assertEquals(PickResult.withSubchannel(mockSubchannel2), picker.pickSubchannel(mockArgs));
@ -2309,7 +2314,8 @@ public class PickFirstLeafLoadBalancerTest {
public void happy_eyeballs_pick_pushes_index_over_end() {
Assume.assumeTrue(enableHappyEyeballs); // This test is only for happy eyeballs
InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, mockSubchannel3);
InOrder inOrder = inOrder(mockHelper, mockSubchannel1, mockSubchannel2, mockSubchannel3,
mockSubchannel2n2, mockSubchannel3n2);
Status error = Status.UNAUTHENTICATED.withDescription("simulated failure");
List<EquivalentAddressGroup> addrs =
@ -2359,9 +2365,9 @@ public class PickFirstLeafLoadBalancerTest {
// Try pushing after end with just picks
listeners[0].onSubchannelState(ConnectivityStateInfo.forNonError(READY));
for (SubchannelStateListener listener : listeners) {
listener.onSubchannelState(ConnectivityStateInfo.forNonError(IDLE));
}
verify(mockSubchannel2).shutdown();
verify(mockSubchannel3).shutdown();
listeners[0].onSubchannelState(ConnectivityStateInfo.forNonError(IDLE));
loadBalancer.acceptResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(addrs).setAttributes(affinity).build());
inOrder.verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
@ -2372,11 +2378,14 @@ public class PickFirstLeafLoadBalancerTest {
}
assertEquals(IDLE, loadBalancer.getConcludedConnectivityState());
for (SubchannelStateListener listener : listeners) {
listener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
}
listeners[0].onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
inOrder.verify(mockSubchannel2n2).start(stateListenerCaptor.capture());
stateListenerCaptor.getValue().onSubchannelState(
ConnectivityStateInfo.forTransientFailure(error));
inOrder.verify(mockSubchannel3n2).start(stateListenerCaptor.capture());
stateListenerCaptor.getValue().onSubchannelState(
ConnectivityStateInfo.forTransientFailure(error));
assertEquals(TRANSIENT_FAILURE, loadBalancer.getConcludedConnectivityState());
}
@Test
@ -2571,9 +2580,22 @@ public class PickFirstLeafLoadBalancerTest {
@Override
public String toString() {
return "FakeSocketAddress-" + name;
return "FakeSocketAddress(" + name + ")";
}
@Override
public boolean equals(Object o) {
if (!(o instanceof FakeSocketAddress)) {
return false;
}
FakeSocketAddress that = (FakeSocketAddress) o;
return this.name.equals(that.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
private void forwardTimeByConnectionDelay() {
@ -2631,15 +2653,26 @@ public class PickFirstLeafLoadBalancerTest {
@Override
public void shutdown() {
listener.onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN));
}
@Override
public void requestConnection() {
listener.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING));
}
@Override
public String toString() {
return "FakeSubchannel@" + hashCode() + "(" + eags + ")";
}
}
private class MockHelperImpl extends LoadBalancer.Helper {
private final List<Subchannel> subchannels;
public MockHelperImpl(List<? extends Subchannel> subchannels) {
this.subchannels = new ArrayList<Subchannel>(subchannels);
}
@Override
public ManagedChannel createOobChannel(EquivalentAddressGroup eag, String authority) {
return null;
@ -2672,16 +2705,17 @@ public class PickFirstLeafLoadBalancerTest {
@Override
public Subchannel createSubchannel(CreateSubchannelArgs args) {
SocketAddress addr = args.getAddresses().get(0).getAddresses().get(0);
List<FakeSubchannel> fakeSubchannels =
Arrays.asList(mockSubchannel1, mockSubchannel2, mockSubchannel3, mockSubchannel4,
mockSubchannel5);
for (int i = 1; i <= 5; i++) {
if (addr.toString().equals(new FakeSocketAddress("server" + i).toString())) {
return fakeSubchannels.get(i - 1);
for (int i = 0; i < subchannels.size(); i++) {
Subchannel subchannel = subchannels.get(i);
List<EquivalentAddressGroup> addrs = subchannel.getAllAddresses();
verify(subchannel, atLeast(1)).getAllAddresses(); // ignore the interaction
if (!args.getAddresses().equals(addrs)) {
continue;
}
subchannels.remove(i);
return subchannel;
}
throw new IllegalArgumentException("Unexpected address: " + addr);
throw new IllegalArgumentException("Unexpected addresses: " + args.getAddresses());
}
}
}