Skip to content

Commit 8e9ceb5

Browse files
authored
core: keep round_robin lb subchannel in TRANSIENT_FAILURE until becoming 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.
1 parent 37913fd commit 8e9ceb5

File tree

2 files changed

+51
-5
lines changed

2 files changed

+51
-5
lines changed

core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,13 @@ private void processSubchannelState(Subchannel subchannel, ConnectivityStateInfo
142142
if (stateInfo.getState() == IDLE) {
143143
subchannel.requestConnection();
144144
}
145-
getSubchannelStateInfoRef(subchannel).value = stateInfo;
145+
Ref<ConnectivityStateInfo> subchannelStateRef = getSubchannelStateInfoRef(subchannel);
146+
if (subchannelStateRef.value.getState().equals(TRANSIENT_FAILURE)) {
147+
if (stateInfo.getState().equals(CONNECTING) || stateInfo.getState().equals(IDLE)) {
148+
return;
149+
}
150+
}
151+
subchannelStateRef.value = stateInfo;
146152
updateBalancingState();
147153
}
148154

core/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,21 +279,61 @@ public void pickAfterStateChange() throws Exception {
279279
Status error = Status.UNKNOWN.withDescription(\\_(ツ)_//¯");
280280
deliverSubchannelState(subchannel,
281281
ConnectivityStateInfo.forTransientFailure(error));
282-
assertThat(subchannelStateInfo.value).isEqualTo(
283-
ConnectivityStateInfo.forTransientFailure(error));
282+
assertThat(subchannelStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
283+
assertThat(subchannelStateInfo.value.getStatus()).isEqualTo(error);
284284
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
285285
assertThat(pickerCaptor.getValue()).isInstanceOf(EmptyPicker.class);
286286

287287
deliverSubchannelState(subchannel,
288288
ConnectivityStateInfo.forNonError(IDLE));
289-
assertThat(subchannelStateInfo.value).isEqualTo(
290-
ConnectivityStateInfo.forNonError(IDLE));
289+
assertThat(subchannelStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
290+
assertThat(subchannelStateInfo.value.getStatus()).isEqualTo(error);
291291

292292
verify(subchannel, times(2)).requestConnection();
293293
verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
294294
verifyNoMoreInteractions(mockHelper);
295295
}
296296

297+
@Test
298+
public void stayTransientFailureUntilReady() {
299+
InOrder inOrder = inOrder(mockHelper);
300+
loadBalancer.handleResolvedAddresses(
301+
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY)
302+
.build());
303+
304+
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), isA(EmptyPicker.class));
305+
306+
// Simulate state transitions for each subchannel individually.
307+
for (Subchannel sc : loadBalancer.getSubchannels()) {
308+
Status error = Status.UNKNOWN.withDescription("connection broken");
309+
deliverSubchannelState(
310+
sc,
311+
ConnectivityStateInfo.forTransientFailure(error));
312+
deliverSubchannelState(
313+
sc,
314+
ConnectivityStateInfo.forNonError(IDLE));
315+
deliverSubchannelState(
316+
sc,
317+
ConnectivityStateInfo.forNonError(CONNECTING));
318+
Ref<ConnectivityStateInfo> scStateInfo = sc.getAttributes().get(
319+
STATE_INFO);
320+
assertThat(scStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
321+
assertThat(scStateInfo.value.getStatus()).isEqualTo(error);
322+
}
323+
inOrder.verify(mockHelper).updateBalancingState(eq(TRANSIENT_FAILURE), isA(EmptyPicker.class));
324+
inOrder.verifyNoMoreInteractions();
325+
326+
Subchannel subchannel = loadBalancer.getSubchannels().iterator().next();
327+
deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(READY));
328+
Ref<ConnectivityStateInfo> subchannelStateInfo = subchannel.getAttributes().get(
329+
STATE_INFO);
330+
assertThat(subchannelStateInfo.value).isEqualTo(ConnectivityStateInfo.forNonError(READY));
331+
inOrder.verify(mockHelper).updateBalancingState(eq(READY), isA(ReadyPicker.class));
332+
333+
verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
334+
verifyNoMoreInteractions(mockHelper);
335+
}
336+
297337
@Test
298338
public void pickerRoundRobin() throws Exception {
299339
Subchannel subchannel = mock(Subchannel.class);

0 commit comments

Comments
 (0)