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
|| rawConnectivityState == READY) {
// start connection attempt at first address
if (oldAddrs.size() == 0) {
// Make tests happy; they don't properly assume starting in CONNECTING
rawConnectivityState = CONNECTING;
updateBalancingState(CONNECTING, new Picker(PickResult.withNoResult()));
cancelScheduleTask();
requestConnection();
}
} else if (rawConnectivityState == IDLE) {
// start connection attempt at first address when requested
SubchannelPicker picker = new RequestConnectionPicker(this);
updateBalancingState(IDLE, picker);
if (rawConnectivityState == READY) {
// connect from beginning when prompted
rawConnectivityState = IDLE;
updateBalancingState(IDLE, new RequestConnectionPicker(this));
} else if (rawConnectivityState == TRANSIENT_FAILURE) {
} else if (rawConnectivityState == CONNECTING || rawConnectivityState == TRANSIENT_FAILURE) {
// start connection attempt at first address
cancelScheduleTask();
requestConnection();

View File

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