core: SubchannelStateListener continues to receive updates after LoadBalancer is shutdown. (#5883)

No more methods on the `LoadBalancer` will be called after
`LoadBalancer#shutdown()` is called.  This includes
`LoadBalancer#handleSubchannelState()` too.  `SubchannelStateListener`
inherited this restriction.  However, this special case makes
`onSubchannelState(SHUTDOWN)` an unreliable way of being notified
about `Subchannel` SHUTDOWN, and may confuse/complicate a
wrapping `LoadBalancer` that expects the full notification (e.g., #5875).

The javadoc isn't clear whether this restriction applies.  I think
it's more useful to make it no apply.
This commit is contained in:
Kun Zhang 2019-06-14 14:43:54 -07:00 committed by GitHub
parent e5bd7f282c
commit c92c9e71bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 14 additions and 9 deletions

View File

@ -349,9 +349,9 @@ public abstract class LoadBalancer {
} }
/** /**
* The channel asks the load-balancer to shutdown. No more callbacks will be called after this * The channel asks the load-balancer to shutdown. No more methods on this class will be called
* method. The implementation should shutdown all Subchannels and OOB channels, and do any other * after this method. The implementation should shutdown all Subchannels and OOB channels, and do
* cleanup as necessary. * any other cleanup as necessary.
* *
* @since 1.2.0 * @since 1.2.0
*/ */

View File

@ -1076,6 +1076,10 @@ final class ManagedChannelImpl extends ManagedChannel implements
new LoadBalancer.SubchannelStateListener() { new LoadBalancer.SubchannelStateListener() {
@Override @Override
public void onSubchannelState(ConnectivityStateInfo newState) { public void onSubchannelState(ConnectivityStateInfo newState) {
// Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match.
if (LbHelperImpl.this != ManagedChannelImpl.this.lbHelper) {
return;
}
lb.handleSubchannelState(subchannel, newState); lb.handleSubchannelState(subchannel, newState);
} }
}; };
@ -1459,11 +1463,8 @@ final class ManagedChannelImpl extends ManagedChannel implements
@Override @Override
void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) { void onStateChange(InternalSubchannel is, ConnectivityStateInfo newState) {
handleInternalSubchannelState(newState); handleInternalSubchannelState(newState);
// Call LB only if it's not shutdown. If LB is shutdown, lbHelper won't match. checkState(listener != null, "listener is null");
if (helper == ManagedChannelImpl.this.lbHelper) { listener.onSubchannelState(newState);
checkState(listener != null, "listener is null");
listener.onSubchannelState(newState);
}
} }
@Override @Override

View File

@ -784,11 +784,15 @@ public class ManagedChannelImplTest {
subchannel1.shutdown(); subchannel1.shutdown();
subchannel2.shutdown(); subchannel2.shutdown();
// No more callback are delivered to LoadBalancer or the state listeners after it's shut down // Since subchannels are shutdown, SubchannelStateListeners will only get SHUTDOWN regardless of
// the transport states.
transportInfo1.listener.transportShutdown(Status.UNAVAILABLE); transportInfo1.listener.transportShutdown(Status.UNAVAILABLE);
transportInfo2.listener.transportReady(); transportInfo2.listener.transportReady();
verify(stateListener1).onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN));
verify(stateListener2).onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN));
verifyNoMoreInteractions(stateListener1, stateListener2); verifyNoMoreInteractions(stateListener1, stateListener2);
// No more callback should be delivered to LoadBalancer after it's shut down
resolver.listener.onError(resolutionError); resolver.listener.onError(resolutionError);
resolver.resolved(); resolver.resolved();
verifyNoMoreInteractions(mockLoadBalancer); verifyNoMoreInteractions(mockLoadBalancer);