c877c87bb4
Summary: Previously, the RPC stack used the network stack only to receive messages. The messages were then added to a separate queue that was processed by different thread pools. This design was inefficient because there was a lock when inserting and getting messages from the common queue. This diff removes the need for separate thread pools by utilising the new network stack design. This is possible because the new network stack allows full processing of the network request without blocking the whole queue. Reviewers: buda, florijan, teon.banek, dgleich, mislav.bradac Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1229
161 lines
4.4 KiB
C++
161 lines
4.4 KiB
C++
#include <algorithm>
|
|
#include <mutex>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "communication/rpc/server.hpp"
|
|
#include "io/network/endpoint.hpp"
|
|
#include "transactions/engine_master.hpp"
|
|
#include "transactions/engine_rpc_messages.hpp"
|
|
#include "transactions/engine_worker.hpp"
|
|
#include "transactions/tx_end_listener.hpp"
|
|
|
|
using namespace tx;
|
|
using namespace communication::rpc;
|
|
|
|
class WorkerEngineTest : public testing::Test {
|
|
protected:
|
|
const std::string local{"127.0.0.1"};
|
|
|
|
Server master_server_{{local, 0}};
|
|
MasterEngine master_{master_server_};
|
|
|
|
WorkerEngine worker_{master_server_.endpoint()};
|
|
};
|
|
|
|
TEST_F(WorkerEngineTest, BeginOnWorker) {
|
|
worker_.Begin();
|
|
auto second = worker_.Begin();
|
|
EXPECT_EQ(master_.RunningTransaction(second->id_)->snapshot().size(), 1);
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, AdvanceOnWorker) {
|
|
auto tx = worker_.Begin();
|
|
auto cid = tx->cid();
|
|
EXPECT_EQ(worker_.Advance(tx->id_), cid + 1);
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, CommitOnWorker) {
|
|
auto tx = worker_.Begin();
|
|
auto tx_id = tx->id_;
|
|
worker_.Commit(*tx);
|
|
EXPECT_TRUE(master_.Info(tx_id).is_committed());
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, AbortOnWorker) {
|
|
auto tx = worker_.Begin();
|
|
auto tx_id = tx->id_;
|
|
worker_.Abort(*tx);
|
|
EXPECT_TRUE(master_.Info(tx_id).is_aborted());
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, RunningTransaction) {
|
|
master_.Begin();
|
|
master_.Begin();
|
|
worker_.RunningTransaction(1);
|
|
worker_.RunningTransaction(2);
|
|
int count = 0;
|
|
worker_.LocalForEachActiveTransaction([&count](Transaction &t) {
|
|
++count;
|
|
if (t.id_ == 1) {
|
|
EXPECT_EQ(t.snapshot(),
|
|
tx::Snapshot(std::vector<tx::transaction_id_t>{}));
|
|
} else {
|
|
EXPECT_EQ(t.snapshot(), tx::Snapshot({1}));
|
|
}
|
|
});
|
|
EXPECT_EQ(count, 2);
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, Info) {
|
|
auto *tx_1 = master_.Begin();
|
|
auto *tx_2 = master_.Begin();
|
|
// We can't check active transactions in the worker (see comments there for
|
|
// info).
|
|
master_.Commit(*tx_1);
|
|
EXPECT_TRUE(master_.Info(1).is_committed());
|
|
EXPECT_TRUE(worker_.Info(1).is_committed());
|
|
master_.Abort(*tx_2);
|
|
EXPECT_TRUE(master_.Info(2).is_aborted());
|
|
EXPECT_TRUE(worker_.Info(2).is_aborted());
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, GlobalGcSnapshot) {
|
|
auto *tx_1 = master_.Begin();
|
|
master_.Begin();
|
|
master_.Commit(*tx_1);
|
|
EXPECT_EQ(master_.GlobalGcSnapshot(), tx::Snapshot({1, 2}));
|
|
EXPECT_EQ(worker_.GlobalGcSnapshot(), master_.GlobalGcSnapshot());
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, GlobalActiveTransactions) {
|
|
auto *tx_1 = master_.Begin();
|
|
master_.Begin();
|
|
auto *tx_3 = master_.Begin();
|
|
master_.Begin();
|
|
master_.Commit(*tx_1);
|
|
master_.Abort(*tx_3);
|
|
EXPECT_EQ(worker_.GlobalActiveTransactions(), tx::Snapshot({2, 4}));
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, GlobalIsActive) {
|
|
auto *tx_1 = master_.Begin();
|
|
master_.Begin();
|
|
auto *tx_3 = master_.Begin();
|
|
master_.Begin();
|
|
master_.Commit(*tx_1);
|
|
master_.Abort(*tx_3);
|
|
EXPECT_FALSE(worker_.GlobalIsActive(1));
|
|
EXPECT_TRUE(worker_.GlobalIsActive(2));
|
|
EXPECT_FALSE(worker_.GlobalIsActive(3));
|
|
EXPECT_TRUE(worker_.GlobalIsActive(4));
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, LocalLast) {
|
|
master_.Begin();
|
|
EXPECT_EQ(worker_.LocalLast(), 0);
|
|
worker_.RunningTransaction(1);
|
|
EXPECT_EQ(worker_.LocalLast(), 1);
|
|
master_.Begin();
|
|
EXPECT_EQ(worker_.LocalLast(), 1);
|
|
master_.Begin();
|
|
EXPECT_EQ(worker_.LocalLast(), 1);
|
|
master_.Begin();
|
|
worker_.RunningTransaction(4);
|
|
EXPECT_EQ(worker_.LocalLast(), 4);
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, LocalForEachActiveTransaction) {
|
|
master_.Begin();
|
|
worker_.RunningTransaction(1);
|
|
master_.Begin();
|
|
master_.Begin();
|
|
master_.Begin();
|
|
worker_.RunningTransaction(4);
|
|
std::unordered_set<tx::transaction_id_t> local;
|
|
worker_.LocalForEachActiveTransaction(
|
|
[&local](Transaction &t) { local.insert(t.id_); });
|
|
EXPECT_EQ(local, std::unordered_set<tx::transaction_id_t>({1, 4}));
|
|
}
|
|
|
|
TEST_F(WorkerEngineTest, TxEndListener) {
|
|
std::atomic<int> has_expired{0};
|
|
TxEndListener worker_end_listner{
|
|
worker_, [&has_expired](transaction_id_t tid) {
|
|
std::cout << "asdasdadas: " << tid << std::endl;
|
|
++has_expired; }};
|
|
|
|
auto sleep_period =
|
|
WorkerEngine::kCacheReleasePeriod + std::chrono::milliseconds(200);
|
|
auto t1 = master_.Begin();
|
|
auto t2 = master_.Begin();
|
|
std::this_thread::sleep_for(sleep_period);
|
|
EXPECT_EQ(has_expired.load(), 0);
|
|
master_.Commit(*t1);
|
|
master_.Abort(*t2);
|
|
std::this_thread::sleep_for(sleep_period);
|
|
EXPECT_EQ(has_expired.load(), 2);
|
|
}
|