diff --git a/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java b/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java index 846ed90aecc..4a6a1ff5611 100644 --- a/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java +++ b/core/src/main/java/io/grpc/util/RoundRobinLoadBalancer.java @@ -166,6 +166,7 @@ public void shutdown() { for (Subchannel subchannel : getSubchannels()) { shutdownSubchannel(subchannel); } + subchannels.clear(); } private static final Status EMPTY_OK = Status.OK.withDescription("no subchannels ready"); diff --git a/core/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java b/core/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java index 5def703e7b4..dcb85ad916d 100644 --- a/core/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java +++ b/core/src/test/java/io/grpc/util/RoundRobinLoadBalancerTest.java @@ -292,6 +292,25 @@ public void pickAfterStateChange() throws Exception { verifyNoMoreInteractions(mockHelper); } + @Test + public void ignoreShutdownSubchannelStateChange() { + InOrder inOrder = inOrder(mockHelper); + loadBalancer.handleResolvedAddresses( + ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY) + .build()); + inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), isA(EmptyPicker.class)); + + loadBalancer.shutdown(); + for (Subchannel sc : loadBalancer.getSubchannels()) { + verify(sc).shutdown(); + // When the subchannel is being shut down, a SHUTDOWN connectivity state is delivered + // back to the subchannel state listener. + deliverSubchannelState(sc, ConnectivityStateInfo.forNonError(SHUTDOWN)); + } + + inOrder.verifyNoMoreInteractions(); + } + @Test public void stayTransientFailureUntilReady() { InOrder inOrder = inOrder(mockHelper);