Improve network performance
Summary: With this patch the number of packets for a simple RPC call is lowered from 22 to 12 (45% reduction). The number of packets for the Bolt protocol is lowered from 26 to 18 (30% reduction). Impact on the Bolt protocol will be a constant of ~ 8 packets less per connection, while the impact on the RPC protocol will be approximately a 45% reduction overall. Reviewers: buda, teon.banek Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1141
This commit is contained in:
parent
6fa06f7ea5
commit
e2f2cd5722
@ -51,11 +51,11 @@ class Client {
|
||||
const std::string &client_name = "memgraph-bolt/0.0.1")
|
||||
: socket_(std::move(socket)) {
|
||||
DLOG(INFO) << "Sending handshake";
|
||||
if (!socket_.Write(kPreamble, sizeof(kPreamble))) {
|
||||
if (!socket_.Write(kPreamble, sizeof(kPreamble), true)) {
|
||||
throw ClientSocketException();
|
||||
}
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!socket_.Write(kProtocol, sizeof(kProtocol))) {
|
||||
if (!socket_.Write(kProtocol, sizeof(kProtocol), i != 3)) {
|
||||
throw ClientSocketException();
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,9 @@ class Session {
|
||||
explicit TimeoutSocket(Session &session) : session_(session) {}
|
||||
|
||||
bool Write(const uint8_t *data, size_t len) {
|
||||
return session_.socket_.Write(data, len,
|
||||
// The have_more flag is hardcoded to false here because the bolt data
|
||||
// is internally buffered and doesn't need to be buffered by the kernel.
|
||||
return session_.socket_.Write(data, len, false,
|
||||
[this] { return !session_.TimedOut(); });
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ std::unique_ptr<Message> Client::Call(std::unique_ptr<Message> request) {
|
||||
// Send service name size.
|
||||
MessageSize service_len = service_name_.size();
|
||||
if (!socket_->Write(reinterpret_cast<uint8_t *>(&service_len),
|
||||
sizeof(MessageSize))) {
|
||||
sizeof(MessageSize), true)) {
|
||||
LOG(ERROR) << "Couldn't send service name size!";
|
||||
socket_ = std::experimental::nullopt;
|
||||
return nullptr;
|
||||
@ -55,7 +55,7 @@ std::unique_ptr<Message> Client::Call(std::unique_ptr<Message> request) {
|
||||
|
||||
// Send current request ID.
|
||||
if (!socket_->Write(reinterpret_cast<uint8_t *>(&request_id),
|
||||
sizeof(uint32_t))) {
|
||||
sizeof(uint32_t), true)) {
|
||||
LOG(ERROR) << "Couldn't send request ID!";
|
||||
socket_ = std::experimental::nullopt;
|
||||
return nullptr;
|
||||
@ -74,7 +74,7 @@ std::unique_ptr<Message> Client::Call(std::unique_ptr<Message> request) {
|
||||
kMaxMessageSize);
|
||||
|
||||
if (!socket_->Write(reinterpret_cast<uint8_t *>(&request_data_size),
|
||||
sizeof(MessageSize))) {
|
||||
sizeof(MessageSize), true)) {
|
||||
LOG(ERROR) << "Couldn't send request size!";
|
||||
socket_ = std::experimental::nullopt;
|
||||
return nullptr;
|
||||
|
@ -63,11 +63,6 @@ void Session::Close() {
|
||||
socket_.get()->Close();
|
||||
}
|
||||
|
||||
bool SendLength(Socket &socket, MessageSize length) {
|
||||
return socket.Write(reinterpret_cast<uint8_t *>(&length),
|
||||
sizeof(MessageSize));
|
||||
}
|
||||
|
||||
void SendMessage(Socket &socket, uint32_t message_id,
|
||||
std::unique_ptr<Message> &message) {
|
||||
CHECK(message) << "Trying to send nullptr instead of message";
|
||||
@ -78,18 +73,20 @@ void SendMessage(Socket &socket, uint32_t message_id,
|
||||
archive << message;
|
||||
|
||||
const std::string &buffer = stream.str();
|
||||
int64_t message_size = sizeof(MessageSize) + buffer.size();
|
||||
uint64_t message_size = sizeof(MessageSize) + buffer.size();
|
||||
CHECK(message_size <= kMaxMessageSize) << fmt::format(
|
||||
"Trying to send message of size {}, max message size is {}", message_size,
|
||||
kMaxMessageSize);
|
||||
|
||||
if (!socket.Write(reinterpret_cast<uint8_t *>(&message_id),
|
||||
sizeof(uint32_t))) {
|
||||
if (!socket.Write(reinterpret_cast<uint8_t *>(&message_id), sizeof(uint32_t),
|
||||
true)) {
|
||||
LOG(WARNING) << "Couldn't send message id!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SendLength(socket, buffer.size())) {
|
||||
MessageSize buffer_size = buffer.size();
|
||||
if (!socket.Write(reinterpret_cast<uint8_t *>(&buffer_size),
|
||||
sizeof(MessageSize), true)) {
|
||||
LOG(WARNING) << "Couldn't send message size!";
|
||||
return;
|
||||
}
|
||||
|
@ -193,12 +193,13 @@ std::experimental::optional<Socket> Socket::Accept() {
|
||||
return Socket(sfd, endpoint);
|
||||
}
|
||||
|
||||
bool Socket::Write(const uint8_t *data, size_t len,
|
||||
bool Socket::Write(const uint8_t *data, size_t len, bool have_more,
|
||||
const std::function<bool()> &keep_retrying) {
|
||||
while (len > 0) {
|
||||
// MSG_NOSIGNAL is here to disable raising a SIGPIPE signal when a
|
||||
// connection dies mid-write, the socket will only return an EPIPE error.
|
||||
auto written = send(socket_, data, len, MSG_NOSIGNAL);
|
||||
int flags = MSG_NOSIGNAL | (have_more ? MSG_MORE : 0);
|
||||
while (len > 0) {
|
||||
auto written = send(socket_, data, len, flags);
|
||||
if (written == -1) {
|
||||
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
|
||||
// Terminal error, return failure.
|
||||
@ -218,9 +219,9 @@ bool Socket::Write(const uint8_t *data, size_t len,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::Write(const std::string &s,
|
||||
bool Socket::Write(const std::string &s, bool have_more,
|
||||
const std::function<bool()> &keep_retrying) {
|
||||
return Write(reinterpret_cast<const uint8_t *>(s.data()), s.size(),
|
||||
return Write(reinterpret_cast<const uint8_t *>(s.data()), s.size(), have_more,
|
||||
keep_retrying);
|
||||
}
|
||||
|
||||
|
@ -130,6 +130,8 @@ class Socket {
|
||||
*
|
||||
* @param data uint8_t* to data that should be written
|
||||
* @param len length of char* or uint8_t* data
|
||||
* @param have_more set to true if you plan to send more data to allow the
|
||||
* kernel to buffer the data instead of immediately sending it out
|
||||
* @param keep_retrying while function executes to true socket will retry to
|
||||
* write data if nonterminal error occurred on socket (EAGAIN, EWOULDBLOCK,
|
||||
* EINTR)... useful if socket is in nonblocking mode or timeout is set on a
|
||||
@ -143,9 +145,9 @@ class Socket {
|
||||
* true if write succeeded
|
||||
* false if write failed
|
||||
*/
|
||||
bool Write(const uint8_t *data, size_t len,
|
||||
bool Write(const uint8_t *data, size_t len, bool have_more = false,
|
||||
const std::function<bool()> &keep_retrying = [] { return false; });
|
||||
bool Write(const std::string &s,
|
||||
bool Write(const std::string &s, bool have_more = false,
|
||||
const std::function<bool()> &keep_retrying = [] { return false; });
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@ class TestSocket {
|
||||
|
||||
int id() const { return socket_; }
|
||||
|
||||
bool Write(const uint8_t *data, size_t len,
|
||||
bool Write(const uint8_t *data, size_t len, bool have_more = false,
|
||||
const std::function<bool()> & = [] { return false; }) {
|
||||
if (!write_success_) return false;
|
||||
for (size_t i = 0; i < len; ++i) output.push_back(data[i]);
|
||||
|
Loading…
Reference in New Issue
Block a user