Replace boost with capnp in RPC

Summary:
Converts the RPC stack to use Cap'n Proto for serialization instead of
boost. There are still some traces of boost in other places in the code,
but most of it is removed. A future diff should cleanup boost for good.

The RPC API is now changed to be more flexible with regards to how
serialize data. This makes the simplest cases a bit more verbose, but
allows complex serialization code to be correctly written instead of
relying on hacks. (For reference, look for the old serialization of
`PullRpc` which had a nasty pointer hacks to inject accessors in
`TypedValue`.)

Since RPC messages were uselessly modeled via inheritance of Message
base class, that class is now removed. Furthermore, that approach
doesn't really work with Cap'n Proto. Instead, each message type is
required to have some type information. This can be automated, so
`define-rpc` has been added to LCP, which hopefully simplifies defining
new RPC request and response messages.

Specify Cap'n Proto schema ID in cmake

This preserves Cap'n Proto generated typeIds across multiple generations
of capnp schemas through LCP. It is imperative that typeId stays the
same to ensure that different compilations of Memgraph may communicate
via RPC in a distributed cluster.

Use CLOS for meta information on C++ types in LCP

Since some structure slots and functions have started to repeat
themselves, it makes sense to model C++ meta information via Common Lisp
Object System.

Depends on D1391

Reviewers: buda, dgleich, mferencevic, mtomic, mculinovic, msantl

Reviewed By: msantl

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1407
This commit is contained in:
Teon Banek 2018-06-04 09:48:48 +02:00
parent e56ed0acce
commit e0474a8e92
112 changed files with 3813 additions and 3110 deletions

36
.gitignore vendored
View File

@ -39,6 +39,38 @@ TAGS
*.capnp.h
# LCP generated C++ & Cap'n Proto files
src/query/plan/operator.hpp
src/query/plan/operator.lcp.cpp
*.lcp.cpp
src/database/counters_rpc_messages.capnp
src/database/counters_rpc_messages.hpp
src/database/state_delta.capnp
src/database/state_delta.hpp
src/distributed/bfs_rpc_messages.capnp
src/distributed/bfs_rpc_messages.hpp
src/distributed/coordination_rpc_messages.capnp
src/distributed/coordination_rpc_messages.hpp
src/distributed/data_rpc_messages.capnp
src/distributed/data_rpc_messages.hpp
src/distributed/durability_rpc_messages.capnp
src/distributed/durability_rpc_messages.hpp
src/distributed/index_rpc_messages.capnp
src/distributed/index_rpc_messages.hpp
src/distributed/plan_rpc_messages.capnp
src/distributed/plan_rpc_messages.hpp
src/distributed/pull_produce_rpc_messages.capnp
src/distributed/pull_produce_rpc_messages.hpp
src/distributed/storage_gc_rpc_messages.capnp
src/distributed/storage_gc_rpc_messages.hpp
src/distributed/token_sharing_rpc_messages.capnp
src/distributed/token_sharing_rpc_messages.hpp
src/distributed/transactional_cache_cleaner_rpc_messages.capnp
src/distributed/transactional_cache_cleaner_rpc_messages.hpp
src/distributed/updates_rpc_messages.capnp
src/distributed/updates_rpc_messages.hpp
src/query/plan/operator.capnp
src/query/plan/operator.hpp
src/stats/stats_rpc_messages.capnp
src/stats/stats_rpc_messages.hpp
src/storage/concurrent_id_mapper_rpc_messages.capnp
src/storage/concurrent_id_mapper_rpc_messages.hpp
src/transactions/engine_rpc_messages.capnp
src/transactions/engine_rpc_messages.hpp

2
init
View File

@ -111,6 +111,7 @@ fi
mkdir -p ./build
# quicklisp package manager for Common Lisp
# TODO: We should at some point cache or have a mirror of packages we use.
quicklisp_install_dir="$HOME/quicklisp"
if [[ -v QUICKLISP_HOME ]]; then
quicklisp_install_dir="${QUICKLISP_HOME}"
@ -121,6 +122,7 @@ if [[ ! -f "${quicklisp_install_dir}/setup.lisp" ]]; then
"
(load \"${DIR}/quicklisp.lisp\")
(quicklisp-quickstart:install :path \"${quicklisp_install_dir}\")
(ql:quickload :cl-ppcre :silent t)
" | sbcl --script || exit 1
rm -rf quicklisp.lisp || exit 1
fi

View File

@ -35,6 +35,7 @@ set(memgraph_src_files
distributed/data_rpc_server.cpp
distributed/produce_rpc_server.cpp
distributed/pull_rpc_clients.cpp
distributed/serialization.cpp
distributed/updates_rpc_clients.cpp
distributed/updates_rpc_server.cpp
durability/paths.cpp
@ -70,6 +71,7 @@ set(memgraph_src_files
transactions/engine_master.cpp
transactions/engine_single_node.cpp
transactions/engine_worker.cpp
transactions/snapshot.cpp
)
# -----------------------------------------------------------------------------
@ -96,23 +98,31 @@ set(lcp_src_files lisp/lcp.lisp ${lcp_exe})
# Use this function to add each lcp file to generation. This way each file is
# standalone and we avoid recompiling everything.
#
# You may pass a CAPNP_SCHEMA <id> keyword argument to generate the Cap'n Proto
# serialization code from .lcp file. You still need to add the generated capnp
# file through `add_capnp` function. To generate the <id> use `capnp id`
# invocation, and specify it here. This preserves correct id information across
# multiple schema generations. If this wasn't the case, wrong typeId
# information will break RPC between different compilations of memgraph.
#
# NOTE: memgraph_src_files and generated_lcp_files are globally updated.
function(add_lcp lcp_file)
set(options CAPNP_SCHEMA)
cmake_parse_arguments(KW "${options}" "" "" ${ARGN})
set(one_value_kwargs CAPNP_SCHEMA)
cmake_parse_arguments(KW "" "${one_value_kwargs}" "" ${ARGN})
string(REGEX REPLACE "\.lcp$" ".hpp" h_file
"${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}")
if (KW_CAPNP_SCHEMA)
string(REGEX REPLACE "\.lcp$" ".capnp" capnp_file
"${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}")
set(capnp_id_command ${CAPNP_EXE})
set(capnp_id ${KW_CAPNP_SCHEMA})
set(capnp_depend capnproto-proj)
set(cpp_file ${CMAKE_CURRENT_SOURCE_DIR}/${lcp_file}.cpp)
# Update *global* memgraph_src_files
set(memgraph_src_files ${memgraph_src_files} ${cpp_file} PARENT_SCOPE)
endif()
add_custom_command(OUTPUT ${h_file} ${cpp_file} ${capnp_file}
COMMAND ${lcp_exe} ${lcp_file} ${capnp_id_command}
COMMAND ${lcp_exe} ${lcp_file} ${capnp_id}
VERBATIM
DEPENDS ${lcp_file} ${lcp_src_files} ${capnp_depend}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
@ -120,18 +130,54 @@ function(add_lcp lcp_file)
set(generated_lcp_files ${generated_lcp_files} ${h_file} ${cpp_file} ${capnp_file} PARENT_SCOPE)
endfunction(add_lcp)
add_lcp(query/plan/operator.lcp CAPNP_SCHEMA)
add_lcp(database/counters_rpc_messages.lcp CAPNP_SCHEMA @0x95a2c3ea3871e945)
add_capnp(database/counters_rpc_messages.capnp)
add_lcp(database/state_delta.lcp CAPNP_SCHEMA @0xdea01657b3563887)
add_capnp(database/state_delta.capnp)
add_lcp(distributed/bfs_rpc_messages.lcp CAPNP_SCHEMA @0x8e508640b09b6d2a)
add_capnp(distributed/bfs_rpc_messages.capnp)
add_lcp(distributed/coordination_rpc_messages.lcp CAPNP_SCHEMA @0x93df0c4703cf98fb)
add_capnp(distributed/coordination_rpc_messages.capnp)
add_lcp(distributed/data_rpc_messages.lcp CAPNP_SCHEMA @0xc1c8a341ba37aaf5)
add_capnp(distributed/data_rpc_messages.capnp)
add_lcp(distributed/durability_rpc_messages.lcp CAPNP_SCHEMA @0xf5e53bc271e2163d)
add_capnp(distributed/durability_rpc_messages.capnp)
add_lcp(distributed/index_rpc_messages.lcp CAPNP_SCHEMA @0xa8aab46862945bd6)
add_capnp(distributed/index_rpc_messages.capnp)
add_lcp(distributed/plan_rpc_messages.lcp CAPNP_SCHEMA @0xfcbc48dc9f106d28)
add_capnp(distributed/plan_rpc_messages.capnp)
add_lcp(distributed/pull_produce_rpc_messages.lcp CAPNP_SCHEMA @0xa78a9254a73685bd)
add_capnp(distributed/pull_produce_rpc_messages.capnp)
add_lcp(distributed/storage_gc_rpc_messages.lcp CAPNP_SCHEMA @0xd705663dfe36cf81)
add_capnp(distributed/storage_gc_rpc_messages.capnp)
add_lcp(distributed/token_sharing_rpc_messages.lcp CAPNP_SCHEMA @0x8f295db54ec4caec)
add_capnp(distributed/token_sharing_rpc_messages.capnp)
add_lcp(distributed/transactional_cache_cleaner_rpc_messages.lcp CAPNP_SCHEMA @0xe2be6183a1ff9e11)
add_capnp(distributed/transactional_cache_cleaner_rpc_messages.capnp)
add_lcp(distributed/updates_rpc_messages.lcp CAPNP_SCHEMA @0x82d5f38d73c7b53a)
add_capnp(distributed/updates_rpc_messages.capnp)
add_lcp(query/plan/operator.lcp CAPNP_SCHEMA @0xe5cae8d045d30c42)
add_capnp(query/plan/operator.capnp)
add_lcp(stats/stats_rpc_messages.lcp CAPNP_SCHEMA @0xc19a87c81b9b4512)
add_capnp(stats/stats_rpc_messages.capnp)
add_lcp(storage/concurrent_id_mapper_rpc_messages.lcp CAPNP_SCHEMA @0xa6068dae93d225dd)
add_capnp(storage/concurrent_id_mapper_rpc_messages.capnp)
add_lcp(transactions/engine_rpc_messages.lcp CAPNP_SCHEMA @0xde02b7c49180cad5)
add_capnp(transactions/engine_rpc_messages.capnp)
add_custom_target(generate_lcp DEPENDS ${generated_lcp_files})
# Registering capnp must come after registering lcp files.
add_capnp(query/frontend/semantic/symbol.capnp)
add_capnp(query/frontend/ast/ast.capnp)
add_capnp(utils/serialization.capnp)
add_capnp(storage/types.capnp)
add_capnp(communication/rpc/messages.capnp)
add_capnp(distributed/serialization.capnp)
add_capnp(durability/recovery.capnp)
add_capnp(query/common.capnp)
add_capnp(query/frontend/ast/ast.capnp)
add_capnp(query/frontend/semantic/symbol.capnp)
add_capnp(storage/serialization.capnp)
add_capnp(transactions/common.capnp)
add_capnp(utils/serialization.capnp)
add_custom_target(generate_capnp DEPENDS generate_lcp ${generated_capnp_files})

View File

@ -1,8 +1,5 @@
#pragma once
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "communication/rpc/messages.hpp"
#include "communication/raft/raft.hpp"
@ -11,38 +8,16 @@ namespace communication::raft {
enum class RpcType { REQUEST_VOTE, APPEND_ENTRIES };
template <class State>
struct PeerRpcRequest : public rpc::Message {
struct PeerRpcRequest {
RpcType type;
RequestVoteRequest request_vote;
AppendEntriesRequest<State> append_entries;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<rpc::Message>(*this);
ar &type;
ar &request_vote;
ar &append_entries;
}
};
struct PeerRpcReply : public rpc::Message {
struct PeerRpcReply {
RpcType type;
RequestVoteReply request_vote;
AppendEntriesReply append_entries;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<rpc::Message>(*this);
ar &type;
ar &request_vote;
ar &append_entries;
}
};
} // namespace communication::raft

View File

@ -30,24 +30,27 @@ class RpcNetwork : public RaftNetworkInterface<State> {
: server_(server), directory_(std::move(directory)) {}
virtual void Start(RaftMember<State> &member) override {
server_.Register<PeerProtocol<State>>(
[&member](const PeerRpcRequest<State> &request) {
auto reply = std::make_unique<PeerRpcReply>();
reply->type = request.type;
switch (request.type) {
case RpcType::REQUEST_VOTE:
reply->request_vote = member.OnRequestVote(request.request_vote);
break;
case RpcType::APPEND_ENTRIES:
reply->append_entries =
member.OnAppendEntries(request.append_entries);
break;
default:
LOG(ERROR) << "Unknown RPC type: "
<< static_cast<int>(request.type);
}
return reply;
});
// TODO: Serialize RPC via Cap'n Proto
// server_.Register<PeerProtocol<State>>(
// [&member](const auto &req_reader, auto *res_builder) {
// PeerRpcRequest<State> request;
// request.Load(req_reader);
// PeerRpcReply reply;
// reply.type = request.type;
// switch (request.type) {
// case RpcType::REQUEST_VOTE:
// reply.request_vote = member.OnRequestVote(request.request_vote);
// break;
// case RpcType::APPEND_ENTRIES:
// reply.append_entries =
// member.OnAppendEntries(request.append_entries);
// break;
// default:
// LOG(ERROR) << "Unknown RPC type: "
// << static_cast<int>(request.type);
// }
// reply.Save(res_builder);
// });
}
virtual bool SendRequestVote(const MemberId &recipient,

View File

@ -1,12 +1,6 @@
#include <chrono>
#include <thread>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "boost/serialization/export.hpp"
#include "boost/serialization/unique_ptr.hpp"
#include "gflags/gflags.h"
#include "communication/rpc/client.hpp"
@ -19,7 +13,8 @@ namespace communication::rpc {
Client::Client(const io::network::Endpoint &endpoint) : endpoint_(endpoint) {}
std::unique_ptr<Message> Client::Call(const Message &request) {
std::experimental::optional<::capnp::FlatArrayMessageReader> Client::Send(
::capnp::MessageBuilder *message) {
std::lock_guard<std::mutex> guard(mutex_);
if (FLAGS_rpc_random_latency) {
@ -39,45 +34,37 @@ std::unique_ptr<Message> Client::Call(const Message &request) {
if (!client_->Connect(endpoint_)) {
LOG(ERROR) << "Couldn't connect to remote address " << endpoint_;
client_ = std::experimental::nullopt;
return nullptr;
return std::experimental::nullopt;
}
}
// Serialize and send request.
std::stringstream request_stream(std::ios_base::out | std::ios_base::binary);
{
boost::archive::binary_oarchive request_archive(request_stream);
// Serialize reference as pointer (to serialize the derived class). The
// request is read in protocol.cpp.
request_archive << &request;
// Archive destructor ensures everything is written.
}
const std::string &request_buffer = request_stream.str();
CHECK(request_buffer.size() <= std::numeric_limits<MessageSize>::max())
auto request_words = ::capnp::messageToFlatArray(*message);
auto request_bytes = request_words.asBytes();
CHECK(request_bytes.size() <= std::numeric_limits<MessageSize>::max())
<< fmt::format(
"Trying to send message of size {}, max message size is {}",
request_buffer.size(), std::numeric_limits<MessageSize>::max());
request_bytes.size(), std::numeric_limits<MessageSize>::max());
MessageSize request_data_size = request_buffer.size();
MessageSize request_data_size = request_bytes.size();
if (!client_->Write(reinterpret_cast<uint8_t *>(&request_data_size),
sizeof(MessageSize), true)) {
LOG(ERROR) << "Couldn't send request size to " << client_->endpoint();
client_ = std::experimental::nullopt;
return nullptr;
return std::experimental::nullopt;
}
if (!client_->Write(request_buffer)) {
if (!client_->Write(request_bytes.begin(), request_bytes.size())) {
LOG(ERROR) << "Couldn't send request data to " << client_->endpoint();
client_ = std::experimental::nullopt;
return nullptr;
return std::experimental::nullopt;
}
// Receive response data size.
if (!client_->Read(sizeof(MessageSize))) {
LOG(ERROR) << "Couldn't get response from " << client_->endpoint();
client_ = std::experimental::nullopt;
return nullptr;
return std::experimental::nullopt;
}
MessageSize response_data_size =
*reinterpret_cast<MessageSize *>(client_->GetData());
@ -87,22 +74,19 @@ std::unique_ptr<Message> Client::Call(const Message &request) {
if (!client_->Read(response_data_size)) {
LOG(ERROR) << "Couldn't get response from " << client_->endpoint();
client_ = std::experimental::nullopt;
return nullptr;
}
std::unique_ptr<Message> response;
{
std::stringstream response_stream(std::ios_base::in |
std::ios_base::binary);
response_stream.str(std::string(reinterpret_cast<char *>(client_->GetData()),
response_data_size));
boost::archive::binary_iarchive response_archive(response_stream);
response_archive >> response;
return std::experimental::nullopt;
}
// Read the response message.
auto data = ::kj::arrayPtr(client_->GetData(), response_data_size);
// Our data is word aligned and padded to 64bit because we use regular
// (non-packed) serialization of Cap'n Proto. So we can use reinterpret_cast.
auto data_words =
::kj::arrayPtr(reinterpret_cast<::capnp::word *>(data.begin()),
reinterpret_cast<::capnp::word *>(data.end()));
::capnp::FlatArrayMessageReader response_message(data_words.asConst());
client_->ShiftData(response_data_size);
return response;
return std::experimental::make_optional(std::move(response_message));
}
void Client::Abort() {

View File

@ -5,62 +5,85 @@
#include <mutex>
#include <random>
#include <capnp/message.h>
#include <capnp/serialize.h>
#include <glog/logging.h>
#include "communication/client.hpp"
#include "communication/rpc/messages.capnp.h"
#include "communication/rpc/messages.hpp"
#include "io/network/endpoint.hpp"
#include "utils/demangle.hpp"
namespace communication::rpc {
// Client is thread safe, but it is recommended to use thread_local clients.
/// Client is thread safe, but it is recommended to use thread_local clients.
class Client {
public:
Client(const io::network::Endpoint &endpoint);
explicit Client(const io::network::Endpoint &endpoint);
// Call function can initiate only one request at the time. Function blocks
// until there is a response. If there was an error nullptr is returned.
template <typename TRequestResponse, typename... Args>
std::unique_ptr<typename TRequestResponse::Response> Call(Args &&... args) {
using Req = typename TRequestResponse::Request;
using Res = typename TRequestResponse::Response;
static_assert(std::is_base_of<Message, Req>::value,
"TRequestResponse::Request must be derived from Message");
static_assert(std::is_base_of<Message, Res>::value,
"TRequestResponse::Response must be derived from Message");
auto request = Req(std::forward<Args>(args)...);
/// Call function can initiate only one request at the time. Function blocks
/// until there is a response. If there was an error nullptr is returned.
template <class TRequestResponse, class... Args>
std::experimental::optional<typename TRequestResponse::Response> Call(
Args &&... args) {
return CallWithLoad<TRequestResponse>(
[](const auto &reader) {
typename TRequestResponse::Response response;
response.Load(reader);
return response;
},
std::forward<Args>(args)...);
}
if (VLOG_IS_ON(12)) {
auto req_type = utils::Demangle(request.type_index().name());
LOG(INFO) << "[RpcClient] sent " << (req_type ? req_type.value() : "");
/// Same as `Call` but the first argument is a response loading function.
template <class TRequestResponse, class... Args>
std::experimental::optional<typename TRequestResponse::Response> CallWithLoad(
std::function<typename TRequestResponse::Response(
const typename TRequestResponse::Response::Capnp::Reader &)>
load,
Args &&... args) {
typename TRequestResponse::Request request(std::forward<Args>(args)...);
auto req_type = TRequestResponse::Request::TypeInfo;
VLOG(12) << "[RpcClient] sent " << req_type.name;
::capnp::MallocMessageBuilder req_msg;
{
auto builder = req_msg.initRoot<capnp::Message>();
builder.setTypeId(req_type.id);
auto data_builder = builder.initData();
auto req_builder =
data_builder
.template initAs<typename TRequestResponse::Request::Capnp>();
request.Save(&req_builder);
}
std::unique_ptr<Message> response = Call(request);
auto *real_response = dynamic_cast<Res *>(response.get());
if (!real_response && response) {
auto maybe_response = Send(&req_msg);
if (!maybe_response) {
return std::experimental::nullopt;
}
auto res_msg = maybe_response->getRoot<capnp::Message>();
auto res_type = TRequestResponse::Response::TypeInfo;
if (res_msg.getTypeId() != res_type.id) {
// Since message_id was checked in private Call function, this means
// something is very wrong (probably on the server side).
LOG(ERROR) << "Message response was of unexpected type";
client_ = std::experimental::nullopt;
return nullptr;
return std::experimental::nullopt;
}
if (VLOG_IS_ON(12) && response) {
auto res_type = utils::Demangle(response->type_index().name());
LOG(INFO) << "[RpcClient] received "
<< (res_type ? res_type.value() : "");
}
VLOG(12) << "[RpcClient] received " << res_type.name;
response.release();
return std::unique_ptr<Res>(real_response);
auto data_reader =
res_msg.getData()
.template getAs<typename TRequestResponse::Response::Capnp>();
return std::experimental::make_optional(load(data_reader));
}
// Call this function from another thread to abort a pending RPC call.
/// Call this function from another thread to abort a pending RPC call.
void Abort();
private:
std::unique_ptr<Message> Call(const Message &request);
std::experimental::optional<::capnp::FlatArrayMessageReader> Send(
::capnp::MessageBuilder *message);
io::network::Endpoint endpoint_;
std::experimental::optional<communication::Client> client_;

View File

@ -14,10 +14,33 @@ namespace communication::rpc {
*/
class ClientPool {
public:
ClientPool(const io::network::Endpoint &endpoint) : endpoint_(endpoint) {}
explicit ClientPool(const io::network::Endpoint &endpoint)
: endpoint_(endpoint) {}
template <typename TRequestResponse, typename... Args>
std::unique_ptr<typename TRequestResponse::Response> Call(Args &&... args) {
template <class TRequestResponse, class... Args>
std::experimental::optional<typename TRequestResponse::Response> Call(
Args &&... args) {
return WithUnusedClient([&](const auto &client) {
return client->template Call<TRequestResponse>(
std::forward<Args>(args)...);
});
};
template <class TRequestResponse, class... Args>
std::experimental::optional<typename TRequestResponse::Response> CallWithLoad(
std::function<typename TRequestResponse::Response(
const typename TRequestResponse::Response::Capnp::Reader &)>
load,
Args &&... args) {
return WithUnusedClient([&](const auto &client) {
return client->template CallWithLoad<TRequestResponse>(
load, std::forward<Args>(args)...);
});
};
private:
template <class TFun>
auto WithUnusedClient(const TFun &fun) {
std::unique_ptr<Client> client;
std::unique_lock<std::mutex> lock(mutex_);
@ -29,14 +52,13 @@ class ClientPool {
}
lock.unlock();
auto resp = client->Call<TRequestResponse>(std::forward<Args>(args)...);
auto res = fun(client);
lock.lock();
unused_clients_.push(std::move(client));
return resp;
};
return res;
}
private:
io::network::Endpoint endpoint_;
std::mutex mutex_;

View File

@ -1,163 +0,0 @@
#pragma once
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/serialization/export.hpp"
#include "database/state_delta.hpp"
#include "distributed/bfs_rpc_messages.hpp"
#include "distributed/coordination_rpc_messages.hpp"
#include "distributed/data_rpc_messages.hpp"
#include "distributed/durability_rpc_messages.hpp"
#include "distributed/index_rpc_messages.hpp"
#include "distributed/plan_rpc_messages.hpp"
#include "distributed/pull_produce_rpc_messages.hpp"
#include "distributed/storage_gc_rpc_messages.hpp"
#include "distributed/transactional_cache_cleaner_rpc_messages.hpp"
#include "distributed/updates_rpc_messages.hpp"
#include "durability/recovery.hpp"
#include "stats/stats_rpc_messages.hpp"
#include "storage/concurrent_id_mapper_rpc_messages.hpp"
#include "transactions/engine_rpc_messages.hpp"
#define ID_VALUE_EXPORT_BOOST_TYPE(type) \
BOOST_CLASS_EXPORT(storage::type##IdReq); \
BOOST_CLASS_EXPORT(storage::type##IdRes); \
BOOST_CLASS_EXPORT(storage::Id##type##Req); \
BOOST_CLASS_EXPORT(storage::Id##type##Res);
ID_VALUE_EXPORT_BOOST_TYPE(Label)
ID_VALUE_EXPORT_BOOST_TYPE(EdgeType)
ID_VALUE_EXPORT_BOOST_TYPE(Property)
#undef ID_VALUE_EXPORT_BOOST_TYPE
// Distributed transaction engine.
BOOST_CLASS_EXPORT(tx::TxAndSnapshot);
BOOST_CLASS_EXPORT(tx::BeginReq);
BOOST_CLASS_EXPORT(tx::BeginRes);
BOOST_CLASS_EXPORT(tx::AdvanceReq);
BOOST_CLASS_EXPORT(tx::AdvanceRes);
BOOST_CLASS_EXPORT(tx::CommitReq);
BOOST_CLASS_EXPORT(tx::CommitRes);
BOOST_CLASS_EXPORT(tx::AbortReq);
BOOST_CLASS_EXPORT(tx::AbortRes);
BOOST_CLASS_EXPORT(tx::SnapshotReq);
BOOST_CLASS_EXPORT(tx::SnapshotRes);
BOOST_CLASS_EXPORT(tx::CommandReq);
BOOST_CLASS_EXPORT(tx::CommandRes);
BOOST_CLASS_EXPORT(tx::GcSnapshotReq);
BOOST_CLASS_EXPORT(tx::ClogInfoReq);
BOOST_CLASS_EXPORT(tx::ClogInfoRes);
BOOST_CLASS_EXPORT(tx::ActiveTransactionsReq);
BOOST_CLASS_EXPORT(tx::EnsureNextIdGreaterReq);
BOOST_CLASS_EXPORT(tx::EnsureNextIdGreaterRes);
BOOST_CLASS_EXPORT(tx::GlobalLastReq);
BOOST_CLASS_EXPORT(tx::GlobalLastRes);
// Distributed coordination.
BOOST_CLASS_EXPORT(durability::RecoveryInfo);
BOOST_CLASS_EXPORT(distributed::RegisterWorkerReq);
BOOST_CLASS_EXPORT(distributed::RegisterWorkerRes);
BOOST_CLASS_EXPORT(distributed::ClusterDiscoveryReq);
BOOST_CLASS_EXPORT(distributed::ClusterDiscoveryRes);
BOOST_CLASS_EXPORT(distributed::StopWorkerReq);
BOOST_CLASS_EXPORT(distributed::StopWorkerRes);
BOOST_CLASS_EXPORT(distributed::NotifyWorkerRecoveredReq);
BOOST_CLASS_EXPORT(distributed::NotifyWorkerRecoveredRes);
// Distributed data exchange.
BOOST_CLASS_EXPORT(distributed::EdgeReq);
BOOST_CLASS_EXPORT(distributed::EdgeRes);
BOOST_CLASS_EXPORT(distributed::VertexReq);
BOOST_CLASS_EXPORT(distributed::VertexRes);
BOOST_CLASS_EXPORT(distributed::TxGidPair);
BOOST_CLASS_EXPORT(distributed::VertexCountReq);
BOOST_CLASS_EXPORT(distributed::VertexCountRes);
// Distributed plan exchange.
BOOST_CLASS_EXPORT(distributed::DispatchPlanReq);
BOOST_CLASS_EXPORT(distributed::DispatchPlanRes);
BOOST_CLASS_EXPORT(distributed::RemovePlanReq);
BOOST_CLASS_EXPORT(distributed::RemovePlanRes);
// Pull.
BOOST_CLASS_EXPORT(distributed::PullReq);
BOOST_CLASS_EXPORT(distributed::PullRes);
BOOST_CLASS_EXPORT(distributed::TransactionCommandAdvancedReq);
BOOST_CLASS_EXPORT(distributed::TransactionCommandAdvancedRes);
// Distributed indexes.
BOOST_CLASS_EXPORT(distributed::BuildIndexReq);
BOOST_CLASS_EXPORT(distributed::BuildIndexRes);
BOOST_CLASS_EXPORT(distributed::IndexLabelPropertyTx);
// Token sharing.
BOOST_CLASS_EXPORT(distributed::TokenTransferReq);
BOOST_CLASS_EXPORT(distributed::TokenTransferRes);
// Stats.
BOOST_CLASS_EXPORT(stats::StatsReq);
BOOST_CLASS_EXPORT(stats::StatsRes);
BOOST_CLASS_EXPORT(stats::BatchStatsReq);
BOOST_CLASS_EXPORT(stats::BatchStatsRes);
// Updates.
BOOST_CLASS_EXPORT(database::StateDelta);
BOOST_CLASS_EXPORT(distributed::UpdateReq);
BOOST_CLASS_EXPORT(distributed::UpdateRes);
BOOST_CLASS_EXPORT(distributed::UpdateApplyReq);
BOOST_CLASS_EXPORT(distributed::UpdateApplyRes);
// Creates.
BOOST_CLASS_EXPORT(distributed::CreateResult);
BOOST_CLASS_EXPORT(distributed::CreateVertexReq);
BOOST_CLASS_EXPORT(distributed::CreateVertexReqData);
BOOST_CLASS_EXPORT(distributed::CreateVertexRes);
BOOST_CLASS_EXPORT(distributed::CreateEdgeReqData);
BOOST_CLASS_EXPORT(distributed::CreateEdgeReq);
BOOST_CLASS_EXPORT(distributed::CreateEdgeRes);
BOOST_CLASS_EXPORT(distributed::AddInEdgeReqData);
BOOST_CLASS_EXPORT(distributed::AddInEdgeReq);
BOOST_CLASS_EXPORT(distributed::AddInEdgeRes);
// Removes.
BOOST_CLASS_EXPORT(distributed::RemoveVertexReq);
BOOST_CLASS_EXPORT(distributed::RemoveVertexRes);
BOOST_CLASS_EXPORT(distributed::RemoveEdgeReq);
BOOST_CLASS_EXPORT(distributed::RemoveEdgeRes);
BOOST_CLASS_EXPORT(distributed::RemoveInEdgeData);
BOOST_CLASS_EXPORT(distributed::RemoveInEdgeReq);
BOOST_CLASS_EXPORT(distributed::RemoveInEdgeRes);
// Durability
BOOST_CLASS_EXPORT(distributed::MakeSnapshotReq);
BOOST_CLASS_EXPORT(distributed::MakeSnapshotRes);
// Storage Gc.
BOOST_CLASS_EXPORT(distributed::GcClearedStatusReq);
BOOST_CLASS_EXPORT(distributed::GcClearedStatusRes);
// Transactional Cache Cleaner.
BOOST_CLASS_EXPORT(distributed::WaitOnTransactionEndReq);
BOOST_CLASS_EXPORT(distributed::WaitOnTransactionEndRes);
// Cursor.
BOOST_CLASS_EXPORT(distributed::CreateBfsSubcursorReq);
BOOST_CLASS_EXPORT(distributed::CreateBfsSubcursorRes);
BOOST_CLASS_EXPORT(distributed::RegisterSubcursorsReq);
BOOST_CLASS_EXPORT(distributed::RegisterSubcursorsRes);
BOOST_CLASS_EXPORT(distributed::RemoveBfsSubcursorReq);
BOOST_CLASS_EXPORT(distributed::RemoveBfsSubcursorRes);
BOOST_CLASS_EXPORT(distributed::SetSourceReq);
BOOST_CLASS_EXPORT(distributed::SetSourceRes);
BOOST_CLASS_EXPORT(distributed::ExpandLevelReq);
BOOST_CLASS_EXPORT(distributed::ExpandLevelRes);
BOOST_CLASS_EXPORT(distributed::SubcursorPullReq);
BOOST_CLASS_EXPORT(distributed::SubcursorPullRes);
BOOST_CLASS_EXPORT(distributed::ExpandToRemoteVertexReq);
BOOST_CLASS_EXPORT(distributed::ExpandToRemoteVertexRes);
BOOST_CLASS_EXPORT(distributed::ReconstructPathReq);
BOOST_CLASS_EXPORT(distributed::ReconstructPathRes);
BOOST_CLASS_EXPORT(distributed::PrepareForExpandReq);
BOOST_CLASS_EXPORT(distributed::PrepareForExpandRes);

View File

@ -0,0 +1,9 @@
@0xd3832c9a1a3d8ec7;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("communication::rpc::capnp");
struct Message {
typeId @0 :UInt64;
data @1 :AnyPointer;
}

View File

@ -1,38 +1,50 @@
#pragma once
#include <cstdint>
#include <memory>
#include <type_traits>
#include <typeindex>
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
namespace communication::rpc {
using MessageSize = uint32_t;
/**
* Base class for messages.
*/
class Message {
public:
virtual ~Message() {}
/**
* Run-time type identification that is used for callbacks.
*
* Warning: this works because of the virtual destructor, don't remove it from
* this class
*/
std::type_index type_index() const { return typeid(*this); }
private:
friend boost::serialization::access;
template <class TArchive>
void serialize(TArchive &, unsigned int) {}
/// Type information on a RPC message.
/// Each message should have a static member `TypeInfo` with this information.
struct MessageType {
/// Unique ID for a message.
uint64_t id;
/// Pretty name of the type.
std::string name;
};
inline bool operator==(const MessageType &a, const MessageType &b) {
return a.id == b.id;
}
inline bool operator!=(const MessageType &a, const MessageType &b) {
return a.id != b.id;
}
inline bool operator<(const MessageType &a, const MessageType &b) {
return a.id < b.id;
}
inline bool operator<=(const MessageType &a, const MessageType &b) {
return a.id <= b.id;
}
inline bool operator>(const MessageType &a, const MessageType &b) {
return a.id > b.id;
}
inline bool operator>=(const MessageType &a, const MessageType &b) {
return a.id >= b.id;
}
/// Each RPC is defined via this struct.
///
/// `TRequest` and `TResponse` are required to be classes which have a static
/// member `TypeInfo` of `MessageType` type. This is used for proper
/// registration and deserialization of RPC types. Additionally, both `TRequest`
/// and `TResponse` are required to define a nested `Capnp` type, which
/// corresponds to the Cap'n Proto schema type, as well as defined the following
/// serialization functions:
/// * void Save(Capnp::Builder *, ...) const
/// * void Load(const Capnp::Reader &, ...)
template <typename TRequest, typename TResponse>
struct RequestResponse {
using Request = TRequest;
@ -40,35 +52,3 @@ struct RequestResponse {
};
} // namespace communication::rpc
// RPC Pimp
#define RPC_NO_MEMBER_MESSAGE(name) \
struct name : public communication::rpc::Message { \
name() {} \
\
private: \
friend class boost::serialization::access; \
\
template <class TArchive> \
void serialize(TArchive &ar, unsigned int) { \
ar &boost::serialization::base_object<communication::rpc::Message>( \
*this); \
} \
}
#define RPC_SINGLE_MEMBER_MESSAGE(name, type) \
struct name : public communication::rpc::Message { \
name() {} \
name(const type &member) : member(member) {} \
type member; \
\
private: \
friend class boost::serialization::access; \
\
template <class TArchive> \
void serialize(TArchive &ar, unsigned int) { \
ar &boost::serialization::base_object<communication::rpc::Message>( \
*this); \
ar &member; \
} \
}

View File

@ -1,11 +1,10 @@
#include <sstream>
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/serialization/unique_ptr.hpp"
#include "capnp/message.h"
#include "capnp/serialize.h"
#include "fmt/format.h"
#include "communication/rpc/messages-inl.hpp"
#include "communication/rpc/messages.capnp.h"
#include "communication/rpc/messages.hpp"
#include "communication/rpc/protocol.hpp"
#include "communication/rpc/server.hpp"
@ -28,65 +27,51 @@ void Session::Execute() {
if (input_stream_.size() < request_size) return;
// Read the request message.
std::unique_ptr<Message> request([this, request_len]() {
Message *req_ptr = nullptr;
std::stringstream stream(std::ios_base::in | std::ios_base::binary);
stream.str(std::string(
reinterpret_cast<char *>(input_stream_.data() + sizeof(MessageSize)),
request_len));
boost::archive::binary_iarchive archive(stream);
// Sent from client.cpp
archive >> req_ptr;
return req_ptr;
}());
auto data =
::kj::arrayPtr(input_stream_.data() + sizeof(request_len), request_len);
// Our data is word aligned and padded to 64bit because we use regular
// (non-packed) serialization of Cap'n Proto. So we can use reinterpret_cast.
auto data_words =
::kj::arrayPtr(reinterpret_cast<::capnp::word *>(data.begin()),
reinterpret_cast<::capnp::word *>(data.end()));
::capnp::FlatArrayMessageReader request_message(data_words.asConst());
auto request = request_message.getRoot<capnp::Message>();
input_stream_.Shift(sizeof(MessageSize) + request_len);
auto callbacks_accessor = server_.callbacks_.access();
auto it = callbacks_accessor.find(request->type_index());
auto it = callbacks_accessor.find(request.getTypeId());
if (it == callbacks_accessor.end()) {
// Throw exception to close the socket and cleanup the session.
throw SessionException(
"Session trying to execute an unregistered RPC call!");
}
if (VLOG_IS_ON(12)) {
auto req_type = utils::Demangle(request->type_index().name());
LOG(INFO) << "[RpcServer] received " << (req_type ? req_type.value() : "");
}
VLOG(12) << "[RpcServer] received " << it->second.req_type.name;
std::unique_ptr<Message> response = it->second(*(request.get()));
if (!response) {
throw SessionException("Trying to send nullptr instead of message");
}
::capnp::MallocMessageBuilder response_message;
// callback fills the message data
auto response_builder = response_message.initRoot<capnp::Message>();
it->second.callback(request, &response_builder);
// Serialize and send response
std::stringstream stream(std::ios_base::out | std::ios_base::binary);
{
boost::archive::binary_oarchive archive(stream);
archive << response;
// Archive destructor ensures everything is written.
}
const std::string &buffer = stream.str();
if (buffer.size() > std::numeric_limits<MessageSize>::max()) {
auto response_words = ::capnp::messageToFlatArray(response_message);
auto response_bytes = response_words.asBytes();
if (response_bytes.size() > std::numeric_limits<MessageSize>::max()) {
throw SessionException(fmt::format(
"Trying to send response of size {}, max response size is {}",
buffer.size(), std::numeric_limits<MessageSize>::max()));
response_bytes.size(), std::numeric_limits<MessageSize>::max()));
}
MessageSize input_stream_size = buffer.size();
MessageSize input_stream_size = response_bytes.size();
if (!output_stream_.Write(reinterpret_cast<uint8_t *>(&input_stream_size),
sizeof(MessageSize), true)) {
throw SessionException("Couldn't send response size!");
}
if (!output_stream_.Write(buffer)) {
if (!output_stream_.Write(response_bytes.begin(), response_bytes.size())) {
throw SessionException("Couldn't send response data!");
}
if (VLOG_IS_ON(12)) {
auto res_type = utils::Demangle(response->type_index().name());
LOG(INFO) << "[RpcServer] sent " << (res_type ? res_type.value() : "");
}
VLOG(12) << "[RpcServer] sent " << it->second.res_type.name;
}
} // namespace communication::rpc

View File

@ -1,10 +1,3 @@
#include "boost/archive/binary_iarchive.hpp"
#include "boost/archive/binary_oarchive.hpp"
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "boost/serialization/export.hpp"
#include "boost/serialization/unique_ptr.hpp"
#include "communication/rpc/server.hpp"
namespace communication::rpc {

View File

@ -1,9 +1,11 @@
#pragma once
#include <type_traits>
#include <unordered_map>
#include <vector>
#include "capnp/any.h"
#include "communication/rpc/messages.capnp.h"
#include "communication/rpc/messages.hpp"
#include "communication/rpc/protocol.hpp"
#include "communication/server.hpp"
@ -27,57 +29,53 @@ class Server {
const io::network::Endpoint &endpoint() const;
template <typename TRequestResponse>
void Register(
std::function<std::unique_ptr<typename TRequestResponse::Response>(
const typename TRequestResponse::Request &)>
callback) {
static_assert(
std::is_base_of<Message, typename TRequestResponse::Request>::value,
"TRequestResponse::Request must be derived from Message");
static_assert(
std::is_base_of<Message, typename TRequestResponse::Response>::value,
"TRequestResponse::Response must be derived from Message");
template <class TRequestResponse>
void Register(std::function<
void(const typename TRequestResponse::Request::Capnp::Reader &,
typename TRequestResponse::Response::Capnp::Builder *)>
callback) {
RpcCallback rpc;
rpc.req_type = TRequestResponse::Request::TypeInfo;
rpc.res_type = TRequestResponse::Response::TypeInfo;
rpc.callback = [callback = callback](const auto &reader, auto *builder) {
auto req_data =
reader.getData()
.template getAs<typename TRequestResponse::Request::Capnp>();
builder->setTypeId(TRequestResponse::Response::TypeInfo.id);
auto data_builder = builder->initData();
auto res_builder =
data_builder
.template initAs<typename TRequestResponse::Response::Capnp>();
callback(req_data, &res_builder);
};
auto callbacks_accessor = callbacks_.access();
auto got = callbacks_accessor.insert(
typeid(typename TRequestResponse::Request),
[callback = callback](const Message &base_message) {
const auto &message =
dynamic_cast<const typename TRequestResponse::Request &>(
base_message);
return callback(message);
});
auto got =
callbacks_accessor.insert(TRequestResponse::Request::TypeInfo.id, rpc);
CHECK(got.second) << "Callback for that message type already registered";
if (VLOG_IS_ON(12)) {
auto req_type =
utils::Demangle(typeid(typename TRequestResponse::Request).name());
auto res_type =
utils::Demangle(typeid(typename TRequestResponse::Response).name());
LOG(INFO) << "[RpcServer] register " << (req_type ? req_type.value() : "")
<< " -> " << (res_type ? res_type.value() : "");
}
VLOG(12) << "[RpcServer] register " << rpc.req_type.name << " -> "
<< rpc.res_type.name;
}
template <typename TRequestResponse>
void UnRegister() {
static_assert(
std::is_base_of<Message, typename TRequestResponse::Request>::value,
"TRequestResponse::Request must be derived from Message");
static_assert(
std::is_base_of<Message, typename TRequestResponse::Response>::value,
"TRequestResponse::Response must be derived from Message");
const MessageType &type = TRequestResponse::Request::TypeInfo;
auto callbacks_accessor = callbacks_.access();
auto deleted =
callbacks_accessor.remove(typeid(typename TRequestResponse::Request));
auto deleted = callbacks_accessor.remove(type.id);
CHECK(deleted) << "Trying to remove unknown message type callback";
}
private:
friend class Session;
ConcurrentMap<std::type_index,
std::function<std::unique_ptr<Message>(const Message &)>>
callbacks_;
struct RpcCallback {
MessageType req_type;
std::function<void(const capnp::Message::Reader &,
capnp::Message::Builder *)>
callback;
MessageType res_type;
};
ConcurrentMap<uint64_t, RpcCallback> callbacks_;
std::mutex mutex_;
communication::Server<Session, Server> server_;

View File

@ -1,23 +1,9 @@
#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"
#include "database/counters_rpc_messages.hpp"
namespace database {
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))
@ -32,13 +18,16 @@ void SingleNodeCounters::Set(const std::string &name, int64_t value) {
MasterCounters::MasterCounters(communication::rpc::Server &server)
: rpc_server_(server) {
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>();
});
rpc_server_.Register<CountersGetRpc>(
[this](const auto &req_reader, auto *res_builder) {
CountersGetRes res(Get(req_reader.getName()));
res.Save(res_builder);
});
rpc_server_.Register<CountersSetRpc>(
[this](const auto &req_reader, auto *res_builder) {
Set(req_reader.getName(), req_reader.getValue());
return std::make_unique<CountersSetRes>();
});
}
WorkerCounters::WorkerCounters(
@ -48,18 +37,12 @@ WorkerCounters::WorkerCounters(
int64_t WorkerCounters::Get(const std::string &name) {
auto response = master_client_pool_.Call<CountersGetRpc>(name);
CHECK(response) << "CountersGetRpc failed";
return response->member;
return response->value;
}
void WorkerCounters::Set(const std::string &name, int64_t value) {
auto response =
master_client_pool_.Call<CountersSetRpc>(CountersSetReqData{name, value});
auto response = master_client_pool_.Call<CountersSetRpc>(name, value);
CHECK(response) << "CountersSetRpc failed";
}
} // namespace database
BOOST_CLASS_EXPORT(database::CountersGetReq);
BOOST_CLASS_EXPORT(database::CountersGetRes);
BOOST_CLASS_EXPORT(database::CountersSetReq);
BOOST_CLASS_EXPORT(database::CountersSetRes);

View File

@ -5,7 +5,6 @@
#include <string>
#include "communication/rpc/client_pool.hpp"
#include "communication/rpc/messages.hpp"
#include "communication/rpc/server.hpp"
#include "data_structures/concurrent/concurrent_map.hpp"
@ -45,7 +44,7 @@ class SingleNodeCounters : public Counters {
/** Implementation for distributed master. */
class MasterCounters : public SingleNodeCounters {
public:
MasterCounters(communication::rpc::Server &server);
explicit MasterCounters(communication::rpc::Server &server);
private:
communication::rpc::Server &rpc_server_;
@ -54,7 +53,7 @@ class MasterCounters : public SingleNodeCounters {
/** Implementation for distributed worker. */
class WorkerCounters : public Counters {
public:
WorkerCounters(communication::rpc::ClientPool &master_client_pool);
explicit WorkerCounters(communication::rpc::ClientPool &master_client_pool);
int64_t Get(const std::string &name) override;
void Set(const std::string &name, int64_t value) override;

View File

@ -0,0 +1,23 @@
#>cpp
#pragma once
#include <string>
#include "communication/rpc/messages.hpp"
#include "database/counters_rpc_messages.capnp.h"
cpp<#
(lcp:namespace database)
(lcp:capnp-namespace "database")
(lcp:define-rpc counters-get
(:request ((name "std::string")))
(:response ((value :int64_t))))
(lcp:define-rpc counters-set
(:request ((name "std::string")
(value :int64_t)))
(:response ()))
(lcp:pop-namespace) ;; database

View File

@ -1,183 +0,0 @@
#pragma once
#include "communication/bolt/v1/decoder/decoder.hpp"
#include "communication/bolt/v1/encoder/primitive_encoder.hpp"
#include "durability/hashed_file_reader.hpp"
#include "durability/hashed_file_writer.hpp"
#include "storage/address_types.hpp"
#include "storage/gid.hpp"
#include "storage/property_value.hpp"
#include "utils/serialization.hpp"
namespace database {
/** Describes single change to the database state. Used for durability (WAL) and
* state communication over network in HA and for distributed remote storage
* changes.
*
* Labels, Properties and EdgeTypes are stored both as values (integers) and
* strings (their names). The values are used when applying deltas in a running
* database. Names are used when recovering the database as it's not guaranteed
* that after recovery the old name<->value mapping will be preserved.
*
* TODO: ensure the mapping is preserved after recovery and don't save strings
* in StateDeltas. */
struct StateDelta {
/** Defines StateDelta type. For each type the comment indicates which values
* need to be stored. All deltas have the transaction_id member, so that's
* omitted in the comment. */
enum class Type {
TRANSACTION_BEGIN,
TRANSACTION_COMMIT,
TRANSACTION_ABORT,
CREATE_VERTEX, // vertex_id
CREATE_EDGE, // edge_id, from_vertex_id, to_vertex_id, edge_type,
// edge_type_name
ADD_OUT_EDGE, // vertex_id, edge_address, vertex_to_address, edge_type
REMOVE_OUT_EDGE, // vertex_id, edge_address
ADD_IN_EDGE, // vertex_id, edge_address, vertex_from_address, edge_type
REMOVE_IN_EDGE, // vertex_id, edge_address
SET_PROPERTY_VERTEX, // vertex_id, property, property_name, property_value
SET_PROPERTY_EDGE, // edge_id, property, property_name, property_value
// remove property is done by setting a PropertyValue::Null
ADD_LABEL, // vertex_id, label, label_name
REMOVE_LABEL, // vertex_id, label, label_name
REMOVE_VERTEX, // vertex_id, check_empty
REMOVE_EDGE, // edge_id
BUILD_INDEX // label, label_name, property, property_name
};
StateDelta() = default;
StateDelta(const enum Type &type, tx::TransactionId tx_id)
: type(type), transaction_id(tx_id) {}
/** Attempts to decode a StateDelta from the given decoder. Returns the
* decoded value if successful, otherwise returns nullopt. */
static std::experimental::optional<StateDelta> Decode(
HashedFileReader &reader,
communication::bolt::Decoder<HashedFileReader> &decoder);
/** Encodes the delta using primitive encoder, and writes out the new hash
* with delta to the writer */
void Encode(
HashedFileWriter &writer,
communication::bolt::PrimitiveEncoder<HashedFileWriter> &encoder) const;
static StateDelta TxBegin(tx::TransactionId tx_id);
static StateDelta TxCommit(tx::TransactionId tx_id);
static StateDelta TxAbort(tx::TransactionId tx_id);
static StateDelta CreateVertex(tx::TransactionId tx_id,
gid::Gid vertex_id);
static StateDelta CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
gid::Gid vertex_from_id, gid::Gid vertex_to_id,
storage::EdgeType edge_type,
const std::string &edge_type_name);
static StateDelta AddOutEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::VertexAddress vertex_to_address,
storage::EdgeAddress edge_address,
storage::EdgeType edge_type);
static StateDelta RemoveOutEdge(tx::TransactionId tx_id,
gid::Gid vertex_id,
storage::EdgeAddress edge_address);
static StateDelta AddInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::VertexAddress vertex_from_address,
storage::EdgeAddress edge_address,
storage::EdgeType edge_type);
static StateDelta RemoveInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::EdgeAddress edge_address);
static StateDelta PropsSetVertex(tx::TransactionId tx_id,
gid::Gid vertex_id,
storage::Property property,
const std::string &property_name,
const PropertyValue &value);
static StateDelta PropsSetEdge(tx::TransactionId tx_id, gid::Gid edge_id,
storage::Property property,
const std::string &property_name,
const PropertyValue &value);
static StateDelta AddLabel(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::Label label,
const std::string &label_name);
static StateDelta RemoveLabel(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::Label label,
const std::string &label_name);
static StateDelta RemoveVertex(tx::TransactionId tx_id, gid::Gid vertex_id,
bool check_empty);
static StateDelta RemoveEdge(tx::TransactionId tx_id, gid::Gid edge_id);
static StateDelta BuildIndex(tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
storage::Property property,
const std::string &property_name);
/// Applies CRUD delta to database accessor. Fails on other types of deltas
void Apply(GraphDbAccessor &dba) const;
// Members valid for every delta.
enum Type type;
tx::TransactionId transaction_id;
// Members valid only for some deltas, see StateDelta::Type comments above.
// TODO: when preparing the WAL for distributed, most likely remove Gids and
// only keep addresses.
gid::Gid vertex_id;
gid::Gid edge_id;
storage::EdgeAddress edge_address;
gid::Gid vertex_from_id;
storage::VertexAddress vertex_from_address;
gid::Gid vertex_to_id;
storage::VertexAddress vertex_to_address;
storage::EdgeType edge_type;
std::string edge_type_name;
storage::Property property;
std::string property_name;
PropertyValue value = PropertyValue::Null;
storage::Label label;
std::string label_name;
bool check_empty;
private:
friend class boost::serialization::access;
BOOST_SERIALIZATION_SPLIT_MEMBER();
template <class TArchive>
void save(TArchive &ar, const unsigned int) const {
ar &type;
ar &transaction_id;
ar &vertex_id;
ar &edge_id;
ar &edge_address;
ar &vertex_from_id;
ar &vertex_from_address;
ar &vertex_to_id;
ar &vertex_to_address;
ar &edge_type;
ar &edge_type_name;
ar &property;
ar &property_name;
utils::SaveTypedValue(ar, value);
ar &label;
ar &label_name;
ar &check_empty;
}
template <class TArchive>
void load(TArchive &ar, const unsigned int) {
ar &type;
ar &transaction_id;
ar &vertex_id;
ar &edge_id;
ar &edge_address;
ar &vertex_from_id;
ar &vertex_from_address;
ar &vertex_to_id;
ar &vertex_to_address;
ar &edge_type;
ar &edge_type_name;
ar &property;
ar &property_name;
query::TypedValue tv;
utils::LoadTypedValue(ar, tv);
value = tv;
ar &label;
ar &label_name;
ar &check_empty;
}
};
} // namespace database

View File

@ -0,0 +1,179 @@
#>cpp
#pragma once
#include "communication/bolt/v1/decoder/decoder.hpp"
#include "communication/bolt/v1/encoder/primitive_encoder.hpp"
#include "database/state_delta.capnp.h"
#include "durability/hashed_file_reader.hpp"
#include "durability/hashed_file_writer.hpp"
#include "storage/address_types.hpp"
#include "storage/gid.hpp"
#include "storage/property_value.hpp"
#include "utils/serialization.hpp"
cpp<#
(lcp:namespace database)
(lcp:capnp-namespace "database")
(lcp:capnp-import 'storage "/storage/serialization.capnp")
(lcp:capnp-import 'dis "/distributed/serialization.capnp")
(lcp:capnp-type-conversion "tx::TransactionId" "UInt64")
(lcp:capnp-type-conversion "gid::Gid" "UInt64")
(lcp:capnp-type-conversion "storage::Label" "Storage.Common")
(lcp:capnp-type-conversion "storage::EdgeType" "Storage.Common")
(lcp:capnp-type-conversion "storage::Property" "Storage.Common")
(lcp:capnp-type-conversion "storage::EdgeAddress" "Storage.Address")
(lcp:capnp-type-conversion "storage::VertexAddress" "Storage.Address")
(lcp:define-struct state-delta ()
(
;; Members valid for every delta.
(type "Type" :capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::StateDelta::Type" "Type")
:capnp-load (lcp:capnp-load-enum "capnp::StateDelta::Type" "Type"))
(transaction-id "tx::TransactionId")
;; Members valid only for some deltas, see StateDelta::Type comments above.
;; TODO: when preparing the WAL for distributed, most likely remove Gids and
;; only keep addresses.
(vertex-id "gid::Gid")
(edge-id "gid::Gid")
(edge-address "storage::EdgeAddress")
(vertex-from-id "gid::Gid")
(vertex-from-address "storage::VertexAddress")
(vertex-to-id "gid::Gid")
(vertex-to-address "storage::VertexAddress")
(edge-type "storage::EdgeType")
(edge-type-name "std::string")
(property "storage::Property")
(property-name "std::string")
(value "PropertyValue" :initval "PropertyValue::Null"
:save-fun #>cpp utils::SaveTypedValue(ar, value); cpp<#
:load-fun
#>cpp
query::TypedValue tv;
utils::LoadTypedValue(ar, tv);
value = tv;
cpp<#
:capnp-type "Dis.TypedValue"
:capnp-save
(lambda (builder member)
#>cpp
utils::SaveCapnpTypedValue(${member}, &${builder});
cpp<#)
:capnp-load
(lambda (reader member)
#>cpp
query::TypedValue tv;
utils::LoadCapnpTypedValue(${reader}, &tv);
${member} = tv;
cpp<#))
(label "storage::Label")
(label-name "std::string")
(check-empty :bool))
(:documentation
"Describes single change to the database state. Used for durability (WAL) and
state communication over network in HA and for distributed remote storage
changes.
Labels, Properties and EdgeTypes are stored both as values (integers) and
strings (their names). The values are used when applying deltas in a running
database. Names are used when recovering the database as it's not guaranteed
that after recovery the old name<->value mapping will be preserved.
TODO: ensure the mapping is preserved after recovery and don't save strings
in StateDeltas.")
(:public
(lcp:define-enum type
(transaction-begin
transaction-commit
transaction-abort
create-vertex ;; vertex_id
create-edge ;; edge_id, from_vertex_id, to_vertex_id, edge_type, edge_type_name
add-out-edge ;; vertex_id, edge_address, vertex_to_address, edge_type
remove-out-edge ;; vertex_id, edge_address
add-in-edge ;; vertex_id, edge_address, vertex_from_address, edge_type
remove-in-edge ;; vertex_id, edge_address
set-property-vertex ;; vertex_id, property, property_name, property_value
set-property-edge ;; edge_id, property, property_name, property_value
;; remove property is done by setting a PropertyValue::Null
add-label ;; vertex_id, label, label_name
remove-label ;; vertex_id, label, label_name
remove-vertex ;; vertex_id, check_empty
remove-edge ;; edge_id
build-index ;; label, label_name, property, property_name
)
(:documentation
"Defines StateDelta type. For each type the comment indicates which values
need to be stored. All deltas have the transaction_id member, so that's
omitted in the comment.")
(:serialize :capnp))
#>cpp
StateDelta() = default;
StateDelta(const enum Type &type, tx::TransactionId tx_id)
: type(type), transaction_id(tx_id) {}
/** Attempts to decode a StateDelta from the given decoder. Returns the
* decoded value if successful, otherwise returns nullopt. */
static std::experimental::optional<StateDelta> Decode(
HashedFileReader &reader,
communication::bolt::Decoder<HashedFileReader> &decoder);
/** Encodes the delta using primitive encoder, and writes out the new hash
* with delta to the writer */
void Encode(
HashedFileWriter &writer,
communication::bolt::PrimitiveEncoder<HashedFileWriter> &encoder) const;
static StateDelta TxBegin(tx::TransactionId tx_id);
static StateDelta TxCommit(tx::TransactionId tx_id);
static StateDelta TxAbort(tx::TransactionId tx_id);
static StateDelta CreateVertex(tx::TransactionId tx_id,
gid::Gid vertex_id);
static StateDelta CreateEdge(tx::TransactionId tx_id, gid::Gid edge_id,
gid::Gid vertex_from_id, gid::Gid vertex_to_id,
storage::EdgeType edge_type,
const std::string &edge_type_name);
static StateDelta AddOutEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::VertexAddress vertex_to_address,
storage::EdgeAddress edge_address,
storage::EdgeType edge_type);
static StateDelta RemoveOutEdge(tx::TransactionId tx_id,
gid::Gid vertex_id,
storage::EdgeAddress edge_address);
static StateDelta AddInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::VertexAddress vertex_from_address,
storage::EdgeAddress edge_address,
storage::EdgeType edge_type);
static StateDelta RemoveInEdge(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::EdgeAddress edge_address);
static StateDelta PropsSetVertex(tx::TransactionId tx_id,
gid::Gid vertex_id,
storage::Property property,
const std::string &property_name,
const PropertyValue &value);
static StateDelta PropsSetEdge(tx::TransactionId tx_id, gid::Gid edge_id,
storage::Property property,
const std::string &property_name,
const PropertyValue &value);
static StateDelta AddLabel(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::Label label,
const std::string &label_name);
static StateDelta RemoveLabel(tx::TransactionId tx_id, gid::Gid vertex_id,
storage::Label label,
const std::string &label_name);
static StateDelta RemoveVertex(tx::TransactionId tx_id, gid::Gid vertex_id,
bool check_empty);
static StateDelta RemoveEdge(tx::TransactionId tx_id, gid::Gid edge_id);
static StateDelta BuildIndex(tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
storage::Property property,
const std::string &property_name);
/// Applies CRUD delta to database accessor. Fails on other types of deltas
void Apply(GraphDbAccessor &dba) const;
cpp<#)
(:serialize :capnp))
(lcp:pop-namespace) ;; database

View File

@ -17,10 +17,11 @@ class StorageGcMaster : public StorageGc {
rpc_server_(rpc_server),
coordination_(coordination) {
rpc_server_.Register<distributed::RanLocalGcRpc>(
[this](const distributed::GcClearedStatusReq &req) {
[this](const auto &req_reader, auto *res_builder) {
distributed::RanLocalGcReq req;
req.Load(req_reader);
std::unique_lock<std::mutex> lock(worker_safe_transaction_mutex_);
worker_safe_transaction_[req.worker_id] = req.local_oldest_active;
return std::make_unique<distributed::GcClearedStatusRes>();
});
}

View File

@ -10,11 +10,11 @@ BfsRpcClients::BfsRpcClients(
distributed::RpcWorkerClients *clients)
: db_(db), subcursor_storage_(subcursor_storage), clients_(clients) {}
std::unordered_map<int, int64_t> BfsRpcClients::CreateBfsSubcursors(
std::unordered_map<int16_t, int64_t> BfsRpcClients::CreateBfsSubcursors(
tx::TransactionId tx_id, query::EdgeAtom::Direction direction,
const std::vector<storage::EdgeType> &edge_types,
query::GraphView graph_view) {
auto futures = clients_->ExecuteOnWorkers<std::pair<int, int64_t>>(
auto futures = clients_->ExecuteOnWorkers<std::pair<int16_t, int64_t>>(
db_->WorkerId(),
[tx_id, direction, &edge_types, graph_view](int worker_id, auto &client) {
auto res = client.template Call<CreateBfsSubcursorRpc>(
@ -22,7 +22,7 @@ std::unordered_map<int, int64_t> BfsRpcClients::CreateBfsSubcursors(
CHECK(res) << "CreateBfsSubcursor RPC failed!";
return std::make_pair(worker_id, res->member);
});
std::unordered_map<int, int64_t> subcursor_ids;
std::unordered_map<int16_t, int64_t> subcursor_ids;
subcursor_ids.emplace(
db_->WorkerId(),
subcursor_storage_->Create(tx_id, direction, edge_types, graph_view));
@ -34,7 +34,7 @@ std::unordered_map<int, int64_t> BfsRpcClients::CreateBfsSubcursors(
}
void BfsRpcClients::RegisterSubcursors(
const std::unordered_map<int, int64_t> &subcursor_ids) {
const std::unordered_map<int16_t, int64_t> &subcursor_ids) {
auto futures = clients_->ExecuteOnWorkers<void>(
db_->WorkerId(), [&subcursor_ids](int worker_id, auto &client) {
auto res = client.template Call<RegisterSubcursorsRpc>(subcursor_ids);
@ -45,7 +45,7 @@ void BfsRpcClients::RegisterSubcursors(
}
void BfsRpcClients::RemoveBfsSubcursors(
const std::unordered_map<int, int64_t> &subcursor_ids) {
const std::unordered_map<int16_t, int64_t> &subcursor_ids) {
auto futures = clients_->ExecuteOnWorkers<void>(
db_->WorkerId(), [&subcursor_ids](int worker_id, auto &client) {
auto res = client.template Call<RemoveBfsSubcursorRpc>(
@ -56,7 +56,7 @@ void BfsRpcClients::RemoveBfsSubcursors(
}
std::experimental::optional<VertexAccessor> BfsRpcClients::Pull(
int worker_id, int64_t subcursor_id, database::GraphDbAccessor *dba) {
int16_t worker_id, int64_t subcursor_id, database::GraphDbAccessor *dba) {
if (worker_id == db_->WorkerId()) {
return subcursor_storage_->Get(subcursor_id)->Pull();
}
@ -75,7 +75,7 @@ std::experimental::optional<VertexAccessor> BfsRpcClients::Pull(
}
bool BfsRpcClients::ExpandLevel(
const std::unordered_map<int, int64_t> &subcursor_ids) {
const std::unordered_map<int16_t, int64_t> &subcursor_ids) {
auto futures = clients_->ExecuteOnWorkers<bool>(
db_->WorkerId(), [&subcursor_ids](int worker_id, auto &client) {
auto res =
@ -92,7 +92,7 @@ bool BfsRpcClients::ExpandLevel(
}
void BfsRpcClients::SetSource(
const std::unordered_map<int, int64_t> &subcursor_ids,
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
storage::VertexAddress source_address) {
CHECK(source_address.is_remote())
<< "SetSource should be called with global address";
@ -109,8 +109,8 @@ void BfsRpcClients::SetSource(
}
bool BfsRpcClients::ExpandToRemoteVertex(
const std::unordered_map<int, int64_t> &subcursor_ids, EdgeAccessor edge,
VertexAccessor vertex) {
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
EdgeAccessor edge, VertexAccessor vertex) {
CHECK(!vertex.is_local())
<< "ExpandToRemoteVertex should not be called with local vertex";
int worker_id = vertex.address().worker_id();
@ -137,7 +137,7 @@ PathSegment BuildPathSegment(ReconstructPathRes *res,
}
PathSegment BfsRpcClients::ReconstructPath(
const std::unordered_map<int, int64_t> &subcursor_ids,
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
storage::VertexAddress vertex, database::GraphDbAccessor *dba) {
int worker_id = vertex.worker_id();
if (worker_id == db_->WorkerId()) {
@ -147,11 +147,11 @@ PathSegment BfsRpcClients::ReconstructPath(
auto res = clients_->GetClientPool(worker_id).Call<ReconstructPathRpc>(
subcursor_ids.at(worker_id), vertex);
return BuildPathSegment(res.get(), dba);
return BuildPathSegment(&res.value(), dba);
}
PathSegment BfsRpcClients::ReconstructPath(
const std::unordered_map<int, int64_t> &subcursor_ids,
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
storage::EdgeAddress edge, database::GraphDbAccessor *dba) {
int worker_id = edge.worker_id();
if (worker_id == db_->WorkerId()) {
@ -160,11 +160,11 @@ PathSegment BfsRpcClients::ReconstructPath(
}
auto res = clients_->GetClientPool(worker_id).Call<ReconstructPathRpc>(
subcursor_ids.at(worker_id), edge);
return BuildPathSegment(res.get(), dba);
return BuildPathSegment(&res.value(), dba);
}
void BfsRpcClients::PrepareForExpand(
const std::unordered_map<int, int64_t> &subcursor_ids, bool clear) {
const std::unordered_map<int16_t, int64_t> &subcursor_ids, bool clear) {
auto res = clients_->ExecuteOnWorkers<void>(
db_->WorkerId(), [clear, &subcursor_ids](int worker_id, auto &client) {
auto res = client.template Call<PrepareForExpandRpc>(

View File

@ -19,39 +19,39 @@ class BfsRpcClients {
distributed::BfsSubcursorStorage *subcursor_storage,
distributed::RpcWorkerClients *clients);
std::unordered_map<int, int64_t> CreateBfsSubcursors(
std::unordered_map<int16_t, int64_t> CreateBfsSubcursors(
tx::TransactionId tx_id, query::EdgeAtom::Direction direction,
const std::vector<storage::EdgeType> &edge_types,
query::GraphView graph_view);
void RegisterSubcursors(
const std::unordered_map<int, int64_t> &subcursor_ids);
const std::unordered_map<int16_t, int64_t> &subcursor_ids);
void RemoveBfsSubcursors(
const std::unordered_map<int, int64_t> &subcursor_ids);
const std::unordered_map<int16_t, int64_t> &subcursor_ids);
std::experimental::optional<VertexAccessor> Pull(
int worker_id, int64_t subcursor_id, database::GraphDbAccessor *dba);
int16_t worker_id, int64_t subcursor_id, database::GraphDbAccessor *dba);
bool ExpandLevel(const std::unordered_map<int, int64_t> &subcursor_ids);
bool ExpandLevel(const std::unordered_map<int16_t, int64_t> &subcursor_ids);
void SetSource(const std::unordered_map<int, int64_t> &subcursor_ids,
void SetSource(const std::unordered_map<int16_t, int64_t> &subcursor_ids,
storage::VertexAddress source_address);
bool ExpandToRemoteVertex(
const std::unordered_map<int, int64_t> &subcursor_ids, EdgeAccessor edge,
VertexAccessor vertex);
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
EdgeAccessor edge, VertexAccessor vertex);
PathSegment ReconstructPath(
const std::unordered_map<int, int64_t> &subcursor_ids,
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
storage::EdgeAddress edge, database::GraphDbAccessor *dba);
PathSegment ReconstructPath(
const std::unordered_map<int, int64_t> &subcursor_ids,
const std::unordered_map<int16_t, int64_t> &subcursor_ids,
storage::VertexAddress vertex, database::GraphDbAccessor *dba);
void PrepareForExpand(const std::unordered_map<int, int64_t> &subcursor_ids,
bool clear);
void PrepareForExpand(
const std::unordered_map<int16_t, int64_t> &subcursor_ids, bool clear);
private:
database::GraphDb *db_;

View File

@ -1,319 +0,0 @@
#pragma once
#include <tuple>
#include "communication/rpc/messages.hpp"
#include "distributed/bfs_subcursor.hpp"
#include "query/plan/operator.hpp"
#include "transactions/type.hpp"
#include "utils/serialization.hpp"
namespace distributed {
template <class TElement>
struct SerializedGraphElement {
using AddressT = storage::Address<mvcc::VersionList<TElement>>;
using AccessorT = RecordAccessor<TElement>;
SerializedGraphElement(AddressT global_address, TElement *old_element_input,
TElement *new_element_input, int worker_id)
: global_address(global_address),
old_element_input(old_element_input),
old_element_output(nullptr),
new_element_input(new_element_input),
new_element_output(nullptr),
worker_id(worker_id) {
CHECK(global_address.is_remote())
<< "Only global addresses should be used with SerializedGraphElement";
}
SerializedGraphElement(const AccessorT &accessor)
: SerializedGraphElement(accessor.GlobalAddress(), accessor.GetOld(),
accessor.GetNew(),
accessor.db_accessor().db().WorkerId()) {}
SerializedGraphElement() {}
AddressT global_address;
TElement *old_element_input;
std::unique_ptr<TElement> old_element_output;
TElement *new_element_input;
std::unique_ptr<TElement> new_element_output;
int worker_id;
private:
friend class boost::serialization::access;
template <class TArchive>
void save(TArchive &ar, unsigned int) const {
ar << global_address;
if (old_element_input) {
ar << true;
SaveElement(ar, *old_element_input, worker_id);
} else {
ar << false;
}
if (new_element_input) {
ar << true;
SaveElement(ar, *new_element_input, worker_id);
} else {
ar << false;
}
}
template <class TArchive>
void load(TArchive &ar, unsigned int) {
ar >> global_address;
static_assert(std::is_same<TElement, Vertex>::value ||
std::is_same<TElement, Edge>::value,
"TElement should be either Vertex or Edge");
bool has_old;
ar >> has_old;
if (has_old) {
if constexpr (std::is_same<TElement, Vertex>::value) {
old_element_output = std::move(LoadVertex(ar));
} else {
old_element_output = std::move(LoadEdge(ar));
}
}
bool has_new;
ar >> has_new;
if (has_new) {
if constexpr (std::is_same<TElement, Vertex>::value) {
new_element_output = std::move(LoadVertex(ar));
} else {
new_element_output = std::move(LoadEdge(ar));
}
}
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
}; // namespace distributed
using SerializedVertex = SerializedGraphElement<Vertex>;
using SerializedEdge = SerializedGraphElement<Edge>;
struct CreateBfsSubcursorReq : public communication::rpc::Message {
tx::TransactionId tx_id;
query::EdgeAtom::Direction direction;
std::vector<storage::EdgeType> edge_types;
query::GraphView graph_view;
CreateBfsSubcursorReq(tx::TransactionId tx_id,
query::EdgeAtom::Direction direction,
std::vector<storage::EdgeType> edge_types,
query::GraphView graph_view)
: tx_id(tx_id),
direction(direction),
edge_types(std::move(edge_types)),
graph_view(graph_view) {}
private:
friend class boost::serialization::access;
CreateBfsSubcursorReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &tx_id &direction &graph_view;
}
};
RPC_SINGLE_MEMBER_MESSAGE(CreateBfsSubcursorRes, int64_t);
using CreateBfsSubcursorRpc =
communication::rpc::RequestResponse<CreateBfsSubcursorReq,
CreateBfsSubcursorRes>;
struct RegisterSubcursorsReq : public communication::rpc::Message {
std::unordered_map<int, int64_t> subcursor_ids;
RegisterSubcursorsReq(std::unordered_map<int, int64_t> subcursor_ids)
: subcursor_ids(std::move(subcursor_ids)) {}
private:
friend class boost::serialization::access;
RegisterSubcursorsReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &subcursor_ids;
}
};
RPC_NO_MEMBER_MESSAGE(RegisterSubcursorsRes);
using RegisterSubcursorsRpc =
communication::rpc::RequestResponse<RegisterSubcursorsReq,
RegisterSubcursorsRes>;
RPC_SINGLE_MEMBER_MESSAGE(RemoveBfsSubcursorReq, int64_t);
RPC_NO_MEMBER_MESSAGE(RemoveBfsSubcursorRes);
using RemoveBfsSubcursorRpc =
communication::rpc::RequestResponse<RemoveBfsSubcursorReq,
RemoveBfsSubcursorRes>;
RPC_SINGLE_MEMBER_MESSAGE(ExpandLevelReq, int64_t);
RPC_SINGLE_MEMBER_MESSAGE(ExpandLevelRes, bool);
using ExpandLevelRpc =
communication::rpc::RequestResponse<ExpandLevelReq, ExpandLevelRes>;
RPC_SINGLE_MEMBER_MESSAGE(SubcursorPullReq, int64_t);
struct SubcursorPullRes : public communication::rpc::Message {
SubcursorPullRes(const VertexAccessor &vertex)
: vertex(std::experimental::in_place, vertex) {}
SubcursorPullRes() : vertex(std::experimental::nullopt) {}
std::experimental::optional<SerializedVertex> vertex;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &vertex;
}
};
using SubcursorPullRpc =
communication::rpc::RequestResponse<SubcursorPullReq, SubcursorPullRes>;
struct SetSourceReq : public communication::rpc::Message {
int64_t subcursor_id;
storage::VertexAddress source;
SetSourceReq(int64_t subcursor_id, storage::VertexAddress source)
: subcursor_id(subcursor_id), source(source) {}
private:
friend class boost::serialization::access;
SetSourceReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &subcursor_id &source;
}
};
RPC_NO_MEMBER_MESSAGE(SetSourceRes);
using SetSourceRpc =
communication::rpc::RequestResponse<SetSourceReq, SetSourceRes>;
struct ExpandToRemoteVertexReq : public communication::rpc::Message {
int64_t subcursor_id;
storage::EdgeAddress edge;
storage::VertexAddress vertex;
ExpandToRemoteVertexReq(int64_t subcursor_id, storage::EdgeAddress edge,
storage::VertexAddress vertex)
: subcursor_id(subcursor_id), edge(edge), vertex(vertex) {}
private:
friend class boost::serialization::access;
ExpandToRemoteVertexReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &subcursor_id &edge &vertex;
}
};
RPC_SINGLE_MEMBER_MESSAGE(ExpandToRemoteVertexRes, bool);
using ExpandToRemoteVertexRpc =
communication::rpc::RequestResponse<ExpandToRemoteVertexReq,
ExpandToRemoteVertexRes>;
struct ReconstructPathReq : public communication::rpc::Message {
int64_t subcursor_id;
std::experimental::optional<storage::VertexAddress> vertex;
std::experimental::optional<storage::EdgeAddress> edge;
ReconstructPathReq(int64_t subcursor_id, storage::VertexAddress vertex)
: subcursor_id(subcursor_id),
vertex(vertex),
edge(std::experimental::nullopt) {}
ReconstructPathReq(int64_t subcursor_id, storage::EdgeAddress edge)
: subcursor_id(subcursor_id),
vertex(std::experimental::nullopt),
edge(edge) {}
private:
friend class boost::serialization::access;
ReconstructPathReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &subcursor_id &vertex &edge;
}
};
struct ReconstructPathRes : public communication::rpc::Message {
int64_t subcursor_id;
std::vector<SerializedEdge> edges;
std::experimental::optional<storage::VertexAddress> next_vertex;
std::experimental::optional<storage::EdgeAddress> next_edge;
ReconstructPathRes(
const std::vector<EdgeAccessor> &edge_accessors,
std::experimental::optional<storage::VertexAddress> next_vertex,
std::experimental::optional<storage::EdgeAddress> next_edge)
: next_vertex(std::move(next_vertex)), next_edge(std::move(next_edge)) {
CHECK(!static_cast<bool>(next_vertex) || !static_cast<bool>(next_edge))
<< "At most one of `next_vertex` and `next_edge` should be set";
for (const auto &edge : edge_accessors) {
edges.emplace_back(edge);
}
}
private:
friend class boost::serialization::access;
ReconstructPathRes() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &edges &next_vertex &next_edge;
}
};
using ReconstructPathRpc =
communication::rpc::RequestResponse<ReconstructPathReq, ReconstructPathRes>;
struct PrepareForExpandReq : public communication::rpc::Message {
int64_t subcursor_id;
bool clear;
PrepareForExpandReq(int64_t subcursor_id, bool clear)
: subcursor_id(subcursor_id), clear(clear) {}
private:
friend class boost::serialization::access;
PrepareForExpandReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &subcursor_id &clear;
}
};
RPC_NO_MEMBER_MESSAGE(PrepareForExpandRes);
using PrepareForExpandRpc =
communication::rpc::RequestResponse<PrepareForExpandReq,
PrepareForExpandRes>;
} // namespace distributed

View File

@ -0,0 +1,280 @@
#>cpp
#pragma once
#include <tuple>
#include "communication/rpc/messages.hpp"
#include "distributed/bfs_rpc_messages.capnp.h"
#include "distributed/bfs_subcursor.hpp"
#include "query/plan/operator.hpp"
#include "transactions/type.hpp"
#include "utils/serialization.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'ast "/query/frontend/ast/ast.capnp")
(lcp:capnp-import 'dis "/distributed/serialization.capnp")
(lcp:capnp-import 'query "/query/common.capnp")
(lcp:capnp-import 'storage "/storage/serialization.capnp")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:capnp-type-conversion "storage::EdgeAddress" "Storage.Address")
(lcp:capnp-type-conversion "storage::VertexAddress" "Storage.Address")
(defun save-element (builder member)
#>cpp
if (${member}) {
if constexpr (std::is_same<TElement, Vertex>::value) {
auto builder = ${builder}.initVertex();
SaveVertex(*${member}, &builder, worker_id);
} else {
auto builder = ${builder}.initEdge();
SaveEdge(*${member}, &builder, worker_id);
}
} else {
${builder}.setNull();
}
cpp<#)
(defun load-element (reader member)
(let ((output-member (cl-ppcre:regex-replace "input$" member "output")))
#>cpp
if (!${reader}.isNull()) {
if constexpr (std::is_same<TElement, Vertex>::value) {
const auto reader = ${reader}.getVertex();
${output-member} = LoadVertex(reader);
} else {
const auto reader = ${reader}.getEdge();
${output-member} = LoadEdge(reader);
}
}
cpp<#))
(lcp:define-struct (serialized-graph-element t-element) ()
((global-address "storage::Address<mvcc::VersionList<TElement>>"
:capnp-type "Storage.Address")
(old-element-input "TElement *"
:save-fun
"if (old_element_input) {
ar << true;
SaveElement(ar, *old_element_input, worker_id);
} else {
ar << false;
}"
:load-fun ""
:capnp-type '((null "Void") (vertex "Dis.Vertex") (edge "Dis.Edge"))
:capnp-save #'save-element :capnp-load #'load-element)
(old-element-output "std::unique_ptr<TElement>"
:save-fun ""
:load-fun
"bool has_old;
ar >> has_old;
if (has_old) {
if constexpr (std::is_same<TElement, Vertex>::value) {
old_element_output = std::move(LoadVertex(ar));
} else {
old_element_output = std::move(LoadEdge(ar));
}
}"
:capnp-save :dont-save)
(new-element-input "TElement *"
:save-fun
"if (new_element_input) {
ar << true;
SaveElement(ar, *new_element_input, worker_id);
} else {
ar << false;
}"
:load-fun ""
:capnp-type '((null "Void") (vertex "Dis.Vertex") (edge "Dis.Edge"))
:capnp-save #'save-element :capnp-load #'load-element)
(new-element-output "std::unique_ptr<TElement>"
:save-fun ""
:load-fun
"bool has_new;
ar >> has_new;
if (has_new) {
if constexpr (std::is_same<TElement, Vertex>::value) {
new_element_output = std::move(LoadVertex(ar));
} else {
new_element_output = std::move(LoadEdge(ar));
}
}"
:capnp-save :dont-save)
(worker-id :int16_t :save-fun "" :load-fun "" :capnp-save :dont-save))
(:public
#>cpp
SerializedGraphElement(storage::Address<mvcc::VersionList<TElement>> global_address,
TElement *old_element_input, TElement *new_element_input,
int16_t worker_id)
: global_address(global_address),
old_element_input(old_element_input),
old_element_output(nullptr),
new_element_input(new_element_input),
new_element_output(nullptr),
worker_id(worker_id) {
CHECK(global_address.is_remote())
<< "Only global addresses should be used with SerializedGraphElement";
}
SerializedGraphElement(const RecordAccessor<TElement> &accessor)
: SerializedGraphElement(accessor.GlobalAddress(), accessor.GetOld(),
accessor.GetNew(),
accessor.db_accessor().db().WorkerId()) {}
SerializedGraphElement() {}
cpp<#)
(:serialize :capnp :type-args '(vertex edge)))
#>cpp
using SerializedVertex = SerializedGraphElement<Vertex>;
using SerializedEdge = SerializedGraphElement<Edge>;
cpp<#
(lcp:define-rpc create-bfs-subcursor
(:request
((tx-id "tx::TransactionId" :capnp-type "UInt64")
(direction "query::EdgeAtom::Direction"
:capnp-type "Ast.EdgeAtom.Direction" :capnp-init nil
:capnp-save (lcp:capnp-save-enum "::query::capnp::EdgeAtom::Direction"
"query::EdgeAtom::Direction"
'(in out both))
:capnp-load (lcp:capnp-load-enum "::query::capnp::EdgeAtom::Direction"
"query::EdgeAtom::Direction"
'(in out both)))
;; TODO(mtomic): Why isn't edge-types serialized?
(edge-types "std::vector<storage::EdgeType>"
:save-fun "" :load-fun "" :capnp-save :dont-save)
(graph-view "query::GraphView"
:capnp-type "Query.GraphView" :capnp-init nil
:capnp-save (lcp:capnp-save-enum "::query::capnp::GraphView"
"query::GraphView"
'(old new))
:capnp-load (lcp:capnp-load-enum "::query::capnp::GraphView"
"query::GraphView"
'(old new)))))
(:response ((member :int64_t))))
(lcp:define-rpc register-subcursors
(:request ((subcursor-ids "std::unordered_map<int16_t, int64_t>"
:capnp-type "Utils.Map(Utils.BoxInt16, Utils.BoxInt64)"
:capnp-save
(lambda (builder member)
#>cpp
utils::SaveMap<utils::capnp::BoxInt16, utils::capnp::BoxInt64>(
${member}, &${builder},
[](auto *builder, const auto &entry) {
auto key_builder = builder->initKey();
key_builder.setValue(entry.first);
auto value_builder = builder->initValue();
value_builder.setValue(entry.second);
});
cpp<#)
:capnp-load
(lambda (reader member)
#>cpp
utils::LoadMap<utils::capnp::BoxInt16, utils::capnp::BoxInt64>(
&${member}, ${reader},
[](const auto &reader) {
int16_t key = reader.getKey().getValue();
int64_t value = reader.getValue().getValue();
return std::make_pair(key, value);
});
cpp<#))))
(:response ()))
(lcp:define-rpc remove-bfs-subcursor
(:request ((member :int64_t)))
(:response ()))
(lcp:define-rpc expand-level
(:request ((member :int64_t)))
(:response ((member :bool))))
(lcp:define-rpc subcursor-pull
(:request ((member :int64_t)))
(:response ((vertex "std::experimental::optional<SerializedVertex>" :initarg :move
:capnp-type "Utils.Optional(SerializedGraphElement)"
:capnp-save (lcp:capnp-save-optional "capnp::SerializedGraphElement" "SerializedVertex")
:capnp-load (lcp:capnp-load-optional "capnp::SerializedGraphElement" "SerializedVertex")))))
(lcp:define-rpc set-source
(:request
((subcursor-id :int64_t)
(source "storage::VertexAddress")))
(:response ()))
(lcp:define-rpc expand-to-remote-vertex
(:request
((subcursor-id :int64_t)
(edge "storage::EdgeAddress")
(vertex "storage::VertexAddress")))
(:response ((member :bool))))
(lcp:define-rpc reconstruct-path
(:request
((subcursor-id :int64_t)
(vertex "std::experimental::optional<storage::VertexAddress>"
:capnp-save (lcp:capnp-save-optional "storage::capnp::Address" "storage::VertexAddress")
:capnp-load (lcp:capnp-load-optional "storage::capnp::Address" "storage::VertexAddress"))
(edge "std::experimental::optional<storage::EdgeAddress>"
:capnp-save (lcp:capnp-save-optional "storage::capnp::Address" "storage::EdgeAddress")
:capnp-load (lcp:capnp-load-optional "storage::capnp::Address" "storage::EdgeAddress")))
(:public
#>cpp
using Capnp = capnp::ReconstructPathReq;
static const communication::rpc::MessageType TypeInfo;
ReconstructPathReq() {}
ReconstructPathReq(int64_t subcursor_id, storage::VertexAddress vertex)
: subcursor_id(subcursor_id),
vertex(vertex),
edge(std::experimental::nullopt) {}
ReconstructPathReq(int64_t subcursor_id, storage::EdgeAddress edge)
: subcursor_id(subcursor_id),
vertex(std::experimental::nullopt),
edge(edge) {}
cpp<#))
(:response
((subcursor-id :int64_t ;; TODO(mtomic): Unused?
:save-fun "" :load-fun "" :capnp-save :dont-save)
(edges "std::vector<SerializedEdge>" :capnp-type "List(SerializedGraphElement)"
:capnp-save (lcp:capnp-save-vector "capnp::SerializedGraphElement" "SerializedEdge")
:capnp-load (lcp:capnp-load-vector "capnp::SerializedGraphElement" "SerializedEdge"))
(next-vertex "std::experimental::optional<storage::VertexAddress>"
:capnp-save (lcp:capnp-save-optional "storage::capnp::Address" "storage::VertexAddress")
:capnp-load (lcp:capnp-load-optional "storage::capnp::Address" "storage::VertexAddress"))
(next-edge "std::experimental::optional<storage::EdgeAddress>"
:capnp-save (lcp:capnp-save-optional "storage::capnp::Address" "storage::EdgeAddress")
:capnp-load (lcp:capnp-load-optional "storage::capnp::Address" "storage::EdgeAddress")))
(:public
#>cpp
using Capnp = capnp::ReconstructPathRes;
static const communication::rpc::MessageType TypeInfo;
ReconstructPathRes() {}
ReconstructPathRes(
const std::vector<EdgeAccessor> &edge_accessors,
std::experimental::optional<storage::VertexAddress> next_vertex,
std::experimental::optional<storage::EdgeAddress> next_edge)
: next_vertex(std::move(next_vertex)), next_edge(std::move(next_edge)) {
CHECK(!static_cast<bool>(next_vertex) || !static_cast<bool>(next_edge))
<< "At most one of `next_vertex` and `next_edge` should be set";
for (const auto &edge : edge_accessors) {
edges.emplace_back(edge);
}
}
cpp<#)))
(lcp:define-rpc prepare-for-expand
(:request
((subcursor-id :int64_t)
(clear :bool)))
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -20,52 +20,78 @@ class BfsRpcServer {
BfsSubcursorStorage *subcursor_storage)
: db_(db), server_(server), subcursor_storage_(subcursor_storage) {
server_->Register<CreateBfsSubcursorRpc>(
[this](const CreateBfsSubcursorReq &req) {
return std::make_unique<CreateBfsSubcursorRes>(
subcursor_storage_->Create(req.tx_id, req.direction,
req.edge_types, req.graph_view));
[this](const auto &req_reader, auto *res_builder) {
CreateBfsSubcursorReq req;
req.Load(req_reader);
CreateBfsSubcursorRes res(subcursor_storage_->Create(
req.tx_id, req.direction, req.edge_types, req.graph_view));
res.Save(res_builder);
});
server_->Register<RegisterSubcursorsRpc>(
[this](const RegisterSubcursorsReq &req) {
[this](const auto &req_reader, auto *res_builder) {
RegisterSubcursorsReq req;
req.Load(req_reader);
subcursor_storage_->Get(req.subcursor_ids.at(db_->WorkerId()))
->RegisterSubcursors(req.subcursor_ids);
return std::make_unique<RegisterSubcursorsRes>();
RegisterSubcursorsRes res;
res.Save(res_builder);
});
server_->Register<RemoveBfsSubcursorRpc>(
[this](const RemoveBfsSubcursorReq &req) {
[this](const auto &req_reader, auto *res_builder) {
RemoveBfsSubcursorReq req;
req.Load(req_reader);
subcursor_storage_->Erase(req.member);
return std::make_unique<RemoveBfsSubcursorRes>();
RemoveBfsSubcursorRes res;
res.Save(res_builder);
});
server_->Register<SetSourceRpc>([this](const SetSourceReq &req) {
subcursor_storage_->Get(req.subcursor_id)->SetSource(req.source);
return std::make_unique<SetSourceRes>();
server_->Register<SetSourceRpc>(
[this](const auto &req_reader, auto *res_builder) {
SetSourceReq req;
req.Load(req_reader);
subcursor_storage_->Get(req.subcursor_id)->SetSource(req.source);
SetSourceRes res;
res.Save(res_builder);
});
server_->Register<ExpandLevelRpc>([this](const auto &req_reader,
auto *res_builder) {
ExpandLevelReq req;
req.Load(req_reader);
ExpandLevelRes res(subcursor_storage_->Get(req.member)->ExpandLevel());
res.Save(res_builder);
});
server_->Register<ExpandLevelRpc>([this](const ExpandLevelReq &req) {
return std::make_unique<ExpandLevelRes>(
subcursor_storage_->Get(req.member)->ExpandLevel());
});
server_->Register<SubcursorPullRpc>([this](const SubcursorPullReq &req) {
auto vertex = subcursor_storage_->Get(req.member)->Pull();
if (!vertex) {
return std::make_unique<SubcursorPullRes>();
}
return std::make_unique<SubcursorPullRes>(*vertex);
});
server_->Register<SubcursorPullRpc>(
[this](const auto &req_reader, auto *res_builder) {
SubcursorPullReq req;
req.Load(req_reader);
auto vertex = subcursor_storage_->Get(req.member)->Pull();
if (!vertex) {
SubcursorPullRes res;
res.Save(res_builder);
return;
}
SubcursorPullRes res(*vertex);
res.Save(res_builder);
});
server_->Register<ExpandToRemoteVertexRpc>(
[this](const ExpandToRemoteVertexReq &req) {
return std::make_unique<ExpandToRemoteVertexRes>(
[this](const auto &req_reader, auto *res_builder) {
ExpandToRemoteVertexReq req;
req.Load(req_reader);
ExpandToRemoteVertexRes res(
subcursor_storage_->Get(req.subcursor_id)
->ExpandToLocalVertex(req.edge, req.vertex));
res.Save(res_builder);
});
server_->Register<ReconstructPathRpc>([this](
const ReconstructPathReq &req) {
server_->Register<ReconstructPathRpc>([this](const auto &req_reader,
auto *res_builder) {
ReconstructPathReq req;
req.Load(req_reader);
auto subcursor = subcursor_storage_->Get(req.subcursor_id);
PathSegment result;
if (req.vertex) {
@ -75,14 +101,18 @@ class BfsRpcServer {
} else {
LOG(FATAL) << "`edge` or `vertex` should be set in ReconstructPathReq";
}
return std::make_unique<ReconstructPathRes>(
result.edges, result.next_vertex, result.next_edge);
ReconstructPathRes res(result.edges, result.next_vertex,
result.next_edge);
res.Save(res_builder);
});
server_->Register<PrepareForExpandRpc>([this](
const PrepareForExpandReq &req) {
server_->Register<PrepareForExpandRpc>([this](const auto &req_reader,
auto *res_builder) {
PrepareForExpandReq req;
req.Load(req_reader);
subcursor_storage_->Get(req.subcursor_id)->PrepareForExpand(req.clear);
return std::make_unique<PrepareForExpandRes>();
PrepareForExpandRes res;
res.Save(res_builder);
});
}

View File

@ -35,7 +35,7 @@ class ExpandBfsSubcursor {
query::GraphView graph_view);
// Stores subcursor ids of other workers.
void RegisterSubcursors(std::unordered_map<int, int64_t> subcursor_ids) {
void RegisterSubcursors(std::unordered_map<int16_t, int64_t> subcursor_ids) {
subcursor_ids_ = std::move(subcursor_ids);
}
@ -91,7 +91,7 @@ class ExpandBfsSubcursor {
database::GraphDbAccessor dba_;
/// IDs of subcursors on other workers, used when sending RPCs.
std::unordered_map<int, int64_t> subcursor_ids_;
std::unordered_map<int16_t, int64_t> subcursor_ids_;
query::EdgeAtom::Direction direction_;
std::vector<storage::EdgeType> edge_types_;

View File

@ -11,7 +11,10 @@ ClusterDiscoveryMaster::ClusterDiscoveryMaster(
: server_(server),
coordination_(coordination),
rpc_worker_clients_(rpc_worker_clients) {
server_.Register<RegisterWorkerRpc>([this](const RegisterWorkerReq &req) {
server_.Register<RegisterWorkerRpc>([this](const auto &req_reader,
auto *res_builder) {
RegisterWorkerReq req;
req.Load(req_reader);
bool registration_successful =
this->coordination_.RegisterWorker(req.desired_worker_id, req.endpoint);
@ -24,15 +27,15 @@ ClusterDiscoveryMaster::ClusterDiscoveryMaster(
});
}
return std::make_unique<RegisterWorkerRes>(
registration_successful, this->coordination_.RecoveryInfo(),
this->coordination_.GetWorkers());
RegisterWorkerRes res(registration_successful,
this->coordination_.RecoveryInfo(),
this->coordination_.GetWorkers());
res.Save(res_builder);
});
server_.Register<NotifyWorkerRecoveredRpc>(
[this](const NotifyWorkerRecoveredReq &req) {
this->coordination_.WorkerRecovered(req.member);
return std::make_unique<NotifyWorkerRecoveredRes>();
[this](const auto &req_reader, auto *res_builder) {
this->coordination_.WorkerRecovered(req_reader.getMember());
});
}

View File

@ -8,10 +8,12 @@ ClusterDiscoveryWorker::ClusterDiscoveryWorker(
Server &server, WorkerCoordination &coordination,
communication::rpc::ClientPool &client_pool)
: server_(server), coordination_(coordination), client_pool_(client_pool) {
server_.Register<ClusterDiscoveryRpc>([this](const ClusterDiscoveryReq &req) {
this->coordination_.RegisterWorker(req.worker_id, req.endpoint);
return std::make_unique<ClusterDiscoveryRes>();
});
server_.Register<ClusterDiscoveryRpc>(
[this](const auto &req_reader, auto *res_builder) {
ClusterDiscoveryReq req;
req.Load(req_reader);
this->coordination_.RegisterWorker(req.worker_id, req.endpoint);
});
}
void ClusterDiscoveryWorker::RegisterWorker(int worker_id) {

View File

@ -1,101 +0,0 @@
#pragma once
#include <experimental/optional>
#include <unordered_map>
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "boost/serialization/unordered_map.hpp"
#include "communication/rpc/messages.hpp"
#include "durability/recovery.hpp"
#include "io/network/endpoint.hpp"
namespace distributed {
using communication::rpc::Message;
using Endpoint = io::network::Endpoint;
struct RegisterWorkerReq : public Message {
// Set desired_worker_id to -1 to get an automatically assigned ID.
RegisterWorkerReq(int desired_worker_id, const Endpoint &endpoint)
: desired_worker_id(desired_worker_id), endpoint(endpoint) {}
int desired_worker_id;
Endpoint endpoint;
private:
friend class boost::serialization::access;
RegisterWorkerReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<Message>(*this);
ar &desired_worker_id;
ar &endpoint;
}
};
struct RegisterWorkerRes : public Message {
RegisterWorkerRes(
bool registration_successful,
std::experimental::optional<durability::RecoveryInfo> recovery_info,
std::unordered_map<int, Endpoint> workers)
: registration_successful(registration_successful),
recovery_info(recovery_info),
workers(std::move(workers)) {}
bool registration_successful;
std::experimental::optional<durability::RecoveryInfo> recovery_info;
std::unordered_map<int, Endpoint> workers;
private:
friend class boost::serialization::access;
RegisterWorkerRes() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<Message>(*this);
ar &registration_successful;
ar &recovery_info;
ar &workers;
}
};
struct ClusterDiscoveryReq : public Message {
ClusterDiscoveryReq(int worker_id, Endpoint endpoint)
: worker_id(worker_id), endpoint(endpoint) {}
int worker_id;
Endpoint endpoint;
private:
friend class boost::serialization::access;
ClusterDiscoveryReq() {}
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<Message>(*this);
ar &worker_id;
ar &endpoint;
}
};
RPC_NO_MEMBER_MESSAGE(ClusterDiscoveryRes);
RPC_NO_MEMBER_MESSAGE(StopWorkerReq);
RPC_NO_MEMBER_MESSAGE(StopWorkerRes);
RPC_SINGLE_MEMBER_MESSAGE(NotifyWorkerRecoveredReq, int);
RPC_NO_MEMBER_MESSAGE(NotifyWorkerRecoveredRes);
using RegisterWorkerRpc =
communication::rpc::RequestResponse<RegisterWorkerReq, RegisterWorkerRes>;
using StopWorkerRpc =
communication::rpc::RequestResponse<StopWorkerReq, StopWorkerRes>;
using NotifyWorkerRecoveredRpc =
communication::rpc::RequestResponse<NotifyWorkerRecoveredReq,
NotifyWorkerRecoveredRes>;
using ClusterDiscoveryRpc =
communication::rpc::RequestResponse<ClusterDiscoveryReq,
ClusterDiscoveryRes>;
} // namespace distributed

View File

@ -0,0 +1,72 @@
#>cpp
#pragma once
#include <experimental/optional>
#include <unordered_map>
#include "communication/rpc/messages.hpp"
#include "distributed/coordination_rpc_messages.capnp.h"
#include "durability/recovery.hpp"
#include "io/network/endpoint.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'dur "/durability/recovery.capnp")
(lcp:capnp-import 'io "/io/network/endpoint.capnp")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:define-rpc register-worker
(:request
((desired-worker-id :int16_t)
(endpoint "io::network::Endpoint" :capnp-type "Io.Endpoint")))
(:response
((registration-successful :bool)
(recovery-info "std::experimental::optional<durability::RecoveryInfo>"
:capnp-type "Utils.Optional(Dur.RecoveryInfo)"
:capnp-save (lcp:capnp-save-optional "durability::capnp::RecoveryInfo"
"durability::RecoveryInfo")
:capnp-load (lcp:capnp-load-optional "durability::capnp::RecoveryInfo"
"durability::RecoveryInfo"))
(workers "std::unordered_map<int, io::network::Endpoint>"
:capnp-type "Utils.Map(Utils.BoxInt16, Io.Endpoint)"
:capnp-save
(lambda (builder member)
#>cpp
utils::SaveMap<utils::capnp::BoxInt16, io::network::capnp::Endpoint>(${member}, &${builder},
[](auto *builder, const auto &entry) {
auto key_builder = builder->initKey();
key_builder.setValue(entry.first);
auto value_builder = builder->initValue();
entry.second.Save(&value_builder);
});
cpp<#)
:capnp-load
(lambda (reader member)
#>cpp
utils::LoadMap<utils::capnp::BoxInt16, io::network::capnp::Endpoint>(&${member}, ${reader},
[](const auto &reader) {
io::network::Endpoint value;
value.Load(reader.getValue());
return std::make_pair(reader.getKey().getValue(), value);
});
cpp<#)))))
(lcp:define-rpc cluster-discovery
(:request
((worker-id :int16_t)
(endpoint "io::network::Endpoint" :capnp-type "Io.Endpoint")))
(:response ()))
(lcp:define-rpc stop-worker
(:request ())
(:response ()))
(lcp:define-rpc notify-worker-recovered
(:request ((member :int64_t)))
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -27,19 +27,18 @@ void WorkerCoordination::WaitForShutdown() {
std::condition_variable cv;
bool shutdown = false;
server_.Register<StopWorkerRpc>([&](const StopWorkerReq &) {
server_.Register<StopWorkerRpc>([&](const auto &req_reader, auto *res_builder) {
std::unique_lock<std::mutex> lk(mutex);
shutdown = true;
lk.unlock();
cv.notify_one();
return std::make_unique<StopWorkerRes>();
});
std::unique_lock<std::mutex> lk(mutex);
cv.wait(lk, [&shutdown] { return shutdown; });
}
Endpoint WorkerCoordination::GetEndpoint(int worker_id) {
io::network::Endpoint WorkerCoordination::GetEndpoint(int worker_id) {
std::lock_guard<std::mutex> guard(lock_);
return Coordination::GetEndpoint(worker_id);
}

View File

@ -14,7 +14,7 @@ std::unique_ptr<Edge> DataRpcClients::RemoteElement(int worker_id,
auto response =
clients_.GetClientPool(worker_id).Call<EdgeRpc>(TxGidPair{tx_id, gid});
CHECK(response) << "EdgeRpc failed";
return std::move(response->name_output_);
return std::move(response->edge_output);
}
template <>
@ -24,7 +24,7 @@ std::unique_ptr<Vertex> DataRpcClients::RemoteElement(int worker_id,
auto response =
clients_.GetClientPool(worker_id).Call<VertexRpc>(TxGidPair{tx_id, gid});
CHECK(response) << "VertexRpc failed";
return std::move(response->name_output_);
return std::move(response->vertex_output);
}
std::unordered_map<int, int64_t> DataRpcClients::VertexCounts(

View File

@ -1,72 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/serialization.hpp"
#include "storage/edge.hpp"
#include "storage/gid.hpp"
#include "storage/vertex.hpp"
#include "transactions/type.hpp"
namespace distributed {
struct TxGidPair {
tx::TransactionId tx_id;
gid::Gid gid;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &tx_id;
ar &gid;
}
};
#define MAKE_RESPONSE(type, name) \
class type##Res : public communication::rpc::Message { \
public: \
type##Res() {} \
type##Res(const type *name, int worker_id) \
: name_input_(name), worker_id_(worker_id) {} \
\
template <class TArchive> \
void save(TArchive &ar, unsigned int) const { \
ar << boost::serialization::base_object< \
const communication::rpc::Message>(*this); \
Save##type(ar, *name_input_, worker_id_); \
} \
\
template <class TArchive> \
void load(TArchive &ar, unsigned int) { \
ar >> boost::serialization::base_object<communication::rpc::Message>( \
*this); \
auto v = Load##type(ar); \
v.swap(name_output_); \
} \
BOOST_SERIALIZATION_SPLIT_MEMBER() \
\
const type *name_input_; \
int worker_id_; \
std::unique_ptr<type> name_output_; \
};
MAKE_RESPONSE(Vertex, vertex)
MAKE_RESPONSE(Edge, edge)
#undef MAKE_RESPONSE
RPC_SINGLE_MEMBER_MESSAGE(VertexReq, TxGidPair);
RPC_SINGLE_MEMBER_MESSAGE(EdgeReq, TxGidPair);
RPC_SINGLE_MEMBER_MESSAGE(VertexCountReq, tx::TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(VertexCountRes, int64_t);
using VertexRpc = communication::rpc::RequestResponse<VertexReq, VertexRes>;
using EdgeRpc = communication::rpc::RequestResponse<EdgeReq, EdgeRes>;
using VertexCountRpc =
communication::rpc::RequestResponse<VertexCountReq, VertexCountRes>;
} // namespace distributed

View File

@ -0,0 +1,76 @@
#>cpp
#pragma once
#include <memory>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/data_rpc_messages.capnp.h"
#include "distributed/serialization.hpp"
#include "storage/edge.hpp"
#include "storage/gid.hpp"
#include "storage/vertex.hpp"
#include "transactions/type.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:capnp-import 'dist "/distributed/serialization.capnp")
(lcp:define-struct tx-gid-pair ()
((tx-id "tx::TransactionId" :capnp-type "UInt64")
(gid "gid::Gid" :capnp-type "UInt64"))
(:serialize :capnp))
(lcp:define-rpc vertex
(:request ((member "TxGidPair")))
(:response
((vertex-input "const Vertex *"
:save-fun "SaveVertex(ar, *vertex_input, worker_id);" :load-fun ""
:capnp-type "Dist.Vertex"
:capnp-save
(lambda (builder member)
#>cpp
SaveVertex(*${member}, &${builder}, worker_id);
cpp<#)
:capnp-load
(lambda (reader member)
(declare (ignore member))
#>cpp
vertex_output = LoadVertex<const capnp::Vertex::Reader>(${reader});
cpp<#))
(worker-id :int64_t :save-fun "" :load-fun "" :capnp-save :dont-save)
(vertex-output "std::unique_ptr<Vertex>" :initarg nil
:save-fun "" :load-fun "vertex_output = LoadVertex(ar);"
:capnp-save :dont-save))))
(lcp:define-rpc edge
(:request ((member "TxGidPair")))
(:response
((edge-input "const Edge *"
:save-fun "SaveEdge(ar, *edge_input, worker_id);" :load-fun ""
:capnp-type "Dist.Edge"
:capnp-save
(lambda (builder member)
#>cpp
SaveEdge(*${member}, &${builder}, worker_id);
cpp<#)
:capnp-load
(lambda (reader member)
(declare (ignore member))
#>cpp
edge_output = LoadEdge<const capnp::Edge::Reader>(${reader});
cpp<#))
(worker-id :int64_t :save-fun "" :load-fun "" :capnp-save :dont-save)
(edge-output "std::unique_ptr<Edge>" :initarg nil
:save-fun "" :load-fun "edge_output = LoadEdge(ar);"
:capnp-save :dont-save))))
(lcp:define-rpc vertex-count
(:request ((member "tx::TransactionId" :capnp-type "UInt64")))
(:response ((member :int64_t))))
(lcp:pop-namespace) ;; distributed

View File

@ -10,27 +10,34 @@ DataRpcServer::DataRpcServer(database::GraphDb &db,
communication::rpc::Server &server)
: db_(db), rpc_server_(server) {
rpc_server_.Register<VertexRpc>(
[this](const VertexReq &req) {
database::GraphDbAccessor dba(db_, req.member.tx_id);
auto vertex = dba.FindVertex(req.member.gid, false);
[this](const auto &req_reader, auto *res_builder) {
database::GraphDbAccessor dba(db_, req_reader.getMember().getTxId());
auto vertex = dba.FindVertex(req_reader.getMember().getGid(), false);
CHECK(vertex.GetOld())
<< "Old record must exist when sending vertex by RPC";
return std::make_unique<VertexRes>(vertex.GetOld(), db_.WorkerId());
VertexRes response(vertex.GetOld(), db_.WorkerId());
response.Save(res_builder);
});
rpc_server_.Register<EdgeRpc>([this](const EdgeReq &req) {
database::GraphDbAccessor dba(db_, req.member.tx_id);
auto edge = dba.FindEdge(req.member.gid, false);
rpc_server_.Register<EdgeRpc>([this](const auto &req_reader,
auto *res_builder) {
database::GraphDbAccessor dba(db_, req_reader.getMember().getTxId());
auto edge = dba.FindEdge(req_reader.getMember().getGid(), false);
CHECK(edge.GetOld()) << "Old record must exist when sending edge by RPC";
return std::make_unique<EdgeRes>(edge.GetOld(), db_.WorkerId());
EdgeRes response(edge.GetOld(), db_.WorkerId());
response.Save(res_builder);
});
rpc_server_.Register<VertexCountRpc>([this](const VertexCountReq &req) {
database::GraphDbAccessor dba(db_, req.member);
int64_t size = 0;
for (auto vertex : dba.Vertices(false)) ++size;
return std::make_unique<VertexCountRes>(size);
});
rpc_server_.Register<VertexCountRpc>(
[this](const auto &req_reader, auto *res_builder) {
VertexCountReq req;
req.Load(req_reader);
database::GraphDbAccessor dba(db_, req.member);
int64_t size = 0;
for (auto vertex : dba.Vertices(false)) ++size;
VertexCountRes res(size);
res.Save(res_builder);
});
}
} // namespace distributed

View File

@ -10,7 +10,7 @@ utils::Future<bool> DurabilityRpcClients::MakeSnapshot(tx::TransactionId tx) {
auto futures = clients_.ExecuteOnWorkers<bool>(
0, [tx](int worker_id, communication::rpc::ClientPool &client_pool) {
auto res = client_pool.Call<MakeSnapshotRpc>(tx);
if (res == nullptr) return false;
if (!res) return false;
return res->member;
});

View File

@ -1,17 +0,0 @@
#pragma once
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "communication/rpc/messages.hpp"
#include "transactions/transaction.hpp"
namespace distributed {
RPC_SINGLE_MEMBER_MESSAGE(MakeSnapshotReq, tx::TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(MakeSnapshotRes, bool);
using MakeSnapshotRpc =
communication::rpc::RequestResponse<MakeSnapshotReq, MakeSnapshotRes>;
} // namespace distributed

View File

@ -0,0 +1,20 @@
#>cpp
#pragma once
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "communication/rpc/messages.hpp"
#include "distributed/durability_rpc_messages.capnp.h"
#include "transactions/transaction.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:define-rpc make-snapshot
(:request ((member "tx::TransactionId" :capnp-type "UInt64")))
(:response ((member :bool))))
(lcp:pop-namespace) ;; distributed

View File

@ -9,10 +9,12 @@ namespace distributed {
DurabilityRpcServer::DurabilityRpcServer(database::GraphDb &db,
communication::rpc::Server &server)
: db_(db), rpc_server_(server) {
rpc_server_.Register<MakeSnapshotRpc>([this](const MakeSnapshotReq &req) {
database::GraphDbAccessor dba(this->db_, req.member);
return std::make_unique<MakeSnapshotRes>(this->db_.MakeSnapshot(dba));
});
rpc_server_.Register<MakeSnapshotRpc>(
[this](const auto &req_reader, auto *res_builder) {
database::GraphDbAccessor dba(this->db_, req_reader.getMember());
MakeSnapshotRes res(this->db_.MakeSnapshot(dba));
res.Save(res_builder);
});
}
} // namespace distributed

View File

@ -1,32 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/serialization.hpp"
namespace distributed {
struct IndexLabelPropertyTx {
storage::Label label;
storage::Property property;
tx::TransactionId tx_id;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &label;
ar &property;
ar &tx_id;
}
};
RPC_SINGLE_MEMBER_MESSAGE(BuildIndexReq, IndexLabelPropertyTx);
RPC_NO_MEMBER_MESSAGE(BuildIndexRes);
using BuildIndexRpc =
communication::rpc::RequestResponse<BuildIndexReq, BuildIndexRes>;
} // namespace distributed

View File

@ -0,0 +1,25 @@
#>cpp
#pragma once
#include <memory>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/serialization.hpp"
#include "distributed/index_rpc_messages.capnp.h"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'storage "/storage/serialization.capnp")
(lcp:define-rpc build-index
(:request
((label "storage::Label" :capnp-type "Storage.Common")
(property "storage::Property" :capnp-type "Storage.Common")
(tx-id "tx::TransactionId" :capnp-type "UInt64")))
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -7,27 +7,27 @@ namespace distributed {
IndexRpcServer::IndexRpcServer(database::GraphDb &db,
communication::rpc::Server &server)
: db_(db), rpc_server_(server) {
rpc_server_.Register<BuildIndexRpc>([this](const BuildIndexReq &req) {
rpc_server_.Register<BuildIndexRpc>(
[this](const auto &req_reader, auto *res_builder) {
BuildIndexReq req;
req.Load(req_reader);
database::LabelPropertyIndex::Key key{req.label, req.property};
database::GraphDbAccessor dba(db_, req.tx_id);
database::LabelPropertyIndex::Key key{req.member.label,
req.member.property};
database::GraphDbAccessor dba(db_, req.member.tx_id);
if (db_.storage().label_property_index_.CreateIndex(key) == false) {
// If we are a distributed worker we just have to wait till the index
// (which should be in progress of being created) is created so that our
// return guarantess that the index has been built - this assumes that
// no worker thread that is creating an index will fail
while (!dba.LabelPropertyIndexExists(key.label_, key.property_)) {
// TODO reconsider this constant, currently rule-of-thumb chosen
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
} else {
dba.PopulateIndex(key);
dba.EnableIndex(key);
}
return std::make_unique<BuildIndexRes>();
});
if (db_.storage().label_property_index_.CreateIndex(key) == false) {
// If we are a distributed worker we just have to wait till the index
// (which should be in progress of being created) is created so that
// our return guarantess that the index has been built - this assumes
// that no worker thread that is creating an index will fail
while (!dba.LabelPropertyIndexExists(key.label_, key.property_)) {
// TODO reconsider this constant, currently rule-of-thumb chosen
std::this_thread::sleep_for(std::chrono::microseconds(100));
}
} else {
dba.PopulateIndex(key);
dba.EnableIndex(key);
}
});
}
} // namespace distributed

View File

@ -4,19 +4,21 @@ namespace distributed {
PlanConsumer::PlanConsumer(communication::rpc::Server &server)
: server_(server) {
server_.Register<DistributedPlanRpc>([this](const DispatchPlanReq &req) {
plan_cache_.access().insert(
req.plan_id_,
std::make_unique<PlanPack>(
req.plan_, req.symbol_table_,
std::move(const_cast<DispatchPlanReq &>(req).storage_)));
return std::make_unique<DispatchPlanRes>();
});
server_.Register<DispatchPlanRpc>(
[this](const auto &req_reader, auto *res_builder) {
DispatchPlanReq req;
req.Load(req_reader);
plan_cache_.access().insert(
req.plan_id, std::make_unique<PlanPack>(req.plan, req.symbol_table,
std::move(req.storage)));
DispatchPlanRes res;
res.Save(res_builder);
});
server_.Register<RemovePlanRpc>([this](const RemovePlanReq &req) {
plan_cache_.access().remove(req.member);
return std::make_unique<RemovePlanRes>();
});
server_.Register<RemovePlanRpc>(
[this](const auto &req_reader, auto *res_builder) {
plan_cache_.access().remove(req_reader.getMember());
});
}
PlanConsumer::PlanPack &PlanConsumer::PlanForId(int64_t plan_id) const {

View File

@ -16,14 +16,14 @@ class PlanConsumer {
public:
struct PlanPack {
PlanPack(std::shared_ptr<query::plan::LogicalOperator> plan,
SymbolTable symbol_table, AstTreeStorage storage)
query::SymbolTable symbol_table, query::AstTreeStorage storage)
: plan(plan),
symbol_table(std::move(symbol_table)),
storage(std::move(storage)) {}
std::shared_ptr<query::plan::LogicalOperator> plan;
SymbolTable symbol_table;
const AstTreeStorage storage;
query::SymbolTable symbol_table;
const query::AstTreeStorage storage;
};
explicit PlanConsumer(communication::rpc::Server &server);

View File

@ -6,13 +6,13 @@ PlanDispatcher::PlanDispatcher(RpcWorkerClients &clients) : clients_(clients) {}
void PlanDispatcher::DispatchPlan(
int64_t plan_id, std::shared_ptr<query::plan::LogicalOperator> plan,
const SymbolTable &symbol_table) {
const query::SymbolTable &symbol_table) {
auto futures = clients_.ExecuteOnWorkers<void>(
0, [plan_id, plan, symbol_table](
int worker_id, communication::rpc::ClientPool &client_pool) {
auto result =
client_pool.Call<DistributedPlanRpc>(plan_id, plan, symbol_table);
CHECK(result) << "DistributedPlanRpc failed";
client_pool.Call<DispatchPlanRpc>(plan_id, plan, symbol_table);
CHECK(result) << "DispatchPlanRpc failed";
});
for (auto &future : futures) {

View File

@ -18,7 +18,7 @@ class PlanDispatcher {
/** Dispatch a plan to all workers and wait for their acknowledgement. */
void DispatchPlan(int64_t plan_id,
std::shared_ptr<query::plan::LogicalOperator> plan,
const SymbolTable &symbol_table);
const query::SymbolTable &symbol_table);
/** Remove a plan from all workers and wait for their acknowledgement. */
void RemovePlan(int64_t plan_id);

View File

@ -1,63 +0,0 @@
#pragma once
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "communication/rpc/messages.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
namespace distributed {
using communication::rpc::Message;
using SymbolTable = query::SymbolTable;
using AstTreeStorage = query::AstTreeStorage;
struct DispatchPlanReq : public Message {
DispatchPlanReq() {}
DispatchPlanReq(int64_t plan_id,
std::shared_ptr<query::plan::LogicalOperator> plan,
SymbolTable symbol_table)
: plan_id_(plan_id), plan_(plan), symbol_table_(symbol_table) {}
int64_t plan_id_;
std::shared_ptr<query::plan::LogicalOperator> plan_;
SymbolTable symbol_table_;
AstTreeStorage storage_;
private:
friend class boost::serialization::access;
BOOST_SERIALIZATION_SPLIT_MEMBER();
template <class TArchive>
void save(TArchive &ar, const unsigned int) const {
ar &boost::serialization::base_object<Message>(*this);
ar &plan_id_;
ar &plan_;
ar &symbol_table_;
}
template <class TArchive>
void load(TArchive &ar, const unsigned int) {
ar &boost::serialization::base_object<Message>(*this);
ar &plan_id_;
ar &plan_;
ar &symbol_table_;
storage_ = std::move(
ar.template get_helper<AstTreeStorage>(AstTreeStorage::kHelperId));
}
};
RPC_NO_MEMBER_MESSAGE(DispatchPlanRes);
using DistributedPlanRpc =
communication::rpc::RequestResponse<DispatchPlanReq, DispatchPlanRes>;
RPC_SINGLE_MEMBER_MESSAGE(RemovePlanReq, int64_t);
RPC_NO_MEMBER_MESSAGE(RemovePlanRes);
using RemovePlanRpc =
communication::rpc::RequestResponse<RemovePlanReq, RemovePlanRes>;
} // namespace distributed

View File

@ -0,0 +1,59 @@
#>cpp
#pragma once
#include "communication/rpc/messages.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol_table.hpp"
#include "query/plan/operator.hpp"
#include "distributed/plan_rpc_messages.capnp.h"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:capnp-import 'plan "/query/plan/operator.capnp")
(lcp:capnp-import 'sem "/query/frontend/semantic/symbol.capnp")
(defun load-plan (reader member)
#>cpp
query::plan::LogicalOperator::LoadHelper helper;
${member} = utils::LoadSharedPtr<query::plan::capnp::LogicalOperator, query::plan::LogicalOperator>(
${reader}, [&helper](const auto &reader) {
auto op = query::plan::LogicalOperator::Construct(reader);
op->Load(reader, &helper);
return op.release();
}, &helper.loaded_ops);
storage = std::move(helper.ast_storage);
cpp<#)
(defun save-plan (builder member)
#>cpp
query::plan::LogicalOperator::SaveHelper helper;
utils::SaveSharedPtr<query::plan::capnp::LogicalOperator, query::plan::LogicalOperator>(
${member}, &${builder},
[&helper](auto *builder, const auto &val) {
val.Save(builder, &helper);
}, &helper.saved_ops);
cpp<#)
(lcp:define-rpc dispatch-plan
(:request
((plan-id :int64_t)
(plan "std::shared_ptr<query::plan::LogicalOperator>"
:capnp-type "Utils.SharedPtr(Plan.LogicalOperator)"
:capnp-save #'save-plan :capnp-load #'load-plan)
(symbol-table "query::SymbolTable" :capnp-type "Sem.SymbolTable")
(storage "query::AstTreeStorage" :initarg nil
:save-fun ""
:load-fun "storage = std::move(ar.template get_helper<query::AstTreeStorage>(query::AstTreeStorage::kHelperId));"
:capnp-save :dont-save)))
(:response ()))
(lcp:define-rpc remove-plan
(:request ((member :int64_t)))
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -96,15 +96,22 @@ ProduceRpcServer::ProduceRpcServer(
produce_rpc_server_(server),
plan_consumer_(plan_consumer),
tx_engine_(tx_engine) {
produce_rpc_server_.Register<PullRpc>([this](const PullReq &req) {
return std::make_unique<PullRes>(Pull(req));
});
produce_rpc_server_.Register<PullRpc>(
[this](const auto &req_reader, auto *res_builder) {
PullReq req;
req.Load(req_reader);
PullRes res(Pull(req));
res.Save(res_builder);
});
produce_rpc_server_.Register<TransactionCommandAdvancedRpc>(
[this](const TransactionCommandAdvancedReq &req) {
[this](const auto &req_reader, auto *res_builder) {
TransactionCommandAdvancedReq req;
req.Load(req_reader);
tx_engine_.UpdateCommand(req.member);
db_.data_manager().ClearCacheForSingleTransaction(req.member);
return std::make_unique<TransactionCommandAdvancedRes>();
TransactionCommandAdvancedRes res;
res.Save(res_builder);
});
}
@ -145,22 +152,22 @@ ProduceRpcServer::OngoingProduce &ProduceRpcServer::GetOngoingProduce(
PullResData ProduceRpcServer::Pull(const PullReq &req) {
auto &ongoing_produce = GetOngoingProduce(req);
PullResData result{db_.WorkerId(), req.send_old, req.send_new};
result.state_and_frames.pull_state = PullState::CURSOR_IN_PROGRESS;
PullResData result(db_.WorkerId(), req.send_old, req.send_new);
result.pull_state = PullState::CURSOR_IN_PROGRESS;
if (req.accumulate) {
result.state_and_frames.pull_state = ongoing_produce.Accumulate();
result.pull_state = ongoing_produce.Accumulate();
// If an error ocurred, we need to return that error.
if (result.state_and_frames.pull_state != PullState::CURSOR_EXHAUSTED) {
if (result.pull_state != PullState::CURSOR_EXHAUSTED) {
return result;
}
}
for (int i = 0; i < req.batch_size; ++i) {
auto pull_result = ongoing_produce.Pull();
result.state_and_frames.pull_state = pull_result.second;
result.pull_state = pull_result.second;
if (pull_result.second != PullState::CURSOR_IN_PROGRESS) break;
result.state_and_frames.frames.emplace_back(std::move(pull_result.first));
result.frames.emplace_back(std::move(pull_result.first));
}
return result;

View File

@ -1,381 +0,0 @@
#pragma once
#include <cstdint>
#include <functional>
#include <string>
#include "boost/serialization/utility.hpp"
#include "boost/serialization/vector.hpp"
#include "communication/rpc/messages.hpp"
#include "distributed/serialization.hpp"
#include "query/frontend/semantic/symbol.hpp"
#include "query/parameters.hpp"
#include "storage/address_types.hpp"
#include "transactions/type.hpp"
#include "utils/serialization.hpp"
namespace distributed {
/// The default number of results returned via RPC from remote execution to the
/// master that requested it.
constexpr int kDefaultBatchSize = 20;
/// Returnd along with a batch of results in the remote-pull RPC. Indicates the
/// state of execution on the worker.
enum class PullState {
CURSOR_EXHAUSTED,
CURSOR_IN_PROGRESS,
SERIALIZATION_ERROR,
LOCK_TIMEOUT_ERROR,
UPDATE_DELETED_ERROR,
RECONSTRUCTION_ERROR,
UNABLE_TO_DELETE_VERTEX_ERROR,
HINTED_ABORT_ERROR,
QUERY_ERROR
};
struct PullReq : public communication::rpc::Message {
PullReq() {}
PullReq(tx::TransactionId tx_id, tx::Snapshot tx_snapshot, int64_t plan_id,
tx::CommandId command_id, const Parameters &params,
std::vector<query::Symbol> symbols, bool accumulate, int batch_size,
bool send_old, bool send_new)
: tx_id(tx_id),
tx_snapshot(tx_snapshot),
plan_id(plan_id),
command_id(command_id),
params(params),
symbols(symbols),
accumulate(accumulate),
batch_size(batch_size),
send_old(send_old),
send_new(send_new) {}
tx::TransactionId tx_id;
tx::Snapshot tx_snapshot;
int64_t plan_id;
tx::CommandId command_id;
Parameters params;
std::vector<query::Symbol> symbols;
bool accumulate;
int batch_size;
// Indicates which of (old, new) records of a graph element should be sent.
bool send_old;
bool send_new;
private:
friend class boost::serialization::access;
template <class TArchive>
void save(TArchive &ar, unsigned int) const {
ar << boost::serialization::base_object<communication::rpc::Message>(*this);
ar << tx_id;
ar << tx_snapshot;
ar << plan_id;
ar << command_id;
ar << params.size();
for (auto &kv : params) {
ar << kv.first;
// Params never contain a vertex/edge, so save plan TypedValue.
utils::SaveTypedValue(ar, kv.second);
}
ar << symbols;
ar << accumulate;
ar << batch_size;
ar << send_old;
ar << send_new;
}
template <class TArchive>
void load(TArchive &ar, unsigned int) {
ar >> boost::serialization::base_object<communication::rpc::Message>(*this);
ar >> tx_id;
ar >> tx_snapshot;
ar >> plan_id;
ar >> command_id;
size_t params_size;
ar >> params_size;
for (size_t i = 0; i < params_size; ++i) {
int token_pos;
ar >> token_pos;
query::TypedValue param;
// Params never contain a vertex/edge, so load plan TypedValue.
utils::LoadTypedValue(ar, param);
params.Add(token_pos, param);
}
ar >> symbols;
ar >> accumulate;
ar >> batch_size;
ar >> send_old;
ar >> send_new;
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
/// The data returned to the end consumer (the Pull operator). Contains
/// only the relevant parts of the response, ready for use.
struct PullData {
PullState pull_state;
std::vector<std::vector<query::TypedValue>> frames;
};
/// The data of the remote pull response. Post-processing is required after
/// deserialization to initialize Vertex/Edge typed values in the frames
/// (possibly encapsulated in lists/maps) to their proper values. This requires
/// a GraphDbAccessor and therefore can't be done as part of deserialization.
///
/// TODO - make it possible to inject a &GraphDbAcessor from the Pull
/// layer
/// all the way into RPC data deserialization to remove the requirement for
/// post-processing. The current approach of holding references to parts of the
/// frame (potentially embedded in lists/maps) is too error-prone.
struct PullResData {
private:
// Temp cache for deserialized vertices and edges. These objects are created
// during deserialization. They are used immediatelly after during
// post-processing. The vertex/edge data ownership gets transfered to the
// Cache, and the `element_in_frame` reference is used to set the
// appropriate accessor to the appropriate value. Not used on side that
// generates the response.
template <typename TRecord>
struct GraphElementData {
using AddressT = storage::Address<mvcc::VersionList<TRecord>>;
using PtrT = std::unique_ptr<TRecord>;
GraphElementData(AddressT address, PtrT old_record, PtrT new_record,
query::TypedValue *element_in_frame)
: global_address(address),
old_record(std::move(old_record)),
new_record(std::move(new_record)),
element_in_frame(element_in_frame) {}
storage::Address<mvcc::VersionList<TRecord>> global_address;
std::unique_ptr<TRecord> old_record;
std::unique_ptr<TRecord> new_record;
// The position in frame is optional. This same structure is used for
// deserializing path elements, in which case the vertex/edge in question is
// not directly part of the frame.
query::TypedValue *element_in_frame;
};
// Same like `GraphElementData`, but for paths.
struct PathData {
PathData(query::TypedValue &path_in_frame) : path_in_frame(path_in_frame) {}
std::vector<GraphElementData<Vertex>> vertices;
std::vector<GraphElementData<Edge>> edges;
query::TypedValue &path_in_frame;
};
public:
PullResData() {} // Default constructor required for serialization.
PullResData(int worker_id, bool send_old, bool send_new)
: worker_id(worker_id), send_old(send_old), send_new(send_new) {}
PullResData(const PullResData &) = delete;
PullResData &operator=(const PullResData &) = delete;
PullResData(PullResData &&) = default;
PullResData &operator=(PullResData &&) = default;
PullData state_and_frames;
// Id of the worker on which the response is created, used for serializing
// vertices (converting local to global addresses).
int worker_id;
// Indicates which of (old, new) records of a graph element should be sent.
bool send_old;
bool send_new;
// Temporary caches used between deserialization and post-processing
// (transfering the ownership of this data to a Cache).
std::vector<GraphElementData<Vertex>> vertices;
std::vector<GraphElementData<Edge>> edges;
std::vector<PathData> paths;
/// Saves a typed value that is a vertex/edge/path.
template <class TArchive>
void SaveGraphElement(TArchive &ar, const query::TypedValue &value) const {
// Helper template function for storing a vertex or an edge.
auto save_element = [&ar, this](auto element_accessor) {
ar << element_accessor.GlobalAddress().raw();
// If both old and new are null, we need to reconstruct.
if (!(element_accessor.GetOld() || element_accessor.GetNew())) {
bool result = element_accessor.Reconstruct();
CHECK(result) << "Attempting to serialize an element not visible to "
"current transaction.";
}
auto *old_rec = element_accessor.GetOld();
if (send_old && old_rec) {
ar << true;
distributed::SaveElement(ar, *old_rec, worker_id);
} else {
ar << false;
}
if (send_new) {
// Must call SwitchNew as that will trigger a potentially necesary
// Reconstruct.
element_accessor.SwitchNew();
auto *new_rec = element_accessor.GetNew();
if (new_rec) {
ar << true;
distributed::SaveElement(ar, *new_rec, worker_id);
} else {
ar << false;
}
} else {
ar << false;
}
};
switch (value.type()) {
case query::TypedValue::Type::Vertex:
save_element(value.ValueVertex());
break;
case query::TypedValue::Type::Edge:
save_element(value.ValueEdge());
break;
case query::TypedValue::Type::Path: {
auto &path = value.ValuePath();
ar << path.size();
save_element(path.vertices()[0]);
for (size_t i = 0; i < path.size(); ++i) {
save_element(path.edges()[i]);
save_element(path.vertices()[i + 1]);
}
break;
}
default:
LOG(FATAL) << "Unsupported graph element type: " << value.type();
}
}
/// Loads a typed value that is a vertex/edge/path. Part of the
/// deserialization process, populates the temporary data caches which are
/// processed later.
template <class TArchive>
void LoadGraphElement(TArchive &ar, query::TypedValue::Type type,
query::TypedValue &value) {
auto load_edge = [](auto &ar) {
bool exists;
ar >> exists;
return exists ? LoadEdge(ar) : nullptr;
};
auto load_vertex = [](auto &ar) {
bool exists;
ar >> exists;
return exists ? LoadVertex(ar) : nullptr;
};
switch (type) {
case query::TypedValue::Type::Vertex: {
storage::VertexAddress::StorageT address;
ar >> address;
vertices.emplace_back(storage::VertexAddress(address), load_vertex(ar),
load_vertex(ar), &value);
break;
}
case query::TypedValue::Type::Edge: {
storage::VertexAddress::StorageT address;
ar >> address;
edges.emplace_back(storage::EdgeAddress(address), load_edge(ar),
load_edge(ar), &value);
break;
}
case query::TypedValue::Type::Path: {
size_t path_size;
ar >> path_size;
paths.emplace_back(value);
auto &path_data = paths.back();
storage::VertexAddress::StorageT vertex_address;
storage::EdgeAddress::StorageT edge_address;
ar >> vertex_address;
path_data.vertices.emplace_back(storage::VertexAddress(vertex_address),
load_vertex(ar), load_vertex(ar),
nullptr);
for (size_t i = 0; i < path_size; ++i) {
ar >> edge_address;
path_data.edges.emplace_back(storage::EdgeAddress(edge_address),
load_edge(ar), load_edge(ar), nullptr);
ar >> vertex_address;
path_data.vertices.emplace_back(
storage::VertexAddress(vertex_address), load_vertex(ar),
load_vertex(ar), nullptr);
}
break;
}
default:
LOG(FATAL) << "Unsupported graph element type: " << type;
}
}
};
class PullRes : public communication::rpc::Message {
public:
PullRes() {}
PullRes(PullResData data) : data(std::move(data)) {}
PullResData data;
private:
friend class boost::serialization::access;
template <class TArchive>
void save(TArchive &ar, unsigned int) const {
ar << boost::serialization::base_object<communication::rpc::Message>(*this);
ar << data.state_and_frames.pull_state;
ar << data.state_and_frames.frames.size();
// We need to indicate how many values are in each frame.
// Assume all the frames have an equal number of elements.
ar << (data.state_and_frames.frames.size() == 0
? 0
: data.state_and_frames.frames[0].size());
for (const auto &frame : data.state_and_frames.frames)
for (const auto &value : frame) {
utils::SaveTypedValue<TArchive>(
ar, value, [this](TArchive &ar, const query::TypedValue &value) {
data.SaveGraphElement(ar, value);
});
}
}
template <class TArchive>
void load(TArchive &ar, unsigned int) {
ar >> boost::serialization::base_object<communication::rpc::Message>(*this);
ar >> data.state_and_frames.pull_state;
size_t frame_count;
ar >> frame_count;
data.state_and_frames.frames.reserve(frame_count);
size_t frame_size;
ar >> frame_size;
for (size_t i = 0; i < frame_count; ++i) {
data.state_and_frames.frames.emplace_back();
auto &current_frame = data.state_and_frames.frames.back();
current_frame.reserve(frame_size);
for (size_t j = 0; j < frame_size; ++j) {
current_frame.emplace_back();
utils::LoadTypedValue<TArchive>(
ar, current_frame.back(),
[this](TArchive &ar, query::TypedValue::TypedValue::Type type,
query::TypedValue &value) {
data.LoadGraphElement(ar, type, value);
});
}
}
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
using PullRpc = communication::rpc::RequestResponse<PullReq, PullRes>;
// TODO make a separate RPC for the continuation of an existing pull, as an
// optimization not to have to send the full PullReqData pack every
// time.
RPC_SINGLE_MEMBER_MESSAGE(TransactionCommandAdvancedReq, tx::TransactionId);
RPC_NO_MEMBER_MESSAGE(TransactionCommandAdvancedRes);
using TransactionCommandAdvancedRpc =
communication::rpc::RequestResponse<TransactionCommandAdvancedReq,
TransactionCommandAdvancedRes>;
} // namespace distributed

View File

@ -0,0 +1,547 @@
#>cpp
#pragma once
#include <cstdint>
#include <functional>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/pull_produce_rpc_messages.capnp.h"
#include "distributed/serialization.hpp"
#include "query/frontend/semantic/symbol.hpp"
#include "query/parameters.hpp"
#include "storage/address_types.hpp"
#include "transactions/type.hpp"
#include "utils/serialization.hpp"
cpp<#
(lcp:in-impl
#>cpp
#include "database/graph_db_accessor.hpp"
#include "distributed/data_manager.hpp"
cpp<#)
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'dis "/distributed/serialization.capnp")
(lcp:capnp-import 'sem "/query/frontend/semantic/symbol.capnp")
(lcp:capnp-import 'tx "/transactions/common.capnp")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:capnp-type-conversion "tx::CommandId" "UInt32")
(lcp:capnp-type-conversion "tx::Snapshot" "Tx.Snapshot")
(lcp:capnp-type-conversion "tx::TransactionId" "UInt64")
#>cpp
/// The default number of results returned via RPC from remote execution to the
/// master that requested it.
constexpr int kDefaultBatchSize = 20;
cpp<#
(lcp:define-enum pull-state
(cursor-exhausted
cursor-in-progress
serialization-error
lock-timeout-error
update-deleted-error
reconstruction-error
unable-to-delete-vertex-error
hinted-abort-error
query-error)
(:documentation "Returned along with a batch of results in the remote-pull
RPC. Indicates the state of execution on the worker.")
(:serialize))
(lcp:define-struct pull-data ()
((pull-state "PullState")
(frames "std::vector<std::vector<query::TypedValue>>"))
(:documentation
"The data returned to the end consumer (the Pull operator). Contains only
the relevant parts of the response, ready for use."))
(lcp:define-struct pull-res-data ()
((pull-state "PullState"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::PullState" "PullState")
:capnp-load (lcp:capnp-load-enum "capnp::PullState" "PullState"))
(frames "std::vector<std::vector<query::TypedValue>>"
:capnp-type "List(List(Dis.TypedValue))"
:capnp-save
(lambda (builder member)
#>cpp
for (size_t frame_i = 0; frame_i < ${member}.size(); ++frame_i) {
const auto &frame = ${member}[frame_i];
auto frame_builder = ${builder}.init(frame_i, frame.size());
for (size_t val_i = 0; val_i < frame.size(); ++val_i) {
const auto &value = frame[val_i];
auto value_builder = frame_builder[val_i];
utils::SaveCapnpTypedValue(
value, &value_builder,
[this](const auto &value, auto *builder) {
this->SaveGraphElement(value, builder);
});
}
}
cpp<#)
:capnp-load
(lambda (reader member)
#>cpp
${member}.reserve(${reader}.size());
for (const auto &frame_reader : ${reader}) {
std::vector<query::TypedValue> current_frame;
current_frame.reserve(frame_reader.size());
for (const auto &value_reader : frame_reader) {
query::TypedValue value;
utils::LoadCapnpTypedValue(
value_reader, &value,
[this, dba](const auto &reader, auto *value) {
this->LoadGraphElement(dba, reader, value);
});
current_frame.emplace_back(value);
}
${member}.emplace_back(current_frame);
}
cpp<#))
(worker-id :int16_t :capnp-save :dont-save
:documentation
"Id of the worker on which the response is created, used for
serializing vertices (converting local to global addresses). Indicates which
of (old, new) records of a graph element should be sent.")
(send-old :bool :capnp-save :dont-save)
(send-new :bool :capnp-save :dont-save)
;; Temporary caches used between deserialization and post-processing
;; (transfering the ownership of this data to a Cache).
(vertices "std::vector<GraphElementData<Vertex>>" :capnp-save :dont-save)
(edges "std::vector<GraphElementData<Edge>>" :capnp-save :dont-save)
(paths "std::vector<PathData>" :capnp-save :dont-save))
(:documentation
"The data of the remote pull response. Post-processing is required after
deserialization to initialize Vertex/Edge typed values in the frames (possibly
encapsulated in lists/maps) to their proper values. This requires a
GraphDbAccessor and therefore can't be done as part of deserialization.
TODO - make it possible to inject a &GraphDbAcessor from the Pull layer all
the way into RPC data deserialization to remove the requirement for
post-processing. The current approach of holding references to parts of the
frame (potentially embedded in lists/maps) is too error-prone.")
(:public
#>cpp
private:
cpp<#
(lcp:define-struct (graph-element-data t-record) ()
((global-address "storage::Address<mvcc::VersionList<TRecord>>")
(old-record "std::unique_ptr<TRecord>")
(new-record "std::unique_ptr<TRecord>")
(element-in-frame
"query::TypedValue *"
:documentation
"The position in frame is optional. This same structure is used for
deserializing path elements, in which case the vertex/edge in question is not
directly part of the frame."))
(:documentation
"Temp cache for deserialized vertices and edges. These objects are
created during deserialization. They are used immediatelly after during
post-processing. The vertex/edge data ownership gets transfered to the Cache,
and the `element_in_frame` reference is used to set the appropriate accessor
to the appropriate value. Not used on side that generates the response.")
(:public
#>cpp
GraphElementData(storage::Address<mvcc::VersionList<TRecord>> address,
std::unique_ptr<TRecord> old_record, std::unique_ptr<TRecord> new_record,
query::TypedValue *element_in_frame)
: global_address(address),
old_record(std::move(old_record)),
new_record(std::move(new_record)),
element_in_frame(element_in_frame) {}
cpp<#))
(lcp:define-struct path-data ()
((vertices "std::vector<GraphElementData<Vertex>>")
(edges "std::vector<GraphElementData<Edge>>")
(path-in-frame "query::TypedValue *"))
(:public
#>cpp
PathData(query::TypedValue *path_in_frame) : path_in_frame(path_in_frame) {}
cpp<#)
(:documentation "Same like `GraphElementData`, but for paths."))
#>cpp
public:
PullResData() {} // Default constructor required for serialization.
PullResData(int worker_id, bool send_old, bool send_new)
: worker_id(worker_id), send_old(send_old), send_new(send_new) {}
PullResData(const PullResData &) = delete;
PullResData &operator=(const PullResData &) = delete;
PullResData(PullResData &&) = default;
PullResData &operator=(PullResData &&) = default;
/// Saves a typed value that is a vertex/edge/path.
template <class TArchive>
void SaveGraphElement(TArchive &ar, const query::TypedValue &value) const {
// Helper template function for storing a vertex or an edge.
auto save_element = [&ar, this](auto element_accessor) {
ar << element_accessor.GlobalAddress().raw();
// If both old and new are null, we need to reconstruct.
if (!(element_accessor.GetOld() || element_accessor.GetNew())) {
bool result = element_accessor.Reconstruct();
CHECK(result) << "Attempting to serialize an element not visible to "
"current transaction.";
}
auto *old_rec = element_accessor.GetOld();
if (send_old && old_rec) {
ar << true;
distributed::SaveElement(ar, *old_rec, worker_id);
} else {
ar << false;
}
if (send_new) {
// Must call SwitchNew as that will trigger a potentially necesary
// Reconstruct.
element_accessor.SwitchNew();
auto *new_rec = element_accessor.GetNew();
if (new_rec) {
ar << true;
distributed::SaveElement(ar, *new_rec, worker_id);
} else {
ar << false;
}
} else {
ar << false;
}
};
switch (value.type()) {
case query::TypedValue::Type::Vertex:
save_element(value.ValueVertex());
break;
case query::TypedValue::Type::Edge:
save_element(value.ValueEdge());
break;
case query::TypedValue::Type::Path: {
auto &path = value.ValuePath();
ar << path.size();
save_element(path.vertices()[0]);
for (size_t i = 0; i < path.size(); ++i) {
save_element(path.edges()[i]);
save_element(path.vertices()[i + 1]);
}
break;
}
default:
LOG(FATAL) << "Unsupported graph element type: " << value.type();
}
}
/// Loads a typed value that is a vertex/edge/path. Part of the
/// deserialization process, populates the temporary data caches which are
/// processed later.
template <class TArchive>
void LoadGraphElement(TArchive &ar, query::TypedValue::Type type,
query::TypedValue &value) {
auto load_edge = [](auto &ar) {
bool exists;
ar >> exists;
return exists ? LoadEdge(ar) : nullptr;
};
auto load_vertex = [](auto &ar) {
bool exists;
ar >> exists;
return exists ? LoadVertex(ar) : nullptr;
};
switch (type) {
case query::TypedValue::Type::Vertex: {
storage::VertexAddress::StorageT address;
ar >> address;
vertices.emplace_back(storage::VertexAddress(address), load_vertex(ar),
load_vertex(ar), &value);
break;
}
case query::TypedValue::Type::Edge: {
storage::VertexAddress::StorageT address;
ar >> address;
edges.emplace_back(storage::EdgeAddress(address), load_edge(ar),
load_edge(ar), &value);
break;
}
case query::TypedValue::Type::Path: {
size_t path_size;
ar >> path_size;
paths.emplace_back(&value);
auto &path_data = paths.back();
storage::VertexAddress::StorageT vertex_address;
storage::EdgeAddress::StorageT edge_address;
ar >> vertex_address;
path_data.vertices.emplace_back(storage::VertexAddress(vertex_address),
load_vertex(ar), load_vertex(ar),
nullptr);
for (size_t i = 0; i < path_size; ++i) {
ar >> edge_address;
path_data.edges.emplace_back(storage::EdgeAddress(edge_address),
load_edge(ar), load_edge(ar), nullptr);
ar >> vertex_address;
path_data.vertices.emplace_back(
storage::VertexAddress(vertex_address), load_vertex(ar),
load_vertex(ar), nullptr);
}
break;
}
default:
LOG(FATAL) << "Unsupported graph element type: " << type;
}
}
cpp<#)
(:private
#>cpp
void SaveGraphElement(const query::TypedValue &,
distributed::capnp::TypedValue::Builder *) const;
void LoadGraphElement(database::GraphDbAccessor *,
const distributed::capnp::TypedValue::Reader &,
query::TypedValue *);
cpp<#)
(:serialize :capnp :load-args '((dba "database::GraphDbAccessor *"))))
(lcp:in-impl
#>cpp
void PullResData::SaveGraphElement(
const query::TypedValue &value,
distributed::capnp::TypedValue::Builder *builder) const {
auto save_element = [this](auto accessor, auto *builder) {
builder->setAddress(accessor.GlobalAddress().raw());
// If both old and new are null, we need to reconstruct
if (!(accessor.GetOld() || accessor.GetNew())) {
bool result = accessor.Reconstruct();
CHECK(result) << "Attempting to serialize an element not visible to "
"current transaction.";
}
auto *old_rec = accessor.GetOld();
if (send_old && old_rec) {
auto old_builder = builder->initOld();
distributed::SaveElement(*old_rec, &old_builder, worker_id);
}
if (send_new) {
// Must call SwitchNew as that will trigger a potentially necesary
// Reconstruct.
accessor.SwitchNew();
auto *new_rec = accessor.GetNew();
if (new_rec) {
auto new_builder = builder->initNew();
distributed::SaveElement(*new_rec, &new_builder, worker_id);
}
}
};
switch (value.type()) {
case query::TypedValue::Type::Vertex: {
auto vertex_builder = builder->initVertex();
save_element(value.ValueVertex(), &vertex_builder);
break;
}
case query::TypedValue::Type::Edge: {
auto edge_builder = builder->initEdge();
save_element(value.ValueEdge(), &edge_builder);
break;
}
case query::TypedValue::Type::Path: {
const auto &path = value.ValuePath();
auto path_builder = builder->initPath();
auto vertices_builder = path_builder.initVertices(path.vertices().size());
for (size_t i = 0; i < path.vertices().size(); ++i) {
auto vertex_builder = vertices_builder[i];
save_element(path.vertices()[i], &vertex_builder);
}
auto edges_builder = path_builder.initEdges(path.edges().size());
for (size_t i = 0; i < path.edges().size(); ++i) {
auto edge_builder = edges_builder[i];
save_element(path.edges()[i], &edge_builder);
}
break;
}
default:
LOG(FATAL) << "Unsupported graph element type: " << value.type();
}
}
void PullResData::LoadGraphElement(
database::GraphDbAccessor *dba,
const distributed::capnp::TypedValue::Reader &reader,
query::TypedValue *value) {
auto load_vertex = [dba](const auto &vertex_reader) {
storage::VertexAddress global_address(vertex_reader.getAddress());
auto old_record =
vertex_reader.hasOld()
? distributed::LoadVertex<const distributed::capnp::Vertex::Reader>(
vertex_reader.getOld())
: nullptr;
auto new_record =
vertex_reader.hasNew()
? distributed::LoadVertex<const distributed::capnp::Vertex::Reader>(
vertex_reader.getNew())
: nullptr;
dba->db()
.data_manager()
.Elements<Vertex>(dba->transaction_id())
.emplace(global_address.gid(), std::move(old_record),
std::move(new_record));
return VertexAccessor(global_address, *dba);
};
auto load_edge = [dba](const auto &edge_reader) {
storage::EdgeAddress global_address(edge_reader.getAddress());
auto old_record =
edge_reader.hasOld()
? distributed::LoadEdge<const distributed::capnp::Edge::Reader>(
edge_reader.getOld())
: nullptr;
auto new_record =
edge_reader.hasNew()
? distributed::LoadEdge<const distributed::capnp::Edge::Reader>(
edge_reader.getNew())
: nullptr;
dba->db()
.data_manager()
.Elements<Edge>(dba->transaction_id())
.emplace(global_address.gid(), std::move(old_record),
std::move(new_record));
return EdgeAccessor(global_address, *dba);
};
switch (reader.which()) {
case distributed::capnp::TypedValue::VERTEX:
*value = load_vertex(reader.getVertex());
break;
case distributed::capnp::TypedValue::EDGE:
*value = load_edge(reader.getEdge());
break;
case distributed::capnp::TypedValue::PATH: {
auto vertices_reader = reader.getPath().getVertices();
auto edges_reader = reader.getPath().getEdges();
query::Path path(load_vertex(vertices_reader[0]));
for (size_t i = 0; i < edges_reader.size(); ++i) {
path.Expand(load_edge(edges_reader[i]));
path.Expand(load_vertex(vertices_reader[i + 1]));
}
*value = path;
break;
}
default:
LOG(FATAL) << "Unsupported graph element type.";
}
}
cpp<#)
(lcp:define-rpc pull
(:request
((tx-id "tx::TransactionId")
(tx-snapshot "tx::Snapshot")
(plan-id :int64_t)
(command-id "tx::CommandId")
(params "Parameters"
:save-fun
"
ar << params.size();
for (auto &kv : params) {
ar << kv.first;
// Params never contain a vertex/edge, so save plan TypedValue.
utils::SaveTypedValue(ar, kv.second);
}
"
:load-fun
"
size_t params_size;
ar >> params_size;
for (size_t i = 0; i < params_size; ++i) {
int token_pos;
ar >> token_pos;
query::TypedValue param;
// Params never contain a vertex/edge, so load plan TypedValue.
utils::LoadTypedValue(ar, param);
params.Add(token_pos, param);
}
"
:capnp-type "Utils.Map(Utils.BoxInt64, Dis.TypedValue)"
:capnp-save
(lambda (builder member)
#>cpp
auto entries_builder = ${builder}.initEntries(${member}.size());
size_t i = 0;
for (auto &entry : params) {
auto builder = entries_builder[i];
auto key_builder = builder.initKey();
key_builder.setValue(entry.first);
auto value_builder = builder.initValue();
utils::SaveCapnpTypedValue(entry.second, &value_builder);
++i;
}
cpp<#)
:capnp-load
(lambda (reader member)
#>cpp
for (const auto &entry_reader : ${reader}.getEntries()) {
query::TypedValue value;
utils::LoadCapnpTypedValue(entry_reader.getValue(), &value);
${member}.Add(entry_reader.getKey().getValue(), value);
}
cpp<#))
(symbols "std::vector<query::Symbol>"
:capnp-type "List(Sem.Symbol)"
:capnp-save (lcp:capnp-save-vector "query::capnp::Symbol" "query::Symbol")
:capnp-load (lcp:capnp-load-vector "query::capnp::Symbol" "query::Symbol"))
(accumulate :bool)
(batch-size :int64_t)
;; Indicates which of (old, new) records of a graph element should be sent.
(send-old :bool)
(send-new :bool)))
(:response
((data "PullResData" :initarg :move
:save-fun
"
ar << data.pull_state;
ar << data.frames.size();
// We need to indicate how many values are in each frame.
// Assume all the frames have an equal number of elements.
ar << (data.frames.size() == 0 ? 0 : data.frames[0].size());
for (const auto &frame : data.frames) {
for (const auto &value : frame) {
utils::SaveTypedValue<TArchive>(
ar, value, [this](TArchive &ar, const query::TypedValue &value) {
data.SaveGraphElement(ar, value);
});
}
}
"
:load-fun
"
ar >> data.pull_state;
size_t frame_count;
ar >> frame_count;
data.frames.reserve(frame_count);
size_t frame_size;
ar >> frame_size;
for (size_t i = 0; i < frame_count; ++i) {
data.frames.emplace_back();
auto &current_frame = data.frames.back();
current_frame.reserve(frame_size);
for (size_t j = 0; j < frame_size; ++j) {
current_frame.emplace_back();
utils::LoadTypedValue<TArchive>(
ar, current_frame.back(),
[this](TArchive &ar, query::TypedValue::TypedValue::Type type,
query::TypedValue &value) {
data.LoadGraphElement(ar, type, value);
});
}
}
"))
(:serialize :capnp :base t :load-args '((dba "database::GraphDbAccessor *")))))
;; TODO make a separate RPC for the continuation of an existing pull, as an
;; optimization not to have to send the full PullReqData pack every time.
(lcp:define-rpc transaction-command-advanced
(:request ((member "tx::TransactionId")))
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -12,53 +12,21 @@ utils::Future<PullData> PullRpcClients::Pull(
tx::CommandId command_id, const Parameters &params,
const std::vector<query::Symbol> &symbols, bool accumulate,
int batch_size) {
return clients_.ExecuteOnWorker<PullData>(
worker_id, [&dba, plan_id, command_id, params, symbols, accumulate,
batch_size](int worker_id, ClientPool &client_pool) {
auto result = client_pool.Call<PullRpc>(
dba.transaction_id(), dba.transaction().snapshot(), plan_id,
command_id, params, symbols, accumulate, batch_size, true, true);
auto handle_vertex = [&dba](auto &v) {
dba.db()
.data_manager()
.Elements<Vertex>(dba.transaction_id())
.emplace(v.global_address.gid(), std::move(v.old_record),
std::move(v.new_record));
if (v.element_in_frame) {
VertexAccessor va(v.global_address, dba);
*v.element_in_frame = va;
}
};
auto handle_edge = [&dba](auto &e) {
dba.db()
.data_manager()
.Elements<Edge>(dba.transaction_id())
.emplace(e.global_address.gid(), std::move(e.old_record),
std::move(e.new_record));
if (e.element_in_frame) {
EdgeAccessor ea(e.global_address, dba);
*e.element_in_frame = ea;
}
};
for (auto &v : result->data.vertices) handle_vertex(v);
for (auto &e : result->data.edges) handle_edge(e);
for (auto &p : result->data.paths) {
handle_vertex(p.vertices[0]);
p.path_in_frame =
query::Path(VertexAccessor(p.vertices[0].global_address, dba));
query::Path &path_in_frame = p.path_in_frame.ValuePath();
for (size_t i = 0; i < p.edges.size(); ++i) {
handle_edge(p.edges[i]);
path_in_frame.Expand(EdgeAccessor(p.edges[i].global_address, dba));
handle_vertex(p.vertices[i + 1]);
path_in_frame.Expand(
VertexAccessor(p.vertices[i + 1].global_address, dba));
}
}
return std::move(result->data.state_and_frames);
});
return clients_.ExecuteOnWorker<
PullData>(worker_id, [&dba, plan_id, command_id, params, symbols,
accumulate, batch_size](int worker_id,
ClientPool &client_pool) {
auto load_pull_res = [&dba](const auto &res_reader) {
PullRes res;
res.Load(res_reader, &dba);
return res;
};
auto result = client_pool.CallWithLoad<PullRpc>(
load_pull_res, dba.transaction_id(), dba.transaction().snapshot(),
plan_id, command_id, params, symbols, accumulate, batch_size, true,
true);
return PullData{result->data.pull_state, std::move(result->data.frames)};
});
}
std::vector<utils::Future<void>>

View File

@ -91,9 +91,8 @@ class IndexRpcClients {
worker_id,
[label, property, transaction_id](
int worker_id, communication::rpc::ClientPool &client_pool) {
return client_pool.Call<BuildIndexRpc>(
distributed::IndexLabelPropertyTx{
label, property, transaction_id}) != nullptr;
return static_cast<bool>(
client_pool.Call<BuildIndexRpc>(label, property, transaction_id));
});
}

View File

@ -0,0 +1,71 @@
@0xccb448f0b998d9c8;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("distributed::capnp");
struct Address {
gid @0 :UInt64;
workerId @1 :Int16;
}
struct PropertyValue {
id @0 :UInt16;
value @1 :TypedValue;
}
struct Edge {
from @0 :Address;
to @1 :Address;
typeId @2 :UInt16;
properties @3 :List(PropertyValue);
}
struct Vertex {
outEdges @0 :List(EdgeEntry);
inEdges @1 :List(EdgeEntry);
labelIds @2 :List(UInt16);
properties @3 :List(PropertyValue);
struct EdgeEntry {
vertexAddress @0 :Address;
edgeAddress @1 :Address;
edgeTypeId @2 :UInt16;
}
}
struct TypedValue {
union {
nullType @0 :Void;
bool @1 :Bool;
integer @2 :Int64;
double @3 :Float64;
string @4 :Text;
list @5 :List(TypedValue);
map @6 :List(Entry);
vertex @7 :VertexAccessor;
edge @8 :EdgeAccessor;
path @9 :Path;
}
struct Entry {
key @0 :Text;
value @1 :TypedValue;
}
struct VertexAccessor {
address @0 :UInt64;
old @1 :Vertex;
new @2: Vertex;
}
struct EdgeAccessor {
address @0 :UInt64;
old @1 :Edge;
new @2: Edge;
}
struct Path {
vertices @0 :List(VertexAccessor);
edges @1 :List(EdgeAccessor);
}
}

View File

@ -0,0 +1,120 @@
#include "distributed/serialization.hpp"
namespace {
template <class TAddress>
void SaveAddress(TAddress address,
distributed::capnp::Address::Builder *builder,
int16_t worker_id) {
builder->setGid(address.is_local() ? address.local()->gid_ : address.gid());
builder->setWorkerId(address.is_local() ? worker_id : address.worker_id());
}
storage::VertexAddress LoadVertexAddress(
const distributed::capnp::Address::Reader &reader) {
return {reader.getGid(), reader.getWorkerId()};
}
storage::EdgeAddress LoadEdgeAddress(
const distributed::capnp::Address::Reader &reader) {
return {reader.getGid(), reader.getWorkerId()};
}
void SaveProperties(
const PropertyValueStore &props,
::capnp::List<distributed::capnp::PropertyValue>::Builder *builder) {
int64_t i = 0;
for (const auto &kv : props) {
auto prop_builder = (*builder)[i];
prop_builder.setId(kv.first.Id());
auto value_builder = prop_builder.initValue();
utils::SaveCapnpTypedValue(kv.second, &value_builder);
++i;
}
}
PropertyValueStore LoadProperties(
const ::capnp::List<distributed::capnp::PropertyValue>::Reader &reader) {
PropertyValueStore props;
for (const auto &prop_reader : reader) {
query::TypedValue value;
utils::LoadCapnpTypedValue(prop_reader.getValue(), &value);
props.set(storage::Property(prop_reader.getId()), value);
}
return props;
}
} // namespace
namespace distributed {
void SaveVertex(const Vertex &vertex, capnp::Vertex::Builder *builder,
int16_t worker_id) {
auto save_edges = [worker_id](const auto &edges, auto *edges_builder) {
int64_t i = 0;
for (const auto &edge : edges) {
auto edge_builder = (*edges_builder)[i];
auto vertex_addr_builder = edge_builder.initVertexAddress();
SaveAddress(edge.vertex, &vertex_addr_builder, worker_id);
auto edge_addr_builder = edge_builder.initEdgeAddress();
SaveAddress(edge.edge, &edge_addr_builder, worker_id);
edge_builder.setEdgeTypeId(edge.edge_type.Id());
++i;
}
};
auto out_builder = builder->initOutEdges(vertex.out_.size());
save_edges(vertex.out_, &out_builder);
auto in_builder = builder->initInEdges(vertex.in_.size());
save_edges(vertex.in_, &in_builder);
auto labels_builder = builder->initLabelIds(vertex.labels_.size());
for (size_t i = 0; i < vertex.labels_.size(); ++i) {
labels_builder.set(i, vertex.labels_[i].Id());
}
auto properties_builder = builder->initProperties(vertex.properties_.size());
SaveProperties(vertex.properties_, &properties_builder);
}
template <>
std::unique_ptr<Vertex> LoadVertex(const capnp::Vertex::Reader &reader) {
auto vertex = std::make_unique<Vertex>();
auto load_edges = [](const auto &edges_reader) {
Edges edges;
for (const auto &edge_reader : edges_reader) {
auto vertex_address = LoadVertexAddress(edge_reader.getVertexAddress());
auto edge_address = LoadEdgeAddress(edge_reader.getEdgeAddress());
storage::EdgeType edge_type(edge_reader.getEdgeTypeId());
edges.emplace(vertex_address, edge_address, edge_type);
}
return edges;
};
vertex->out_ = load_edges(reader.getOutEdges());
vertex->in_ = load_edges(reader.getInEdges());
for (const auto &label_id : reader.getLabelIds()) {
vertex->labels_.emplace_back(label_id);
}
vertex->properties_ = LoadProperties(reader.getProperties());
return vertex;
}
void SaveEdge(const Edge &edge, capnp::Edge::Builder *builder,
int16_t worker_id) {
auto from_builder = builder->initFrom();
SaveAddress(edge.from_, &from_builder, worker_id);
auto to_builder = builder->initTo();
SaveAddress(edge.to_, &to_builder, worker_id);
builder->setTypeId(edge.edge_type_.Id());
auto properties_builder = builder->initProperties(edge.properties_.size());
SaveProperties(edge.properties_, &properties_builder);
}
template <>
std::unique_ptr<Edge> LoadEdge(const capnp::Edge::Reader &reader) {
auto from = LoadVertexAddress(reader.getFrom());
auto to = LoadVertexAddress(reader.getTo());
auto edge =
std::make_unique<Edge>(from, to, storage::EdgeType{reader.getTypeId()});
edge->properties_ = LoadProperties(reader.getProperties());
return edge;
}
} // namespace distributed

View File

@ -4,6 +4,7 @@
#include <memory>
#include <vector>
#include "distributed/serialization.capnp.h"
#include "storage/address_types.hpp"
#include "storage/edge.hpp"
#include "storage/types.hpp"
@ -38,6 +39,9 @@ void SaveProperties(TArchive &ar, const PropertyValueStore &props) {
}
} // namespace impl
void SaveVertex(const Vertex &vertex, capnp::Vertex::Builder *builder,
int16_t worker_id);
/**
* Saves the given vertex into the given Boost archive.
*
@ -68,6 +72,9 @@ void SaveVertex(TArchive &ar, const Vertex &vertex, int worker_id) {
impl::SaveProperties(ar, vertex.properties_);
}
void SaveEdge(const Edge &edge, capnp::Edge::Builder *builder,
int16_t worker_id);
/**
* Saves the given edge into the given Boost archive.
*
@ -85,6 +92,18 @@ void SaveEdge(TArchive &ar, const Edge &edge, int worker_id) {
impl::SaveProperties(ar, edge.properties_);
}
/// Alias for `SaveEdge` allowing for param type resolution.
inline void SaveElement(const Edge &record, capnp::Edge::Builder *builder,
int16_t worker_id) {
return SaveEdge(record, builder, worker_id);
}
/// Alias for `SaveVertex` allowing for param type resolution.
inline void SaveElement(const Vertex &record, capnp::Vertex::Builder *builder,
int16_t worker_id) {
return SaveVertex(record, builder, worker_id);
}
/// Alias for `SaveEdge` allowing for param type resolution.
template <typename TArchive>
void SaveElement(TArchive &ar, const Edge &record, int worker_id) {
@ -163,6 +182,9 @@ std::unique_ptr<Vertex> LoadVertex(TArchive &ar) {
return vertex;
}
template <>
std::unique_ptr<Vertex> LoadVertex(const capnp::Vertex::Reader &reader);
/**
* Loads an Edge from the given archive and returns it.
*
@ -181,4 +203,7 @@ std::unique_ptr<Edge> LoadEdge(TArchive &ar) {
return edge;
}
template <>
std::unique_ptr<Edge> LoadEdge(const capnp::Edge::Reader &reader);
} // namespace distributed

View File

@ -1,39 +0,0 @@
#pragma once
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "communication/rpc/messages.hpp"
#include "io/network/endpoint.hpp"
#include "transactions/transaction.hpp"
namespace distributed {
using communication::rpc::Message;
using Endpoint = io::network::Endpoint;
struct GcClearedStatusReq : public Message {
GcClearedStatusReq() {}
GcClearedStatusReq(tx::TransactionId local_oldest_active, int worker_id)
: local_oldest_active(local_oldest_active), worker_id(worker_id) {}
tx::TransactionId local_oldest_active;
int worker_id;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<Message>(*this);
ar &local_oldest_active;
ar &worker_id;
}
};
RPC_NO_MEMBER_MESSAGE(GcClearedStatusRes);
using RanLocalGcRpc =
communication::rpc::RequestResponse<GcClearedStatusReq, GcClearedStatusRes>;
} // namespace distributed

View File

@ -0,0 +1,20 @@
#>cpp
#pragma once
#include "communication/rpc/messages.hpp"
#include "distributed/storage_gc_rpc_messages.capnp.h"
#include "io/network/endpoint.hpp"
#include "transactions/transaction.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:define-rpc ran-local-gc
(:request
((local-oldest-active "tx::TransactionId" :capnp-type "UInt64")
(worker-id :int16_t)))
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -1,16 +0,0 @@
#pragma once
#include <memory>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/serialization.hpp"
namespace distributed {
RPC_NO_MEMBER_MESSAGE(TokenTransferReq);
RPC_NO_MEMBER_MESSAGE(TokenTransferRes);
using TokenTransferRpc =
communication::rpc::RequestResponse<TokenTransferReq, TokenTransferRes>;
} // namespace distributed

View File

@ -0,0 +1,20 @@
#>cpp
#pragma once
#include <memory>
#include <string>
#include "communication/rpc/messages.hpp"
#include "distributed/serialization.hpp"
#include "distributed/token_sharing_rpc_messages.capnp.h"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:define-rpc token-transfer
(:request ())
(:response ()))
(lcp:pop-namespace) ;; distributed

View File

@ -29,10 +29,7 @@ class TokenSharingRpcServer {
clients_(clients),
dgp_(db) {
server_->Register<distributed::TokenTransferRpc>(
[this](const distributed::TokenTransferReq &req) {
token_ = true;
return std::make_unique<distributed::TokenTransferRes>();
});
[this](const auto &req_reader, auto *res_builder) { token_ = true; });
runner_ = std::thread([this]() {
while (true) {

View File

@ -72,11 +72,10 @@ class WorkerTransactionalCacheCleaner : public TransactionalCacheCleaner {
rpc_server_(server),
produce_server_(produce_server) {
Register(tx_engine);
rpc_server_.Register<WaitOnTransactionEndRpc>(
[this](const WaitOnTransactionEndReq &req) {
produce_server_.FinishAndClearOngoingProducePlans(req.member);
return std::make_unique<WaitOnTransactionEndRes>();
});
rpc_server_.Register<WaitOnTransactionEndRpc>([this](const auto &req_reader,
auto *res_builder) {
produce_server_.FinishAndClearOngoingProducePlans(req_reader.getMember());
});
}
private:

View File

@ -1,13 +0,0 @@
#pragma once
#include "communication/rpc/messages.hpp"
#include "transactions/type.hpp"
namespace distributed {
RPC_SINGLE_MEMBER_MESSAGE(WaitOnTransactionEndReq, tx::TransactionId);
RPC_NO_MEMBER_MESSAGE(WaitOnTransactionEndRes);
using WaitOnTransactionEndRpc =
communication::rpc::RequestResponse<WaitOnTransactionEndReq,
WaitOnTransactionEndRes>;
};

View File

@ -0,0 +1,17 @@
#>cpp
#pragma once
#include "distributed/transactional_cache_cleaner_rpc_messages.capnp.h"
#include "communication/rpc/messages.hpp"
#include "transactions/type.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:define-rpc wait-on-transaction-end
(:request ((member "tx::TransactionId" :capnp-type "UInt64")))
(:response ()))
(lcp:pop-namespace)

View File

@ -50,7 +50,6 @@ storage::EdgeAddress UpdatesRpcClients::CreateEdge(
tx::TransactionId tx_id, VertexAccessor &from, VertexAccessor &to,
storage::EdgeType edge_type) {
CHECK(from.address().is_remote()) << "In CreateEdge `from` must be remote";
int from_worker = from.address().worker_id();
auto res = worker_clients_.GetClientPool(from_worker)
.Call<CreateEdgeRpc>(CreateEdgeReqData{

View File

@ -1,203 +0,0 @@
#pragma once
#include <unordered_map>
#include "boost/serialization/vector.hpp"
#include "communication/rpc/messages.hpp"
#include "database/state_delta.hpp"
#include "storage/address_types.hpp"
#include "storage/gid.hpp"
#include "transactions/type.hpp"
#include "utils/serialization.hpp"
namespace distributed {
/// The result of sending or applying a deferred update to a worker.
enum class UpdateResult {
DONE,
SERIALIZATION_ERROR,
LOCK_TIMEOUT_ERROR,
UPDATE_DELETED_ERROR,
UNABLE_TO_DELETE_VERTEX_ERROR
};
RPC_SINGLE_MEMBER_MESSAGE(UpdateReq, database::StateDelta);
RPC_SINGLE_MEMBER_MESSAGE(UpdateRes, UpdateResult);
using UpdateRpc = communication::rpc::RequestResponse<UpdateReq, UpdateRes>;
RPC_SINGLE_MEMBER_MESSAGE(UpdateApplyReq, tx::TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(UpdateApplyRes, UpdateResult);
using UpdateApplyRpc =
communication::rpc::RequestResponse<UpdateApplyReq, UpdateApplyRes>;
struct CreateResult {
UpdateResult result;
// Only valid if creation was successful.
gid::Gid gid;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &result;
ar &gid;
}
};
struct CreateVertexReqData {
tx::TransactionId tx_id;
std::vector<storage::Label> labels;
std::unordered_map<storage::Property, query::TypedValue> properties;
private:
friend class boost::serialization::access;
template <class TArchive>
void save(TArchive &ar, unsigned int) const {
ar << tx_id;
ar << labels;
ar << properties.size();
for (auto &kv : properties) {
ar << kv.first;
utils::SaveTypedValue(ar, kv.second);
}
}
template <class TArchive>
void load(TArchive &ar, unsigned int) {
ar >> tx_id;
ar >> labels;
size_t props_size;
ar >> props_size;
for (size_t i = 0; i < props_size; ++i) {
storage::Property p;
ar >> p;
query::TypedValue tv;
utils::LoadTypedValue(ar, tv);
properties.emplace(p, std::move(tv));
}
}
BOOST_SERIALIZATION_SPLIT_MEMBER()
};
RPC_SINGLE_MEMBER_MESSAGE(CreateVertexReq, CreateVertexReqData);
RPC_SINGLE_MEMBER_MESSAGE(CreateVertexRes, CreateResult);
using CreateVertexRpc =
communication::rpc::RequestResponse<CreateVertexReq, CreateVertexRes>;
struct CreateEdgeReqData {
gid::Gid from;
storage::VertexAddress to;
storage::EdgeType edge_type;
tx::TransactionId tx_id;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &from;
ar &to;
ar &edge_type;
ar &tx_id;
}
};
RPC_SINGLE_MEMBER_MESSAGE(CreateEdgeReq, CreateEdgeReqData);
RPC_SINGLE_MEMBER_MESSAGE(CreateEdgeRes, CreateResult);
using CreateEdgeRpc =
communication::rpc::RequestResponse<CreateEdgeReq, CreateEdgeRes>;
struct AddInEdgeReqData {
storage::VertexAddress from;
storage::EdgeAddress edge_address;
gid::Gid to;
storage::EdgeType edge_type;
tx::TransactionId tx_id;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &from;
ar &edge_address;
ar &to;
ar &edge_type;
ar &tx_id;
}
};
RPC_SINGLE_MEMBER_MESSAGE(AddInEdgeReq, AddInEdgeReqData);
RPC_SINGLE_MEMBER_MESSAGE(AddInEdgeRes, UpdateResult);
using AddInEdgeRpc =
communication::rpc::RequestResponse<AddInEdgeReq, AddInEdgeRes>;
struct RemoveVertexReqData {
gid::Gid gid;
tx::TransactionId tx_id;
bool check_empty;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &gid;
ar &tx_id;
ar &check_empty;
}
};
RPC_SINGLE_MEMBER_MESSAGE(RemoveVertexReq, RemoveVertexReqData);
RPC_SINGLE_MEMBER_MESSAGE(RemoveVertexRes, UpdateResult);
using RemoveVertexRpc =
communication::rpc::RequestResponse<RemoveVertexReq, RemoveVertexRes>;
struct RemoveEdgeData {
tx::TransactionId tx_id;
gid::Gid edge_id;
gid::Gid vertex_from_id;
storage::VertexAddress vertex_to_address;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &tx_id;
ar &edge_id;
ar &vertex_from_id;
ar &vertex_to_address;
}
};
RPC_SINGLE_MEMBER_MESSAGE(RemoveEdgeReq, RemoveEdgeData);
RPC_SINGLE_MEMBER_MESSAGE(RemoveEdgeRes, UpdateResult);
using RemoveEdgeRpc =
communication::rpc::RequestResponse<RemoveEdgeReq, RemoveEdgeRes>;
struct RemoveInEdgeData {
tx::TransactionId tx_id;
gid::Gid vertex;
storage::EdgeAddress edge_address;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &tx_id;
ar &vertex;
ar &edge_address;
}
};
RPC_SINGLE_MEMBER_MESSAGE(RemoveInEdgeReq, RemoveInEdgeData);
RPC_SINGLE_MEMBER_MESSAGE(RemoveInEdgeRes, UpdateResult);
using RemoveInEdgeRpc =
communication::rpc::RequestResponse<RemoveInEdgeReq, RemoveInEdgeRes>;
} // namespace distributed

View File

@ -0,0 +1,187 @@
#>cpp
#pragma once
#include <unordered_map>
#include "communication/rpc/messages.hpp"
#include "database/state_delta.hpp"
#include "distributed/updates_rpc_messages.capnp.h"
#include "storage/address_types.hpp"
#include "storage/gid.hpp"
#include "transactions/type.hpp"
#include "utils/serialization.hpp"
cpp<#
(lcp:namespace distributed)
(lcp:capnp-namespace "distributed")
(lcp:capnp-import 'db "/database/state_delta.capnp")
(lcp:capnp-import 'dis "/distributed/serialization.capnp")
(lcp:capnp-import 'storage "/storage/serialization.capnp")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:capnp-type-conversion "tx::TransactionId" "UInt64")
(lcp:capnp-type-conversion "gid::Gid" "UInt64")
(lcp:capnp-type-conversion "storage::Label" "Storage.Common")
(lcp:capnp-type-conversion "storage::EdgeType" "Storage.Common")
(lcp:capnp-type-conversion "storage::Property" "Storage.Common")
(lcp:capnp-type-conversion "storage::EdgeAddress" "Storage.Address")
(lcp:capnp-type-conversion "storage::VertexAddress" "Storage.Address")
(lcp:define-enum update-result
(done
serialization-error
lock-timeout-error
update-deleted-error
unable-to-delete-vertex-error)
(:documentation "The result of sending or applying a deferred update to a worker.")
(:serialize))
(lcp:define-rpc update
(:request ((member "database::StateDelta" :capnp-type "Db.StateDelta")))
(:response ((member "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult")))))
(lcp:define-rpc update-apply
(:request ((member "tx::TransactionId")))
(:response ((member "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult")))))
(lcp:define-struct create-result ()
((result "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult"))
(gid "gid::Gid" :documentation "Only valid if creation was successful."))
(:serialize :boost :capnp))
(lcp:define-struct create-vertex-req-data ()
((tx-id "tx::TransactionId")
(labels "std::vector<storage::Label>"
:capnp-save (lcp:capnp-save-vector "storage::capnp::Common" "storage::Label")
:capnp-load (lcp:capnp-load-vector "storage::capnp::Common" "storage::Label"))
(properties "std::unordered_map<storage::Property, query::TypedValue>"
:save-fun
#>cpp
ar << properties.size();
for (auto &kv : properties) {
ar << kv.first;
utils::SaveTypedValue(ar, kv.second);
}
cpp<#
:load-fun
#>cpp
size_t props_size;
ar >> props_size;
for (size_t i = 0; i < props_size; ++i) {
storage::Property p;
ar >> p;
query::TypedValue tv;
utils::LoadTypedValue(ar, tv);
properties.emplace(p, std::move(tv));
}
cpp<#
:capnp-type "Utils.Map(Storage.Common, Dis.TypedValue)"
:capnp-save
(lambda (builder member)
#>cpp
utils::SaveMap<storage::capnp::Common, capnp::TypedValue>(
${member}, &${builder},
[](auto *builder, const auto &entry) {
auto key_builder = builder->initKey();
entry.first.Save(&key_builder);
auto value_builder = builder->initValue();
utils::SaveCapnpTypedValue(entry.second, &value_builder);
});
cpp<#)
:capnp-load
(lambda (reader member)
#>cpp
utils::LoadMap<storage::capnp::Common, capnp::TypedValue>(
&${member}, ${reader},
[](const auto &reader) {
storage::Property prop;
prop.Load(reader.getKey());
query::TypedValue value;
utils::LoadCapnpTypedValue(reader.getValue(), &value);
return std::make_pair(prop, value);
});
cpp<#)))
(:serialize :capnp))
(lcp:define-rpc create-vertex
(:request ((member "CreateVertexReqData")))
(:response ((member "CreateResult"))))
(lcp:define-struct create-edge-req-data ()
((from "gid::Gid")
(to "storage::VertexAddress")
(edge-type "storage::EdgeType")
(tx-id "tx::TransactionId"))
(:serialize :capnp))
(lcp:define-rpc create-edge
(:request ((member "CreateEdgeReqData")))
(:response ((member "CreateResult"))))
(lcp:define-struct add-in-edge-req-data ()
((from "storage::VertexAddress")
(edge-address "storage::EdgeAddress")
(to "gid::Gid")
(edge-type "storage::EdgeType")
(tx-id "tx::TransactionId"))
(:serialize :capnp))
(lcp:define-rpc add-in-edge
(:request ((member "AddInEdgeReqData")))
(:response ((member "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult")))))
(lcp:define-struct remove-vertex-req-data ()
((gid "gid::Gid")
(tx-id "tx::TransactionId")
(check-empty :bool))
(:serialize :capnp))
(lcp:define-rpc remove-vertex
(:request ((member "RemoveVertexReqData")))
(:response ((member "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult")))))
(lcp:define-struct remove-edge-data ()
((tx-id "tx::TransactionId")
(edge-id "gid::Gid")
(vertex-from-id "gid::Gid")
(vertex-to-address "storage::VertexAddress"))
(:serialize :capnp))
(lcp:define-rpc remove-edge
(:request ((member "RemoveEdgeData")))
(:response ((member "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult")))))
(lcp:define-struct remove-in-edge-data ()
((tx-id "tx::TransactionId")
(vertex "gid::Gid")
(edge-address "storage::EdgeAddress"))
(:serialize :capnp))
(lcp:define-rpc remove-in-edge
(:request ((member "RemoveInEdgeData")))
(:response ((member "UpdateResult"
:capnp-init nil
:capnp-save (lcp:capnp-save-enum "capnp::UpdateResult" "UpdateResult")
:capnp-load (lcp:capnp-load-enum "capnp::UpdateResult" "UpdateResult")))))
(lcp:pop-namespace) ;; distributed

View File

@ -175,7 +175,9 @@ UpdateResult UpdatesRpcServer::TransactionUpdates<TRecordAccessor>::Apply() {
UpdatesRpcServer::UpdatesRpcServer(database::GraphDb &db,
communication::rpc::Server &server)
: db_(db) {
server.Register<UpdateRpc>([this](const UpdateReq &req) {
server.Register<UpdateRpc>([this](const auto &req_reader, auto *res_builder) {
UpdateReq req;
req.Load(req_reader);
using DeltaType = database::StateDelta::Type;
auto &delta = req.member;
switch (delta.type) {
@ -183,74 +185,106 @@ UpdatesRpcServer::UpdatesRpcServer(database::GraphDb &db,
case DeltaType::ADD_LABEL:
case DeltaType::REMOVE_LABEL:
case database::StateDelta::Type::REMOVE_OUT_EDGE:
case database::StateDelta::Type::REMOVE_IN_EDGE:
return std::make_unique<UpdateRes>(
case database::StateDelta::Type::REMOVE_IN_EDGE: {
UpdateRes res(
GetUpdates(vertex_updates_, delta.transaction_id).Emplace(delta));
case DeltaType::SET_PROPERTY_EDGE:
return std::make_unique<UpdateRes>(
res.Save(res_builder);
return;
}
case DeltaType::SET_PROPERTY_EDGE: {
UpdateRes res(
GetUpdates(edge_updates_, delta.transaction_id).Emplace(delta));
res.Save(res_builder);
return;
}
default:
LOG(FATAL) << "Can't perform a remote update with delta type: "
<< static_cast<int>(req.member.type);
}
});
server.Register<UpdateApplyRpc>([this](const UpdateApplyReq &req) {
return std::make_unique<UpdateApplyRes>(Apply(req.member));
});
server.Register<UpdateApplyRpc>(
[this](const auto &req_reader, auto *res_builder) {
UpdateApplyReq req;
req.Load(req_reader);
UpdateApplyRes res(Apply(req.member));
res.Save(res_builder);
});
server.Register<CreateVertexRpc>([this](const CreateVertexReq &req) {
server.Register<CreateVertexRpc>([this](const auto &req_reader,
auto *res_builder) {
CreateVertexReq req;
req.Load(req_reader);
gid::Gid gid = GetUpdates(vertex_updates_, req.member.tx_id)
.CreateVertex(req.member.labels, req.member.properties);
return std::make_unique<CreateVertexRes>(
CreateResult{UpdateResult::DONE, gid});
CreateVertexRes res(CreateResult{UpdateResult::DONE, gid});
res.Save(res_builder);
});
server.Register<CreateEdgeRpc>([this](const CreateEdgeReq &req) {
server.Register<CreateEdgeRpc>(
[this](const auto &req_reader, auto *res_builder) {
CreateEdgeReq req;
req.Load(req_reader);
auto data = req.member;
auto creation_result = CreateEdge(data);
// If `from` and `to` are both on this worker, we handle it in this
// RPC call. Do it only if CreateEdge succeeded.
if (creation_result.result == UpdateResult::DONE &&
data.to.worker_id() == db_.WorkerId()) {
auto to_delta = database::StateDelta::AddInEdge(
data.tx_id, data.to.gid(), {data.from, db_.WorkerId()},
{creation_result.gid, db_.WorkerId()}, data.edge_type);
creation_result.result =
GetUpdates(vertex_updates_, data.tx_id).Emplace(to_delta);
}
CreateEdgeRes res(creation_result);
res.Save(res_builder);
});
server.Register<AddInEdgeRpc>(
[this](const auto &req_reader, auto *res_builder) {
AddInEdgeReq req;
req.Load(req_reader);
auto to_delta = database::StateDelta::AddInEdge(
req.member.tx_id, req.member.to, req.member.from,
req.member.edge_address, req.member.edge_type);
auto result =
GetUpdates(vertex_updates_, req.member.tx_id).Emplace(to_delta);
AddInEdgeRes res(result);
res.Save(res_builder);
});
server.Register<RemoveVertexRpc>(
[this](const auto &req_reader, auto *res_builder) {
RemoveVertexReq req;
req.Load(req_reader);
auto to_delta = database::StateDelta::RemoveVertex(
req.member.tx_id, req.member.gid, req.member.check_empty);
auto result =
GetUpdates(vertex_updates_, req.member.tx_id).Emplace(to_delta);
RemoveVertexRes res(result);
res.Save(res_builder);
});
server.Register<RemoveEdgeRpc>(
[this](const auto &req_reader, auto *res_builder) {
RemoveEdgeReq req;
req.Load(req_reader);
RemoveEdgeRes res(RemoveEdge(req.member));
res.Save(res_builder);
});
server.Register<RemoveInEdgeRpc>([this](const auto &req_reader,
auto *res_builder) {
RemoveInEdgeReq req;
req.Load(req_reader);
auto data = req.member;
auto creation_result = CreateEdge(data);
// If `from` and `to` are both on this worker, we handle it in this
// RPC call. Do it only if CreateEdge succeeded.
if (creation_result.result == UpdateResult::DONE &&
data.to.worker_id() == db_.WorkerId()) {
auto to_delta = database::StateDelta::AddInEdge(
data.tx_id, data.to.gid(), {data.from, db_.WorkerId()},
{creation_result.gid, db_.WorkerId()}, data.edge_type);
creation_result.result =
GetUpdates(vertex_updates_, data.tx_id).Emplace(to_delta);
}
return std::make_unique<CreateEdgeRes>(creation_result);
});
server.Register<AddInEdgeRpc>([this](const AddInEdgeReq &req) {
auto to_delta = database::StateDelta::AddInEdge(
req.member.tx_id, req.member.to, req.member.from,
req.member.edge_address, req.member.edge_type);
auto result =
GetUpdates(vertex_updates_, req.member.tx_id).Emplace(to_delta);
return std::make_unique<AddInEdgeRes>(result);
});
server.Register<RemoveVertexRpc>([this](const RemoveVertexReq &req) {
auto to_delta = database::StateDelta::RemoveVertex(
req.member.tx_id, req.member.gid, req.member.check_empty);
auto result =
GetUpdates(vertex_updates_, req.member.tx_id).Emplace(to_delta);
return std::make_unique<RemoveVertexRes>(result);
});
server.Register<RemoveEdgeRpc>([this](const RemoveEdgeReq &req) {
return std::make_unique<RemoveEdgeRes>(RemoveEdge(req.member));
});
server.Register<RemoveInEdgeRpc>([this](const RemoveInEdgeReq &req) {
auto data = req.member;
return std::make_unique<RemoveInEdgeRes>(
GetUpdates(vertex_updates_, data.tx_id)
.Emplace(database::StateDelta::RemoveInEdge(data.tx_id, data.vertex,
data.edge_address)));
RemoveInEdgeRes res(GetUpdates(vertex_updates_, data.tx_id)
.Emplace(database::StateDelta::RemoveInEdge(
data.tx_id, data.vertex, data.edge_address)));
res.Save(res_builder);
});
}

View File

@ -0,0 +1,9 @@
@0xb3d70bc0576218f3;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("durability::capnp");
struct RecoveryInfo {
snapshotTxId @0 :UInt64;
maxWalTxId @1 :UInt64;
}

View File

@ -1,5 +1,6 @@
#include "durability/recovery.hpp"
#include <experimental/filesystem>
#include <limits>
#include <unordered_map>
@ -16,6 +17,8 @@
#include "transactions/type.hpp"
#include "utils/algorithm.hpp"
namespace fs = std::experimental::filesystem;
namespace durability {
bool ReadSnapshotSummary(HashedFileReader &buffer, int64_t &vertex_count,

View File

@ -1,16 +1,14 @@
#pragma once
#include <experimental/filesystem>
#include <experimental/optional>
#include <unordered_map>
#include "database/graph_db.hpp"
#include "durability/hashed_file_reader.hpp"
#include "durability/recovery.capnp.h"
#include "storage/vertex_accessor.hpp"
#include "transactions/type.hpp"
namespace fs = std::experimental::filesystem;
namespace durability {
/// Stores info on what was (or needs to be) recovered from durability.
@ -28,6 +26,16 @@ struct RecoveryInfo {
}
bool operator!=(const RecoveryInfo &other) const { return !(*this == other); }
void Save(capnp::RecoveryInfo::Builder *builder) const {
builder->setSnapshotTxId(snapshot_tx_id);
builder->setMaxWalTxId(max_wal_tx_id);
}
void Load(const capnp::RecoveryInfo::Reader &reader) {
snapshot_tx_id = reader.getSnapshotTxId();
max_wal_tx_id = reader.getMaxWalTxId();
}
private:
friend class boost::serialization::access;

View File

@ -4,7 +4,29 @@ set(io_src_files
network/socket.cpp
network/utils.cpp)
# Use this function to add each capnp file to generation. This way each file is
# standalone and we avoid recompiling everything.
# NOTE: io_src_files and io_capnp_files are globally updated.
# TODO: This is duplicated from src/CMakeLists.txt, find a good way to
# generalize this on per subdirectory basis.
function(add_capnp capnp_src_file)
set(cpp_file ${CMAKE_CURRENT_SOURCE_DIR}/${capnp_src_file}.c++)
set(h_file ${CMAKE_CURRENT_SOURCE_DIR}/${capnp_src_file}.h)
add_custom_command(OUTPUT ${cpp_file} ${h_file}
COMMAND ${CAPNP_EXE} compile -o${CAPNP_CXX_EXE} ${capnp_src_file} -I ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${capnp_src_file} capnproto-proj
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
# Update *global* io_capnp_files
set(io_capnp_files ${io_capnp_files} ${cpp_file} ${h_file} PARENT_SCOPE)
# Update *global* io_src_files
set(io_src_files ${io_src_files} ${cpp_file} PARENT_SCOPE)
endfunction(add_capnp)
add_capnp(network/endpoint.capnp)
add_custom_target(generate_io_capnp DEPENDS ${io_capnp_files})
add_library(mg-io STATIC ${io_src_files})
target_link_libraries(mg-io stdc++fs Threads::Threads fmt glog mg-utils)
# TODO: Remove this dependency when we switch to capnp
target_link_libraries(mg-io ${Boost_SERIALIZATION_LIBRARY_RELEASE})
target_link_libraries(mg-io capnp kj)
add_dependencies(mg-io generate_io_capnp)

View File

@ -0,0 +1,10 @@
@0x93c2449a1e02365a;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("io::network::capnp");
struct Endpoint {
address @0 :Text;
port @1 :UInt16;
family @2 :UInt8;
}

View File

@ -24,6 +24,18 @@ Endpoint::Endpoint(const std::string &address, uint16_t port)
CHECK(family_ != 0) << "Not a valid IPv4 or IPv6 address: " << address;
}
void Endpoint::Save(capnp::Endpoint::Builder *builder) const {
builder->setAddress(address_);
builder->setPort(port_);
builder->setFamily(family_);
}
void Endpoint::Load(const capnp::Endpoint::Reader &reader) {
address_ = reader.getAddress();
port_ = reader.getPort();
family_ = reader.getFamily();
}
bool Endpoint::operator==(const Endpoint &other) const {
return address_ == other.address_ && port_ == other.port_ &&
family_ == other.family_;

View File

@ -5,8 +5,7 @@
#include <iostream>
#include <string>
#include "boost/serialization/access.hpp"
#include "io/network/endpoint.capnp.h"
#include "utils/exceptions.hpp"
namespace io::network {
@ -28,16 +27,10 @@ class Endpoint {
bool operator==(const Endpoint &other) const;
friend std::ostream &operator<<(std::ostream &os, const Endpoint &endpoint);
void Save(capnp::Endpoint::Builder *builder) const;
void Load(const capnp::Endpoint::Reader &reader);
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &address_;
ar &port_;
ar &family_;
}
std::string address_;
uint16_t port_{0};
unsigned char family_{0};

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,8 @@
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("query::capnp");
using Utils = import "/utils/serialization.capnp";
using Storage = import "/storage/types.capnp";
using Dis = import "/distributed/serialization.capnp";
using Storage = import "/storage/serialization.capnp";
using Symbols = import "/query/frontend/semantic/symbol.capnp";
struct Tree {
@ -227,7 +227,7 @@ struct BaseLiteral {
struct PrimitiveLiteral {
tokenPosition @0 :Int32;
value @1 :Utils.TypedValue;
value @1 :Dis.TypedValue;
}
struct ListLiteral {

View File

@ -249,7 +249,7 @@ void PrimitiveLiteral::Save(capnp::BaseLiteral::Builder *base_literal_builder,
auto primitive_literal_builder = base_literal_builder->initPrimitiveLiteral();
primitive_literal_builder.setTokenPosition(token_position_);
auto typed_value_builder = primitive_literal_builder.getValue();
utils::SaveCapnpTypedValue(value_, typed_value_builder);
utils::SaveCapnpTypedValue(value_, &typed_value_builder);
}
void PrimitiveLiteral::Load(const capnp::Tree::Reader &reader,
@ -259,7 +259,7 @@ void PrimitiveLiteral::Load(const capnp::Tree::Reader &reader,
auto pl_reader =
reader.getExpression().getBaseLiteral().getPrimitiveLiteral();
auto typed_value_reader = pl_reader.getValue();
utils::LoadCapnpTypedValue(value_, typed_value_reader);
utils::LoadCapnpTypedValue(typed_value_reader, &value_);
token_position_ = pl_reader.getTokenPosition();
}

View File

@ -19,3 +19,13 @@ struct Symbol {
userDeclared @3 :Bool;
tokenPosition @4 :Int32;
}
struct SymbolTable {
position @0 :Int32;
table @1 :List(Entry);
struct Entry {
key @0 :Int32;
val @1 :Symbol;
}
}

View File

@ -7,6 +7,7 @@
#include "boost/serialization/serialization.hpp"
#include "query/frontend/ast/ast.hpp"
#include "query/frontend/semantic/symbol.capnp.h"
#include "query/frontend/semantic/symbol.hpp"
namespace query {
@ -30,6 +31,29 @@ class SymbolTable final {
const auto &table() const { return table_; }
void Save(capnp::SymbolTable::Builder *builder) const {
builder->setPosition(position_);
auto list_builder = builder->initTable(table_.size());
size_t i = 0;
for (const auto &entry : table_) {
auto entry_builder = list_builder[i++];
entry_builder.setKey(entry.first);
auto sym_builder = entry_builder.initVal();
entry.second.Save(&sym_builder);
}
}
void Load(const capnp::SymbolTable::Reader &reader) {
position_ = reader.getPosition();
table_.clear();
for (const auto &entry_reader : reader.getTable()) {
int key = entry_reader.getKey();
Symbol val;
val.Load(entry_reader.getVal());
table_[key] = val;
}
}
private:
int position_{0};
std::map<int, Symbol> table_;

View File

@ -1345,10 +1345,10 @@ class DistributedExpandBfsCursor : public query::plan::Cursor {
int current_depth_{-1};
// Map from worker IDs to their corresponding subcursors.
std::unordered_map<int, int64_t> subcursor_ids_;
std::unordered_map<int16_t, int64_t> subcursor_ids_;
// Next worker master should try pulling from.
std::unordered_map<int, int64_t>::iterator pull_pos_;
std::unordered_map<int16_t, int64_t>::iterator pull_pos_;
};
class ExpandWeightedShortestPathCursor : public query::plan::Cursor {

View File

@ -140,7 +140,7 @@ cpp<#
(lcp:capnp-namespace "query::plan")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:capnp-import 'storage "/storage/types.capnp")
(lcp:capnp-import 'storage "/storage/serialization.capnp")
(lcp:capnp-import 'ast "/query/frontend/ast/ast.capnp")
(lcp:capnp-import 'semantic "/query/frontend/semantic/symbol.capnp")
(lcp:capnp-import 'common "/query/common.capnp")
@ -274,73 +274,6 @@ cpp<#
LoadPointers(${archive}, ${member-name});
cpp<#)
(defun capnp-save-vector (capnp-type cpp-type &optional lambda-code)
(let ((lambda-code (if lambda-code
lambda-code
"[](auto *builder, const auto &val) { val.Save(builder); }")))
(lambda (builder member-name)
#>cpp
utils::SaveVector<${capnp-type}, ${cpp-type}>(${member-name}, &${builder}, ${lambda-code});
cpp<#)))
(defun capnp-load-vector (capnp-type cpp-type &optional lambda-code)
(let ((lambda-code (if lambda-code
lambda-code
(format nil
"[](const auto &reader) { ~A val; val.Load(reader); return val; }"
cpp-type))))
(lambda (reader member-name)
#>cpp
utils::LoadVector<${capnp-type}, ${cpp-type}>(&${member-name}, ${reader}, ${lambda-code});
cpp<#)))
(defun capnp-save-optional (capnp-type cpp-type &optional lambda-code)
(let ((lambda-code (if lambda-code
lambda-code
"[](auto *builder, const auto &val) { val.Save(builder); }")))
(lambda (builder member)
#>cpp
utils::SaveOptional<${capnp-type}, ${cpp-type}>(${member}, &${builder}, ${lambda-code});
cpp<#)))
(defun capnp-load-optional (capnp-type cpp-type &optional lambda-code)
(let ((lambda-code (if lambda-code
lambda-code
(format nil
"[](const auto &reader) { ~A val; val.Load(reader); return val; }"
cpp-type))))
(lambda (reader member)
#>cpp
${member} = utils::LoadOptional<${capnp-type}, ${cpp-type}>(${reader}, ${lambda-code});
cpp<#)))
(defun capnp-save-enum (capnp-type cpp-type enum-values)
(lambda (builder member)
(let* ((member-setter (remove #\_ (string-capitalize member)))
(cases (mapcar (lambda (value-symbol)
(let ((value (cl-ppcre:regex-replace-all "-" (string value-symbol) "_")))
#>cpp
case ${cpp-type}::${value}:
${builder}->set${member-setter}(${capnp-type}::${value});
break;
cpp<#))
enum-values)))
(format nil "switch (~A) {~%~{~A~%~}}" member (mapcar #'lcp::raw-cpp-string cases)))))
(defun capnp-load-enum (capnp-type cpp-type enum-values)
(lambda (reader member)
(let* ((member-getter (remove #\_ (string-capitalize member)))
(cases (mapcar (lambda (value-symbol)
(let ((value (cl-ppcre:regex-replace-all "-" (string value-symbol) "_")))
#>cpp
case ${capnp-type}::${value}:
${member} = ${cpp-type}::${value};
break;
cpp<#))
enum-values)))
(format nil "switch (~A.get~A()) {~%~{~A~%~}}"
reader member-getter (mapcar #'lcp::raw-cpp-string cases)))))
(defun save-ast-pointer (builder member)
(let ((member-getter (remove #\_ (string-capitalize member))))
#>cpp
@ -361,19 +294,19 @@ cpp<#
cpp<#)))
(defun save-ast-vector (ast-type)
(capnp-save-vector "::query::capnp::Tree" ast-type
"[helper](auto *builder, const auto &val) {
val->Save(builder, &helper->saved_ast_uids);
}"))
(lcp:capnp-save-vector "::query::capnp::Tree" ast-type
"[helper](auto *builder, const auto &val) {
val->Save(builder, &helper->saved_ast_uids);
}"))
(defun load-ast-vector (ast-type)
(capnp-load-vector "::query::capnp::Tree" ast-type
(format
nil
"[helper](const auto &reader) {
// We expect the unsafe downcast via static_cast to work.
return static_cast<~A>(helper->ast_storage.Load(reader, &helper->loaded_ast_uids));
}" ast-type)))
(lcp:capnp-load-vector "::query::capnp::Tree" ast-type
(format
nil
"[helper](const auto &reader) {
// We expect the unsafe downcast via static_cast to work.
return static_cast<~A>(helper->ast_storage.Load(reader, &helper->loaded_ast_uids));
}" ast-type)))
(defun save-operator-pointer (builder member-name)
#>cpp
@ -586,10 +519,10 @@ chained in cases when longer paths need creating.
(output-symbol "Symbol" :reader t :scope :protected)
(graph-view "GraphView" :reader t :scope :protected
:capnp-init nil
:capnp-save (capnp-save-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:capnp-load (capnp-load-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:capnp-save (lcp:capnp-save-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:capnp-load (lcp:capnp-load-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:documentation
"Controls which graph state is used to produce vertices.
@ -661,8 +594,8 @@ given label.
auto value_builder = builder->initValue();
bound.value()->Save(&value_builder, &helper->saved_ast_uids);
}"))
(funcall (capnp-save-optional "::utils::capnp::Bound<::query::capnp::Tree>" "Bound"
save-bound)
(funcall (lcp:capnp-save-optional "::utils::capnp::Bound<::query::capnp::Tree>" "Bound"
save-bound)
builder member)))
(defun load-optional-bound (reader member)
@ -673,8 +606,8 @@ given label.
auto *value = static_cast<Expression*>(helper->ast_storage.Load(reader.getValue(), &helper->loaded_ast_uids));
return Bound(value, type);
}"))
(funcall (capnp-load-optional "::utils::capnp::Bound<::query::capnp::Tree>" "Bound"
load-bound)
(funcall (lcp:capnp-load-optional "::utils::capnp::Bound<::query::capnp::Tree>" "Bound"
load-bound)
reader member)))
(lcp:define-class scan-all-by-label-property-range (scan-all)
@ -805,13 +738,13 @@ property value.
(edge-symbol "Symbol" :reader t :scope :protected)
(direction "EdgeAtom::Direction" :reader t :scope :protected
:capnp-type "Ast.EdgeAtom.Direction" :capnp-init nil
:capnp-save (capnp-save-enum "::query::capnp::EdgeAtom::Direction" "EdgeAtom::Direction"
'(in out both))
:capnp-load (capnp-load-enum "::query::capnp::EdgeAtom::Direction" "EdgeAtom::Direction"
'(in out both)))
:capnp-save (lcp:capnp-save-enum "::query::capnp::EdgeAtom::Direction" "EdgeAtom::Direction"
'(in out both))
:capnp-load (lcp:capnp-load-enum "::query::capnp::EdgeAtom::Direction" "EdgeAtom::Direction"
'(in out both)))
(edge-types "std::vector<storage::EdgeType>" :reader t :scope :protected
:capnp-save (capnp-save-vector "::storage::capnp::Common" "storage::EdgeType")
:capnp-load (capnp-load-vector "::storage::capnp::Common" "storage::EdgeType"))
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::EdgeType")
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::EdgeType"))
;; the input op and the symbol under which the op's result
;; can be found in the frame
(input "std::shared_ptr<LogicalOperator>" :scope :protected
@ -823,10 +756,10 @@ property value.
been expanded and should be just validated in the frame.")
(graph-view "GraphView" :reader t :scope :protected
:capnp-init nil
:capnp-save (capnp-save-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:capnp-load (capnp-load-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:capnp-save (lcp:capnp-save-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:capnp-load (lcp:capnp-load-enum "::query::capnp::GraphView" "GraphView"
'(old new))
:documentation
"from which state the input node should get expanded"))
(:documentation
@ -969,10 +902,10 @@ pulled.")
(lcp:define-class expand-variable (logical-operator expand-common)
((type "EdgeAtom::Type" :reader t :capnp-type "Ast.EdgeAtom.Type"
:capnp-init nil
:capnp-save (capnp-save-enum "::query::capnp::EdgeAtom::Type" "EdgeAtom::Type"
'(single depth-first breadth-first weighted-shortest-path))
:capnp-load (capnp-load-enum "::query::capnp::EdgeAtom::Type" "EdgeAtom::Type"
'(single depth-first breadth-first weighted-shortest-path)))
:capnp-save (lcp:capnp-save-enum "::query::capnp::EdgeAtom::Type" "EdgeAtom::Type"
'(single depth-first breadth-first weighted-shortest-path))
:capnp-load (lcp:capnp-load-enum "::query::capnp::EdgeAtom::Type" "EdgeAtom::Type"
'(single depth-first breadth-first weighted-shortest-path)))
(is-reverse :bool :documentation
"True if the path should be written as expanding from node_symbol to input_symbol.")
(lower-bound "Expression *" :save-fun #'save-pointer :load-fun #'load-pointer
@ -985,15 +918,15 @@ pulled.")
:documentation "Optional upper bound of the variable length expansion, defaults are (1, inf)")
(filter-lambda "Lambda")
(weight-lambda "std::experimental::optional<Lambda>"
:capnp-save (capnp-save-optional
:capnp-save (lcp:capnp-save-optional
"capnp::ExpandVariable::Lambda" "Lambda"
"[helper](auto *builder, const auto &val) { val.Save(builder, helper); }")
:capnp-load (capnp-load-optional
:capnp-load (lcp:capnp-load-optional
"capnp::ExpandVariable::Lambda" "Lambda"
"[helper](const auto &reader) { Lambda val; val.Load(reader, helper); return val; }"))
(total-weight "std::experimental::optional<Symbol>"
:capnp-save (capnp-save-optional "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-optional "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-optional "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-optional "::query::capnp::Symbol" "Symbol")))
(:documentation
"Variable-length expansion operator. For a node existing in
the frame it expands a variable number of edges and places them
@ -1088,8 +1021,8 @@ pulled.")
:capnp-load #'load-operator-pointer)
(path-symbol "Symbol" :reader t)
(path-elements "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Constructs a named path from it's elements and places it on the frame.")
(:public
@ -1324,8 +1257,8 @@ can be stored (a TypedValue that can be converted to PropertyValue).")
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
:save-fun #'save-pointer :load-fun #'load-pointer)
(op "Op" :capnp-init nil
:capnp-save (capnp-save-enum "capnp::SetProperties::Op" "Op" '(update replace))
:capnp-load (capnp-load-enum "capnp::SetProperties::Op" "Op" '(update replace))))
:capnp-save (lcp:capnp-save-enum "capnp::SetProperties::Op" "Op")
:capnp-load (lcp:capnp-load-enum "capnp::SetProperties::Op" "Op")))
(:documentation
"Logical op for setting the whole properties set on a vertex or an edge.
@ -1336,12 +1269,13 @@ Supports setting (replacing the whole properties set with another) and
updating.")
(:public
(lcp:define-enum op
"Defines how setting the properties works.
(update replace)
(:documentation "Defines how setting the properties works.
@c UPDATE means that the current property set is augmented with additional
ones (existing props of the same name are replaced), while @c REPLACE means
that the old props are discarded and replaced with new ones."
update replace)
that the old props are discarded and replaced with new ones.")
(:serialize :capnp))
#>cpp
SetProperties(const std::shared_ptr<LogicalOperator> &input,
@ -1390,8 +1324,8 @@ that the old props are discarded and replaced with new ones."
:capnp-load #'load-operator-pointer)
(input-symbol "Symbol")
(labels "std::vector<storage::Label>"
:capnp-save (capnp-save-vector "::storage::capnp::Common" "storage::Label")
:capnp-load (capnp-load-vector "::storage::capnp::Common" "storage::Label")))
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
(:documentation
"Logical operator for setting an arbitrary number of labels on a Vertex.
@ -1478,8 +1412,8 @@ It does NOT remove labels that are already set on that Vertex.")
:capnp-load #'load-operator-pointer)
(input-symbol "Symbol")
(labels "std::vector<storage::Label>"
:capnp-save (capnp-save-vector "::storage::capnp::Common" "storage::Label")
:capnp-load (capnp-load-vector "::storage::capnp::Common" "storage::Label")))
:capnp-save (lcp:capnp-save-vector "::storage::capnp::Common" "storage::Label")
:capnp-load (lcp:capnp-load-vector "::storage::capnp::Common" "storage::Label")))
(:documentation
"Logical operator for removing an arbitrary number of labels on a Vertex.
@ -1522,8 +1456,8 @@ If a label does not exist on a Vertex, nothing happens.")
:capnp-load #'load-operator-pointer)
(expand-symbol "Symbol")
(previous-symbols "std::vector<Symbol>"
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Filter whose Pull returns true only when the given
expand_symbol frame value (the latest expansion) is not
@ -1585,8 +1519,8 @@ between edges and an edge lists).")
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol"))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
(advance-command :bool :reader t))
(:documentation
"Pulls everything from the input before passing it through.
@ -1667,10 +1601,10 @@ cpp<#
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(aggregations "std::vector<Element>" :reader t
:capnp-save (capnp-save-vector
:capnp-save (lcp:capnp-save-vector
"capnp::Aggregate::Element" "Element"
"[helper](auto *builder, const auto &val) { val.Save(builder, helper); }")
:capnp-load (capnp-load-vector
:capnp-load (lcp:capnp-load-vector
"capnp::Aggregate::Element" "Element"
"[helper](const auto &reader) { Element val; val.Load(reader, helper); return val; }"))
(group-by "std::vector<Expression *>" :reader t
@ -1679,8 +1613,8 @@ cpp<#
:capnp-load (load-ast-vector "Expression *")
:save-fun #'save-pointers :load-fun #'load-pointers)
(remember "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Performs an arbitrary number of aggregations of data
from the given input grouped by the given criteria.
@ -1706,10 +1640,10 @@ elements are in an undefined state after aggregation.")
:capnp-save #'save-ast-pointer :capnp-load (load-ast-pointer "Expression *")
:save-fun #'save-pointer :load-fun #'load-pointer)
(op "Aggregation::Op" :capnp-type "Ast.Aggregation.Op"
:capnp-init nil :capnp-save (capnp-save-enum "::query::capnp::Aggregation::Op" "Aggregation::Op"
'(count min max sum avg collect-list collect-map))
:capnp-load (capnp-load-enum "::query::capnp::Aggregation::Op" "Aggregation::Op"
'(count min max sum avg collect-list collect-map)))
:capnp-init nil :capnp-save (lcp:capnp-save-enum "::query::capnp::Aggregation::Op" "Aggregation::Op"
'(count min max sum avg collect-list collect-map))
:capnp-load (lcp:capnp-load-enum "::query::capnp::Aggregation::Op" "Aggregation::Op"
'(count min max sum avg collect-list collect-map)))
(output-sym "Symbol"))
(:documentation
"An aggregation element, contains:
@ -1949,8 +1883,8 @@ input should be performed).")
:capnp-load (load-ast-vector "Expression *")
:save-fun #'save-pointers :load-fun #'load-pointers)
(output-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Logical operator for ordering (sorting) results.
@ -2080,8 +2014,8 @@ documentation.")
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(optional-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Optional operator. Used for optional match. For every
successful Pull from the input branch a Pull from the optional
@ -2186,8 +2120,8 @@ Input is optional (unwind can be the first clause in a query).")
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(value-symbols "std::vector<Symbol>"
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Ensures that only distinct rows are yielded.
This implementation accepts a vector of Symbols
@ -2272,14 +2206,14 @@ case the index already exists, nothing happens.")
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(union-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol"))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
(left-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol"))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
(right-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"A logical operator that applies UNION operator on inputs and places the
result on the frame.
@ -2326,8 +2260,8 @@ vectors of symbols used by each of the inputs.")
:capnp-load #'load-operator-pointer)
(plan-id :int64_t :initval 0 :reader t)
(symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"An operator in distributed Memgraph that yields both local and remote (from
other workers) frames. Obtaining remote frames is done through RPC calls to
@ -2424,14 +2358,14 @@ Logic of the synchronize operator is:
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(left-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol"))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
(right-op "std::shared_ptr<LogicalOperator>" :reader t
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(right-symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol")))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol")))
(:documentation
"Operator for producing a Cartesian product from 2 input branches")
(:public
@ -2464,8 +2398,8 @@ Logic of the synchronize operator is:
:capnp-load #'load-operator-pointer)
(plan-id :int64_t :initval 0 :reader t)
(symbols "std::vector<Symbol>" :reader t
:capnp-save (capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (capnp-load-vector "::query::capnp::Symbol" "Symbol"))
:capnp-save (lcp:capnp-save-vector "::query::capnp::Symbol" "Symbol")
:capnp-load (lcp:capnp-load-vector "::query::capnp::Symbol" "Symbol"))
(order-by "std::vector<Expression *>" :reader t
:capnp-type "List(Ast.Tree)"
:capnp-save (save-ast-vector "Expression *")

View File

@ -1,62 +0,0 @@
#pragma once
#include "boost/serialization/access.hpp"
#include "boost/serialization/base_object.hpp"
#include "boost/serialization/string.hpp"
#include "boost/serialization/utility.hpp"
#include "boost/serialization/vector.hpp"
#include "communication/rpc/messages.hpp"
#include "utils/timestamp.hpp"
namespace stats {
struct StatsReq : public communication::rpc::Message {
StatsReq() {}
StatsReq(std::string metric_path,
std::vector<std::pair<std::string, std::string>> tags, double value)
: metric_path(metric_path),
tags(tags),
value(value),
timestamp(utils::Timestamp::Now().SecSinceTheEpoch()) {}
std::string metric_path;
std::vector<std::pair<std::string, std::string>> tags;
double value;
uint64_t timestamp;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &metric_path &tags &value &timestamp;
}
};
RPC_NO_MEMBER_MESSAGE(StatsRes);
struct BatchStatsReq : public communication::rpc::Message {
BatchStatsReq() {}
explicit BatchStatsReq(std::vector<StatsReq> requests) : requests(requests) {}
std::vector<StatsReq> requests;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &boost::serialization::base_object<communication::rpc::Message>(*this);
ar &requests;
}
};
RPC_NO_MEMBER_MESSAGE(BatchStatsRes);
using StatsRpc = communication::rpc::RequestResponse<StatsReq, StatsRes>;
using BatchStatsRpc =
communication::rpc::RequestResponse<BatchStatsReq, BatchStatsRes>;
} // namespace stats

View File

@ -0,0 +1,50 @@
#>cpp
#pragma once
#include "communication/rpc/messages.hpp"
#include "stats/stats_rpc_messages.capnp.h"
#include "utils/serialization.hpp"
#include "utils/timestamp.hpp"
cpp<#
(lcp:namespace stats)
(lcp:capnp-namespace "stats")
(lcp:capnp-import 'utils "/utils/serialization.capnp")
(lcp:define-rpc stats
(:request
((metric-path "std::string")
(tags "std::vector<std::pair<std::string, std::string>>"
:capnp-type "List(Utils.Pair(Text, Text))"
:capnp-save
(lcp:capnp-save-vector
"utils::capnp::Pair<::capnp::Text, ::capnp::Text>"
"std::pair<std::string, std::string>"
"[](auto *builder, const auto &pair) {
builder->setFirst(pair.first);
builder->setSecond(pair.second);
}")
:capnp-load
(lcp:capnp-load-vector
"utils::capnp::Pair<::capnp::Text, ::capnp::Text>"
"std::pair<std::string, std::string>"
"[](const auto &reader) {
std::string first = reader.getFirst();
std::string second = reader.getSecond();
return std::make_pair(first, second);
}"))
(value :double)
(timestamp :uint64_t :initarg nil)))
(:response ()))
(lcp:define-rpc batch-stats
(:request
((requests "std::vector<StatsReq>"
:capnp-type "List(StatsReq)"
:capnp-save (lcp:capnp-save-vector "capnp::StatsReq" "StatsReq")
:capnp-load (lcp:capnp-load-vector "capnp::StatsReq" "StatsReq"))))
(:response ()))
(lcp:pop-namespace) ;; stats

View File

@ -2,9 +2,9 @@
#include <cstdint>
#include "boost/serialization/access.hpp"
#include "glog/logging.h"
#include "storage/serialization.capnp.h"
#include "storage/gid.hpp"
namespace storage {
@ -89,13 +89,16 @@ class Address {
return storage_ == other.storage_;
}
void Save(capnp::Address::Builder *builder) const {
builder->setStorage(storage_);
}
void Load(const capnp::Address::Reader &reader) {
storage_ = reader.getStorage();
}
private:
StorageT storage_{0};
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &storage_;
}
};
} // namespace storage

View File

@ -10,16 +10,24 @@ namespace {
template <typename TId>
void RegisterRpc(MasterConcurrentIdMapper<TId> &mapper,
communication::rpc::Server &rpc_server);
#define ID_VALUE_RPC_CALLS(type) \
template <> \
void RegisterRpc<type>(MasterConcurrentIdMapper<type> & mapper, \
communication::rpc::Server & rpc_server) { \
rpc_server.Register<type##IdRpc>([&mapper](const type##IdReq &req) { \
return std::make_unique<type##IdRes>(mapper.value_to_id(req.member)); \
}); \
rpc_server.Register<Id##type##Rpc>([&mapper](const Id##type##Req &req) { \
return std::make_unique<Id##type##Res>(mapper.id_to_value(req.member)); \
}); \
#define ID_VALUE_RPC_CALLS(type) \
template <> \
void RegisterRpc<type>(MasterConcurrentIdMapper<type> & mapper, \
communication::rpc::Server & rpc_server) { \
rpc_server.Register<type##IdRpc>( \
[&mapper](const auto &req_reader, auto *res_builder) { \
type##IdReq req; \
req.Load(req_reader); \
type##IdRes res(mapper.value_to_id(req.member)); \
res.Save(res_builder); \
}); \
rpc_server.Register<Id##type##Rpc>( \
[&mapper](const auto &req_reader, auto *res_builder) { \
Id##type##Req req; \
req.Load(req_reader); \
Id##type##Res res(mapper.id_to_value(req.member)); \
res.Save(res_builder); \
}); \
}
using namespace storage;

View File

@ -1,29 +0,0 @@
#pragma once
#include <chrono>
#include "communication/rpc/messages.hpp"
#include "storage/types.hpp"
#include "transactions/commit_log.hpp"
#include "transactions/snapshot.hpp"
#include "transactions/type.hpp"
namespace storage {
#define ID_VALUE_RPC(type) \
RPC_SINGLE_MEMBER_MESSAGE(type##IdReq, std::string); \
RPC_SINGLE_MEMBER_MESSAGE(type##IdRes, storage::type); \
using type##IdRpc = \
communication::rpc::RequestResponse<type##IdReq, type##IdRes>; \
RPC_SINGLE_MEMBER_MESSAGE(Id##type##Req, storage::type); \
RPC_SINGLE_MEMBER_MESSAGE(Id##type##Res, std::string); \
using Id##type##Rpc = \
communication::rpc::RequestResponse<Id##type##Req, Id##type##Res>;
ID_VALUE_RPC(Label)
ID_VALUE_RPC(EdgeType)
ID_VALUE_RPC(Property)
#undef ID_VALUE_RPC
} // namespace storage

View File

@ -0,0 +1,44 @@
#>cpp
#pragma once
#include <chrono>
#include "communication/rpc/messages.hpp"
#include "storage/concurrent_id_mapper_rpc_messages.capnp.h"
#include "storage/types.hpp"
#include "transactions/commit_log.hpp"
#include "transactions/snapshot.hpp"
#include "transactions/type.hpp"
cpp<#
(lcp:namespace storage)
(lcp:capnp-namespace "storage")
(lcp:capnp-import 's "/storage/serialization.capnp")
(lcp:define-rpc label-id
(:request ((member "std::string")))
(:response ((member "Label" :capnp-type "S.Common"))))
(lcp:define-rpc id-label
(:request ((member "Label" :capnp-type "S.Common")))
(:response ((member "std::string"))))
(lcp:define-rpc edge-type-id
(:request ((member "std::string")))
(:response ((member "EdgeType" :capnp-type "S.Common"))))
(lcp:define-rpc id-edge-type
(:request ((member "EdgeType" :capnp-type "S.Common")))
(:response ((member "std::string"))))
(lcp:define-rpc property-id
(:request ((member "std::string")))
(:response ((member "Property" :capnp-type "S.Common"))))
(lcp:define-rpc id-property
(:request ((member "Property" :capnp-type "S.Common")))
(:response ((member "std::string"))))
(lcp:pop-namespace)

View File

@ -15,3 +15,7 @@ struct Common {
struct Label {}
struct EdgeType {}
struct Property {}
struct Address {
storage @0 :UInt64;
}

View File

@ -6,8 +6,8 @@
#include "boost/serialization/base_object.hpp"
#include "glog/logging.h"
#include "types.capnp.h"
#include "storage/serialization.capnp.h"
#include "utils/total_ordering.hpp"
namespace storage {

View File

@ -1,9 +1,8 @@
#pragma once
#include "boost/serialization/access.hpp"
#include "data_structures/bitset/dynamic_bitset.hpp"
#include "type.hpp"
#include "transactions/common.capnp.h"
#include "transactions/type.hpp"
namespace tx {
@ -57,14 +56,15 @@ class CommitLog {
operator uint8_t() const { return flags_; }
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &flags_;
void Save(capnp::CommitLogInfo::Builder *builder) const {
builder->setFlags(flags_);
}
void Load(const capnp::CommitLogInfo::Reader &reader) {
flags_ = reader.getFlags();
}
private:
uint8_t flags_{0};
};

View File

@ -0,0 +1,12 @@
@0xcdbe169866471033;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("tx::capnp");
struct Snapshot {
transactionIds @0 :List(UInt64);
}
struct CommitLogInfo {
flags @0 :UInt8;
}

View File

@ -6,6 +6,7 @@
#include "data_structures/concurrent/concurrent_map.hpp"
#include "transactions/commit_log.hpp"
#include "transactions/snapshot.hpp"
#include "transactions/transaction.hpp"
#include "transactions/type.hpp"

View File

@ -15,61 +15,74 @@ MasterEngine::MasterEngine(communication::rpc::Server &server,
: SingleNodeEngine(wal),
rpc_server_(server),
ongoing_produce_joiner_(rpc_worker_clients) {
rpc_server_.Register<BeginRpc>([this](const BeginReq &) {
auto tx = Begin();
return std::make_unique<BeginRes>(TxAndSnapshot{tx->id_, tx->snapshot()});
});
rpc_server_.Register<AdvanceRpc>([this](const AdvanceReq &req) {
return std::make_unique<AdvanceRes>(Advance(req.member));
});
rpc_server_.Register<CommitRpc>([this](const CommitReq &req) {
Commit(*RunningTransaction(req.member));
return std::make_unique<CommitRes>();
});
rpc_server_.Register<AbortRpc>([this](const AbortReq &req) {
Abort(*RunningTransaction(req.member));
return std::make_unique<AbortRes>();
});
rpc_server_.Register<SnapshotRpc>([this](const SnapshotReq &req) {
// It is guaranteed that the Worker will not be requesting this for a
// transaction that's done, and that there are no race conditions here.
return std::make_unique<SnapshotRes>(
RunningTransaction(req.member)->snapshot());
});
rpc_server_.Register<CommandRpc>([this](const CommandReq &req) {
// It is guaranteed that the Worker will not be requesting this for a
// transaction that's done, and that there are no race conditions here.
return std::make_unique<CommandRes>(RunningTransaction(req.member)->cid());
});
rpc_server_.Register<GcSnapshotRpc>(
[this](const communication::rpc::Message &) {
return std::make_unique<SnapshotRes>(GlobalGcSnapshot());
rpc_server_.Register<BeginRpc>(
[this](const auto &req_reader, auto *res_builder) {
auto tx = this->Begin();
BeginRes res(TxAndSnapshot{tx->id_, tx->snapshot()});
res.Save(res_builder);
});
rpc_server_.Register<ClogInfoRpc>([this](const ClogInfoReq &req) {
return std::make_unique<ClogInfoRes>(Info(req.member));
});
rpc_server_.Register<AdvanceRpc>(
[this](const auto &req_reader, auto *res_builder) {
AdvanceRes res(this->Advance(req_reader.getMember()));
res.Save(res_builder);
});
rpc_server_.Register<CommitRpc>(
[this](const auto &req_reader, auto *res_builder) {
this->Commit(*this->RunningTransaction(req_reader.getMember()));
});
rpc_server_.Register<AbortRpc>(
[this](const auto &req_reader, auto *res_builder) {
this->Abort(*this->RunningTransaction(req_reader.getMember()));
});
rpc_server_.Register<SnapshotRpc>(
[this](const auto &req_reader, auto *res_builder) {
// It is guaranteed that the Worker will not be requesting this for a
// transaction that's done, and that there are no race conditions here.
SnapshotRes res(
this->RunningTransaction(req_reader.getMember())->snapshot());
res.Save(res_builder);
});
rpc_server_.Register<CommandRpc>(
[this](const auto &req_reader, auto *res_builder) {
// It is guaranteed that the Worker will not be requesting this for a
// transaction that's done, and that there are no race conditions here.
CommandRes res(this->RunningTransaction(req_reader.getMember())->cid());
res.Save(res_builder);
});
rpc_server_.Register<GcSnapshotRpc>(
[this](const auto &req_reader, auto *res_builder) {
GcSnapshotRes res(this->GlobalGcSnapshot());
res.Save(res_builder);
});
rpc_server_.Register<ClogInfoRpc>(
[this](const auto &req_reader, auto *res_builder) {
ClogInfoRes res(this->Info(req_reader.getMember()));
res.Save(res_builder);
});
rpc_server_.Register<ActiveTransactionsRpc>(
[this](const communication::rpc::Message &) {
return std::make_unique<SnapshotRes>(GlobalActiveTransactions());
[this](const auto &req_reader, auto *res_builder) {
ActiveTransactionsRes res(this->GlobalActiveTransactions());
res.Save(res_builder);
});
rpc_server_.Register<EnsureNextIdGreaterRpc>(
[this](const EnsureNextIdGreaterReq &req) {
EnsureNextIdGreater(req.member);
return std::make_unique<EnsureNextIdGreaterRes>();
[this](const auto &req_reader, auto *res_builder) {
this->EnsureNextIdGreater(req_reader.getMember());
});
rpc_server_.Register<GlobalLastRpc>([this](const GlobalLastReq &) {
return std::make_unique<GlobalLastRes>(GlobalLast());
});
rpc_server_.Register<GlobalLastRpc>(
[this](const auto &req_reader, auto *res_builder) {
GlobalLastRes res(this->GlobalLast());
res.Save(res_builder);
});
}
void MasterEngine::Commit(const Transaction &t) {

View File

@ -1,70 +0,0 @@
#pragma once
#include "communication/rpc/messages.hpp"
#include "transactions/commit_log.hpp"
#include "transactions/snapshot.hpp"
#include "transactions/type.hpp"
namespace tx {
RPC_NO_MEMBER_MESSAGE(BeginReq);
struct TxAndSnapshot {
TransactionId tx_id;
Snapshot snapshot;
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &tx_id;
ar &snapshot;
}
};
RPC_SINGLE_MEMBER_MESSAGE(BeginRes, TxAndSnapshot);
using BeginRpc = communication::rpc::RequestResponse<BeginReq, BeginRes>;
RPC_SINGLE_MEMBER_MESSAGE(AdvanceReq, TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(AdvanceRes, CommandId);
using AdvanceRpc = communication::rpc::RequestResponse<AdvanceReq, AdvanceRes>;
RPC_SINGLE_MEMBER_MESSAGE(CommitReq, TransactionId);
RPC_NO_MEMBER_MESSAGE(CommitRes);
using CommitRpc = communication::rpc::RequestResponse<CommitReq, CommitRes>;
RPC_SINGLE_MEMBER_MESSAGE(AbortReq, TransactionId);
RPC_NO_MEMBER_MESSAGE(AbortRes);
using AbortRpc = communication::rpc::RequestResponse<AbortReq, AbortRes>;
RPC_SINGLE_MEMBER_MESSAGE(SnapshotReq, TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(SnapshotRes, Snapshot);
using SnapshotRpc =
communication::rpc::RequestResponse<SnapshotReq, SnapshotRes>;
RPC_SINGLE_MEMBER_MESSAGE(CommandReq, TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(CommandRes, CommandId);
using CommandRpc = communication::rpc::RequestResponse<CommandReq, CommandRes>;
RPC_NO_MEMBER_MESSAGE(GcSnapshotReq);
using GcSnapshotRpc =
communication::rpc::RequestResponse<GcSnapshotReq, SnapshotRes>;
RPC_SINGLE_MEMBER_MESSAGE(ClogInfoReq, TransactionId);
RPC_SINGLE_MEMBER_MESSAGE(ClogInfoRes, CommitLog::Info);
using ClogInfoRpc =
communication::rpc::RequestResponse<ClogInfoReq, ClogInfoRes>;
RPC_NO_MEMBER_MESSAGE(ActiveTransactionsReq);
using ActiveTransactionsRpc =
communication::rpc::RequestResponse<ActiveTransactionsReq, SnapshotRes>;
RPC_SINGLE_MEMBER_MESSAGE(EnsureNextIdGreaterReq, TransactionId);
RPC_NO_MEMBER_MESSAGE(EnsureNextIdGreaterRes);
using EnsureNextIdGreaterRpc =
communication::rpc::RequestResponse<EnsureNextIdGreaterReq,
EnsureNextIdGreaterRes>;
RPC_NO_MEMBER_MESSAGE(GlobalLastReq);
RPC_SINGLE_MEMBER_MESSAGE(GlobalLastRes, TransactionId);
using GlobalLastRpc =
communication::rpc::RequestResponse<GlobalLastReq, GlobalLastRes>;
} // namespace tx

View File

@ -0,0 +1,69 @@
#>cpp
#pragma once
#include "communication/rpc/messages.hpp"
#include "transactions/commit_log.hpp"
#include "transactions/engine_rpc_messages.capnp.h"
#include "transactions/snapshot.hpp"
#include "transactions/type.hpp"
cpp<#
(lcp:namespace tx)
(lcp:capnp-namespace "tx")
(lcp:capnp-import 'tx "/transactions/common.capnp")
(lcp:capnp-type-conversion "TransactionId" "UInt64")
(lcp:capnp-type-conversion "CommandId" "UInt32")
(lcp:capnp-type-conversion "Snapshot" "Tx.Snapshot")
(lcp:define-struct tx-and-snapshot ()
((tx-id "TransactionId")
(snapshot "Snapshot"))
(:serialize :capnp))
(lcp:define-rpc begin
(:request ())
(:response ((member "TxAndSnapshot"))))
(lcp:define-rpc advance
(:request ((member "TransactionId")))
(:response ((member "CommandId"))))
(lcp:define-rpc commit
(:request ((member "TransactionId")))
(:response ()))
(lcp:define-rpc abort
(:request ((member "TransactionId")))
(:response ()))
(lcp:define-rpc snapshot
(:request ((member "TransactionId")))
(:response ((member "Snapshot"))))
(lcp:define-rpc command
(:request ((member "TransactionId")))
(:response ((member "CommandId"))))
(lcp:define-rpc gc-snapshot
(:request ())
(:response ((member "Snapshot"))))
(lcp:define-rpc clog-info
(:request ((member "TransactionId")))
(:response ((member "CommitLog::Info" :capnp-type "Tx.CommitLogInfo"))))
(lcp:define-rpc active-transactions
(:request ())
(:response ((member "Snapshot"))))
(lcp:define-rpc ensure-next-id-greater
(:request ((member "TransactionId")))
(:response ()))
(lcp:define-rpc global-last
(:request ())
(:response ((member "TransactionId"))))
(lcp:pop-namespace) ;; tx

View File

@ -0,0 +1,16 @@
#include "transactions/snapshot.hpp"
#include "utils/serialization.hpp"
namespace tx {
void Snapshot::Save(capnp::Snapshot::Builder *builder) const {
auto list_builder = builder->initTransactionIds(transaction_ids_.size());
utils::SaveVector(transaction_ids_, &list_builder);
}
void Snapshot::Load(const capnp::Snapshot::Reader &reader) {
utils::LoadVector(&transaction_ids_, reader.getTransactionIds());
}
} // namespace tx

View File

@ -4,10 +4,8 @@
#include <iostream>
#include <vector>
#include "boost/serialization/access.hpp"
#include "boost/serialization/vector.hpp"
#include "glog/logging.h"
#include "transactions/common.capnp.h"
#include "transactions/type.hpp"
#include "utils/algorithm.hpp"
@ -86,14 +84,11 @@ class Snapshot {
return stream;
}
void Save(capnp::Snapshot::Builder *builder) const;
void Load(const capnp::Snapshot::Reader &reader);
private:
friend class boost::serialization::access;
template <class TArchive>
void serialize(TArchive &ar, unsigned int) {
ar &transaction_ids_;
}
std::vector<TransactionId> transaction_ids_;
};
} // namespace tx

View File

@ -1,10 +1,15 @@
@0xe7647d63b36c2c65;
using Cxx = import "/capnp/c++.capnp";
$Cxx.namespace("utils::capnp");
# Primitive type wrappers
struct BoxInt16 {
value @0 :Int16;
}
# Primitive types wrappers.
struct BoxInt32 {
value @0 :Int32;
}
@ -13,12 +18,16 @@ struct BoxInt64 {
value @0 :Int64;
}
struct BoxUInt16 {
value @0 :UInt16;
}
struct BoxUInt32 {
value @0 :UInt32;
}
struct BoxUInt64 {
value @0 :UInt32;
value @0 :UInt64;
}
struct BoxFloat32 {
@ -33,8 +42,7 @@ struct BoxBool {
value @0 :Bool;
}
# CPP Types.
# C++ STL types
struct Optional(T) {
union {
@ -62,28 +70,22 @@ struct SharedPtr(T) {
}
}
# Our types
struct TypedValue {
union {
nullType @0 :Void;
bool @1 :Bool;
integer @2 :Int64;
double @3 :Float64;
string @4 :Text;
list @5 :List(TypedValue);
map @6 :List(Entry);
# TODO vertex accessor
# TODO edge accessor
# TODO path
}
struct Map(K, V) {
entries @0 :List(Entry);
struct Entry {
key @0 :Text;
value @1 :TypedValue;
key @0 :K;
value @1 :V;
}
}
struct Pair(First, Second) {
first @0 :First;
second @1 :Second;
}
# Our types
struct Bound(T) {
type @0 :Type;
value @1 :T;

View File

@ -2,12 +2,15 @@
#include <experimental/optional>
#include "boost/serialization/optional.hpp"
#include "boost/serialization/serialization.hpp"
#include "boost/serialization/split_free.hpp"
#include "distributed/serialization.capnp.h"
#include "query/typed_value.hpp"
#include "storage/edge.hpp"
#include "storage/vertex.hpp"
#include "utils/exceptions.hpp"
#include "utils/serialization.capnp.h"
namespace boost::serialization {
@ -63,64 +66,114 @@ void load(TArchive &ar, std::experimental::optional<T> &opt,
namespace utils {
inline void SaveCapnpTypedValue(const query::TypedValue &value,
capnp::TypedValue::Builder &builder) {
inline void SaveCapnpTypedValue(
const query::TypedValue &value,
distributed::capnp::TypedValue::Builder *builder,
std::function<void(const query::TypedValue &,
distributed::capnp::TypedValue::Builder *)>
save_graph_element = nullptr) {
switch (value.type()) {
case query::TypedValue::Type::Null:
builder.setNullType();
builder->setNullType();
return;
case query::TypedValue::Type::Bool:
builder.setBool(value.Value<bool>());
builder->setBool(value.Value<bool>());
return;
case query::TypedValue::Type::Int:
builder.setInteger(value.Value<int64_t>());
builder->setInteger(value.Value<int64_t>());
return;
case query::TypedValue::Type::Double:
builder.setDouble(value.Value<double>());
builder->setDouble(value.Value<double>());
return;
case query::TypedValue::Type::String:
builder.setString(value.Value<std::string>());
builder->setString(value.Value<std::string>());
return;
case query::TypedValue::Type::List:
case query::TypedValue::Type::Map:
case query::TypedValue::Type::List: {
const auto &values = value.Value<std::vector<query::TypedValue>>();
auto list_builder = builder->initList(values.size());
for (size_t i = 0; i < values.size(); ++i) {
auto value_builder = list_builder[i];
SaveCapnpTypedValue(values[i], &value_builder, save_graph_element);
}
return;
}
case query::TypedValue::Type::Map: {
const auto &map = value.Value<std::map<std::string, query::TypedValue>>();
auto map_builder = builder->initMap(map.size());
size_t i = 0;
for (const auto &kv : map) {
auto kv_builder = map_builder[i];
kv_builder.setKey(kv.first);
auto value_builder = kv_builder.initValue();
SaveCapnpTypedValue(kv.second, &value_builder, save_graph_element);
++i;
}
return;
}
case query::TypedValue::Type::Vertex:
case query::TypedValue::Type::Edge:
case query::TypedValue::Type::Path:
throw utils::NotYetImplemented("Capnp serialize typed value");
if (save_graph_element) {
save_graph_element(value, builder);
} else {
throw utils::BasicException(
"Unable to serialize TypedValue of type: {}", value.type());
}
}
}
inline void LoadCapnpTypedValue(query::TypedValue &value,
capnp::TypedValue::Reader &reader) {
inline void LoadCapnpTypedValue(
const distributed::capnp::TypedValue::Reader &reader,
query::TypedValue *value,
std::function<void(const distributed::capnp::TypedValue::Reader &,
query::TypedValue *)>
load_graph_element = nullptr) {
switch (reader.which()) {
case capnp::TypedValue::BOOL:
value = reader.getBool();
case distributed::capnp::TypedValue::NULL_TYPE:
*value = query::TypedValue::Null;
return;
case capnp::TypedValue::DOUBLE:
value = reader.getDouble();
case distributed::capnp::TypedValue::BOOL:
*value = reader.getBool();
return;
// case capnp::TypedValue::EDGE:
// // TODO
// return;
case capnp::TypedValue::INTEGER:
value = reader.getInteger();
case distributed::capnp::TypedValue::INTEGER:
*value = reader.getInteger();
return;
case capnp::TypedValue::LIST:
throw utils::NotYetImplemented("Capnp deserialize typed value");
case capnp::TypedValue::MAP:
throw utils::NotYetImplemented("Capnp deserialize typed value");
case capnp::TypedValue::NULL_TYPE:
value = query::TypedValue::Null;
case distributed::capnp::TypedValue::DOUBLE:
*value = reader.getDouble();
return;
// case query::capnp::TypedValue::PATH:
// // TODO
// return;
case capnp::TypedValue::STRING:
value = reader.getString().cStr();
case distributed::capnp::TypedValue::STRING:
*value = reader.getString().cStr();
return;
// case query::capnp::TypedValue::VERTEX:
// // TODO
// return;
case distributed::capnp::TypedValue::LIST: {
std::vector<query::TypedValue> list;
list.reserve(reader.getList().size());
for (const auto &value_reader : reader.getList()) {
list.emplace_back();
LoadCapnpTypedValue(value_reader, &list.back(), load_graph_element);
}
*value = list;
return;
}
case distributed::capnp::TypedValue::MAP: {
std::map<std::string, query::TypedValue> map;
for (const auto &kv_reader : reader.getMap()) {
auto key = kv_reader.getKey().cStr();
LoadCapnpTypedValue(kv_reader.getValue(), &map[key],
load_graph_element);
}
*value = map;
return;
}
case distributed::capnp::TypedValue::VERTEX:
case distributed::capnp::TypedValue::EDGE:
case distributed::capnp::TypedValue::PATH:
if (load_graph_element) {
load_graph_element(reader, value);
} else {
throw utils::BasicException(
"Unexpected TypedValue type '{}' when loading from archive",
reader.which());
}
}
}
@ -161,6 +214,34 @@ inline void LoadVector(
}
}
template <class TCapnpKey, class TCapnpValue, class TMap>
void SaveMap(const TMap &map,
typename capnp::Map<TCapnpKey, TCapnpValue>::Builder *map_builder,
std::function<void(
typename capnp::Map<TCapnpKey, TCapnpValue>::Entry::Builder *,
const typename TMap::value_type &)>
save) {
auto entries_builder = map_builder->initEntries(map.size());
size_t i = 0;
for (const auto &entry : map) {
auto entry_builder = entries_builder[i];
save(&entry_builder, entry);
++i;
}
}
template <class TCapnpKey, class TCapnpValue, class TMap>
void LoadMap(
TMap *map,
const typename capnp::Map<TCapnpKey, TCapnpValue>::Reader &map_reader,
std::function<typename TMap::value_type(
const typename capnp::Map<TCapnpKey, TCapnpValue>::Entry::Reader &)>
load) {
for (const auto &entry_reader : map_reader.getEntries()) {
map->insert(load(entry_reader));
}
}
template <typename TCapnp, typename T>
inline void SaveOptional(
const std::experimental::optional<T> &data,

Some files were not shown because too many files have changed in this diff Show More