Skip to content

Commit

Permalink
core: keep round_robin lb subchannel in TRANSIENT_FAILURE until becom…
Browse files Browse the repository at this point in the history
…ing READY (#6657)

Make each subchannel created by RR stay in TRANSIENT_FAILURE state until READY. That is, each subchannel ignores consequent non-READY states after TRANSIENT_FAILURE.
  • Loading branch information
voidzcy committed Apr 6, 2020
1 parent 37913fd commit 8e9ceb5
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
8 changes: 7 additions & 1 deletion core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java
Expand Up @@ -142,7 +142,13 @@ private void processSubchannelState(Subchannel subchannel, ConnectivityStateInfo
if (stateInfo.getState() == IDLE) {
subchannel.requestConnection();
}
getSubchannelStateInfoRef(subchannel).value = stateInfo;
Ref<ConnectivityStateInfo> subchannelStateRef = getSubchannelStateInfoRef(subchannel);
if (subchannelStateRef.value.getState().equals(TRANSIENT_FAILURE)) {
if (stateInfo.getState().equals(CONNECTING) || stateInfo.getState().equals(IDLE)) {
return;
}
}
subchannelStateRef.value = stateInfo;
updateBalancingState();
}

Expand Down
48 changes: 44 additions & 4 deletions core/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java
Expand Up @@ -279,21 +279,61 @@ public void pickAfterStateChange() throws Exception {
Status error = Status.UNKNOWN.withDescription(\\_(ツ)_//¯");
deliverSubchannelState(subchannel,
ConnectivityStateInfo.forTransientFailure(error));
assertThat(subchannelStateInfo.value).isEqualTo(
ConnectivityStateInfo.forTransientFailure(error));
assertThat(subchannelStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
assertThat(subchannelStateInfo.value.getStatus()).isEqualTo(error);
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
assertThat(pickerCaptor.getValue()).isInstanceOf(EmptyPicker.class);

deliverSubchannelState(subchannel,
ConnectivityStateInfo.forNonError(IDLE));
assertThat(subchannelStateInfo.value).isEqualTo(
ConnectivityStateInfo.forNonError(IDLE));
assertThat(subchannelStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
assertThat(subchannelStateInfo.value.getStatus()).isEqualTo(error);

verify(subchannel, times(2)).requestConnection();
verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
verifyNoMoreInteractions(mockHelper);
}

@Test
public void stayTransientFailureUntilReady() {
InOrder inOrder = inOrder(mockHelper);
loadBalancer.handleResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY)
.build());

inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), isA(EmptyPicker.class));

// Simulate state transitions for each subchannel individually.
for (Subchannel sc : loadBalancer.getSubchannels()) {
Status error = Status.UNKNOWN.withDescription("connection broken");
deliverSubchannelState(
sc,
ConnectivityStateInfo.forTransientFailure(error));
deliverSubchannelState(
sc,
ConnectivityStateInfo.forNonError(IDLE));
deliverSubchannelState(
sc,
ConnectivityStateInfo.forNonError(CONNECTING));
Ref<ConnectivityStateInfo> scStateInfo = sc.getAttributes().get(
STATE_INFO);
assertThat(scStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
assertThat(scStateInfo.value.getStatus()).isEqualTo(error);
}
inOrder.verify(mockHelper).updateBalancingState(eq(TRANSIENT_FAILURE), isA(EmptyPicker.class));
inOrder.verifyNoMoreInteractions();

Subchannel subchannel = loadBalancer.getSubchannels().iterator().next();
deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(READY));
Ref<ConnectivityStateInfo> subchannelStateInfo = subchannel.getAttributes().get(
STATE_INFO);
assertThat(subchannelStateInfo.value).isEqualTo(ConnectivityStateInfo.forNonError(READY));
inOrder.verify(mockHelper).updateBalancingState(eq(READY), isA(ReadyPicker.class));

verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
verifyNoMoreInteractions(mockHelper);
}

@Test
public void pickerRoundRobin() throws Exception {
Subchannel subchannel = mock(Subchannel.class);
Expand Down

0 comments on commit 8e9ceb5

Please sign in to comment.