|
@@ -26,9 +26,6 @@
|
|
|
|
|
|
#include <asio/read.hpp>
|
|
|
|
|
|
-#include <google/protobuf/io/coded_stream.h>
|
|
|
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
|
|
|
-
|
|
|
namespace hdfs {
|
|
|
|
|
|
namespace pb = ::google::protobuf;
|
|
@@ -37,17 +34,18 @@ namespace pbio = ::google::protobuf::io;
|
|
|
using namespace ::hadoop::common;
|
|
|
using namespace ::std::placeholders;
|
|
|
|
|
|
-static void
|
|
|
-ConstructPacket(std::string *res,
|
|
|
- std::initializer_list<const pb::MessageLite *> headers,
|
|
|
- const std::string *request) {
|
|
|
+static const int kNoRetry = -1;
|
|
|
+
|
|
|
+static void AddHeadersToPacket(
|
|
|
+ std::string *res, std::initializer_list<const pb::MessageLite *> headers,
|
|
|
+ const std::string *payload) {
|
|
|
int len = 0;
|
|
|
std::for_each(
|
|
|
headers.begin(), headers.end(),
|
|
|
[&len](const pb::MessageLite *v) { len += DelimitedPBMessageSize(v); });
|
|
|
- if (request) {
|
|
|
- len += pbio::CodedOutputStream::VarintSize32(request->size()) +
|
|
|
- request->size();
|
|
|
+
|
|
|
+ if (payload) {
|
|
|
+ len += payload->size();
|
|
|
}
|
|
|
|
|
|
int net_len = htonl(len);
|
|
@@ -58,6 +56,7 @@ ConstructPacket(std::string *res,
|
|
|
os.WriteRaw(reinterpret_cast<const char *>(&net_len), sizeof(net_len));
|
|
|
|
|
|
uint8_t *buf = os.GetDirectBufferForNBytesAndAdvance(len);
|
|
|
+ assert(buf);
|
|
|
|
|
|
std::for_each(
|
|
|
headers.begin(), headers.end(), [&buf](const pb::MessageLite *v) {
|
|
@@ -65,19 +64,43 @@ ConstructPacket(std::string *res,
|
|
|
buf = v->SerializeWithCachedSizesToArray(buf);
|
|
|
});
|
|
|
|
|
|
- if (request) {
|
|
|
- buf = pbio::CodedOutputStream::WriteVarint32ToArray(request->size(), buf);
|
|
|
- buf = os.WriteStringToArray(*request, buf);
|
|
|
+ if (payload) {
|
|
|
+ buf = os.WriteStringToArray(*payload, buf);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void SetRequestHeader(RpcEngine *engine, int call_id,
|
|
|
- const std::string &method_name,
|
|
|
+static void ConstructPayload(std::string *res, const pb::MessageLite *header) {
|
|
|
+ int len = DelimitedPBMessageSize(header);
|
|
|
+ res->reserve(len);
|
|
|
+ pbio::StringOutputStream ss(res);
|
|
|
+ pbio::CodedOutputStream os(&ss);
|
|
|
+ uint8_t *buf = os.GetDirectBufferForNBytesAndAdvance(len);
|
|
|
+ assert(buf);
|
|
|
+ buf = pbio::CodedOutputStream::WriteVarint32ToArray(header->ByteSize(), buf);
|
|
|
+ buf = header->SerializeWithCachedSizesToArray(buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void ConstructPayload(std::string *res, const std::string *request) {
|
|
|
+ int len =
|
|
|
+ pbio::CodedOutputStream::VarintSize32(request->size()) + request->size();
|
|
|
+ res->reserve(len);
|
|
|
+ pbio::StringOutputStream ss(res);
|
|
|
+ pbio::CodedOutputStream os(&ss);
|
|
|
+ uint8_t *buf = os.GetDirectBufferForNBytesAndAdvance(len);
|
|
|
+ assert(buf);
|
|
|
+ buf = pbio::CodedOutputStream::WriteVarint32ToArray(request->size(), buf);
|
|
|
+ buf = os.WriteStringToArray(*request, buf);
|
|
|
+}
|
|
|
+
|
|
|
+static void SetRequestHeader(LockFreeRpcEngine *engine, int call_id,
|
|
|
+ const std::string &method_name, int retry_count,
|
|
|
RpcRequestHeaderProto *rpc_header,
|
|
|
RequestHeaderProto *req_header) {
|
|
|
rpc_header->set_rpckind(RPC_PROTOCOL_BUFFER);
|
|
|
rpc_header->set_rpcop(RpcRequestHeaderProto::RPC_FINAL_PACKET);
|
|
|
rpc_header->set_callid(call_id);
|
|
|
+ if (retry_count != kNoRetry)
|
|
|
+ rpc_header->set_retrycount(retry_count);
|
|
|
rpc_header->set_clientid(engine->client_name());
|
|
|
|
|
|
req_header->set_methodname(method_name);
|
|
@@ -87,64 +110,84 @@ static void SetRequestHeader(RpcEngine *engine, int call_id,
|
|
|
|
|
|
RpcConnection::~RpcConnection() {}
|
|
|
|
|
|
-RpcConnection::Request::Request(RpcConnection *parent,
|
|
|
- const std::string &method_name,
|
|
|
- const std::string &request, Handler &&handler)
|
|
|
- : call_id_(parent->engine_->NextCallId()), timer_(parent->io_service()),
|
|
|
- handler_(std::move(handler)) {
|
|
|
- RpcRequestHeaderProto rpc_header;
|
|
|
- RequestHeaderProto req_header;
|
|
|
- SetRequestHeader(parent->engine_, call_id_, method_name, &rpc_header,
|
|
|
- &req_header);
|
|
|
- ConstructPacket(&payload_, {&rpc_header, &req_header}, &request);
|
|
|
+Request::Request(LockFreeRpcEngine *engine, const std::string &method_name,
|
|
|
+ const std::string &request, Handler &&handler)
|
|
|
+ : engine_(engine),
|
|
|
+ method_name_(method_name),
|
|
|
+ call_id_(engine->NextCallId()),
|
|
|
+ timer_(engine->io_service()),
|
|
|
+ handler_(std::move(handler)),
|
|
|
+ retry_count_(engine->retry_policy() ? 0 : kNoRetry) {
|
|
|
+ ConstructPayload(&payload_, &request);
|
|
|
+}
|
|
|
+
|
|
|
+Request::Request(LockFreeRpcEngine *engine, const std::string &method_name,
|
|
|
+ const pb::MessageLite *request, Handler &&handler)
|
|
|
+ : engine_(engine),
|
|
|
+ method_name_(method_name),
|
|
|
+ call_id_(engine->NextCallId()),
|
|
|
+ timer_(engine->io_service()),
|
|
|
+ handler_(std::move(handler)),
|
|
|
+ retry_count_(engine->retry_policy() ? 0 : kNoRetry) {
|
|
|
+ ConstructPayload(&payload_, request);
|
|
|
}
|
|
|
|
|
|
-RpcConnection::Request::Request(RpcConnection *parent,
|
|
|
- const std::string &method_name,
|
|
|
- const pb::MessageLite *request,
|
|
|
- Handler &&handler)
|
|
|
- : call_id_(parent->engine_->NextCallId()), timer_(parent->io_service()),
|
|
|
- handler_(std::move(handler)) {
|
|
|
+Request::Request(LockFreeRpcEngine *engine, Handler &&handler)
|
|
|
+ : engine_(engine),
|
|
|
+ call_id_(-1),
|
|
|
+ timer_(engine->io_service()),
|
|
|
+ handler_(std::move(handler)),
|
|
|
+ retry_count_(engine->retry_policy() ? 0 : kNoRetry) {
|
|
|
+}
|
|
|
+
|
|
|
+void Request::GetPacket(std::string *res) const {
|
|
|
+ if (payload_.empty())
|
|
|
+ return;
|
|
|
+
|
|
|
RpcRequestHeaderProto rpc_header;
|
|
|
RequestHeaderProto req_header;
|
|
|
- SetRequestHeader(parent->engine_, call_id_, method_name, &rpc_header,
|
|
|
+ SetRequestHeader(engine_, call_id_, method_name_, retry_count_, &rpc_header,
|
|
|
&req_header);
|
|
|
- ConstructPacket(&payload_, {&rpc_header, &req_header, request}, nullptr);
|
|
|
+ AddHeadersToPacket(res, {&rpc_header, &req_header}, &payload_);
|
|
|
}
|
|
|
|
|
|
-void RpcConnection::Request::OnResponseArrived(pbio::CodedInputStream *is,
|
|
|
- const Status &status) {
|
|
|
+void Request::OnResponseArrived(pbio::CodedInputStream *is,
|
|
|
+ const Status &status) {
|
|
|
handler_(is, status);
|
|
|
}
|
|
|
|
|
|
-RpcConnection::RpcConnection(RpcEngine *engine)
|
|
|
- : engine_(engine), resp_state_(kReadLength), resp_length_(0) {}
|
|
|
+RpcConnection::RpcConnection(LockFreeRpcEngine *engine)
|
|
|
+ : engine_(engine),
|
|
|
+ connected_(false) {}
|
|
|
|
|
|
::asio::io_service &RpcConnection::io_service() {
|
|
|
return engine_->io_service();
|
|
|
}
|
|
|
|
|
|
-void RpcConnection::Start() {
|
|
|
+void RpcConnection::StartReading() {
|
|
|
io_service().post(std::bind(&RpcConnection::OnRecvCompleted, this,
|
|
|
::asio::error_code(), 0));
|
|
|
}
|
|
|
|
|
|
-void RpcConnection::FlushPendingRequests() {
|
|
|
- io_service().post([this]() {
|
|
|
+void RpcConnection::AsyncFlushPendingRequests() {
|
|
|
+ std::shared_ptr<RpcConnection> shared_this = shared_from_this();
|
|
|
+ io_service().post([shared_this, this]() {
|
|
|
+ std::lock_guard<std::mutex> state_lock(connection_state_lock_);
|
|
|
+
|
|
|
if (!request_over_the_wire_) {
|
|
|
- OnSendCompleted(::asio::error_code(), 0);
|
|
|
+ FlushPendingRequests();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
-void RpcConnection::HandleRpcResponse(const std::vector<char> &data) {
|
|
|
- /* assumed to be called from a context that has already acquired the
|
|
|
- * engine_state_lock */
|
|
|
- pbio::ArrayInputStream ar(&data[0], data.size());
|
|
|
- pbio::CodedInputStream in(&ar);
|
|
|
- in.PushLimit(data.size());
|
|
|
+void RpcConnection::HandleRpcResponse(std::shared_ptr<Response> response) {
|
|
|
+ assert(lock_held(connection_state_lock_)); // Must be holding lock before calling
|
|
|
+
|
|
|
+ response->ar.reset(new pbio::ArrayInputStream(&response->data_[0], response->data_.size()));
|
|
|
+ response->in.reset(new pbio::CodedInputStream(response->ar.get()));
|
|
|
+ response->in->PushLimit(response->data_.size());
|
|
|
RpcResponseHeaderProto h;
|
|
|
- ReadDelimitedPBMessage(&in, &h);
|
|
|
+ ReadDelimitedPBMessage(response->in.get(), &h);
|
|
|
|
|
|
auto req = RemoveFromRunningQueue(h.callid());
|
|
|
if (!req) {
|
|
@@ -152,12 +195,15 @@ void RpcConnection::HandleRpcResponse(const std::vector<char> &data) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- Status stat;
|
|
|
+ Status status;
|
|
|
if (h.has_exceptionclassname()) {
|
|
|
- stat =
|
|
|
+ status =
|
|
|
Status::Exception(h.exceptionclassname().c_str(), h.errormsg().c_str());
|
|
|
}
|
|
|
- req->OnResponseArrived(&in, stat);
|
|
|
+
|
|
|
+ io_service().post([req, response, status]() {
|
|
|
+ req->OnResponseArrived(response->in.get(), status); // Never call back while holding a lock
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
void RpcConnection::HandleRpcTimeout(std::shared_ptr<Request> req,
|
|
@@ -166,7 +212,7 @@ void RpcConnection::HandleRpcTimeout(std::shared_ptr<Request> req,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- std::lock_guard<std::mutex> state_lock(engine_state_lock_);
|
|
|
+ std::lock_guard<std::mutex> state_lock(connection_state_lock_);
|
|
|
auto r = RemoveFromRunningQueue(req->call_id());
|
|
|
if (!r) {
|
|
|
// The RPC might have been finished and removed from the queue
|
|
@@ -179,6 +225,8 @@ void RpcConnection::HandleRpcTimeout(std::shared_ptr<Request> req,
|
|
|
}
|
|
|
|
|
|
std::shared_ptr<std::string> RpcConnection::PrepareHandshakePacket() {
|
|
|
+ assert(lock_held(connection_state_lock_)); // Must be holding lock before calling
|
|
|
+
|
|
|
static const char kHandshakeHeader[] = {'h', 'r', 'p', 'c',
|
|
|
RpcEngine::kRpcVersion, 0, 0};
|
|
|
auto res =
|
|
@@ -192,25 +240,27 @@ std::shared_ptr<std::string> RpcConnection::PrepareHandshakePacket() {
|
|
|
|
|
|
IpcConnectionContextProto handshake;
|
|
|
handshake.set_protocol(engine_->protocol_name());
|
|
|
- ConstructPacket(res.get(), {&h, &handshake}, nullptr);
|
|
|
+ AddHeadersToPacket(res.get(), {&h, &handshake}, nullptr);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
void RpcConnection::AsyncRpc(
|
|
|
const std::string &method_name, const ::google::protobuf::MessageLite *req,
|
|
|
std::shared_ptr<::google::protobuf::MessageLite> resp,
|
|
|
- const Callback &handler) {
|
|
|
- std::lock_guard<std::mutex> state_lock(engine_state_lock_);
|
|
|
+ const RpcCallback &handler) {
|
|
|
+ std::lock_guard<std::mutex> state_lock(connection_state_lock_);
|
|
|
|
|
|
auto wrapped_handler =
|
|
|
[resp, handler](pbio::CodedInputStream *is, const Status &status) {
|
|
|
if (status.ok()) {
|
|
|
- ReadDelimitedPBMessage(is, resp.get());
|
|
|
+ if (is) { // Connect messages will not have an is
|
|
|
+ ReadDelimitedPBMessage(is, resp.get());
|
|
|
+ }
|
|
|
}
|
|
|
handler(status);
|
|
|
};
|
|
|
|
|
|
- auto r = std::make_shared<Request>(this, method_name, req,
|
|
|
+ auto r = std::make_shared<Request>(engine_, method_name, req,
|
|
|
std::move(wrapped_handler));
|
|
|
pending_requests_.push_back(r);
|
|
|
FlushPendingRequests();
|
|
@@ -219,29 +269,62 @@ void RpcConnection::AsyncRpc(
|
|
|
void RpcConnection::AsyncRawRpc(const std::string &method_name,
|
|
|
const std::string &req,
|
|
|
std::shared_ptr<std::string> resp,
|
|
|
- Callback &&handler) {
|
|
|
- std::lock_guard<std::mutex> state_lock(engine_state_lock_);
|
|
|
-
|
|
|
- auto wrapped_handler =
|
|
|
- [this, resp, handler](pbio::CodedInputStream *is, const Status &status) {
|
|
|
- if (status.ok()) {
|
|
|
- uint32_t size = 0;
|
|
|
- is->ReadVarint32(&size);
|
|
|
- auto limit = is->PushLimit(size);
|
|
|
- is->ReadString(resp.get(), limit);
|
|
|
- is->PopLimit(limit);
|
|
|
- }
|
|
|
- handler(status);
|
|
|
- };
|
|
|
+ RpcCallback &&handler) {
|
|
|
+ std::lock_guard<std::mutex> state_lock(connection_state_lock_);
|
|
|
+
|
|
|
+ std::shared_ptr<RpcConnection> shared_this = shared_from_this();
|
|
|
+ auto wrapped_handler = [shared_this, this, resp, handler](
|
|
|
+ pbio::CodedInputStream *is, const Status &status) {
|
|
|
+ if (status.ok()) {
|
|
|
+ uint32_t size = 0;
|
|
|
+ is->ReadVarint32(&size);
|
|
|
+ auto limit = is->PushLimit(size);
|
|
|
+ is->ReadString(resp.get(), limit);
|
|
|
+ is->PopLimit(limit);
|
|
|
+ }
|
|
|
+ handler(status);
|
|
|
+ };
|
|
|
|
|
|
- auto r = std::make_shared<Request>(this, method_name, req,
|
|
|
+ auto r = std::make_shared<Request>(engine_, method_name, req,
|
|
|
std::move(wrapped_handler));
|
|
|
pending_requests_.push_back(r);
|
|
|
FlushPendingRequests();
|
|
|
}
|
|
|
|
|
|
+void RpcConnection::PreEnqueueRequests(
|
|
|
+ std::vector<std::shared_ptr<Request>> requests) {
|
|
|
+ // Public method - acquire lock
|
|
|
+ std::lock_guard<std::mutex> state_lock(connection_state_lock_);
|
|
|
+ assert(!connected_);
|
|
|
+
|
|
|
+ pending_requests_.insert(pending_requests_.end(), requests.begin(),
|
|
|
+ requests.end());
|
|
|
+ // Don't start sending yet; will flush when connected
|
|
|
+}
|
|
|
+
|
|
|
+void RpcConnection::CommsError(const Status &status) {
|
|
|
+ assert(lock_held(connection_state_lock_)); // Must be holding lock before calling
|
|
|
+
|
|
|
+ Disconnect();
|
|
|
+
|
|
|
+ // Anything that has been queued to the connection (on the fly or pending)
|
|
|
+ // will get dinged for a retry
|
|
|
+ std::vector<std::shared_ptr<Request>> requestsToReturn;
|
|
|
+ std::transform(requests_on_fly_.begin(), requests_on_fly_.end(),
|
|
|
+ std::back_inserter(requestsToReturn),
|
|
|
+ std::bind(&RequestOnFlyMap::value_type::second, _1));
|
|
|
+ requests_on_fly_.clear();
|
|
|
+
|
|
|
+ requestsToReturn.insert(requestsToReturn.end(),
|
|
|
+ std::make_move_iterator(pending_requests_.begin()),
|
|
|
+ std::make_move_iterator(pending_requests_.end()));
|
|
|
+ pending_requests_.clear();
|
|
|
+
|
|
|
+ engine_->AsyncRpcCommsError(status, requestsToReturn);
|
|
|
+}
|
|
|
+
|
|
|
void RpcConnection::ClearAndDisconnect(const ::asio::error_code &ec) {
|
|
|
- Shutdown();
|
|
|
+ Disconnect();
|
|
|
std::vector<std::shared_ptr<Request>> requests;
|
|
|
std::transform(requests_on_fly_.begin(), requests_on_fly_.end(),
|
|
|
std::back_inserter(requests),
|
|
@@ -256,8 +339,8 @@ void RpcConnection::ClearAndDisconnect(const ::asio::error_code &ec) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-std::shared_ptr<RpcConnection::Request>
|
|
|
-RpcConnection::RemoveFromRunningQueue(int call_id) {
|
|
|
+std::shared_ptr<Request> RpcConnection::RemoveFromRunningQueue(int call_id) {
|
|
|
+ assert(lock_held(connection_state_lock_)); // Must be holding lock before calling
|
|
|
auto it = requests_on_fly_.find(call_id);
|
|
|
if (it == requests_on_fly_.end()) {
|
|
|
return std::shared_ptr<Request>();
|