core: In PF, disjoint update while READY should transition to IDLE

This is the same as if we received a GOAWAY. We wait for the next RPC to
begin connecting again. This is InternalSubchannel's behavior.
This commit is contained in:
Eric Anderson 2024-08-17 11:23:01 -07:00
parent f20167d602
commit ee3ffef3ee
2 changed files with 22 additions and 15 deletions

View File

@ -152,20 +152,18 @@ final class PickFirstLeafLoadBalancer extends LoadBalancer {
} }
} }
if (oldAddrs.size() == 0 || rawConnectivityState == CONNECTING if (oldAddrs.size() == 0) {
|| rawConnectivityState == READY) { // Make tests happy; they don't properly assume starting in CONNECTING
// start connection attempt at first address
rawConnectivityState = CONNECTING; rawConnectivityState = CONNECTING;
updateBalancingState(CONNECTING, new Picker(PickResult.withNoResult())); updateBalancingState(CONNECTING, new Picker(PickResult.withNoResult()));
cancelScheduleTask(); }
requestConnection();
} else if (rawConnectivityState == IDLE) { if (rawConnectivityState == READY) {
// start connection attempt at first address when requested // connect from beginning when prompted
SubchannelPicker picker = new RequestConnectionPicker(this); rawConnectivityState = IDLE;
updateBalancingState(IDLE, picker); updateBalancingState(IDLE, new RequestConnectionPicker(this));
} else if (rawConnectivityState == TRANSIENT_FAILURE) { } else if (rawConnectivityState == CONNECTING || rawConnectivityState == TRANSIENT_FAILURE) {
// start connection attempt at first address // start connection attempt at first address
cancelScheduleTask(); cancelScheduleTask();
requestConnection(); requestConnection();

View File

@ -1121,10 +1121,17 @@ public class PickFirstLeafLoadBalancerTest {
loadBalancer.acceptResolvedAddresses( loadBalancer.acceptResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(newServers).setAttributes(affinity).build()); ResolvedAddresses.newBuilder().setAddresses(newServers).setAttributes(affinity).build());
inOrder.verify(mockSubchannel1).shutdown(); inOrder.verify(mockSubchannel1).shutdown();
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture()); inOrder.verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
inOrder.verify(mockSubchannel3, never()).start(stateListenerCaptor.capture());
// Trigger connection creation
picker = pickerCaptor.getValue();
assertEquals(PickResult.withNoResult(), picker.pickSubchannel(mockArgs));
inOrder.verify(mockSubchannel3).start(stateListenerCaptor.capture()); inOrder.verify(mockSubchannel3).start(stateListenerCaptor.capture());
SubchannelStateListener stateListener3 = stateListenerCaptor.getValue(); SubchannelStateListener stateListener3 = stateListenerCaptor.getValue();
inOrder.verify(mockSubchannel3).requestConnection(); inOrder.verify(mockSubchannel3).requestConnection();
stateListener3.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING));
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
if (enableHappyEyeballs) { if (enableHappyEyeballs) {
forwardTimeByConnectionDelay(); forwardTimeByConnectionDelay();
@ -1171,17 +1178,19 @@ public class PickFirstLeafLoadBalancerTest {
loadBalancer.acceptResolvedAddresses( loadBalancer.acceptResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(newestServers).setAttributes(affinity).build()); ResolvedAddresses.newBuilder().setAddresses(newestServers).setAttributes(affinity).build());
inOrder.verify(mockSubchannel3).shutdown(); inOrder.verify(mockSubchannel3).shutdown();
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture()); inOrder.verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture()); assertEquals(IDLE, loadBalancer.getConcludedConnectivityState());
stateListener = stateListenerCaptor.getValue();
assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState());
picker = pickerCaptor.getValue(); picker = pickerCaptor.getValue();
// Calling pickSubchannel() twice gave the same result // Calling pickSubchannel() twice gave the same result
assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs)); assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs));
// But the picker calls requestConnection() only once // But the picker calls requestConnection() only once
inOrder.verify(mockSubchannel1n2).start(stateListenerCaptor.capture());
stateListener = stateListenerCaptor.getValue();
inOrder.verify(mockSubchannel1n2).requestConnection(); inOrder.verify(mockSubchannel1n2).requestConnection();
stateListener.onSubchannelState(ConnectivityStateInfo.forNonError(CONNECTING));
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
assertEquals(PickResult.withNoResult(), pickerCaptor.getValue().pickSubchannel(mockArgs)); assertEquals(PickResult.withNoResult(), pickerCaptor.getValue().pickSubchannel(mockArgs));
assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState()); assertEquals(CONNECTING, loadBalancer.getConcludedConnectivityState());