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:
parent
e56ed0acce
commit
e0474a8e92
36
.gitignore
vendored
36
.gitignore
vendored
@ -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
2
init
@ -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
|
||||
|
@ -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})
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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_;
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
9
src/communication/rpc/messages.capnp
Normal file
9
src/communication/rpc/messages.capnp
Normal 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;
|
||||
}
|
@ -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; \
|
||||
} \
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
23
src/database/counters_rpc_messages.lcp
Normal file
23
src/database/counters_rpc_messages.lcp
Normal 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
|
@ -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
|
179
src/database/state_delta.lcp
Normal file
179
src/database/state_delta.lcp
Normal 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
|
@ -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>();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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>(
|
||||
|
@ -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_;
|
||||
|
@ -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
|
280
src/distributed/bfs_rpc_messages.lcp
Normal file
280
src/distributed/bfs_rpc_messages.lcp
Normal 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
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 ®istration_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
|
72
src/distributed/coordination_rpc_messages.lcp
Normal file
72
src/distributed/coordination_rpc_messages.lcp
Normal 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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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
|
76
src/distributed/data_rpc_messages.lcp
Normal file
76
src/distributed/data_rpc_messages.lcp
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
});
|
||||
|
||||
|
@ -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
|
20
src/distributed/durability_rpc_messages.lcp
Normal file
20
src/distributed/durability_rpc_messages.lcp
Normal 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
|
@ -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
|
||||
|
@ -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
|
25
src/distributed/index_rpc_messages.lcp
Normal file
25
src/distributed/index_rpc_messages.lcp
Normal 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
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
59
src/distributed/plan_rpc_messages.lcp
Normal file
59
src/distributed/plan_rpc_messages.lcp
Normal 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
|
@ -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;
|
||||
|
@ -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 ¶ms,
|
||||
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 ¤t_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
|
547
src/distributed/pull_produce_rpc_messages.lcp
Normal file
547
src/distributed/pull_produce_rpc_messages.lcp
Normal 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 ¤t_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
|
@ -12,53 +12,21 @@ utils::Future<PullData> PullRpcClients::Pull(
|
||||
tx::CommandId command_id, const Parameters ¶ms,
|
||||
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>>
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
|
||||
|
71
src/distributed/serialization.capnp
Normal file
71
src/distributed/serialization.capnp
Normal 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);
|
||||
}
|
||||
}
|
120
src/distributed/serialization.cpp
Normal file
120
src/distributed/serialization.cpp
Normal 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
|
@ -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
|
||||
|
@ -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
|
20
src/distributed/storage_gc_rpc_messages.lcp
Normal file
20
src/distributed/storage_gc_rpc_messages.lcp
Normal 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
|
@ -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
|
20
src/distributed/token_sharing_rpc_messages.lcp
Normal file
20
src/distributed/token_sharing_rpc_messages.lcp
Normal 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
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
};
|
17
src/distributed/transactional_cache_cleaner_rpc_messages.lcp
Normal file
17
src/distributed/transactional_cache_cleaner_rpc_messages.lcp
Normal 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)
|
@ -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{
|
||||
|
@ -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
|
187
src/distributed/updates_rpc_messages.lcp
Normal file
187
src/distributed/updates_rpc_messages.lcp
Normal 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
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
9
src/durability/recovery.capnp
Normal file
9
src/durability/recovery.capnp
Normal file
@ -0,0 +1,9 @@
|
||||
@0xb3d70bc0576218f3;
|
||||
|
||||
using Cxx = import "/capnp/c++.capnp";
|
||||
$Cxx.namespace("durability::capnp");
|
||||
|
||||
struct RecoveryInfo {
|
||||
snapshotTxId @0 :UInt64;
|
||||
maxWalTxId @1 :UInt64;
|
||||
}
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
10
src/io/network/endpoint.capnp
Normal file
10
src/io/network/endpoint.capnp
Normal 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;
|
||||
}
|
@ -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_;
|
||||
|
@ -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
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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 {
|
||||
|
@ -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 *")
|
||||
|
@ -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 ×tamp;
|
||||
}
|
||||
};
|
||||
|
||||
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
|
50
src/stats/stats_rpc_messages.lcp
Normal file
50
src/stats/stats_rpc_messages.lcp
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
44
src/storage/concurrent_id_mapper_rpc_messages.lcp
Normal file
44
src/storage/concurrent_id_mapper_rpc_messages.lcp
Normal 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)
|
@ -15,3 +15,7 @@ struct Common {
|
||||
struct Label {}
|
||||
struct EdgeType {}
|
||||
struct Property {}
|
||||
|
||||
struct Address {
|
||||
storage @0 :UInt64;
|
||||
}
|
@ -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 {
|
||||
|
@ -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};
|
||||
};
|
||||
|
||||
|
12
src/transactions/common.capnp
Normal file
12
src/transactions/common.capnp
Normal 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;
|
||||
}
|
@ -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"
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
69
src/transactions/engine_rpc_messages.lcp
Normal file
69
src/transactions/engine_rpc_messages.lcp
Normal 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
|
16
src/transactions/snapshot.cpp
Normal file
16
src/transactions/snapshot.cpp
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user