Skip to content

Commit 368c43a

Browse files
authored
core: throw away subchannel references after round_robin is shutdown (grpc#8132)
Triggering balancing state updates after already being shutdown can be confusing for the upstream of round_robin. In cases of the callers not managing round_robin's lifecycle (e.g., not ignoring updates after it shuts down round_robin, which it should), it can make problem very bad, especially with the behavior that round_robin is actually propagating TRANSIENT_FAILURE with a picker that buffers RPCs. This change only polishes round_robin by always preserving its invariant. Callers/LBs should not rely on this and should still manage the balancing updates from its downstream correctly based on the downstream's lifetime.
1 parent 02ff64f commit 368c43a

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ public void shutdown() {
166166
for (Subchannel subchannel : getSubchannels()) {
167167
shutdownSubchannel(subchannel);
168168
}
169+
subchannels.clear();
169170
}
170171

171172
private static final Status EMPTY_OK = Status.OK.withDescription("no subchannels ready");

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,25 @@ public void pickAfterStateChange() throws Exception {
292292
verifyNoMoreInteractions(mockHelper);
293293
}
294294

295+
@Test
296+
public void ignoreShutdownSubchannelStateChange() {
297+
InOrder inOrder = inOrder(mockHelper);
298+
loadBalancer.handleResolvedAddresses(
299+
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY)
300+
.build());
301+
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), isA(EmptyPicker.class));
302+
303+
loadBalancer.shutdown();
304+
for (Subchannel sc : loadBalancer.getSubchannels()) {
305+
verify(sc).shutdown();
306+
// When the subchannel is being shut down, a SHUTDOWN connectivity state is delivered
307+
// back to the subchannel state listener.
308+
deliverSubchannelState(sc, ConnectivityStateInfo.forNonError(SHUTDOWN));
309+
}
310+
311+
inOrder.verifyNoMoreInteractions();
312+
}
313+
295314
@Test
296315
public void stayTransientFailureUntilReady() {
297316
InOrder inOrder = inOrder(mockHelper);

0 commit comments

Comments
 (0)