memgraph/tests/unit/network_timeouts.cpp
Matej Ferencevic 53c405c699 Throw exceptions on RPC failure and Distributed error handling
Summary:
This diff changes the RPC layer to directly return `TResponse` to the user when
issuing a `Call<...>` RPC call. The call throws an exception on failure
(instead of the previous return `nullopt`).

All servers (network, RPC and distributed) are set to have explicit `Shutdown`
methods so that a controlled shutdown can always be performed. The object
destructors now have `CHECK`s to enforce that the `AwaitShutdown` methods were
called.

The distributed memgraph is changed that none of the binaries (master/workers)
crash when there is a communication failure. Instead, the whole cluster starts
a graceful shutdown when a persistent communication error is detected.
Transient errors are allowed during execution. The transaction that errored out
will be aborted on the whole cluster. The cluster state is managed using a new
Heartbeat RPC call.

Reviewers: buda, teon.banek, msantl

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1604
2018-09-27 16:27:40 +02:00

125 lines
3.6 KiB
C++

#include <chrono>
#include <iostream>
#include <thread>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "gtest/gtest.h"
#include "communication/server.hpp"
#include "io/network/socket.hpp"
using namespace std::chrono_literals;
class TestData {};
class TestSession {
public:
TestSession(TestData *, const io::network::Endpoint &,
communication::InputStream *input_stream,
communication::OutputStream *output_stream)
: input_stream_(input_stream), output_stream_(output_stream) {}
void Execute() {
LOG(INFO) << "Received data: '"
<< std::string(
reinterpret_cast<const char *>(input_stream_->data()),
input_stream_->size())
<< "'";
if (input_stream_->data()[0] == 'e') {
std::this_thread::sleep_for(std::chrono::seconds(5));
}
output_stream_->Write(input_stream_->data(), input_stream_->size());
input_stream_->Shift(input_stream_->size());
}
private:
communication::InputStream *input_stream_;
communication::OutputStream *output_stream_;
};
const std::string safe_query("tttt");
const std::string expensive_query("eeee");
bool QueryServer(io::network::Socket &socket, const std::string &query) {
if (!socket.Write(query)) return false;
char response[105];
int len = 0;
while (len < query.size()) {
int got = socket.Read(response + len, query.size() - len);
if (got <= 0) return false;
response[got] = 0;
len += got;
}
if (std::string(response, strlen(response)) != query) return false;
return true;
}
TEST(NetworkTimeouts, InactiveSession) {
// Instantiate the server and set the session timeout to 2 seconds.
TestData test_data;
communication::ServerContext context;
communication::Server<TestSession, TestData> server{
{"127.0.0.1", 0}, &test_data, &context, 2, "Test", 1};
// Create the client and connect to the server.
io::network::Socket client;
ASSERT_TRUE(client.Connect(server.endpoint()));
// Send some data to the server.
ASSERT_TRUE(QueryServer(client, safe_query));
for (int i = 0; i < 3; ++i) {
// After this sleep the session should still be alive.
std::this_thread::sleep_for(500ms);
// Send some data to the server.
ASSERT_TRUE(QueryServer(client, safe_query));
}
// After this sleep the session should have timed out.
std::this_thread::sleep_for(3500ms);
ASSERT_FALSE(QueryServer(client, safe_query));
// Shutdown the server.
server.Shutdown();
server.AwaitShutdown();
}
TEST(NetworkTimeouts, ActiveSession) {
// Instantiate the server and set the session timeout to 2 seconds.
TestData test_data;
communication::ServerContext context;
communication::Server<TestSession, TestData> server{
{"127.0.0.1", 0}, &test_data, &context, 2, "Test", 1};
// Create the client and connect to the server.
io::network::Socket client;
ASSERT_TRUE(client.Connect(server.endpoint()));
// Send some data to the server.
ASSERT_TRUE(QueryServer(client, expensive_query));
for (int i = 0; i < 3; ++i) {
// After this sleep the session should still be alive.
std::this_thread::sleep_for(500ms);
// Send some data to the server.
ASSERT_TRUE(QueryServer(client, safe_query));
}
// After this sleep the session should have timed out.
std::this_thread::sleep_for(3500ms);
ASSERT_FALSE(QueryServer(client, safe_query));
// Shutdown the server.
server.Shutdown();
server.AwaitShutdown();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
google::InitGoogleLogging(argv[0]);
return RUN_ALL_TESTS();
}