Prepare counter for distributed
Reviewers: dgleich, teon.banek Reviewed By: teon.banek Differential Revision: https://phabricator.memgraph.io/D1090
This commit is contained in:
parent
b736c37199
commit
4a0345e1c5
@ -9,6 +9,7 @@ set(memgraph_src_files
|
|||||||
communication/messaging/protocol.cpp
|
communication/messaging/protocol.cpp
|
||||||
communication/rpc/rpc.cpp
|
communication/rpc/rpc.cpp
|
||||||
data_structures/concurrent/skiplist_gc.cpp
|
data_structures/concurrent/skiplist_gc.cpp
|
||||||
|
database/counters.cpp
|
||||||
database/graph_db.cpp
|
database/graph_db.cpp
|
||||||
database/graph_db_accessor.cpp
|
database/graph_db_accessor.cpp
|
||||||
database/graph_db_config.cpp
|
database/graph_db_config.cpp
|
||||||
|
72
src/database/counters.cpp
Normal file
72
src/database/counters.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#include "database/counters.hpp"
|
||||||
|
|
||||||
|
#include "boost/archive/binary_iarchive.hpp"
|
||||||
|
#include "boost/archive/binary_oarchive.hpp"
|
||||||
|
#include "boost/serialization/export.hpp"
|
||||||
|
#include "boost/serialization/utility.hpp"
|
||||||
|
|
||||||
|
namespace database {
|
||||||
|
|
||||||
|
const std::string kCountersRpc = "CountersRpc";
|
||||||
|
const auto kCountersRpcTimeout = 300ms;
|
||||||
|
|
||||||
|
RPC_SINGLE_MEMBER_MESSAGE(CountersGetReq, std::string);
|
||||||
|
RPC_SINGLE_MEMBER_MESSAGE(CountersGetRes, int64_t);
|
||||||
|
using CountersGetRpc =
|
||||||
|
communication::rpc::RequestResponse<CountersGetReq, CountersGetRes>;
|
||||||
|
|
||||||
|
using CountersSetReqData = std::pair<std::string, int64_t>;
|
||||||
|
RPC_SINGLE_MEMBER_MESSAGE(CountersSetReq, CountersSetReqData);
|
||||||
|
RPC_NO_MEMBER_MESSAGE(CountersSetRes);
|
||||||
|
using CountersSetRpc =
|
||||||
|
communication::rpc::RequestResponse<CountersSetReq, CountersSetRes>;
|
||||||
|
|
||||||
|
int64_t SingleNodeCounters::Get(const std::string &name) {
|
||||||
|
return counters_.access()
|
||||||
|
.emplace(name, std::make_tuple(name), std::make_tuple(0))
|
||||||
|
.first->second.fetch_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleNodeCounters::Set(const std::string &name, int64_t value) {
|
||||||
|
auto name_counter_pair = counters_.access().emplace(
|
||||||
|
name, std::make_tuple(name), std::make_tuple(value));
|
||||||
|
if (!name_counter_pair.second) name_counter_pair.first->second.store(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
MasterCounters::MasterCounters(communication::messaging::System &system)
|
||||||
|
: rpc_server_(system, kCountersRpc) {
|
||||||
|
rpc_server_.Register<CountersGetRpc>([this](const CountersGetReq &req) {
|
||||||
|
return std::make_unique<CountersGetRes>(Get(req.member));
|
||||||
|
});
|
||||||
|
rpc_server_.Register<CountersSetRpc>([this](const CountersSetReq &req) {
|
||||||
|
Set(req.member.first, req.member.second);
|
||||||
|
return std::make_unique<CountersSetRes>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MasterCounters::Start() { rpc_server_.Start(); }
|
||||||
|
void MasterCounters::Shutdown() { rpc_server_.Shutdown(); }
|
||||||
|
|
||||||
|
WorkerCounters::WorkerCounters(
|
||||||
|
communication::messaging::System &system,
|
||||||
|
const io::network::NetworkEndpoint &master_endpoint)
|
||||||
|
: rpc_client_(system, master_endpoint, kCountersRpc) {}
|
||||||
|
|
||||||
|
int64_t WorkerCounters::Get(const std::string &name) {
|
||||||
|
auto response = rpc_client_.Call<CountersGetRpc>(kCountersRpcTimeout, name);
|
||||||
|
CHECK(response) << "CountersGetRpc - failed to get response from master";
|
||||||
|
return response->member;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WorkerCounters::Set(const std::string &name, int64_t value) {
|
||||||
|
auto response = rpc_client_.Call<CountersSetRpc>(
|
||||||
|
kCountersRpcTimeout, CountersSetReqData{name, value});
|
||||||
|
CHECK(response) << "CountersSetRpc - failed to get response from master";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace database
|
||||||
|
|
||||||
|
BOOST_CLASS_EXPORT(database::CountersGetReq);
|
||||||
|
BOOST_CLASS_EXPORT(database::CountersGetRes);
|
||||||
|
BOOST_CLASS_EXPORT(database::CountersSetReq);
|
||||||
|
BOOST_CLASS_EXPORT(database::CountersSetRes);
|
69
src/database/counters.hpp
Normal file
69
src/database/counters.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "communication/messaging/distributed.hpp"
|
||||||
|
#include "communication/rpc/rpc.hpp"
|
||||||
|
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||||
|
#include "utils/rpc_pimp.hpp"
|
||||||
|
|
||||||
|
namespace database {
|
||||||
|
|
||||||
|
/** A set of counter that are guaranteed to produce unique, consecutive values
|
||||||
|
* on each call. */
|
||||||
|
class Counters {
|
||||||
|
public:
|
||||||
|
virtual ~Counters() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current value of the counter with the given name, and
|
||||||
|
* increments that counter. If the counter with the given name does not exist,
|
||||||
|
* a new counter is created and this function returns 0.
|
||||||
|
*/
|
||||||
|
virtual int64_t Get(const std::string &name) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the counter with the given name to the given value. Returns nothing.
|
||||||
|
* If the counter with the given name does not exist, a new counter is created
|
||||||
|
* and set to the given value.
|
||||||
|
*/
|
||||||
|
virtual void Set(const std::string &name, int64_t values) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Implementation for the single-node memgraph */
|
||||||
|
class SingleNodeCounters : public Counters {
|
||||||
|
public:
|
||||||
|
int64_t Get(const std::string &name) override;
|
||||||
|
void Set(const std::string &name, int64_t value) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConcurrentMap<std::string, std::atomic<int64_t>> counters_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Implementation for distributed master. */
|
||||||
|
class MasterCounters : public SingleNodeCounters {
|
||||||
|
public:
|
||||||
|
MasterCounters(communication::messaging::System &system);
|
||||||
|
void Start();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
private:
|
||||||
|
communication::rpc::Server rpc_server_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Implementation for distributed worker. */
|
||||||
|
class WorkerCounters : public Counters {
|
||||||
|
public:
|
||||||
|
WorkerCounters(communication::messaging::System &system,
|
||||||
|
const io::network::NetworkEndpoint &master_endpoint);
|
||||||
|
|
||||||
|
int64_t Get(const std::string &name) override;
|
||||||
|
void Set(const std::string &name, int64_t value) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
communication::rpc::Client rpc_client_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace database
|
@ -23,6 +23,7 @@ namespace fs = std::experimental::filesystem;
|
|||||||
|
|
||||||
GraphDb::GraphDb(Config config) : GraphDb(config, 0) {
|
GraphDb::GraphDb(Config config) : GraphDb(config, 0) {
|
||||||
tx_engine_ = std::make_unique<tx::MasterEngine>(&wal_);
|
tx_engine_ = std::make_unique<tx::MasterEngine>(&wal_);
|
||||||
|
counters_ = std::make_unique<database::SingleNodeCounters>();
|
||||||
INIT_MAPPERS(storage::SingleNodeConcurrentIdMapper);
|
INIT_MAPPERS(storage::SingleNodeConcurrentIdMapper);
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
@ -33,6 +34,9 @@ GraphDb::GraphDb(communication::messaging::System &system,
|
|||||||
auto tx_engine = std::make_unique<tx::MasterEngine>(&wal_);
|
auto tx_engine = std::make_unique<tx::MasterEngine>(&wal_);
|
||||||
tx_engine->StartServer(system);
|
tx_engine->StartServer(system);
|
||||||
tx_engine_ = std::move(tx_engine);
|
tx_engine_ = std::move(tx_engine);
|
||||||
|
auto counters = std::make_unique<database::MasterCounters>(system);
|
||||||
|
counters->Start();
|
||||||
|
counters_ = std::move(counters);
|
||||||
INIT_MAPPERS(storage::MasterConcurrentIdMapper, system);
|
INIT_MAPPERS(storage::MasterConcurrentIdMapper, system);
|
||||||
get_endpoint_ = [&master](int worker_id) {
|
get_endpoint_ = [&master](int worker_id) {
|
||||||
return master.GetEndpoint(worker_id);
|
return master.GetEndpoint(worker_id);
|
||||||
@ -45,6 +49,8 @@ GraphDb::GraphDb(communication::messaging::System &system, int worker_id,
|
|||||||
Endpoint master_endpoint, Config config)
|
Endpoint master_endpoint, Config config)
|
||||||
: GraphDb(config, worker_id) {
|
: GraphDb(config, worker_id) {
|
||||||
tx_engine_ = std::make_unique<tx::WorkerEngine>(system, master_endpoint);
|
tx_engine_ = std::make_unique<tx::WorkerEngine>(system, master_endpoint);
|
||||||
|
counters_ =
|
||||||
|
std::make_unique<database::WorkerCounters>(system, master_endpoint);
|
||||||
INIT_MAPPERS(storage::WorkerConcurrentIdMapper, system, master_endpoint);
|
INIT_MAPPERS(storage::WorkerConcurrentIdMapper, system, master_endpoint);
|
||||||
get_endpoint_ = [&worker](int worker_id) {
|
get_endpoint_ = [&worker](int worker_id) {
|
||||||
return worker.GetEndpoint(worker_id);
|
return worker.GetEndpoint(worker_id);
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||||
#include "data_structures/concurrent/concurrent_set.hpp"
|
#include "data_structures/concurrent/concurrent_set.hpp"
|
||||||
|
#include "database/counters.hpp"
|
||||||
#include "database/graph_db_datatypes.hpp"
|
#include "database/graph_db_datatypes.hpp"
|
||||||
#include "database/indexes/key_index.hpp"
|
#include "database/indexes/key_index.hpp"
|
||||||
#include "database/indexes/label_property_index.hpp"
|
#include "database/indexes/label_property_index.hpp"
|
||||||
@ -167,7 +168,7 @@ class GraphDb {
|
|||||||
Scheduler transaction_killer_;
|
Scheduler transaction_killer_;
|
||||||
|
|
||||||
// DB level global counters, used in the "counter" function.
|
// DB level global counters, used in the "counter" function.
|
||||||
ConcurrentMap<std::string, std::atomic<int64_t>> counters_;
|
std::unique_ptr<database::Counters> counters_;
|
||||||
|
|
||||||
// Returns Endpoint info for worker ID. Different implementation in master vs.
|
// Returns Endpoint info for worker ID. Different implementation in master vs.
|
||||||
// worker. Unused in single-node version.
|
// worker. Unused in single-node version.
|
||||||
|
@ -407,15 +407,11 @@ const std::string &GraphDbAccessor::PropertyName(
|
|||||||
}
|
}
|
||||||
|
|
||||||
int64_t GraphDbAccessor::Counter(const std::string &name) {
|
int64_t GraphDbAccessor::Counter(const std::string &name) {
|
||||||
return db_.counters_.access()
|
return db_.counters_->Get(name);
|
||||||
.emplace(name, std::make_tuple(name), std::make_tuple(0))
|
|
||||||
.first->second.fetch_add(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphDbAccessor::CounterSet(const std::string &name, int64_t value) {
|
void GraphDbAccessor::CounterSet(const std::string &name, int64_t value) {
|
||||||
auto name_counter_pair = db_.counters_.access().emplace(
|
db_.counters_->Set(name, value);
|
||||||
name, std::make_tuple(name), std::make_tuple(value));
|
|
||||||
if (!name_counter_pair.second) name_counter_pair.first->second.store(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GraphDbAccessor::IndexInfo() const {
|
std::vector<std::string> GraphDbAccessor::IndexInfo() const {
|
||||||
|
@ -15,9 +15,10 @@ namespace storage {
|
|||||||
template <typename TId>
|
template <typename TId>
|
||||||
class ConcurrentIdMapper {
|
class ConcurrentIdMapper {
|
||||||
public:
|
public:
|
||||||
|
virtual ~ConcurrentIdMapper() {}
|
||||||
|
|
||||||
virtual TId value_to_id(const std::string &value) = 0;
|
virtual TId value_to_id(const std::string &value) = 0;
|
||||||
virtual const std::string &id_to_value(const TId &id) = 0;
|
virtual const std::string &id_to_value(const TId &id) = 0;
|
||||||
virtual ~ConcurrentIdMapper() {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace storage
|
} // namespace storage
|
||||||
|
34
tests/unit/counters.cpp
Normal file
34
tests/unit/counters.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include "communication/messaging/distributed.hpp"
|
||||||
|
#include "database/counters.hpp"
|
||||||
|
|
||||||
|
const std::string kLocal = "127.0.0.1";
|
||||||
|
|
||||||
|
TEST(CountersDistributed, All) {
|
||||||
|
communication::messaging::System master_sys(kLocal, 0);
|
||||||
|
database::MasterCounters master(master_sys);
|
||||||
|
master.Start();
|
||||||
|
|
||||||
|
communication::messaging::System w1_sys(kLocal, 0);
|
||||||
|
database::WorkerCounters w1(w1_sys, master_sys.endpoint());
|
||||||
|
|
||||||
|
communication::messaging::System w2_sys(kLocal, 0);
|
||||||
|
database::WorkerCounters w2(w2_sys, master_sys.endpoint());
|
||||||
|
|
||||||
|
EXPECT_EQ(w1.Get("a"), 0);
|
||||||
|
EXPECT_EQ(w1.Get("a"), 1);
|
||||||
|
EXPECT_EQ(w2.Get("a"), 2);
|
||||||
|
EXPECT_EQ(w1.Get("a"), 3);
|
||||||
|
EXPECT_EQ(master.Get("a"), 4);
|
||||||
|
|
||||||
|
EXPECT_EQ(master.Get("b"), 0);
|
||||||
|
EXPECT_EQ(w2.Get("b"), 1);
|
||||||
|
w1.Set("b", 42);
|
||||||
|
EXPECT_EQ(w2.Get("b"), 42);
|
||||||
|
|
||||||
|
w2_sys.Shutdown();
|
||||||
|
w1_sys.Shutdown();
|
||||||
|
master.Shutdown();
|
||||||
|
master_sys.Shutdown();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user