This repository has been archived by the owner on Jan 7, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Control where Thrift/Future client callbacks executed
Summary: There are multiple ways of calling Thrift RPC on the client side with respect to how result of the call is delivered: 1. sync: blocking call, client thread is waiting. 2. async: asynchronous, result delivered through callback 3. Future-based: caller gets Future that is fulfilled when response is received 4. SemiFuture: the same as above but with SemiFuture instead of Future 5. coroutine (if enabled): caller gets coro task which is completed with RPC result This abundance of APIs gives us a problem because on our side we need to integrate them somehow with our Worker-based threading model. ### How we will do it #1 is trivial (if you call it from Worker you will get result in the same Worker thread but nonetheless you should never use it in production because it consumes the whole Worker). #4 and #5 trivial as well because caller has to provide execution context for getting result anyway and can simply use Worker. But #2 is problematic because by default it will deliver response in Thrift's own IO thread even if client method has been called from Worker (as well as #3 but this one should never be used in practice because Futures are deprecated). ### How to solve async methods? Async methods require callback to passed. We will provide option to set callback executor when creating a new Thrift client and when async_ method is called we will wrap client-provided callback into one which simply delegates execution to this callback executor. As a nice side benefit It will work with Future methods as well. Reviewed By: MohamedBassem Differential Revision: D22866545 fbshipit-source-id: 781b36ba38506cd9a1c78162cd1127afab7ed39e
- Loading branch information
1 parent
a0879b2
commit a8fe5d2
Showing
7 changed files
with
223 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/** | ||
* Copyright (c) 2017-present, Facebook, Inc. and its affiliates. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
#include "logdevice/common/thrift/RocketChannelWrapper.h" | ||
|
||
using apache::thrift::ClientReceiveState; | ||
using apache::thrift::RequestClientCallback; | ||
using CallbackPtr = RequestClientCallback::Ptr; | ||
|
||
namespace facebook { namespace logdevice { namespace detail { | ||
namespace { | ||
template <bool oneWay> | ||
/** | ||
* Ensures client provided callback is called from provided executor unless they | ||
* are safe to call inline. User-defined callbacks are never safe by default but | ||
* most of Thrift callbacks are (e.g. callback adapting async call to | ||
* Future/SemiFuture/coroutines API). | ||
*/ | ||
class ExecutorRequestCallback final : public RequestClientCallback { | ||
public: | ||
ExecutorRequestCallback(CallbackPtr cb, | ||
folly::Executor::KeepAlive<> executor_keep_alive) | ||
: executor_keep_alive_(std::move(executor_keep_alive)), | ||
cb_(std::move(cb)) { | ||
ld_check(executor_keep_alive_); | ||
} | ||
|
||
void onRequestSent() noexcept override { | ||
if (oneWay) { | ||
executor_keep_alive_->add( | ||
[cb = std::move(cb_)]() mutable { cb.release()->onRequestSent(); }); | ||
delete this; | ||
} else { | ||
requestSent_ = true; | ||
} | ||
} | ||
void onResponse(ClientReceiveState&& rs) noexcept override { | ||
executor_keep_alive_->add([requestSent = requestSent_, | ||
cb = std::move(cb_), | ||
rs = std::move(rs)]() mutable { | ||
if (requestSent) { | ||
cb->onRequestSent(); | ||
} | ||
cb.release()->onResponse(std::move(rs)); | ||
}); | ||
delete this; | ||
} | ||
void onResponseError(folly::exception_wrapper ex) noexcept override { | ||
executor_keep_alive_->add([requestSent = requestSent_, | ||
cb = std::move(cb_), | ||
ex = std::move(ex)]() mutable { | ||
if (requestSent) { | ||
cb->onRequestSent(); | ||
} | ||
cb.release()->onResponseError(std::move(ex)); | ||
}); | ||
delete this; | ||
} | ||
|
||
private: | ||
bool requestSent_{false}; | ||
folly::Executor::KeepAlive<> executor_keep_alive_; | ||
CallbackPtr cb_; | ||
}; | ||
|
||
} // namespace | ||
|
||
template <bool oneWayCb> | ||
CallbackPtr RocketChannelWrapper::wrapIfUnsafe(CallbackPtr cob) { | ||
if (!cob->isInlineSafe()) { | ||
return CallbackPtr(new ExecutorRequestCallback<oneWayCb>( | ||
std::move(cob), getKeepAliveToken(callback_executor_))); | ||
} else { | ||
return cob; | ||
} | ||
} | ||
|
||
void RocketChannelWrapper::sendRequestResponse( | ||
const apache::thrift::RpcOptions& options, | ||
folly::StringPiece method_name, | ||
apache::thrift::SerializedRequest&& request, | ||
std::shared_ptr<apache::thrift::transport::THeader> header, | ||
CallbackPtr cob) { | ||
cob = wrapIfUnsafe<false>(std::move(cob)); | ||
channel_->sendRequestResponse(options, | ||
method_name, | ||
std::move(request), | ||
std::move(header), | ||
std::move(cob)); | ||
} | ||
|
||
void RocketChannelWrapper::sendRequestNoResponse( | ||
const apache::thrift::RpcOptions& options, | ||
folly::StringPiece method_name, | ||
apache::thrift::SerializedRequest&& request, | ||
std::shared_ptr<apache::thrift::transport::THeader> header, | ||
CallbackPtr cob) { | ||
cob = wrapIfUnsafe<true>(std::move(cob)); | ||
channel_->sendRequestNoResponse(options, | ||
method_name, | ||
std::move(request), | ||
std::move(header), | ||
std::move(cob)); | ||
} | ||
}}} // namespace facebook::logdevice::detail |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters