-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
HBASE-28792: AsyncTableImpl should call coprocessor callbacks in a defined order #6168
HBASE-28792: AsyncTableImpl should call coprocessor callbacks in a defined order #6168
Conversation
This comment has been minimized.
This comment has been minimized.
fee3060
to
02b99bc
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The intention here is that, in AsyncTableImpl, we will always call any callbacks in a thread pool, in order to prevent blocking operations in callbacks blocking the system thread(like netty event loop). If you want high performance, you can use RawAsyncTable directly. Here I think we should still use the thread pool, but use some tricks to let onComplete to be called at last. Thanks. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@Apache9 Understood. I've brought back the thread pool usage and added a barrier to force onComplete() to wait until onRegionComplete() is done. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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 agree with Duo about the threadpool bit. I think this Phaser approach works fine. I'm curious if there's a "better" implementation based on chaining CompletableFutures ... I'll need to spend some time with the APIs to see if I have a suggestion.
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.
So unfortunately our CoprocessorCallback
API seems to completely preclude use of the CompletableFuture
chaining strategy. I think you're on the right path here.
pool.execute(context.wrap(callback::onComplete)); | ||
pool.execute(context.wrap(() -> { | ||
// Guarantee that onComplete() is called after all onRegionComplete()'s are called | ||
regionCompletesInProgress.arriveAndAwaitAdvance(); |
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.
According to the Javadoc on this method, onComplete
must implement a happens-after semantic for all onRegionComplete
and all onRegionError
invocations. This implies that there are multiple regions to complete AND multiple forms of completion, so I think that this can be solved with a phaser ; you need a more general purpose mutex.
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.
Good point that onRegionError should be included in the barrier. I'm guessing you meant to say "so I think that this cannot be solved with a phaser," but I disagree. The phaser can be used in onRegionError just the same as onRegionComplete.
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.
Oh I see. Phaser is more powerful than I initially understood -- very cool! Yes, this looks better to me.
🎊 +1 overall
This message was automatically generated. |
🎊 +1 overall
This message was automatically generated. |
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.
Nice fix.
pool.execute(context.wrap(callback::onComplete)); | ||
pool.execute(context.wrap(() -> { | ||
// Guarantee that onComplete() is called after all onRegionComplete()'s are called | ||
regionCompletesInProgress.arriveAndAwaitAdvance(); |
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.
Oh I see. Phaser is more powerful than I initially understood -- very cool! Yes, this looks better to me.
…fined order (apache#6168) Signed-off-by: Nick Dimiduk <[email protected]>
…fined order (apache#6168) Signed-off-by: Nick Dimiduk <[email protected]>
Ah, just 5 minutes late... LGTM. Thanks @charlesconnell and @ndimiduk ! |
…fined order (apache#6168) Signed-off-by: Nick Dimiduk <[email protected]>
…fined order (apache#6168) Signed-off-by: Nick Dimiduk <[email protected]>
All good. Welcome back @Apache9 ! |
…fined order (#6168) Signed-off-by: Nick Dimiduk <[email protected]>
…ks in a defined order (#6168)" to branch-2 (#6200) Signed-off-by: Nick Dimiduk <[email protected]> Co-authored-by: Charles Connell <[email protected]>
…fined order (#6168) Signed-off-by: Nick Dimiduk <[email protected]> Co-authored-by: Charles Connell <[email protected]>
…fined order (#6168) Signed-off-by: Nick Dimiduk <[email protected]> Co-authored-by: Charles Connell <[email protected]>
…fined order (#6168) Signed-off-by: Nick Dimiduk <[email protected]> Co-authored-by: Charles Connell <[email protected]>
To call a coprocessor endpoint asynchronously, you start by calling
AsyncTable#coprocessorService()
, which gives you aCoprocessorServiceBuilder
, and a few steps later you can talk to your coprocessor over the network. One argument toAsyncTable#coprocessorService()
is aCoprocessorCallback
object, which contains several methods that will be called during the lifecycle of a coprocessor endpoint call.AsyncTableImpl
's implementation ofAsyncTable#coprocessorService()
wraps yourCoprocessorCallback
with its own that delegates the work to a thread pool. A snippet of this:The trouble with this is that your implementations of
onRegionComplete()
andonComplete()
will end up getting called in a random order, and/or at the same time. The tasks of calling them are delegated to a thread pool, and the completion of those tasks is not waited on, so the thread pool can choose any ordering it wants to. Troublingly, onComplete() can be called before the finalonRegionComplete()
, which is an violation of the contract specified in theCoprocessorCallback#onComplete()
javadoc.This PR eliminates the use of a thread pool for calling the coprocessor callback, and calls them directly on the thread that received the response from the server.