From 1a2f138e092bfa71bb00a156ec6d86bfedc9e430 Mon Sep 17 00:00:00 2001 From: Kostas Kyrimis Date: Fri, 2 Sep 2022 16:19:42 +0300 Subject: [PATCH] middleware prototype --- src/io/rsm/shard_rsm.hpp | 2 +- src/query/v2/middleware.hpp | 230 +++++++++++++++++++++++++++++ src/query/v2/requests.hpp | 223 +++++++++++++++++++++++++++++ tests/simulation/CMakeLists.txt | 2 + tests/simulation/middleware.cpp | 238 +++++++++++++++++++++++++++++++ tests/simulation/sharded_map.cpp | 6 +- 6 files changed, 697 insertions(+), 4 deletions(-) create mode 100644 src/query/v2/middleware.hpp create mode 100644 src/query/v2/requests.hpp create mode 100644 tests/simulation/middleware.cpp diff --git a/src/io/rsm/shard_rsm.hpp b/src/io/rsm/shard_rsm.hpp index ff2b24aaf..6af50c746 100644 --- a/src/io/rsm/shard_rsm.hpp +++ b/src/io/rsm/shard_rsm.hpp @@ -48,7 +48,7 @@ using memgraph::io::simulator::SimulatorTransport; using memgraph::storage::v3::LabelId; using memgraph::storage::v3::PropertyValue; -using ShardRsmKey = std::vector; +using ShardRsmKey = std::vector; struct StorageWriteRequest { LabelId label_id; diff --git a/src/query/v2/middleware.hpp b/src/query/v2/middleware.hpp new file mode 100644 index 000000000..7f28f8056 --- /dev/null +++ b/src/query/v2/middleware.hpp @@ -0,0 +1,230 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coordinator/coordinator_client.hpp" +#include "coordinator/coordinator_rsm.hpp" +#include "coordinator/shard_map.hpp" +#include "io/address.hpp" +#include "io/errors.hpp" +#include "io/rsm/raft.hpp" +#include "io/rsm/rsm_client.hpp" +#include "io/rsm/shard_rsm.hpp" +#include "io/simulator/simulator.hpp" +#include "io/simulator/simulator_transport.hpp" +#include "query/v2/requests.hpp" +#include "utils/result.hpp" + +template +class RsmStorageClientManager { + public: + using CompoundKey = memgraph::io::rsm::ShardRsmKey; + RsmStorageClientManager() = default; + RsmStorageClientManager(const RsmStorageClientManager &) = delete; + RsmStorageClientManager(RsmStorageClientManager &&) = delete; + + void AddClient(const std::string &label, CompoundKey cm_k, TStorageClient client) { + cli_cache_[label].insert({std::move(cm_k), std::move(client)}); + } + + bool Exists(const std::string &label, const CompoundKey &cm_k) { return cli_cache_[label].contains(cm_k); } + + void PurgeCache() { cli_cache_.clear(); } + // void EvictFromCache(std::vector); + TStorageClient &GetClient(const std::string &label, CompoundKey key) { return cli_cache_[label].find(key)->second; } + + private: + std::unordered_map> cli_cache_; +}; + +// In execution context an object exists +struct ExecutionState { + using CompoundKey = memgraph::io::rsm::ShardRsmKey; + using Shard = memgraph::coordinator::Shard; + std::optional> state_; + std::string label; + // using CompoundKey = memgraph::coordinator::CompoundKey; + std::optional key; +}; + +namespace rsm = memgraph::io::rsm; + +// TODO(kostasrim)rename this class template +template +class QueryEngineMiddleware { + public: + using StorageClient = + memgraph::coordinator::RsmClient; + using CoordinatorClient = memgraph::coordinator::CoordinatorClient; + using Address = memgraph::io::Address; + using Shard = memgraph::coordinator::Shard; + using ShardMap = memgraph::coordinator::ShardMap; + using CompoundKey = memgraph::coordinator::CompoundKey; + using memgraph::io::Io; + QueryEngineMiddleware(CoordinatorClient coord, Io &&io) + : coord_cli_(std::move(coord)), io_(std::move(io)) {} + + std::vector Request(ScanVerticesRequest rqst, ExecutionState &state) { + MaybeUpdateShardMap(); + MaybeUpdateExecutionState(state); + std::vector responses; + for (const auto &shard : *state.state_) { + auto &storage_client = GetStorageClientForShard(state.label, *state.key); + auto read_response_result = storage_client.SendReadRequest(rqst); + // RETRY on timeouts? + // Sometimes this produces a timeout. Temporary solution is to use a while(true) as was done in shard_map test + if (read_response_result.HasError()) { + throw std::runtime_error("Handle gracefully!"); + } + responses.push_back(read_response_result.Value()); + } + // TODO(kostasrim) Update state accordingly + return responses; + // For a future based API. Also maybe introduce a `Retry` function that accepts a lambda which is the request + // and a number denoting the number of times the request is retried until an exception or an error is returned. + // std::vector> requests; + // for (const auto &shard : state.state_) { + // auto &storage_client = GetStorageClientForShard(state.Label, rqst.label); + // requests.push_back(client->Request(rqst)); + // } + // + // std::vector responses; + // for (auto &f : requests) { + // f.wait(); + // if (f.HasError()) { + // // handle error + // } + // responses.push_back(std::move(f).Value()); + // } + } + + CreateVerticesResponse Request(CreateVerticesRequest rqst, ExecutionState &state) { + // MaybeUpdateShardMap(); + // MaybeUpdateExecutionState(); + } + + size_t TestRequest(ExecutionState &state) { + MaybeUpdateShardMap(); + MaybeUpdateExecutionState(state); + for (auto &st : *state.state_) { + auto &storage_client = GetStorageClientForShard(state.label, *state.key); + + rsm::StorageWriteRequest storage_req; + storage_req.key = *state.key; + storage_req.value = 469; + auto write_response_result = storage_client.SendWriteRequest(storage_req); + if (write_response_result.HasError()) { + throw std::runtime_error("Handle gracefully!"); + } + auto write_response = write_response_result.GetValue(); + + bool cas_succeeded = write_response.shard_rsm_success; + + if (!cas_succeeded) { + throw std::runtime_error("Handler gracefully!"); + } + rsm::StorageReadRequest storage_get_req; + storage_get_req.key = *state.key; + + auto get_response_result = storage_client.SendReadRequest(storage_get_req); + if (get_response_result.HasError()) { + throw std::runtime_error("Handler gracefully!"); + } + auto get_response = get_response_result.GetValue(); + auto val = get_response.value.value(); + return val; + } + return 0; + } + + private: + void MaybeUpdateShardMap() { + memgraph::coordinator::HlcRequest req{.last_shard_map_version = shards_map_.GetHlc()}; + auto read_res = coord_cli_.SendReadRequest(req); + if (read_res.HasError()) { + // handle error gracefully + // throw some error + } + auto coordinator_read_response = read_res.GetValue(); + auto hlc_response = std::get(coordinator_read_response); + if (hlc_response.fresher_shard_map) { + // error here new shard map shouldn't exist + } + + // Transaction ID to be used later... + auto transaction_id = hlc_response.new_hlc; + + if (hlc_response.fresher_shard_map) { + shards_map_ = hlc_response.fresher_shard_map.value(); + } else { + throw std::runtime_error("Should handle gracefully!"); + } + } + + void MaybeUpdateExecutionState(ExecutionState &state) { + if (state.state_) { + return; + } + state.state_ = std::make_optional>(); + const auto &shards = shards_map_.shards[state.label]; + if (state.key) { + if (auto it = shards.find(*state.key); it != shards.end()) { + state.state_->push_back(it->second); + return; + } + // throw here + } + + for (const auto &[key, shard] : shards) { + state.state_->push_back(shard); + } + } + + // std::vector GetStorageClientFromShardforRange(const std::string &label, const CompoundKey &start, + // const CompoundKey &end); + StorageClient &GetStorageClientForShard(const std::string &label, const CompoundKey &cm_k) { + if (storage_cli_manager_.Exists(label, cm_k)) { + return storage_cli_manager_.GetClient(label, cm_k); + } + auto target_shard = shards_map_.GetShardForKey(label, cm_k); + AddStorageClientToManager(std::move(target_shard), label, cm_k); + return storage_cli_manager_.GetClient(label, cm_k); + } + + void AddStorageClientToManager(Shard target_shard, const std::string &label, const CompoundKey &cm_k) { + MG_ASSERT(!target_shard.empty()); + auto leader_addr = target_shard.front(); + std::vector
addresses; + for (auto &address : target_shard) { + addresses.push_back(std::move(address.address)); + } + auto cli = StorageClient(io_, std::move(leader_addr.address), std::move(addresses)); + storage_cli_manager_.AddClient(label, cm_k, std::move(cli)); + } + + ShardMap shards_map_; + CoordinatorClient coord_cli_; + RsmStorageClientManager storage_cli_manager_; + Io io_; + // TODO(kostasrim) Add batch prefetching +}; diff --git a/src/query/v2/requests.hpp b/src/query/v2/requests.hpp new file mode 100644 index 000000000..421a7fd54 --- /dev/null +++ b/src/query/v2/requests.hpp @@ -0,0 +1,223 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "storage/v3/id_types.hpp" +#include "storage/v3/property_value.hpp" + +/// Hybrid-logical clock +struct Hlc { + uint64_t logical_id; + using Duration = std::chrono::microseconds; + using Time = std::chrono::time_point; + Time coordinator_wall_clock; + + bool operator==(const Hlc &other) const = default; +}; + +struct Label { + size_t id; +}; + +// TODO(kostasrim) update this with CompoundKey, same for the rest of the file. +using PrimaryKey = std::vector; +using VertexId = std::pair; +using Gid = size_t; +using PropertyId = memgraph::storage::v3::PropertyId; + +struct EdgeType { + std::string name; +}; + +struct EdgeId { + VertexId id; + Gid gid; +}; + +struct Vertex { + VertexId id; + std::vector