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:
Matej Ferencevic 2018-01-25 13:08:59 +01:00
parent 6fa06f7ea5
commit e2f2cd5722
7 changed files with 26 additions and 24 deletions

View File

@ -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();
}
}

View File

@ -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(); });
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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; });
/**

View File

@ -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]);