-
Notifications
You must be signed in to change notification settings - Fork 3.9k
api: pass Subchannel state updates to SubchannelStateListener rather than LoadBalancer (take 2) #5722
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This reverts commit f8d0868.
It's a pain to migrate the SubchannelPool to the new API. Since CachedSubchannelHelper is on the way, it's easier to switch to it when it's ready. Keeping GRPCLB with the old API would also confirm the backward compatibility.
Also stop passing Subchannel to SubchannelStateListener. This is part of the bigger change to remove Subchannel from all arguments on the LoadBalancer API, so that wrapping of Subchannel won't cause identity problems. Details in grpc#5676
b3a9b3e
to
98710a3
Compare
There is a caveat, that if the parent LB and the child LB are using different versions of APIs, the parent won't be able to intercept Subchannel states, like what HealthCheckingLoadBalancerFactory is doing.
I can't think of a way to perfectly solve this. Hopefully and probably nobody other than us is intercepting Subchannel states. |
* Starts the Subchannel. Can only be called once. | ||
* | ||
* <p>This method <strong>must be called from the {@link #getSynchronizationContext | ||
* Synchronization Context}</strong>, otherwise it may throw. This is to avoid the race between |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this comment is now a bit out-of-date. For example the race mentioned in "This is to avoid the race ..." is no longer a problem for the caller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rephrased.
@@ -1164,6 +1148,7 @@ public ChannelLogger getChannelLogger() { | |||
|
|||
/** | |||
* Asks the Subchannel to create a connection (aka transport), if there isn't an active one. | |||
* Has no effect if {@link #start} has not been called or {@link #shutdown} has been called. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should throw if start has not been called. I'd be willing to have a blanket statement for the class that all methods may throw if called before start. We could look at ClientCall for seeing how we worded it there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized we can't make the sync-context requirement for this method, because pick-first calls it from the data-path. I have sent out #5736
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our discussion offline, we will keep the sync-context requirement, and all methods except shutdown()
should be called after start()
.
@@ -1529,6 +1550,18 @@ public void run() { | |||
|
|||
@Override | |||
public void requestConnection() { | |||
syncContext.execute(new Runnable() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this changed? It seems it should be safe to check started
here without going onto the syncContext.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reverted per our discussion. There is a happens-after relationship between start()
and the other methods, thus started
don't need to be read in the syncContext.
@@ -274,13 +279,16 @@ void setServiceName(@Nullable String newServiceName) { | |||
adjustHealthCheck(); | |||
} | |||
|
|||
void updateRawState(ConnectivityStateInfo rawState) { | |||
public void onSubchannelState(ConnectivityStateInfo rawState) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't HealthCheckState
implementing SubchannelStateListener
any more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. Made HealthCheckState
implement SubchannelStateListener
again.
@@ -881,6 +883,13 @@ private void maybeTerminateChannel() { | |||
} | |||
} | |||
|
|||
// Must be called from syncContext | |||
private void handleInternalSubchannelState(ConnectivityStateInfo newState) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to move this method, because you can access the LbHelperImpl field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method doesn't need to be on LbHelperImpl
in the first place. What it does is in the scope of the top-level class.
}); | ||
} | ||
|
||
private void internalShutdown() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is only used in one place. Consider move it to a named inner class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is intended to replace shutdown()
when we are ready to turn the warning into an exception. I have added a comment to make that clear.
} | ||
|
||
private void internalShutdown() { | ||
syncContext.throwIfNotInThisSynchronizationContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's obvious in syncContext already.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my response above.
syncContext.execute(new Runnable() { | ||
@Override | ||
public void run() { | ||
listener.onSubchannelState(ConnectivityStateInfo.forNonError(SHUTDOWN)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The javadoc says SHUTDOWN can be safely ignored
, do we need call onSubchannelState(SHUTDOWN)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The paragraph where this statement is from is the continuation of the previous paragraph, which talks about the need to create a picker for each update. Although a LoadBalancer usually don't need to react to a SHUTDOWN update, the ChannelImpl still need to propagate it for completeness. And in some cases e.g., HealthCheckingLoadBalancer and CachedSubchannelHelper, the SHUTDOWN update is actually consumed for cleanups. I have rephrased the javadoc to prevent confusion.
requestConnection() in sync-context, because it's not necessary.
@ejona86 @dapengzhang0 @voidzcy thanks for the review. PTAL. |
We will require Subchannel.requestConnection() to be called from sync-context (grpc#5722), but SubchannelPicker.requestConnection() is currently calling it with the assumption of thread-safety. Actually SubchannelPicker.requestConnection() is called already from sync-context by ChannelImpl, it makes more sense to move this method to LoadBalancer where all other methods are sync-context'ed, rather than making SubchannelPicker.requestConnection() sync-context'ed and fragmenting the SubchannelPicker API because pickSubchannel() is thread-safe. C++ also has the requestConnection() equivalent on their LoadBalancer interface.
We will require Subchannel.requestConnection() to be called from sync-context (#5722), but SubchannelPicker.requestConnection() is currently calling it with the assumption of thread-safety. Actually SubchannelPicker.requestConnection() is called already from sync-context by ChannelImpl, it makes more sense to move this method to LoadBalancer where all other methods are sync-context'ed, rather than making SubchannelPicker.requestConnection() sync-context'ed and fragmenting the SubchannelPicker API because pickSubchannel() is thread-safe. C++ also has the requestConnection() equivalent on their LoadBalancer interface.
This is a revised version of #5503 (62b03fd), which was rolled back in f8d0868. The newer version passes SubchannelStateListener to Subchannel.start() instead of SubchannelCreationArgs, which allows us to remove the Subchannel argument from the listener, which works as a solution for #5676.
LoadBalancers that call the old
createSubchannel()
will getstart()
implicitly called with a listener that passes updates to the deprecatedLoadBalancer.handleSubchannelState()
. Those who call the newcreateSubchannel()
will have to callstart()
explicitly.This PR has 3 commits:
Nothing new is introduced in commits 1 and 2. Reviewers should only need to review commit 3.