-
Notifications
You must be signed in to change notification settings - Fork 4k
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.
|
|
||
| /** | ||
| * 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().
|
|
||
| @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.
| } | ||
|
|
||
| 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.
| } | ||
|
|
||
| // 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.