Merge branch 'project-pineapples' into T0919-MG-implement-get-properties-storage

This commit is contained in:
Kostas Kyrimis 2022-11-24 15:29:05 +02:00
commit 07a8ac0db8
70 changed files with 2224 additions and 1568 deletions

68
src/common/errors.hpp Normal file
View File

@ -0,0 +1,68 @@
// 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 <cstdint>
#include <string_view>
namespace memgraph::common {
enum class ErrorCode : uint8_t {
SERIALIZATION_ERROR,
NONEXISTENT_OBJECT,
DELETED_OBJECT,
VERTEX_HAS_EDGES,
PROPERTIES_DISABLED,
VERTEX_ALREADY_INSERTED,
// Schema Violations
SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL,
SCHEMA_VERTEX_PROPERTY_WRONG_TYPE,
SCHEMA_VERTEX_UPDATE_PRIMARY_KEY,
SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL,
SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY,
SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
OBJECT_NOT_FOUND,
};
constexpr std::string_view ErrorCodeToString(const ErrorCode code) {
switch (code) {
case ErrorCode::SERIALIZATION_ERROR:
return "SERIALIZATION_ERROR";
case ErrorCode::NONEXISTENT_OBJECT:
return "NONEXISTENT_OBJECT";
case ErrorCode::DELETED_OBJECT:
return "DELETED_OBJECT";
case ErrorCode::VERTEX_HAS_EDGES:
return "VERTEX_HAS_EDGES";
case ErrorCode::PROPERTIES_DISABLED:
return "PROPERTIES_DISABLED";
case ErrorCode::VERTEX_ALREADY_INSERTED:
return "VERTEX_ALREADY_INSERTED";
case ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL:
return "SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL";
case ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE:
return "SCHEMA_VERTEX_PROPERTY_WRONG_TYPE";
case ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY:
return "SCHEMA_VERTEX_UPDATE_PRIMARY_KEY";
case ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL:
return "SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL";
case ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY:
return "SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY";
case ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED:
return "SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED";
case ErrorCode::OBJECT_NOT_FOUND:
return "OBJECT_NOT_FOUND";
}
}
} // namespace memgraph::common

View File

@ -71,6 +71,9 @@ struct QueueInner {
// starvation by sometimes randomizing priorities, rather than following a strict // starvation by sometimes randomizing priorities, rather than following a strict
// prioritization. // prioritization.
std::deque<Message> queue; std::deque<Message> queue;
uint64_t submitted = 0;
uint64_t calls_to_pop = 0;
}; };
/// There are two reasons to implement our own Queue instead of using /// There are two reasons to implement our own Queue instead of using
@ -86,6 +89,8 @@ class Queue {
MG_ASSERT(inner_.use_count() > 0); MG_ASSERT(inner_.use_count() > 0);
std::unique_lock<std::mutex> lock(inner_->mu); std::unique_lock<std::mutex> lock(inner_->mu);
inner_->submitted++;
inner_->queue.emplace_back(std::move(message)); inner_->queue.emplace_back(std::move(message));
} // lock dropped before notifying condition variable } // lock dropped before notifying condition variable
@ -96,6 +101,9 @@ class Queue {
MG_ASSERT(inner_.use_count() > 0); MG_ASSERT(inner_.use_count() > 0);
std::unique_lock<std::mutex> lock(inner_->mu); std::unique_lock<std::mutex> lock(inner_->mu);
inner_->calls_to_pop++;
inner_->cv.notify_all();
while (inner_->queue.empty()) { while (inner_->queue.empty()) {
inner_->cv.wait(lock); inner_->cv.wait(lock);
} }
@ -105,6 +113,15 @@ class Queue {
return message; return message;
} }
void BlockOnQuiescence() const {
MG_ASSERT(inner_.use_count() > 0);
std::unique_lock<std::mutex> lock(inner_->mu);
while (inner_->calls_to_pop <= inner_->submitted) {
inner_->cv.wait(lock);
}
}
}; };
/// A CoordinatorWorker owns Raft<CoordinatorRsm> instances. receives messages from the MachineManager. /// A CoordinatorWorker owns Raft<CoordinatorRsm> instances. receives messages from the MachineManager.
@ -129,9 +146,7 @@ class CoordinatorWorker {
public: public:
CoordinatorWorker(io::Io<IoImpl> io, Queue queue, Coordinator coordinator) CoordinatorWorker(io::Io<IoImpl> io, Queue queue, Coordinator coordinator)
: io_(std::move(io)), : io_(std::move(io)), queue_(std::move(queue)), coordinator_{std::move(io_), {}, std::move(coordinator)} {}
queue_(std::move(queue)),
coordinator_{std::move(io_.ForkLocal()), {}, std::move(coordinator)} {}
CoordinatorWorker(CoordinatorWorker &&) noexcept = default; CoordinatorWorker(CoordinatorWorker &&) noexcept = default;
CoordinatorWorker &operator=(CoordinatorWorker &&) noexcept = default; CoordinatorWorker &operator=(CoordinatorWorker &&) noexcept = default;
@ -140,15 +155,12 @@ class CoordinatorWorker {
~CoordinatorWorker() = default; ~CoordinatorWorker() = default;
void Run() { void Run() {
while (true) { bool should_continue = true;
while (should_continue) {
Message message = queue_.Pop(); Message message = queue_.Pop();
const bool should_continue = std::visit( should_continue = std::visit([this](auto &&msg) { return this->Process(std::forward<decltype(msg)>(msg)); },
[this](auto &&msg) { return this->Process(std::forward<decltype(msg)>(msg)); }, std::move(message)); std::move(message));
if (!should_continue) {
return;
}
} }
} }
}; };

View File

@ -228,7 +228,7 @@ Hlc ShardMap::IncrementShardMapVersion() noexcept {
return shard_map_version; return shard_map_version;
} }
// TODO(antaljanosbenjamin) use a single map for all name id // TODO(antaljanosbenjamin) use a single map for all name id
// mapping and a single counter to maintain the next id // mapping and a single counter to maintain the next id
std::unordered_map<uint64_t, std::string> ShardMap::IdToNames() { std::unordered_map<uint64_t, std::string> ShardMap::IdToNames() {
std::unordered_map<uint64_t, std::string> id_to_names; std::unordered_map<uint64_t, std::string> id_to_names;
@ -248,6 +248,25 @@ std::unordered_map<uint64_t, std::string> ShardMap::IdToNames() {
Hlc ShardMap::GetHlc() const noexcept { return shard_map_version; } Hlc ShardMap::GetHlc() const noexcept { return shard_map_version; }
boost::uuids::uuid NewShardUuid(uint64_t shard_id) {
return boost::uuids::uuid{0,
0,
0,
0,
0,
0,
0,
0,
static_cast<unsigned char>(shard_id >> 56U),
static_cast<unsigned char>(shard_id >> 48U),
static_cast<unsigned char>(shard_id >> 40U),
static_cast<unsigned char>(shard_id >> 32U),
static_cast<unsigned char>(shard_id >> 24U),
static_cast<unsigned char>(shard_id >> 16U),
static_cast<unsigned char>(shard_id >> 8U),
static_cast<unsigned char>(shard_id)};
}
std::vector<ShardToInitialize> ShardMap::AssignShards(Address storage_manager, std::vector<ShardToInitialize> ShardMap::AssignShards(Address storage_manager,
std::set<boost::uuids::uuid> initialized) { std::set<boost::uuids::uuid> initialized) {
std::vector<ShardToInitialize> ret{}; std::vector<ShardToInitialize> ret{};
@ -268,6 +287,7 @@ std::vector<ShardToInitialize> ShardMap::AssignShards(Address storage_manager,
if (initialized.contains(aas.address.unique_id)) { if (initialized.contains(aas.address.unique_id)) {
machine_contains_shard = true; machine_contains_shard = true;
if (aas.status != Status::CONSENSUS_PARTICIPANT) { if (aas.status != Status::CONSENSUS_PARTICIPANT) {
mutated = true;
spdlog::info("marking shard as full consensus participant: {}", aas.address.unique_id); spdlog::info("marking shard as full consensus participant: {}", aas.address.unique_id);
aas.status = Status::CONSENSUS_PARTICIPANT; aas.status = Status::CONSENSUS_PARTICIPANT;
} }
@ -292,10 +312,13 @@ std::vector<ShardToInitialize> ShardMap::AssignShards(Address storage_manager,
} }
if (!machine_contains_shard && shard.size() < label_space.replication_factor) { if (!machine_contains_shard && shard.size() < label_space.replication_factor) {
// increment version for each new uuid for deterministic creation
IncrementShardMapVersion();
Address address = storage_manager; Address address = storage_manager;
// TODO(tyler) use deterministic UUID so that coordinators don't diverge here // TODO(tyler) use deterministic UUID so that coordinators don't diverge here
address.unique_id = boost::uuids::uuid{boost::uuids::random_generator()()}, address.unique_id = NewShardUuid(shard_map_version.logical_id);
spdlog::info("assigning shard manager to shard"); spdlog::info("assigning shard manager to shard");
@ -383,6 +406,7 @@ std::optional<LabelId> ShardMap::InitializeNewLabel(std::string label_name, std:
void ShardMap::AddServer(Address server_address) { void ShardMap::AddServer(Address server_address) {
// Find a random place for the server to plug in // Find a random place for the server to plug in
} }
std::optional<LabelId> ShardMap::GetLabelId(const std::string &label) const { std::optional<LabelId> ShardMap::GetLabelId(const std::string &label) const {
if (const auto it = labels.find(label); it != labels.end()) { if (const auto it = labels.find(label); it != labels.end()) {
return it->second; return it->second;

View File

@ -100,6 +100,28 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
#undef BINARY_OPERATOR_VISITOR #undef BINARY_OPERATOR_VISITOR
#undef UNARY_OPERATOR_VISITOR #undef UNARY_OPERATOR_VISITOR
void HandleObjectAccessError(Error &shard_error, const std::string_view accessed_object) {
switch (shard_error) {
case Error::DELETED_OBJECT:
throw ExpressionRuntimeException("Trying to access {} on a deleted object.", accessed_object);
case Error::NONEXISTENT_OBJECT:
throw ExpressionRuntimeException("Trying to access {} from a node object doesn't exist.", accessed_object);
case Error::SERIALIZATION_ERROR:
case Error::VERTEX_HAS_EDGES:
case Error::PROPERTIES_DISABLED:
case Error::VERTEX_ALREADY_INSERTED:
case Error::OBJECT_NOT_FOUND:
throw ExpressionRuntimeException("Unexpected error when accessing {}.", accessed_object);
case Error::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL:
case Error::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE:
case Error::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY:
case Error::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL:
case Error::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY:
case Error::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED:
throw ExpressionRuntimeException("Unexpected schema violation when accessing {}.", accessed_object);
}
}
TypedValue Visit(AndOperator &op) override { TypedValue Visit(AndOperator &op) override {
auto value1 = op.expression1_->Accept(*this); auto value1 = op.expression1_->Accept(*this);
if (value1.IsBool() && !value1.ValueBool()) { if (value1.IsBool() && !value1.ValueBool()) {
@ -396,17 +418,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
has_label = vertex.HasLabel(StorageView::NEW, GetLabel(label)); has_label = vertex.HasLabel(StorageView::NEW, GetLabel(label));
} }
if (has_label.HasError()) { if (has_label.HasError()) {
switch (has_label.GetError()) { HandleObjectAccessError(has_label.GetError().code, "labels");
case Error::DELETED_OBJECT:
throw ExpressionRuntimeException("Trying to access labels on a deleted node.");
case Error::NONEXISTENT_OBJECT:
throw ExpressionRuntimeException("Trying to access labels from a node that doesn't exist.");
case Error::SERIALIZATION_ERROR:
case Error::VERTEX_HAS_EDGES:
case Error::PROPERTIES_DISABLED:
case Error::VERTEX_ALREADY_INSERTED:
throw ExpressionRuntimeException("Unexpected error when accessing labels.");
}
} }
return *has_label; return *has_label;
} }
@ -744,17 +756,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
maybe_prop = record_accessor.GetProperty(StorageView::NEW, ctx_->properties[prop.ix]); maybe_prop = record_accessor.GetProperty(StorageView::NEW, ctx_->properties[prop.ix]);
} }
if (maybe_prop.HasError()) { if (maybe_prop.HasError()) {
switch (maybe_prop.GetError()) { HandleObjectAccessError(maybe_prop.GetError().code, "property");
case Error::DELETED_OBJECT:
throw ExpressionRuntimeException("Trying to get a property from a deleted object.");
case Error::NONEXISTENT_OBJECT:
throw ExpressionRuntimeException("Trying to get a property from an object that doesn't exist.");
case Error::SERIALIZATION_ERROR:
case Error::VERTEX_HAS_EDGES:
case Error::PROPERTIES_DISABLED:
case Error::VERTEX_ALREADY_INSERTED:
throw ExpressionRuntimeException("Unexpected error when getting a property.");
}
} }
return conv_(*maybe_prop, ctx_->memory); return conv_(*maybe_prop, ctx_->memory);
} }
@ -773,17 +775,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
maybe_prop = record_accessor.GetProperty(view_, dba_->NameToProperty(name)); maybe_prop = record_accessor.GetProperty(view_, dba_->NameToProperty(name));
} }
if (maybe_prop.HasError()) { if (maybe_prop.HasError()) {
switch (maybe_prop.GetError()) { HandleObjectAccessError(maybe_prop.GetError().code, "property");
case Error::DELETED_OBJECT:
throw ExpressionRuntimeException("Trying to get a property from a deleted object.");
case Error::NONEXISTENT_OBJECT:
throw ExpressionRuntimeException("Trying to get a property from an object that doesn't exist.");
case Error::SERIALIZATION_ERROR:
case Error::VERTEX_HAS_EDGES:
case Error::PROPERTIES_DISABLED:
case Error::VERTEX_ALREADY_INSERTED:
throw ExpressionRuntimeException("Unexpected error when getting a property.");
}
} }
return conv_(*maybe_prop, ctx_->memory); return conv_(*maybe_prop, ctx_->memory);
} }

View File

@ -15,13 +15,13 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "common/errors.hpp"
#include "coordinator/shard_map.hpp" #include "coordinator/shard_map.hpp"
#include "query/v2/accessors.hpp" #include "query/v2/accessors.hpp"
#include "query/v2/requests.hpp" #include "query/v2/requests.hpp"
#include "query/v2/shard_request_manager.hpp" #include "query/v2/shard_request_manager.hpp"
#include "storage/v3/edge_accessor.hpp" #include "storage/v3/edge_accessor.hpp"
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"
@ -71,9 +71,9 @@ query::v2::TypedValue ToTypedValue(const Value &value) {
} }
} }
storage::v3::Result<communication::bolt::Vertex> ToBoltVertex( communication::bolt::Vertex ToBoltVertex(const query::v2::accessors::VertexAccessor &vertex,
const query::v2::accessors::VertexAccessor &vertex, const msgs::ShardRequestManagerInterface *shard_request_manager, const msgs::ShardRequestManagerInterface *shard_request_manager,
storage::v3::View /*view*/) { storage::v3::View /*view*/) {
auto id = communication::bolt::Id::FromUint(0); auto id = communication::bolt::Id::FromUint(0);
auto labels = vertex.Labels(); auto labels = vertex.Labels();
@ -91,9 +91,9 @@ storage::v3::Result<communication::bolt::Vertex> ToBoltVertex(
return communication::bolt::Vertex{id, new_labels, new_properties}; return communication::bolt::Vertex{id, new_labels, new_properties};
} }
storage::v3::Result<communication::bolt::Edge> ToBoltEdge( communication::bolt::Edge ToBoltEdge(const query::v2::accessors::EdgeAccessor &edge,
const query::v2::accessors::EdgeAccessor &edge, const msgs::ShardRequestManagerInterface *shard_request_manager, const msgs::ShardRequestManagerInterface *shard_request_manager,
storage::v3::View /*view*/) { storage::v3::View /*view*/) {
// TODO(jbajic) Fix bolt communication // TODO(jbajic) Fix bolt communication
auto id = communication::bolt::Id::FromUint(0); auto id = communication::bolt::Id::FromUint(0);
auto from = communication::bolt::Id::FromUint(0); auto from = communication::bolt::Id::FromUint(0);
@ -108,69 +108,64 @@ storage::v3::Result<communication::bolt::Edge> ToBoltEdge(
return communication::bolt::Edge{id, from, to, type, new_properties}; return communication::bolt::Edge{id, from, to, type, new_properties};
} }
storage::v3::Result<communication::bolt::Path> ToBoltPath( communication::bolt::Path ToBoltPath(const query::v2::accessors::Path & /*edge*/,
const query::v2::accessors::Path & /*edge*/, const msgs::ShardRequestManagerInterface * /*shard_request_manager*/, const msgs::ShardRequestManagerInterface * /*shard_request_manager*/,
storage::v3::View /*view*/) { storage::v3::View /*view*/) {
// TODO(jbajic) Fix bolt communication // TODO(jbajic) Fix bolt communication
return {storage::v3::Error::DELETED_OBJECT}; MG_ASSERT(false, "Path is unimplemented!");
return {};
} }
storage::v3::Result<Value> ToBoltValue(const query::v2::TypedValue &value, Value ToBoltValue(const query::v2::TypedValue &value, const msgs::ShardRequestManagerInterface *shard_request_manager,
const msgs::ShardRequestManagerInterface *shard_request_manager, storage::v3::View view) {
storage::v3::View view) {
switch (value.type()) { switch (value.type()) {
case query::v2::TypedValue::Type::Null: case query::v2::TypedValue::Type::Null:
return Value(); return {};
case query::v2::TypedValue::Type::Bool: case query::v2::TypedValue::Type::Bool:
return Value(value.ValueBool()); return {value.ValueBool()};
case query::v2::TypedValue::Type::Int: case query::v2::TypedValue::Type::Int:
return Value(value.ValueInt()); return {value.ValueInt()};
case query::v2::TypedValue::Type::Double: case query::v2::TypedValue::Type::Double:
return Value(value.ValueDouble()); return {value.ValueDouble()};
case query::v2::TypedValue::Type::String: case query::v2::TypedValue::Type::String:
return Value(std::string(value.ValueString())); return {std::string(value.ValueString())};
case query::v2::TypedValue::Type::List: { case query::v2::TypedValue::Type::List: {
std::vector<Value> values; std::vector<Value> values;
values.reserve(value.ValueList().size()); values.reserve(value.ValueList().size());
for (const auto &v : value.ValueList()) { for (const auto &v : value.ValueList()) {
auto maybe_value = ToBoltValue(v, shard_request_manager, view); auto value = ToBoltValue(v, shard_request_manager, view);
if (maybe_value.HasError()) return maybe_value.GetError(); values.emplace_back(std::move(value));
values.emplace_back(std::move(*maybe_value));
} }
return Value(std::move(values)); return {std::move(values)};
} }
case query::v2::TypedValue::Type::Map: { case query::v2::TypedValue::Type::Map: {
std::map<std::string, Value> map; std::map<std::string, Value> map;
for (const auto &kv : value.ValueMap()) { for (const auto &kv : value.ValueMap()) {
auto maybe_value = ToBoltValue(kv.second, shard_request_manager, view); auto value = ToBoltValue(kv.second, shard_request_manager, view);
if (maybe_value.HasError()) return maybe_value.GetError(); map.emplace(kv.first, std::move(value));
map.emplace(kv.first, std::move(*maybe_value));
} }
return Value(std::move(map)); return {std::move(map)};
} }
case query::v2::TypedValue::Type::Vertex: { case query::v2::TypedValue::Type::Vertex: {
auto maybe_vertex = ToBoltVertex(value.ValueVertex(), shard_request_manager, view); auto vertex = ToBoltVertex(value.ValueVertex(), shard_request_manager, view);
if (maybe_vertex.HasError()) return maybe_vertex.GetError(); return {std::move(vertex)};
return Value(std::move(*maybe_vertex));
} }
case query::v2::TypedValue::Type::Edge: { case query::v2::TypedValue::Type::Edge: {
auto maybe_edge = ToBoltEdge(value.ValueEdge(), shard_request_manager, view); auto edge = ToBoltEdge(value.ValueEdge(), shard_request_manager, view);
if (maybe_edge.HasError()) return maybe_edge.GetError(); return {std::move(edge)};
return Value(std::move(*maybe_edge));
} }
case query::v2::TypedValue::Type::Path: { case query::v2::TypedValue::Type::Path: {
auto maybe_path = ToBoltPath(value.ValuePath(), shard_request_manager, view); auto path = ToBoltPath(value.ValuePath(), shard_request_manager, view);
if (maybe_path.HasError()) return maybe_path.GetError(); return {std::move(path)};
return Value(std::move(*maybe_path));
} }
case query::v2::TypedValue::Type::Date: case query::v2::TypedValue::Type::Date:
return Value(value.ValueDate()); return {value.ValueDate()};
case query::v2::TypedValue::Type::LocalTime: case query::v2::TypedValue::Type::LocalTime:
return Value(value.ValueLocalTime()); return {value.ValueLocalTime()};
case query::v2::TypedValue::Type::LocalDateTime: case query::v2::TypedValue::Type::LocalDateTime:
return Value(value.ValueLocalDateTime()); return {value.ValueLocalDateTime()};
case query::v2::TypedValue::Type::Duration: case query::v2::TypedValue::Type::Duration:
return Value(value.ValueDuration()); return {value.ValueDuration()};
} }
} }

View File

@ -20,6 +20,7 @@
#include "storage/v3/result.hpp" #include "storage/v3/result.hpp"
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"
#include "utils/result.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
class EdgeAccessor; class EdgeAccessor;
@ -35,36 +36,36 @@ namespace memgraph::glue::v2 {
/// @param storage::v3::View for deciding which vertex attributes are visible. /// @param storage::v3::View for deciding which vertex attributes are visible.
/// ///
/// @throw std::bad_alloc /// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Vertex> ToBoltVertex( communication::bolt::Vertex ToBoltVertex(const storage::v3::VertexAccessor &vertex,
const storage::v3::VertexAccessor &vertex, const msgs::ShardRequestManagerInterface *shard_request_manager, const msgs::ShardRequestManagerInterface *shard_request_manager,
storage::v3::View view); storage::v3::View view);
/// @param storage::v3::EdgeAccessor for converting to communication::bolt::Edge. /// @param storage::v3::EdgeAccessor for converting to communication::bolt::Edge.
/// @param msgs::ShardRequestManagerInterface *shard_request_manager getting edge type and property names. /// @param msgs::ShardRequestManagerInterface *shard_request_manager getting edge type and property names.
/// @param storage::v3::View for deciding which edge attributes are visible. /// @param storage::v3::View for deciding which edge attributes are visible.
/// ///
/// @throw std::bad_alloc /// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Edge> ToBoltEdge( communication::bolt::Edge ToBoltEdge(const storage::v3::EdgeAccessor &edge,
const storage::v3::EdgeAccessor &edge, const msgs::ShardRequestManagerInterface *shard_request_manager, const msgs::ShardRequestManagerInterface *shard_request_manager,
storage::v3::View view); storage::v3::View view);
/// @param query::v2::Path for converting to communication::bolt::Path. /// @param query::v2::Path for converting to communication::bolt::Path.
/// @param msgs::ShardRequestManagerInterface *shard_request_manager ToBoltVertex and ToBoltEdge. /// @param msgs::ShardRequestManagerInterface *shard_request_manager ToBoltVertex and ToBoltEdge.
/// @param storage::v3::View for ToBoltVertex and ToBoltEdge. /// @param storage::v3::View for ToBoltVertex and ToBoltEdge.
/// ///
/// @throw std::bad_alloc /// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Path> ToBoltPath( communication::bolt::Path ToBoltPath(const query::v2::accessors::Path &path,
const query::v2::accessors::Path &path, const msgs::ShardRequestManagerInterface *shard_request_manager, const msgs::ShardRequestManagerInterface *shard_request_manager,
storage::v3::View view); storage::v3::View view);
/// @param query::v2::TypedValue for converting to communication::bolt::Value. /// @param query::v2::TypedValue for converting to communication::bolt::Value.
/// @param msgs::ShardRequestManagerInterface *shard_request_manager ToBoltVertex and ToBoltEdge. /// @param msgs::ShardRequestManagerInterface *shard_request_manager ToBoltVertex and ToBoltEdge.
/// @param storage::v3::View for ToBoltVertex and ToBoltEdge. /// @param storage::v3::View for ToBoltVertex and ToBoltEdge.
/// ///
/// @throw std::bad_alloc /// @throw std::bad_alloc
storage::v3::Result<communication::bolt::Value> ToBoltValue( communication::bolt::Value ToBoltValue(const query::v2::TypedValue &value,
const query::v2::TypedValue &value, const msgs::ShardRequestManagerInterface *shard_request_manager, const msgs::ShardRequestManagerInterface *shard_request_manager,
storage::v3::View view); storage::v3::View view);
query::v2::TypedValue ToTypedValue(const communication::bolt::Value &value); query::v2::TypedValue ToTypedValue(const communication::bolt::Value &value);

View File

@ -20,6 +20,8 @@
#include <boost/uuid/uuid_generators.hpp> #include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp> #include <boost/uuid/uuid_io.hpp>
#include "utils/logging.hpp"
namespace memgraph::io { namespace memgraph::io {
struct PartialAddress { struct PartialAddress {
@ -58,18 +60,39 @@ struct Address {
uint16_t last_known_port; uint16_t last_known_port;
static Address TestAddress(uint16_t port) { static Address TestAddress(uint16_t port) {
MG_ASSERT(port <= 255);
return Address{ return Address{
.unique_id = boost::uuids::uuid{boost::uuids::random_generator()()}, .unique_id = boost::uuids::uuid{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static_cast<unsigned char>(port)},
.last_known_port = port, .last_known_port = port,
}; };
} }
// NB: don't use this in test code because it is non-deterministic
static Address UniqueLocalAddress() { static Address UniqueLocalAddress() {
return Address{ return Address{
.unique_id = boost::uuids::uuid{boost::uuids::random_generator()()}, .unique_id = boost::uuids::uuid{boost::uuids::random_generator()()},
}; };
} }
/// `Coordinator`s have constant UUIDs because there is at most one per ip/port pair.
Address ForkLocalCoordinator() {
return Address{
.unique_id = boost::uuids::uuid{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
.last_known_ip = last_known_ip,
.last_known_port = last_known_port,
};
}
/// `ShardManager`s have constant UUIDs because there is at most one per ip/port pair.
Address ForkLocalShardManager() {
return Address{
.unique_id = boost::uuids::uuid{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
.last_known_ip = last_known_ip,
.last_known_port = last_known_port,
};
}
/// Returns a new ID with the same IP and port but a unique UUID. /// Returns a new ID with the same IP and port but a unique UUID.
Address ForkUniqueAddress() { Address ForkUniqueAddress() {
return Address{ return Address{

View File

@ -64,7 +64,6 @@ class Shared {
waiting_ = true; waiting_ = true;
while (!item_) { while (!item_) {
bool simulator_progressed = false;
if (simulator_notifier_) [[unlikely]] { if (simulator_notifier_) [[unlikely]] {
// We can't hold our own lock while notifying // We can't hold our own lock while notifying
// the simulator because notifying the simulator // the simulator because notifying the simulator
@ -77,7 +76,7 @@ class Shared {
// so we have to get out of its way to avoid // so we have to get out of its way to avoid
// a cyclical deadlock. // a cyclical deadlock.
lock.unlock(); lock.unlock();
simulator_progressed = std::invoke(simulator_notifier_); std::invoke(simulator_notifier_);
lock.lock(); lock.lock();
if (item_) { if (item_) {
// item may have been filled while we // item may have been filled while we
@ -85,8 +84,7 @@ class Shared {
// the simulator of our waiting_ status. // the simulator of our waiting_ status.
break; break;
} }
} } else {
if (!simulator_progressed) [[likely]] {
cv_.wait(lock); cv_.wait(lock);
} }
MG_ASSERT(!consumed_, "Future consumed twice!"); MG_ASSERT(!consumed_, "Future consumed twice!");

View File

@ -13,6 +13,7 @@
#include <chrono> #include <chrono>
#include <cmath> #include <cmath>
#include <compare>
#include <unordered_map> #include <unordered_map>
#include <boost/core/demangle.hpp> #include <boost/core/demangle.hpp>
@ -39,6 +40,8 @@ struct LatencyHistogramSummary {
Duration p100; Duration p100;
Duration sum; Duration sum;
friend bool operator==(const LatencyHistogramSummary &lhs, const LatencyHistogramSummary &rhs) = default;
friend std::ostream &operator<<(std::ostream &in, const LatencyHistogramSummary &histo) { friend std::ostream &operator<<(std::ostream &in, const LatencyHistogramSummary &histo) {
in << "{ \"count\": " << histo.count; in << "{ \"count\": " << histo.count;
in << ", \"p0\": " << histo.p0.count(); in << ", \"p0\": " << histo.p0.count();
@ -80,6 +83,8 @@ struct LatencyHistogramSummaries {
return output; return output;
} }
friend bool operator==(const LatencyHistogramSummaries &lhs, const LatencyHistogramSummaries &rhs) = default;
friend std::ostream &operator<<(std::ostream &in, const LatencyHistogramSummaries &histo) { friend std::ostream &operator<<(std::ostream &in, const LatencyHistogramSummaries &histo) {
using memgraph::utils::print_helpers::operator<<; using memgraph::utils::print_helpers::operator<<;
in << histo.latencies; in << histo.latencies;

View File

@ -19,7 +19,6 @@
#include <map> #include <map>
#include <set> #include <set>
#include <thread> #include <thread>
#include <unordered_map>
#include <vector> #include <vector>
#include <boost/core/demangle.hpp> #include <boost/core/demangle.hpp>
@ -182,7 +181,7 @@ struct PendingClientRequest {
struct Leader { struct Leader {
std::map<Address, FollowerTracker> followers; std::map<Address, FollowerTracker> followers;
std::unordered_map<LogIndex, PendingClientRequest> pending_client_requests; std::map<LogIndex, PendingClientRequest> pending_client_requests;
Time last_broadcast = Time::min(); Time last_broadcast = Time::min();
std::string static ToString() { return "\tLeader \t"; } std::string static ToString() { return "\tLeader \t"; }
@ -683,7 +682,7 @@ class Raft {
return Leader{ return Leader{
.followers = std::move(followers), .followers = std::move(followers),
.pending_client_requests = std::unordered_map<LogIndex, PendingClientRequest>(), .pending_client_requests = std::map<LogIndex, PendingClientRequest>(),
}; };
} }

View File

@ -23,6 +23,12 @@ namespace memgraph::io::simulator {
void SimulatorHandle::ShutDown() { void SimulatorHandle::ShutDown() {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
should_shut_down_ = true; should_shut_down_ = true;
for (auto it = promises_.begin(); it != promises_.end();) {
auto &[promise_key, dop] = *it;
std::move(dop).promise.TimeOut();
it = promises_.erase(it);
}
can_receive_.clear();
cv_.notify_all(); cv_.notify_all();
} }
@ -46,52 +52,84 @@ void SimulatorHandle::IncrementServerCountAndWaitForQuiescentState(Address addre
const bool all_servers_blocked = blocked_servers == server_addresses_.size(); const bool all_servers_blocked = blocked_servers == server_addresses_.size();
if (all_servers_blocked) { if (all_servers_blocked) {
spdlog::trace("quiescent state detected - {} out of {} servers now blocked on receive", blocked_servers,
server_addresses_.size());
return; return;
} }
spdlog::trace("not returning from quiescent because we see {} blocked out of {}", blocked_servers,
server_addresses_.size());
cv_.wait(lock); cv_.wait(lock);
} }
} }
bool SortInFlight(const std::pair<Address, OpaqueMessage> &lhs, const std::pair<Address, OpaqueMessage> &rhs) {
// NB: never sort based on the request ID etc...
// This should only be used from std::stable_sort
// because by comparing on the from_address alone,
// we expect the sender ordering to remain
// deterministic.
const auto &[addr_1, om_1] = lhs;
const auto &[addr_2, om_2] = rhs;
return om_1.from_address < om_2.from_address;
}
bool SimulatorHandle::MaybeTickSimulator() { bool SimulatorHandle::MaybeTickSimulator() {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
const size_t blocked_servers = blocked_on_receive_.size(); const size_t blocked_servers = blocked_on_receive_.size();
if (blocked_servers < server_addresses_.size()) { if (should_shut_down_ || blocked_servers < server_addresses_.size()) {
// we only need to advance the simulator when all // we only need to advance the simulator when all
// servers have reached a quiescent state, blocked // servers have reached a quiescent state, blocked
// on their own futures or receive methods. // on their own futures or receive methods.
return false; return false;
} }
// We allow the simulator to progress the state of the system only
// after all servers are blocked on receive.
spdlog::trace("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ simulator tick ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
stats_.simulator_ticks++; stats_.simulator_ticks++;
blocked_on_receive_.clear();
cv_.notify_all(); cv_.notify_all();
TimeoutPromisesPastDeadline(); bool timed_anything_out = TimeoutPromisesPastDeadline();
if (timed_anything_out) {
spdlog::trace("simulator progressing: timed out a request");
}
const Duration clock_advance = std::chrono::microseconds{time_distrib_(rng_)};
// We don't always want to advance the clock with every message that we deliver because
// when we advance it for every message, it causes timeouts to occur for any "request path"
// over a certain length. Alternatively, we don't want to simply deliver multiple messages
// in a single simulator tick because that would reduce the amount of concurrent message
// mixing that may naturally occur in production. This approach is to mod the random clock
// advance by a prime number (hopefully avoiding most harmonic effects that would be introduced
// by only advancing the clock by an even amount etc...) and only advancing the clock close to
// half of the time.
if (clock_advance.count() % 97 > 49) {
spdlog::trace("simulator progressing: clock advanced by {}", clock_advance.count());
cluster_wide_time_microseconds_ += clock_advance;
stats_.elapsed_time = cluster_wide_time_microseconds_ - config_.start_time;
}
if (cluster_wide_time_microseconds_ >= config_.abort_time) {
spdlog::error(
"Cluster has executed beyond its configured abort_time, and something may be failing to make progress "
"in an expected amount of time. The SimulatorConfig.rng_seed for this run is {}",
config_.rng_seed);
throw utils::BasicException{"Cluster has executed beyond its configured abort_time"};
}
if (in_flight_.empty()) { if (in_flight_.empty()) {
// return early here because there are no messages to schedule
// We tick the clock forward when all servers are blocked but
// there are no in-flight messages to schedule delivery of.
const Duration clock_advance = std::chrono::microseconds{time_distrib_(rng_)};
cluster_wide_time_microseconds_ += clock_advance;
if (cluster_wide_time_microseconds_ >= config_.abort_time) {
if (should_shut_down_) {
return false;
}
spdlog::error(
"Cluster has executed beyond its configured abort_time, and something may be failing to make progress "
"in an expected amount of time.");
throw utils::BasicException{"Cluster has executed beyond its configured abort_time"};
}
return true; return true;
} }
if (config_.scramble_messages) { std::stable_sort(in_flight_.begin(), in_flight_.end(), SortInFlight);
if (config_.scramble_messages && in_flight_.size() > 1) {
// scramble messages // scramble messages
std::uniform_int_distribution<size_t> swap_distrib(0, in_flight_.size() - 1); std::uniform_int_distribution<size_t> swap_distrib(0, in_flight_.size() - 1);
const size_t swap_index = swap_distrib(rng_); const size_t swap_index = swap_distrib(rng_);
@ -120,17 +158,22 @@ bool SimulatorHandle::MaybeTickSimulator() {
if (should_drop || normal_timeout) { if (should_drop || normal_timeout) {
stats_.timed_out_requests++; stats_.timed_out_requests++;
dop.promise.TimeOut(); dop.promise.TimeOut();
spdlog::trace("simulator timing out request ");
} else { } else {
stats_.total_responses++; stats_.total_responses++;
Duration response_latency = cluster_wide_time_microseconds_ - dop.requested_at; Duration response_latency = cluster_wide_time_microseconds_ - dop.requested_at;
auto type_info = opaque_message.type_info; auto type_info = opaque_message.type_info;
dop.promise.Fill(std::move(opaque_message), response_latency); dop.promise.Fill(std::move(opaque_message), response_latency);
histograms_.Measure(type_info, response_latency); histograms_.Measure(type_info, response_latency);
spdlog::trace("simulator replying to request");
} }
} else if (should_drop) { } else if (should_drop) {
// don't add it anywhere, let it drop // don't add it anywhere, let it drop
spdlog::trace("simulator silently dropping request");
} else { } else {
// add to can_receive_ if not // add to can_receive_ if not
spdlog::trace("simulator adding message to can_receive_ from {} to {}", opaque_message.from_address.last_known_port,
opaque_message.to_address.last_known_port);
const auto &[om_vec, inserted] = const auto &[om_vec, inserted] =
can_receive_.try_emplace(to_address.ToPartialAddress(), std::vector<OpaqueMessage>()); can_receive_.try_emplace(to_address.ToPartialAddress(), std::vector<OpaqueMessage>());
om_vec->second.emplace_back(std::move(opaque_message)); om_vec->second.emplace_back(std::move(opaque_message));

View File

@ -52,26 +52,29 @@ class SimulatorHandle {
std::set<Address> blocked_on_receive_; std::set<Address> blocked_on_receive_;
std::set<Address> server_addresses_; std::set<Address> server_addresses_;
std::mt19937 rng_; std::mt19937 rng_;
std::uniform_int_distribution<int> time_distrib_{5, 50}; std::uniform_int_distribution<int> time_distrib_{0, 30000};
std::uniform_int_distribution<int> drop_distrib_{0, 99}; std::uniform_int_distribution<int> drop_distrib_{0, 99};
SimulatorConfig config_; SimulatorConfig config_;
MessageHistogramCollector histograms_; MessageHistogramCollector histograms_;
RequestId request_id_counter_{0}; RequestId request_id_counter_{0};
void TimeoutPromisesPastDeadline() { bool TimeoutPromisesPastDeadline() {
bool timed_anything_out = false;
const Time now = cluster_wide_time_microseconds_; const Time now = cluster_wide_time_microseconds_;
for (auto it = promises_.begin(); it != promises_.end();) { for (auto it = promises_.begin(); it != promises_.end();) {
auto &[promise_key, dop] = *it; auto &[promise_key, dop] = *it;
if (dop.deadline < now && config_.perform_timeouts) { if (dop.deadline < now && config_.perform_timeouts) {
spdlog::info("timing out request from requester {}.", promise_key.requester_address.ToString()); spdlog::trace("timing out request from requester {}.", promise_key.requester_address.ToString());
std::move(dop).promise.TimeOut(); std::move(dop).promise.TimeOut();
it = promises_.erase(it); it = promises_.erase(it);
stats_.timed_out_requests++; stats_.timed_out_requests++;
timed_anything_out = true;
} else { } else {
++it; ++it;
} }
} }
return timed_anything_out;
} }
public: public:
@ -103,6 +106,7 @@ class SimulatorHandle {
template <Message Request, Message Response> template <Message Request, Message Response>
ResponseFuture<Response> SubmitRequest(Address to_address, Address from_address, Request &&request, Duration timeout, ResponseFuture<Response> SubmitRequest(Address to_address, Address from_address, Request &&request, Duration timeout,
std::function<bool()> &&maybe_tick_simulator) { std::function<bool()> &&maybe_tick_simulator) {
spdlog::trace("submitting request to {}", to_address.last_known_port);
auto type_info = TypeInfoFor(request); auto type_info = TypeInfoFor(request);
auto [future, promise] = memgraph::io::FuturePromisePairWithNotifier<ResponseResult<Response>>( auto [future, promise] = memgraph::io::FuturePromisePairWithNotifier<ResponseResult<Response>>(
@ -146,8 +150,6 @@ class SimulatorHandle {
requires(sizeof...(Ms) > 0) RequestResult<Ms...> Receive(const Address &receiver, Duration timeout) { requires(sizeof...(Ms) > 0) RequestResult<Ms...> Receive(const Address &receiver, Duration timeout) {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
blocked_on_receive_.emplace(receiver);
const Time deadline = cluster_wide_time_microseconds_ + timeout; const Time deadline = cluster_wide_time_microseconds_ + timeout;
auto partial_address = receiver.ToPartialAddress(); auto partial_address = receiver.ToPartialAddress();
@ -164,38 +166,40 @@ class SimulatorHandle {
auto m_opt = std::move(message).Take<Ms...>(); auto m_opt = std::move(message).Take<Ms...>();
MG_ASSERT(m_opt.has_value(), "Wrong message type received compared to the expected type"); MG_ASSERT(m_opt.has_value(), "Wrong message type received compared to the expected type");
blocked_on_receive_.erase(receiver);
return std::move(m_opt).value(); return std::move(m_opt).value();
} }
} }
lock.unlock(); if (!should_shut_down_) {
bool made_progress = MaybeTickSimulator(); if (!blocked_on_receive_.contains(receiver)) {
lock.lock(); blocked_on_receive_.emplace(receiver);
if (!should_shut_down_ && !made_progress) { spdlog::trace("blocking receiver {}", receiver.ToPartialAddress().port);
cv_.notify_all();
}
cv_.wait(lock); cv_.wait(lock);
} }
} }
spdlog::trace("timing out receiver {}", receiver.ToPartialAddress().port);
blocked_on_receive_.erase(receiver);
return TimedOut{}; return TimedOut{};
} }
template <Message M> template <Message M>
void Send(Address to_address, Address from_address, RequestId request_id, M message) { void Send(Address to_address, Address from_address, RequestId request_id, M message) {
spdlog::trace("sending message from {} to {}", from_address.last_known_port, to_address.last_known_port);
auto type_info = TypeInfoFor(message); auto type_info = TypeInfoFor(message);
std::unique_lock<std::mutex> lock(mu_); {
std::any message_any(std::move(message)); std::unique_lock<std::mutex> lock(mu_);
OpaqueMessage om{.to_address = to_address, std::any message_any(std::move(message));
.from_address = from_address, OpaqueMessage om{.to_address = to_address,
.request_id = request_id, .from_address = from_address,
.message = std::move(message_any), .request_id = request_id,
.type_info = type_info}; .message = std::move(message_any),
in_flight_.emplace_back(std::make_pair(std::move(to_address), std::move(om))); .type_info = type_info};
in_flight_.emplace_back(std::make_pair(std::move(to_address), std::move(om)));
stats_.total_messages++; stats_.total_messages++;
} // lock dropped before cv notification
cv_.notify_all(); cv_.notify_all();
} }

View File

@ -13,6 +13,10 @@
#include <cstdint> #include <cstdint>
#include <fmt/format.h>
#include "io/time.hpp"
namespace memgraph::io::simulator { namespace memgraph::io::simulator {
struct SimulatorStats { struct SimulatorStats {
uint64_t total_messages = 0; uint64_t total_messages = 0;
@ -21,5 +25,22 @@ struct SimulatorStats {
uint64_t total_requests = 0; uint64_t total_requests = 0;
uint64_t total_responses = 0; uint64_t total_responses = 0;
uint64_t simulator_ticks = 0; uint64_t simulator_ticks = 0;
Duration elapsed_time;
friend bool operator==(const SimulatorStats & /* lhs */, const SimulatorStats & /* rhs */) = default;
friend std::ostream &operator<<(std::ostream &in, const SimulatorStats &stats) {
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(stats.elapsed_time).count();
std::string formated = fmt::format(
"SimulatorStats {{ total_messages: {}, dropped_messages: {}, timed_out_requests: {}, total_requests: {}, "
"total_responses: {}, simulator_ticks: {}, elapsed_time: {}ms }}",
stats.total_messages, stats.dropped_messages, stats.timed_out_requests, stats.total_requests,
stats.total_responses, stats.simulator_ticks, elapsed_ms);
in << formated;
return in;
}
}; };
}; // namespace memgraph::io::simulator }; // namespace memgraph::io::simulator

View File

@ -137,7 +137,14 @@ class Io {
Address GetAddress() { return address_; } Address GetAddress() { return address_; }
void SetAddress(Address address) { address_ = address; } void SetAddress(Address address) { address_ = address; }
Io<I> ForkLocal() { return Io(implementation_, address_.ForkUniqueAddress()); } Io<I> ForkLocal(boost::uuids::uuid uuid) {
Address new_address{
.unique_id = uuid,
.last_known_ip = address_.last_known_ip,
.last_known_port = address_.last_known_port,
};
return Io(implementation_, new_address);
}
LatencyHistogramSummaries ResponseLatencies() { return implementation_.ResponseLatencies(); } LatencyHistogramSummaries ResponseLatencies() { return implementation_.ResponseLatencies(); }
}; };

View File

@ -42,6 +42,7 @@ struct MachineConfig {
boost::asio::ip::address listen_ip; boost::asio::ip::address listen_ip;
uint16_t listen_port; uint16_t listen_port;
size_t shard_worker_threads = std::max(static_cast<unsigned int>(1), std::thread::hardware_concurrency()); size_t shard_worker_threads = std::max(static_cast<unsigned int>(1), std::thread::hardware_concurrency());
bool sync_message_handling = false;
}; };
} // namespace memgraph::machine_manager } // namespace memgraph::machine_manager

View File

@ -78,10 +78,10 @@ class MachineManager {
MachineManager(io::Io<IoImpl> io, MachineConfig config, Coordinator coordinator) MachineManager(io::Io<IoImpl> io, MachineConfig config, Coordinator coordinator)
: io_(io), : io_(io),
config_(config), config_(config),
coordinator_address_(io.GetAddress().ForkUniqueAddress()), coordinator_address_(io.GetAddress().ForkLocalCoordinator()),
shard_manager_{io.ForkLocal(), config.shard_worker_threads, coordinator_address_} { shard_manager_{io.ForkLocal(io.GetAddress().ForkLocalShardManager().unique_id), config.shard_worker_threads,
auto coordinator_io = io.ForkLocal(); coordinator_address_} {
coordinator_io.SetAddress(coordinator_address_); auto coordinator_io = io.ForkLocal(coordinator_address_.unique_id);
CoordinatorWorker coordinator_worker{coordinator_io, coordinator_queue_, coordinator}; CoordinatorWorker coordinator_worker{coordinator_io, coordinator_queue_, coordinator};
coordinator_handle_ = std::jthread([coordinator = std::move(coordinator_worker)]() mutable { coordinator.Run(); }); coordinator_handle_ = std::jthread([coordinator = std::move(coordinator_worker)]() mutable { coordinator.Run(); });
} }
@ -101,11 +101,23 @@ class MachineManager {
Address CoordinatorAddress() { return coordinator_address_; } Address CoordinatorAddress() { return coordinator_address_; }
void Run() { void Run() {
while (!io_.ShouldShutDown()) { while (true) {
MaybeBlockOnSyncHandling();
if (io_.ShouldShutDown()) {
break;
}
const auto now = io_.Now(); const auto now = io_.Now();
uint64_t now_us = now.time_since_epoch().count();
uint64_t next_us = next_cron_.time_since_epoch().count();
if (now >= next_cron_) { if (now >= next_cron_) {
spdlog::info("now {} >= next_cron_ {}", now_us, next_us);
next_cron_ = Cron(); next_cron_ = Cron();
} else {
spdlog::info("now {} < next_cron_ {}", now_us, next_us);
} }
Duration receive_timeout = std::max(next_cron_, now) - now; Duration receive_timeout = std::max(next_cron_, now) - now;
@ -194,10 +206,27 @@ class MachineManager {
} }
private: private:
// This method exists for controlling concurrency
// during deterministic simulation testing.
void MaybeBlockOnSyncHandling() {
if (!config_.sync_message_handling) {
return;
}
// block on coordinator
coordinator_queue_.BlockOnQuiescence();
// block on shards
shard_manager_.BlockOnQuiescence();
}
Time Cron() { Time Cron() {
spdlog::info("running MachineManager::Cron, address {}", io_.GetAddress().ToString()); spdlog::info("running MachineManager::Cron, address {}", io_.GetAddress().ToString());
coordinator_queue_.Push(coordinator::coordinator_worker::Cron{}); coordinator_queue_.Push(coordinator::coordinator_worker::Cron{});
return shard_manager_.Cron(); MaybeBlockOnSyncHandling();
Time ret = shard_manager_.Cron();
MaybeBlockOnSyncHandling();
return ret;
} }
}; };

View File

@ -33,6 +33,7 @@
#include <spdlog/sinks/dist_sink.h> #include <spdlog/sinks/dist_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h> #include <spdlog/sinks/stdout_color_sinks.h>
#include "common/errors.hpp"
#include "communication/bolt/v1/constants.hpp" #include "communication/bolt/v1/constants.hpp"
#include "communication/websocket/auth.hpp" #include "communication/websocket/auth.hpp"
#include "communication/websocket/server.hpp" #include "communication/websocket/server.hpp"
@ -480,20 +481,9 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
const auto &summary = interpreter_.Pull(&stream, n, qid); const auto &summary = interpreter_.Pull(&stream, n, qid);
std::map<std::string, memgraph::communication::bolt::Value> decoded_summary; std::map<std::string, memgraph::communication::bolt::Value> decoded_summary;
for (const auto &kv : summary) { for (const auto &kv : summary) {
auto maybe_value = memgraph::glue::v2::ToBoltValue(kv.second, interpreter_.GetShardRequestManager(), auto bolt_value = memgraph::glue::v2::ToBoltValue(kv.second, interpreter_.GetShardRequestManager(),
memgraph::storage::v3::View::NEW); memgraph::storage::v3::View::NEW);
if (maybe_value.HasError()) { decoded_summary.emplace(kv.first, std::move(bolt_value));
switch (maybe_value.GetError()) {
case memgraph::storage::v3::Error::DELETED_OBJECT:
case memgraph::storage::v3::Error::SERIALIZATION_ERROR:
case memgraph::storage::v3::Error::VERTEX_HAS_EDGES:
case memgraph::storage::v3::Error::PROPERTIES_DISABLED:
case memgraph::storage::v3::Error::NONEXISTENT_OBJECT:
case memgraph::storage::v3::Error::VERTEX_ALREADY_INSERTED:
throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming summary.");
}
}
decoded_summary.emplace(kv.first, std::move(*maybe_value));
} }
return decoded_summary; return decoded_summary;
} catch (const memgraph::query::v2::QueryException &e) { } catch (const memgraph::query::v2::QueryException &e) {
@ -514,21 +504,8 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
std::vector<memgraph::communication::bolt::Value> decoded_values; std::vector<memgraph::communication::bolt::Value> decoded_values;
decoded_values.reserve(values.size()); decoded_values.reserve(values.size());
for (const auto &v : values) { for (const auto &v : values) {
auto maybe_value = memgraph::glue::v2::ToBoltValue(v, shard_request_manager_, memgraph::storage::v3::View::NEW); auto bolt_value = memgraph::glue::v2::ToBoltValue(v, shard_request_manager_, memgraph::storage::v3::View::NEW);
if (maybe_value.HasError()) { decoded_values.emplace_back(std::move(bolt_value));
switch (maybe_value.GetError()) {
case memgraph::storage::v3::Error::DELETED_OBJECT:
throw memgraph::communication::bolt::ClientError("Returning a deleted object as a result.");
case memgraph::storage::v3::Error::NONEXISTENT_OBJECT:
throw memgraph::communication::bolt::ClientError("Returning a nonexistent object as a result.");
case memgraph::storage::v3::Error::VERTEX_HAS_EDGES:
case memgraph::storage::v3::Error::SERIALIZATION_ERROR:
case memgraph::storage::v3::Error::PROPERTIES_DISABLED:
case memgraph::storage::v3::Error::VERTEX_ALREADY_INSERTED:
throw memgraph::communication::bolt::ClientError("Unexpected storage error when streaming results.");
}
}
decoded_values.emplace_back(std::move(*maybe_value));
} }
encoder_->MessageRecord(decoded_values); encoder_->MessageRecord(decoded_values);
} }

View File

@ -21,11 +21,12 @@
#include "query/v2/requests.hpp" #include "query/v2/requests.hpp"
#include "storage/v3/conversions.hpp" #include "storage/v3/conversions.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"
namespace memgraph::msgs { namespace memgraph::msgs {
class ShardRequestManagerInterface; class ShardRequestManagerInterface;
} // namespace memgraph::msgs } // namespace memgraph::msgs
namespace memgraph::query::v2 { namespace memgraph::query::v2 {
@ -44,6 +45,6 @@ class Callable {
} // namespace detail } // namespace detail
using ExpressionEvaluator = memgraph::expr::ExpressionEvaluator< using ExpressionEvaluator = memgraph::expr::ExpressionEvaluator<
TypedValue, memgraph::query::v2::EvaluationContext, memgraph::msgs::ShardRequestManagerInterface, storage::v3::View, TypedValue, memgraph::query::v2::EvaluationContext, memgraph::msgs::ShardRequestManagerInterface, storage::v3::View,
storage::v3::LabelId, msgs::Value, detail::Callable, memgraph::storage::v3::Error, memgraph::expr::QueryEngineTag>; storage::v3::LabelId, msgs::Value, detail::Callable, common::ErrorCode, memgraph::expr::QueryEngineTag>;
} // namespace memgraph::query::v2 } // namespace memgraph::query::v2

View File

@ -20,7 +20,6 @@
#include "query/v2/bindings/symbol.hpp" #include "query/v2/bindings/symbol.hpp"
#include "query/v2/bindings/typed_value.hpp" #include "query/v2/bindings/typed_value.hpp"
#include "query/v2/db_accessor.hpp"
#include "query/v2/exceptions.hpp" #include "query/v2/exceptions.hpp"
#include "query/v2/frontend/ast/ast.hpp" #include "query/v2/frontend/ast/ast.hpp"
#include "query/v2/path.hpp" #include "query/v2/path.hpp"
@ -28,7 +27,6 @@
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp" #include "storage/v3/result.hpp"
#include "storage/v3/shard_operation_result.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"
#include "utils/exceptions.hpp" #include "utils/exceptions.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
@ -82,39 +80,5 @@ inline void ExpectType(const Symbol &symbol, const TypedValue &value, TypedValue
throw QueryRuntimeException("Expected a {} for '{}', but got {}.", expected, symbol.name(), value.type()); throw QueryRuntimeException("Expected a {} for '{}', but got {}.", expected, symbol.name(), value.type());
} }
template <typename T>
concept AccessorWithSetProperty = requires(T accessor, const storage::v3::PropertyId key,
const storage::v3::PropertyValue new_value) {
{ accessor.SetProperty(key, new_value) } -> std::same_as<storage::v3::Result<storage::v3::PropertyValue>>;
};
template <typename T>
concept AccessorWithSetPropertyAndValidate = requires(T accessor, const storage::v3::PropertyId key,
const storage::v3::PropertyValue new_value) {
{
accessor.SetPropertyAndValidate(key, new_value)
} -> std::same_as<storage::v3::ShardOperationResult<storage::v3::PropertyValue>>;
};
template <typename TRecordAccessor>
concept RecordAccessor =
AccessorWithSetProperty<TRecordAccessor> || AccessorWithSetPropertyAndValidate<TRecordAccessor>;
inline void HandleErrorOnPropertyUpdate(const storage::v3::Error error) {
switch (error) {
case storage::v3::Error::SERIALIZATION_ERROR:
throw TransactionSerializationException();
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to set properties on a deleted object.");
case storage::v3::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException("Can't set property because properties on edges are disabled.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::NONEXISTENT_OBJECT:
case storage::v3::Error::VERTEX_ALREADY_INSERTED:
throw QueryRuntimeException("Unexpected error when setting a property.");
}
}
int64_t QueryTimestamp(); int64_t QueryTimestamp();
} // namespace memgraph::query::v2 } // namespace memgraph::query::v2

View File

@ -23,7 +23,6 @@
#include "storage/v3/key_store.hpp" #include "storage/v3/key_store.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp" #include "storage/v3/result.hpp"
#include "storage/v3/shard_operation_result.hpp"
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Our communication layer and query engine don't mix // Our communication layer and query engine don't mix
@ -37,7 +36,7 @@
// This cannot be avoided by simple include orderings so we // This cannot be avoided by simple include orderings so we
// simply undefine those macros as we're sure that libkrb5 // simply undefine those macros as we're sure that libkrb5
// won't and can't be used anywhere in the query engine. // won't and can't be used anywhere in the query engine.
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/result.hpp" #include "utils/result.hpp"
@ -65,24 +64,11 @@ class EdgeAccessor final {
auto Properties(storage::v3::View view) const { return impl_.Properties(view); } auto Properties(storage::v3::View view) const { return impl_.Properties(view); }
storage::v3::Result<storage::v3::PropertyValue> GetProperty(storage::v3::View view, storage::v3::ShardResult<storage::v3::PropertyValue> GetProperty(storage::v3::View view,
storage::v3::PropertyId key) const { storage::v3::PropertyId key) const {
return impl_.GetProperty(key, view); return impl_.GetProperty(key, view);
} }
storage::v3::Result<storage::v3::PropertyValue> SetProperty(storage::v3::PropertyId key,
const storage::v3::PropertyValue &value) {
return impl_.SetProperty(key, value);
}
storage::v3::Result<storage::v3::PropertyValue> RemoveProperty(storage::v3::PropertyId key) {
return SetProperty(key, storage::v3::PropertyValue());
}
storage::v3::Result<std::map<storage::v3::PropertyId, storage::v3::PropertyValue>> ClearProperties() {
return impl_.ClearProperties();
}
VertexAccessor To() const; VertexAccessor To() const;
VertexAccessor From() const; VertexAccessor From() const;
@ -114,53 +100,19 @@ class VertexAccessor final {
auto PrimaryKey(storage::v3::View view) const { return impl_.PrimaryKey(view); } auto PrimaryKey(storage::v3::View view) const { return impl_.PrimaryKey(view); }
storage::v3::ShardOperationResult<bool> AddLabel(storage::v3::LabelId label) { storage::v3::ShardResult<bool> HasLabel(storage::v3::View view, storage::v3::LabelId label) const {
return impl_.AddLabelAndValidate(label);
}
storage::v3::ShardOperationResult<bool> AddLabelAndValidate(storage::v3::LabelId label) {
return impl_.AddLabelAndValidate(label);
}
storage::v3::ShardOperationResult<bool> RemoveLabel(storage::v3::LabelId label) {
return impl_.RemoveLabelAndValidate(label);
}
storage::v3::ShardOperationResult<bool> RemoveLabelAndValidate(storage::v3::LabelId label) {
return impl_.RemoveLabelAndValidate(label);
}
storage::v3::Result<bool> HasLabel(storage::v3::View view, storage::v3::LabelId label) const {
return impl_.HasLabel(label, view); return impl_.HasLabel(label, view);
} }
auto Properties(storage::v3::View view) const { return impl_.Properties(view); } auto Properties(storage::v3::View view) const { return impl_.Properties(view); }
storage::v3::Result<storage::v3::PropertyValue> GetProperty(storage::v3::View view, storage::v3::ShardResult<storage::v3::PropertyValue> GetProperty(storage::v3::View view,
storage::v3::PropertyId key) const { storage::v3::PropertyId key) const {
return impl_.GetProperty(key, view); return impl_.GetProperty(key, view);
} }
storage::v3::ShardOperationResult<storage::v3::PropertyValue> SetProperty(storage::v3::PropertyId key,
const storage::v3::PropertyValue &value) {
return impl_.SetPropertyAndValidate(key, value);
}
storage::v3::ShardOperationResult<storage::v3::PropertyValue> SetPropertyAndValidate(
storage::v3::PropertyId key, const storage::v3::PropertyValue &value) {
return impl_.SetPropertyAndValidate(key, value);
}
storage::v3::ShardOperationResult<storage::v3::PropertyValue> RemovePropertyAndValidate(storage::v3::PropertyId key) {
return SetPropertyAndValidate(key, storage::v3::PropertyValue{});
}
storage::v3::Result<std::map<storage::v3::PropertyId, storage::v3::PropertyValue>> ClearProperties() {
return impl_.ClearProperties();
}
auto InEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types) const auto InEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types) const
-> storage::v3::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> { -> storage::v3::ShardResult<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
auto maybe_edges = impl_.InEdges(view, edge_types); auto maybe_edges = impl_.InEdges(view, edge_types);
if (maybe_edges.HasError()) return maybe_edges.GetError(); if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
@ -170,7 +122,7 @@ class VertexAccessor final {
auto InEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types, auto InEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types,
const VertexAccessor &dest) const const VertexAccessor &dest) const
-> storage::v3::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> { -> storage::v3::ShardResult<decltype(iter::imap(MakeEdgeAccessor, *impl_.InEdges(view)))> {
const auto dest_id = dest.impl_.Id(view).GetValue(); const auto dest_id = dest.impl_.Id(view).GetValue();
auto maybe_edges = impl_.InEdges(view, edge_types, &dest_id); auto maybe_edges = impl_.InEdges(view, edge_types, &dest_id);
if (maybe_edges.HasError()) return maybe_edges.GetError(); if (maybe_edges.HasError()) return maybe_edges.GetError();
@ -178,7 +130,7 @@ class VertexAccessor final {
} }
auto OutEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types) const auto OutEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types) const
-> storage::v3::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> { -> storage::v3::ShardResult<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
auto maybe_edges = impl_.OutEdges(view, edge_types); auto maybe_edges = impl_.OutEdges(view, edge_types);
if (maybe_edges.HasError()) return maybe_edges.GetError(); if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
@ -188,16 +140,16 @@ class VertexAccessor final {
auto OutEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types, auto OutEdges(storage::v3::View view, const std::vector<storage::v3::EdgeTypeId> &edge_types,
const VertexAccessor &dest) const const VertexAccessor &dest) const
-> storage::v3::Result<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> { -> storage::v3::ShardResult<decltype(iter::imap(MakeEdgeAccessor, *impl_.OutEdges(view)))> {
const auto dest_id = dest.impl_.Id(view).GetValue(); const auto dest_id = dest.impl_.Id(view).GetValue();
auto maybe_edges = impl_.OutEdges(view, edge_types, &dest_id); auto maybe_edges = impl_.OutEdges(view, edge_types, &dest_id);
if (maybe_edges.HasError()) return maybe_edges.GetError(); if (maybe_edges.HasError()) return maybe_edges.GetError();
return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges)); return iter::imap(MakeEdgeAccessor, std::move(*maybe_edges));
} }
storage::v3::Result<size_t> InDegree(storage::v3::View view) const { return impl_.InDegree(view); } storage::v3::ShardResult<size_t> InDegree(storage::v3::View view) const { return impl_.InDegree(view); }
storage::v3::Result<size_t> OutDegree(storage::v3::View view) const { return impl_.OutDegree(view); } storage::v3::ShardResult<size_t> OutDegree(storage::v3::View view) const { return impl_.OutDegree(view); }
// TODO(jbajic) Fix Remove Gid // TODO(jbajic) Fix Remove Gid
static int64_t CypherId() { return 1; } static int64_t CypherId() { return 1; }

View File

@ -224,12 +224,4 @@ class VersionInfoInMulticommandTxException : public QueryException {
: QueryException("Version info query not allowed in multicommand transactions.") {} : QueryException("Version info query not allowed in multicommand transactions.") {}
}; };
/**
* An exception for an illegal operation that violates schema
*/
class SchemaViolationException : public QueryRuntimeException {
public:
using QueryRuntimeException::QueryRuntimeException;
};
} // namespace memgraph::query::v2 } // namespace memgraph::query::v2

View File

@ -47,7 +47,6 @@
#include "query/v2/shard_request_manager.hpp" #include "query/v2/shard_request_manager.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/storage.hpp"
#include "utils/algorithm.hpp" #include "utils/algorithm.hpp"
#include "utils/csv_parsing.hpp" #include "utils/csv_parsing.hpp"
#include "utils/event_counter.hpp" #include "utils/event_counter.hpp"
@ -800,7 +799,11 @@ InterpreterContext::InterpreterContext(storage::v3::Shard *db, const Interpreter
Interpreter::Interpreter(InterpreterContext *interpreter_context) : interpreter_context_(interpreter_context) { Interpreter::Interpreter(InterpreterContext *interpreter_context) : interpreter_context_(interpreter_context) {
MG_ASSERT(interpreter_context_, "Interpreter context must not be NULL"); MG_ASSERT(interpreter_context_, "Interpreter context must not be NULL");
auto query_io = interpreter_context_->io.ForkLocal();
// TODO(tyler) make this deterministic so that it can be tested.
auto random_uuid = boost::uuids::uuid{boost::uuids::random_generator()()};
auto query_io = interpreter_context_->io.ForkLocal(random_uuid);
shard_request_manager_ = std::make_unique<msgs::ShardRequestManager<io::local_transport::LocalTransport>>( shard_request_manager_ = std::make_unique<msgs::ShardRequestManager<io::local_transport::LocalTransport>>(
coordinator::CoordinatorClient<io::local_transport::LocalTransport>( coordinator::CoordinatorClient<io::local_transport::LocalTransport>(
query_io, interpreter_context_->coordinator_address, std::vector{interpreter_context_->coordinator_address}), query_io, interpreter_context_->coordinator_address, std::vector{interpreter_context_->coordinator_address}),

View File

@ -26,6 +26,7 @@
#include <cppitertools/chain.hpp> #include <cppitertools/chain.hpp>
#include <cppitertools/imap.hpp> #include <cppitertools/imap.hpp>
#include "common/errors.hpp"
#include "expr/ast/pretty_print_ast_to_original_expression.hpp" #include "expr/ast/pretty_print_ast_to_original_expression.hpp"
#include "expr/exceptions.hpp" #include "expr/exceptions.hpp"
#include "query/exceptions.hpp" #include "query/exceptions.hpp"
@ -563,27 +564,6 @@ UniqueCursorPtr ScanAllById::MakeCursor(utils::MemoryResource *mem) const {
std::move(vertices), "ScanAllById"); std::move(vertices), "ScanAllById");
} }
namespace {
template <class TEdges>
auto UnwrapEdgesResult(storage::v3::Result<TEdges> &&result) {
if (result.HasError()) {
switch (result.GetError()) {
case storage::v3::Error::DELETED_OBJECT:
throw QueryRuntimeException("Trying to get relationships of a deleted node.");
case storage::v3::Error::NONEXISTENT_OBJECT:
throw query::v2::QueryRuntimeException("Trying to get relationships from a node that doesn't exist.");
case storage::v3::Error::VERTEX_HAS_EDGES:
case storage::v3::Error::SERIALIZATION_ERROR:
case storage::v3::Error::PROPERTIES_DISABLED:
throw QueryRuntimeException("Unexpected error when accessing relationships.");
}
}
return std::move(*result);
}
} // namespace
Expand::Expand(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol, Symbol node_symbol, Expand::Expand(const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol, Symbol node_symbol,
Symbol edge_symbol, EdgeAtom::Direction direction, Symbol edge_symbol, EdgeAtom::Direction direction,
const std::vector<storage::v3::EdgeTypeId> &edge_types, bool existing_node, storage::v3::View view) const std::vector<storage::v3::EdgeTypeId> &edge_types, bool existing_node, storage::v3::View view)
@ -836,45 +816,9 @@ std::vector<Symbol> SetProperties::ModifiedSymbols(const SymbolTable &table) con
SetProperties::SetPropertiesCursor::SetPropertiesCursor(const SetProperties &self, utils::MemoryResource *mem) SetProperties::SetPropertiesCursor::SetPropertiesCursor(const SetProperties &self, utils::MemoryResource *mem)
: self_(self), input_cursor_(self.input_->MakeCursor(mem)) {} : self_(self), input_cursor_(self.input_->MakeCursor(mem)) {}
namespace {
template <typename T>
concept AccessorWithProperties = requires(T value, storage::v3::PropertyId property_id,
storage::v3::PropertyValue property_value) {
{
value.ClearProperties()
} -> std::same_as<storage::v3::Result<std::map<storage::v3::PropertyId, storage::v3::PropertyValue>>>;
{value.SetProperty(property_id, property_value)};
};
} // namespace
bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, ExecutionContext &context) { bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, ExecutionContext &context) {
SCOPED_PROFILE_OP("SetProperties"); SCOPED_PROFILE_OP("SetProperties");
return false; return false;
// if (!input_cursor_->Pull(frame, context)) return false;
//
// TypedValue &lhs = frame[self_.input_symbol_];
//
// // Set, just like Create needs to see the latest changes.
// ExpressionEvaluator evaluator(&frame, context.symbol_table, context.evaluation_context, context.db_accessor,
// storage::v3::View::NEW);
// TypedValue rhs = self_.rhs_->Accept(evaluator);
//
// switch (lhs.type()) {
// case TypedValue::Type::Vertex:
// SetPropertiesOnRecord(&lhs.ValueVertex(), rhs, self_.op_, &context);
// break;
// case TypedValue::Type::Edge:
// SetPropertiesOnRecord(&lhs.ValueEdge(), rhs, self_.op_, &context);
// break;
// case TypedValue::Type::Null:
// // Skip setting properties on Null (can occur in optional match).
// break;
// default:
// throw QueryRuntimeException("Properties can only be set on edges and vertices.");
// }
// return true;
} }
void SetProperties::SetPropertiesCursor::Shutdown() { input_cursor_->Shutdown(); } void SetProperties::SetPropertiesCursor::Shutdown() { input_cursor_->Shutdown(); }

View File

@ -24,6 +24,7 @@
#include "coordinator/hybrid_logical_clock.hpp" #include "coordinator/hybrid_logical_clock.hpp"
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
namespace memgraph::msgs { namespace memgraph::msgs {
@ -317,6 +318,11 @@ struct Value {
} }
}; };
struct ShardError {
common::ErrorCode code;
std::string message;
};
struct Expression { struct Expression {
std::string expression; std::string expression;
}; };
@ -357,7 +363,7 @@ struct ScanResultRow {
}; };
struct ScanVerticesResponse { struct ScanVerticesResponse {
bool success; std::optional<ShardError> error;
std::optional<VertexId> next_start_id; std::optional<VertexId> next_start_id;
std::vector<ScanResultRow> results; std::vector<ScanResultRow> results;
}; };
@ -398,6 +404,7 @@ struct GetPropertiesResponse {
std::vector<GetPropertiesResultRow> result_row; std::vector<GetPropertiesResultRow> result_row;
enum RequestResult : uint16_t { OUT_OF_SHARD_RANGE, SUCCESS, FAILURE }; enum RequestResult : uint16_t { OUT_OF_SHARD_RANGE, SUCCESS, FAILURE };
RequestResult result; RequestResult result;
std::optional<ShardError> error;
}; };
enum class EdgeDirection : uint8_t { OUT = 1, IN = 2, BOTH = 3 }; enum class EdgeDirection : uint8_t { OUT = 1, IN = 2, BOTH = 3 };
@ -419,7 +426,9 @@ struct ExpandOneRequest {
std::vector<std::string> vertex_expressions; std::vector<std::string> vertex_expressions;
std::vector<std::string> edge_expressions; std::vector<std::string> edge_expressions;
std::optional<std::vector<OrderBy>> order_by; std::vector<OrderBy> order_by_vertices;
std::vector<OrderBy> order_by_edges;
// Limit the edges or the vertices? // Limit the edges or the vertices?
std::optional<size_t> limit; std::optional<size_t> limit;
std::vector<std::string> filters; std::vector<std::string> filters;
@ -462,14 +471,16 @@ struct ExpandOneResultRow {
}; };
struct ExpandOneResponse { struct ExpandOneResponse {
bool success; std::optional<ShardError> error;
std::vector<ExpandOneResultRow> result; std::vector<ExpandOneResultRow> result;
}; };
struct UpdateVertexProp { struct UpdateVertex {
PrimaryKey primary_key; PrimaryKey primary_key;
// This should be a map // Labels are first added and then removed from vertices
std::vector<std::pair<PropertyId, Value>> property_updates; std::vector<LabelId> add_labels;
std::vector<LabelId> remove_labels;
std::map<PropertyId, Value> property_updates;
}; };
struct UpdateEdgeProp { struct UpdateEdgeProp {
@ -496,7 +507,7 @@ struct CreateVerticesRequest {
}; };
struct CreateVerticesResponse { struct CreateVerticesResponse {
bool success; std::optional<ShardError> error;
}; };
struct DeleteVerticesRequest { struct DeleteVerticesRequest {
@ -507,16 +518,16 @@ struct DeleteVerticesRequest {
}; };
struct DeleteVerticesResponse { struct DeleteVerticesResponse {
bool success; std::optional<ShardError> error;
}; };
struct UpdateVerticesRequest { struct UpdateVerticesRequest {
Hlc transaction_id; Hlc transaction_id;
std::vector<UpdateVertexProp> new_properties; std::vector<UpdateVertex> update_vertices;
}; };
struct UpdateVerticesResponse { struct UpdateVerticesResponse {
bool success; std::optional<ShardError> error;
}; };
/* /*
@ -538,7 +549,7 @@ struct CreateExpandRequest {
}; };
struct CreateExpandResponse { struct CreateExpandResponse {
bool success; std::optional<ShardError> error;
}; };
struct DeleteEdgesRequest { struct DeleteEdgesRequest {
@ -547,7 +558,7 @@ struct DeleteEdgesRequest {
}; };
struct DeleteEdgesResponse { struct DeleteEdgesResponse {
bool success; std::optional<ShardError> error;
}; };
struct UpdateEdgesRequest { struct UpdateEdgesRequest {
@ -556,7 +567,7 @@ struct UpdateEdgesRequest {
}; };
struct UpdateEdgesResponse { struct UpdateEdgesResponse {
bool success; std::optional<ShardError> error;
}; };
struct CommitRequest { struct CommitRequest {
@ -565,7 +576,7 @@ struct CommitRequest {
}; };
struct CommitResponse { struct CommitResponse {
bool success; std::optional<ShardError> error;
}; };
using ReadRequests = std::variant<ExpandOneRequest, GetPropertiesRequest, ScanVerticesRequest>; using ReadRequests = std::variant<ExpandOneRequest, GetPropertiesRequest, ScanVerticesRequest>;

View File

@ -206,7 +206,7 @@ class ShardRequestManager : public ShardRequestManagerInterface {
} }
WriteResponses write_response_variant = commit_response.GetValue(); WriteResponses write_response_variant = commit_response.GetValue();
auto &response = std::get<CommitResponse>(write_response_variant); auto &response = std::get<CommitResponse>(write_response_variant);
if (!response.success) { if (response.error) {
throw std::runtime_error("Commit request did not succeed"); throw std::runtime_error("Commit request did not succeed");
} }
} }
@ -311,7 +311,7 @@ class ShardRequestManager : public ShardRequestManagerInterface {
WriteResponses response_variant = write_response_result.GetValue(); WriteResponses response_variant = write_response_result.GetValue();
CreateExpandResponse mapped_response = std::get<CreateExpandResponse>(response_variant); CreateExpandResponse mapped_response = std::get<CreateExpandResponse>(response_variant);
if (!mapped_response.success) { if (mapped_response.error) {
throw std::runtime_error("CreateExpand request did not succeed"); throw std::runtime_error("CreateExpand request did not succeed");
} }
responses.push_back(mapped_response); responses.push_back(mapped_response);
@ -601,7 +601,7 @@ class ShardRequestManager : public ShardRequestManagerInterface {
WriteResponses response_variant = poll_result->GetValue(); WriteResponses response_variant = poll_result->GetValue();
auto response = std::get<CreateVerticesResponse>(response_variant); auto response = std::get<CreateVerticesResponse>(response_variant);
if (!response.success) { if (response.error) {
throw std::runtime_error("CreateVertices request did not succeed"); throw std::runtime_error("CreateVertices request did not succeed");
} }
responses.push_back(response); responses.push_back(response);
@ -637,7 +637,7 @@ class ShardRequestManager : public ShardRequestManagerInterface {
// Currently a boolean flag for signaling the overall success of the // Currently a boolean flag for signaling the overall success of the
// ExpandOne request does not exist. But it should, so here we assume // ExpandOne request does not exist. But it should, so here we assume
// that it is already in place. // that it is already in place.
if (!response.success) { if (response.error) {
throw std::runtime_error("ExpandOne request did not succeed"); throw std::runtime_error("ExpandOne request did not succeed");
} }
@ -680,7 +680,7 @@ class ShardRequestManager : public ShardRequestManagerInterface {
ReadResponses read_response_variant = await_result->GetValue(); ReadResponses read_response_variant = await_result->GetValue();
auto response = std::get<ScanVerticesResponse>(read_response_variant); auto response = std::get<ScanVerticesResponse>(read_response_variant);
if (!response.success) { if (response.error) {
throw std::runtime_error("ScanAll request did not succeed"); throw std::runtime_error("ScanAll request did not succeed");
} }

View File

@ -16,12 +16,10 @@ set(storage_v3_src_files
schemas.cpp schemas.cpp
schema_validator.cpp schema_validator.cpp
shard.cpp shard.cpp
storage.cpp
shard_rsm.cpp shard_rsm.cpp
bindings/typed_value.cpp bindings/typed_value.cpp
expr.cpp expr.cpp
request_helper.cpp request_helper.cpp)
storage.cpp)
# ###################### # ######################
find_package(gflags REQUIRED) find_package(gflags REQUIRED)

View File

@ -78,8 +78,8 @@ class DbAccessor final {
return VerticesIterable(accessor_->Vertices(label, property, lower, upper, view)); return VerticesIterable(accessor_->Vertices(label, property, lower, upper, view));
} }
storage::v3::Result<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to, storage::v3::ShardResult<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to,
const storage::v3::EdgeTypeId &edge_type) { const storage::v3::EdgeTypeId &edge_type) {
static constexpr auto kDummyGid = storage::v3::Gid::FromUint(0); static constexpr auto kDummyGid = storage::v3::Gid::FromUint(0);
auto maybe_edge = accessor_->CreateEdge(from->Id(storage::v3::View::NEW).GetValue(), auto maybe_edge = accessor_->CreateEdge(from->Id(storage::v3::View::NEW).GetValue(),
to->Id(storage::v3::View::NEW).GetValue(), edge_type, kDummyGid); to->Id(storage::v3::View::NEW).GetValue(), edge_type, kDummyGid);
@ -87,7 +87,7 @@ class DbAccessor final {
return EdgeAccessor(*maybe_edge); return EdgeAccessor(*maybe_edge);
} }
storage::v3::Result<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge) { storage::v3::ShardResult<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge) {
auto res = accessor_->DeleteEdge(edge->FromVertex(), edge->ToVertex(), edge->Gid()); auto res = accessor_->DeleteEdge(edge->FromVertex(), edge->ToVertex(), edge->Gid());
if (res.HasError()) { if (res.HasError()) {
return res.GetError(); return res.GetError();
@ -101,7 +101,7 @@ class DbAccessor final {
return std::make_optional<EdgeAccessor>(*value); return std::make_optional<EdgeAccessor>(*value);
} }
storage::v3::Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachRemoveVertex( storage::v3::ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachRemoveVertex(
VertexAccessor *vertex_accessor) { VertexAccessor *vertex_accessor) {
using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>; using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
@ -125,7 +125,7 @@ class DbAccessor final {
return std::make_optional<ReturnType>(vertex, std::move(deleted_edges)); return std::make_optional<ReturnType>(vertex, std::move(deleted_edges));
} }
storage::v3::Result<std::optional<VertexAccessor>> RemoveVertex(VertexAccessor *vertex_accessor) { storage::v3::ShardResult<std::optional<VertexAccessor>> RemoveVertex(VertexAccessor *vertex_accessor) {
auto res = accessor_->DeleteVertex(vertex_accessor); auto res = accessor_->DeleteVertex(vertex_accessor);
if (res.HasError()) { if (res.HasError()) {
return res.GetError(); return res.GetError();

View File

@ -21,6 +21,7 @@
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/property_store.hpp" #include "storage/v3/property_store.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"
#include "utils/memory.hpp" #include "utils/memory.hpp"
@ -87,6 +88,6 @@ struct EvaluationContext {
using ExpressionEvaluator = using ExpressionEvaluator =
memgraph::expr::ExpressionEvaluator<TypedValue, EvaluationContext, DbAccessor, storage::v3::View, memgraph::expr::ExpressionEvaluator<TypedValue, EvaluationContext, DbAccessor, storage::v3::View,
storage::v3::LabelId, storage::v3::PropertyStore, PropertyToTypedValueConverter, storage::v3::LabelId, storage::v3::PropertyStore, PropertyToTypedValueConverter,
memgraph::storage::v3::Error>; common::ErrorCode>;
} // namespace memgraph::storage::v3 } // namespace memgraph::storage::v3

View File

@ -15,6 +15,7 @@
#include "storage/v3/mvcc.hpp" #include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp" #include "storage/v3/schema_validator.hpp"
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
#include "utils/memory_tracker.hpp" #include "utils/memory_tracker.hpp"
@ -54,13 +55,13 @@ const VertexId &EdgeAccessor::FromVertex() const { return from_vertex_; }
const VertexId &EdgeAccessor::ToVertex() const { return to_vertex_; } const VertexId &EdgeAccessor::ToVertex() const { return to_vertex_; }
Result<PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) { ShardResult<PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED; if (!config_.properties_on_edges) return SHARD_ERROR(ErrorCode::PROPERTIES_DISABLED);
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, edge_.ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (edge_.ptr->deleted) return Error::DELETED_OBJECT; if (edge_.ptr->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
auto current_value = edge_.ptr->properties.GetProperty(property); auto current_value = edge_.ptr->properties.GetProperty(property);
// We could skip setting the value if the previous one is the same to the new // We could skip setting the value if the previous one is the same to the new
@ -75,12 +76,12 @@ Result<PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const Prope
return std::move(current_value); return std::move(current_value);
} }
Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() { ShardResult<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() {
if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED; if (!config_.properties_on_edges) return SHARD_ERROR(ErrorCode::PROPERTIES_DISABLED);
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, edge_.ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (edge_.ptr->deleted) return Error::DELETED_OBJECT; if (edge_.ptr->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
auto properties = edge_.ptr->properties.Properties(); auto properties = edge_.ptr->properties.Properties();
for (const auto &property : properties) { for (const auto &property : properties) {
@ -92,11 +93,11 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() {
return std::move(properties); return std::move(properties);
} }
Result<PropertyValue> EdgeAccessor::GetProperty(View view, PropertyId property) const { ShardResult<PropertyValue> EdgeAccessor::GetProperty(View view, PropertyId property) const {
return GetProperty(property, view); return GetProperty(property, view);
} }
Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view) const { ShardResult<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view) const {
if (!config_.properties_on_edges) return PropertyValue(); if (!config_.properties_on_edges) return PropertyValue();
auto exists = true; auto exists = true;
auto deleted = edge_.ptr->deleted; auto deleted = edge_.ptr->deleted;
@ -128,12 +129,12 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view)
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return std::move(value); return std::move(value);
} }
Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view) const { ShardResult<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view) const {
if (!config_.properties_on_edges) return std::map<PropertyId, PropertyValue>{}; if (!config_.properties_on_edges) return std::map<PropertyId, PropertyValue>{};
auto exists = true; auto exists = true;
auto deleted = edge_.ptr->deleted; auto deleted = edge_.ptr->deleted;
@ -174,8 +175,8 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view)
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return std::move(properties); return std::move(properties);
} }

View File

@ -56,19 +56,19 @@ class EdgeAccessor final {
/// Set a property value and return the old value. /// Set a property value and return the old value.
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value); ShardResult<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
/// Remove all properties and return old values for each removed property. /// Remove all properties and return old values for each removed property.
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::map<PropertyId, PropertyValue>> ClearProperties(); ShardResult<std::map<PropertyId, PropertyValue>> ClearProperties();
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<PropertyValue> GetProperty(PropertyId property, View view) const; ShardResult<PropertyValue> GetProperty(PropertyId property, View view) const;
Result<PropertyValue> GetProperty(View view, PropertyId property) const; ShardResult<PropertyValue> GetProperty(View view, PropertyId property) const;
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const; ShardResult<std::map<PropertyId, PropertyValue>> Properties(View view) const;
Gid Gid() const noexcept { Gid Gid() const noexcept {
if (config_.properties_on_edges) { if (config_.properties_on_edges) {

View File

@ -165,7 +165,7 @@ std::any ParseExpression(const std::string &expr, memgraph::expr::AstStorage &st
return visitor.visit(ast); return visitor.visit(ast);
} }
TypedValue ComputeExpression(DbAccessor &dba, const std::optional<memgraph::storage::v3::VertexAccessor> &v_acc, TypedValue ComputeExpression(DbAccessor &dba, const memgraph::storage::v3::VertexAccessor &v_acc,
const std::optional<memgraph::storage::v3::EdgeAccessor> &e_acc, const std::optional<memgraph::storage::v3::EdgeAccessor> &e_acc,
const std::string &expression, std::string_view node_name, std::string_view edge_name) { const std::string &expression, std::string_view node_name, std::string_view edge_name) {
AstStorage storage; AstStorage storage;
@ -192,10 +192,11 @@ TypedValue ComputeExpression(DbAccessor &dba, const std::optional<memgraph::stor
return position_symbol_pair.second.name() == node_name; return position_symbol_pair.second.name() == node_name;
}) != symbol_table.table().end()); }) != symbol_table.table().end());
frame[symbol_table.at(node_identifier)] = *v_acc; frame[symbol_table.at(node_identifier)] = v_acc;
} }
if (edge_identifier.symbol_pos_ != -1) { if (edge_identifier.symbol_pos_ != -1) {
MG_ASSERT(e_acc.has_value());
MG_ASSERT(std::find_if(symbol_table.table().begin(), symbol_table.table().end(), MG_ASSERT(std::find_if(symbol_table.table().begin(), symbol_table.table().end(),
[&edge_name](const std::pair<int32_t, Symbol> &position_symbol_pair) { [&edge_name](const std::pair<int32_t, Symbol> &position_symbol_pair) {
return position_symbol_pair.second.name() == edge_name; return position_symbol_pair.second.name() == edge_name;

View File

@ -9,6 +9,8 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#pragma once
#include <vector> #include <vector>
#include "db_accessor.hpp" #include "db_accessor.hpp"
@ -48,8 +50,7 @@ auto Eval(TExpression *expr, EvaluationContext &ctx, AstStorage &storage, Expres
std::any ParseExpression(const std::string &expr, AstStorage &storage); std::any ParseExpression(const std::string &expr, AstStorage &storage);
TypedValue ComputeExpression(DbAccessor &dba, const std::optional<VertexAccessor> &v_acc, TypedValue ComputeExpression(DbAccessor &dba, const VertexAccessor &v_acc, const std::optional<EdgeAccessor> &e_acc,
const std::optional<EdgeAccessor> &e_acc, const std::string &expression, const std::string &expression, std::string_view node_name, std::string_view edge_name);
std::string_view node_name, std::string_view edge_name);
} // namespace memgraph::storage::v3 } // namespace memgraph::storage::v3

View File

@ -14,47 +14,434 @@
#include <iterator> #include <iterator>
#include <vector> #include <vector>
#include "pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/db_accessor.hpp" #include "storage/v3/bindings/db_accessor.hpp"
#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/expr.hpp" #include "storage/v3/expr.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/value_conversions.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
using msgs::Label;
using msgs::PropertyId;
std::vector<Element> OrderByElements(Shard::Accessor &acc, DbAccessor &dba, VerticesIterable &vertices_iterable, using conversions::ConvertPropertyVector;
std::vector<msgs::OrderBy> &order_bys) { using conversions::FromPropertyValueToValue;
std::vector<Element> ordered; using conversions::ToMsgsVertexId;
ordered.reserve(acc.ApproximateVertexCount());
std::vector<Ordering> ordering; namespace {
ordering.reserve(order_bys.size());
for (const auto &order : order_bys) { using AllEdgePropertyDataStucture = std::map<PropertyId, msgs::Value>;
switch (order.direction) { using SpecificEdgePropertyDataStucture = std::vector<msgs::Value>;
case memgraph::msgs::OrderingDirection::ASCENDING: {
ordering.push_back(Ordering::ASC); using AllEdgeProperties = std::tuple<msgs::VertexId, msgs::Gid, AllEdgePropertyDataStucture>;
break; using SpecificEdgeProperties = std::tuple<msgs::VertexId, msgs::Gid, SpecificEdgePropertyDataStucture>;
}
case memgraph::msgs::OrderingDirection::DESCENDING: { using SpecificEdgePropertiesVector = std::vector<SpecificEdgeProperties>;
ordering.push_back(Ordering::DESC); using AllEdgePropertiesVector = std::vector<AllEdgeProperties>;
break;
} struct VertexIdCmpr {
} bool operator()(const storage::v3::VertexId *lhs, const storage::v3::VertexId *rhs) const { return *lhs < *rhs; }
};
std::optional<std::map<PropertyId, Value>> PrimaryKeysFromAccessor(const VertexAccessor &acc, View view,
const Schemas::Schema &schema) {
std::map<PropertyId, Value> ret;
auto props = acc.Properties(view);
auto maybe_pk = acc.PrimaryKey(view);
if (maybe_pk.HasError()) {
spdlog::debug("Encountered an error while trying to get vertex primary key.");
return std::nullopt;
} }
auto compare_typed_values = TypedValueVectorCompare(ordering); auto &pk = maybe_pk.GetValue();
for (auto it = vertices_iterable.begin(); it != vertices_iterable.end(); ++it) { MG_ASSERT(schema.second.size() == pk.size(), "PrimaryKey size does not match schema!");
std::vector<TypedValue> properties_order_by; for (size_t i{0}; i < schema.second.size(); ++i) {
properties_order_by.reserve(order_bys.size()); ret.emplace(schema.second[i].property_id, FromPropertyValueToValue(std::move(pk[i])));
for (const auto &order_by : order_bys) {
auto val =
ComputeExpression(dba, *it, std::nullopt, order_by.expression.expression, expr::identifier_node_symbol, "");
properties_order_by.push_back(std::move(val));
}
ordered.push_back({std::move(properties_order_by), *it});
} }
std::sort(ordered.begin(), ordered.end(), [&compare_typed_values](const auto &pair1, const auto &pair2) { return ret;
return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by); }
ShardResult<std::vector<msgs::Label>> FillUpSourceVertexSecondaryLabels(const std::optional<VertexAccessor> &v_acc,
const msgs::ExpandOneRequest &req) {
auto secondary_labels = v_acc->Labels(View::NEW);
if (secondary_labels.HasError()) {
spdlog::debug("Encountered an error while trying to get the secondary labels of a vertex. Transaction id: {}",
req.transaction_id.logical_id);
return secondary_labels.GetError();
}
auto &sec_labels = secondary_labels.GetValue();
std::vector<msgs::Label> msgs_secondary_labels;
msgs_secondary_labels.reserve(sec_labels.size());
std::transform(sec_labels.begin(), sec_labels.end(), std::back_inserter(msgs_secondary_labels),
[](auto label_id) { return msgs::Label{.id = label_id}; });
return msgs_secondary_labels;
}
ShardResult<std::map<PropertyId, Value>> FillUpSourceVertexProperties(const std::optional<VertexAccessor> &v_acc,
const msgs::ExpandOneRequest &req,
storage::v3::View view,
const Schemas::Schema &schema) {
std::map<PropertyId, Value> src_vertex_properties;
if (!req.src_vertex_properties) {
auto props = v_acc->Properties(View::NEW);
if (props.HasError()) {
spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}",
req.transaction_id.logical_id);
return props.GetError();
}
for (auto &[key, val] : props.GetValue()) {
src_vertex_properties.insert(std::make_pair(key, FromPropertyValueToValue(std::move(val))));
}
auto pks = PrimaryKeysFromAccessor(*v_acc, view, schema);
if (pks) {
src_vertex_properties.merge(*pks);
}
} else if (req.src_vertex_properties.value().empty()) {
// NOOP
} else {
for (const auto &prop : req.src_vertex_properties.value()) {
auto prop_val = v_acc->GetProperty(prop, View::OLD);
if (prop_val.HasError()) {
spdlog::debug("Encountered an error while trying to access vertex properties. Transaction id: {}",
req.transaction_id.logical_id);
return prop_val.GetError();
}
src_vertex_properties.insert(std::make_pair(prop, FromPropertyValueToValue(std::move(prop_val.GetValue()))));
}
}
return src_vertex_properties;
}
ShardResult<std::array<std::vector<EdgeAccessor>, 2>> FillUpConnectingEdges(
const std::optional<VertexAccessor> &v_acc, const msgs::ExpandOneRequest &req,
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness) {
std::vector<EdgeTypeId> edge_types{};
edge_types.reserve(req.edge_types.size());
std::transform(req.edge_types.begin(), req.edge_types.end(), std::back_inserter(edge_types),
[](const msgs::EdgeType &edge_type) { return edge_type.id; });
std::vector<EdgeAccessor> in_edges;
std::vector<EdgeAccessor> out_edges;
switch (req.direction) {
case msgs::EdgeDirection::OUT: {
auto out_edges_result = v_acc->OutEdges(View::NEW, edge_types);
if (out_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
return out_edges_result.GetError();
}
out_edges =
maybe_filter_based_on_edge_uniqueness(std::move(out_edges_result.GetValue()), msgs::EdgeDirection::OUT);
break;
}
case msgs::EdgeDirection::IN: {
auto in_edges_result = v_acc->InEdges(View::NEW, edge_types);
if (in_edges_result.HasError()) {
spdlog::debug(
"Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}"[req.transaction_id
.logical_id]);
return in_edges_result.GetError();
}
in_edges = maybe_filter_based_on_edge_uniqueness(std::move(in_edges_result.GetValue()), msgs::EdgeDirection::IN);
break;
}
case msgs::EdgeDirection::BOTH: {
auto in_edges_result = v_acc->InEdges(View::NEW, edge_types);
if (in_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get in-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
return in_edges_result.GetError();
}
in_edges = maybe_filter_based_on_edge_uniqueness(std::move(in_edges_result.GetValue()), msgs::EdgeDirection::IN);
auto out_edges_result = v_acc->OutEdges(View::NEW, edge_types);
if (out_edges_result.HasError()) {
spdlog::debug("Encountered an error while trying to get out-going EdgeAccessors. Transaction id: {}",
req.transaction_id.logical_id);
return out_edges_result.GetError();
}
out_edges =
maybe_filter_based_on_edge_uniqueness(std::move(out_edges_result.GetValue()), msgs::EdgeDirection::OUT);
break;
}
}
return std::array<std::vector<EdgeAccessor>, 2>{std::move(in_edges), std::move(out_edges)};
}
template <bool are_in_edges>
ShardResult<void> FillEdges(const std::vector<EdgeAccessor> &edges, msgs::ExpandOneResultRow &row,
const EdgeFiller &edge_filler) {
for (const auto &edge : edges) {
if (const auto res = edge_filler(edge, are_in_edges, row); res.HasError()) {
return res.GetError();
}
}
return {};
}
}; // namespace
ShardResult<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(const VertexAccessor &acc,
const std::vector<PropertyId> &props,
View view) {
std::map<PropertyId, Value> ret;
for (const auto &prop : props) {
auto result = acc.GetProperty(prop, view);
if (result.HasError()) {
spdlog::debug("Encountered an Error while trying to get a vertex property.");
return result.GetError();
}
auto &value = result.GetValue();
ret.emplace(std::make_pair(prop, FromPropertyValueToValue(std::move(value))));
}
return ret;
}
std::vector<TypedValue> EvaluateVertexExpressions(DbAccessor &dba, const VertexAccessor &v_acc,
const std::vector<std::string> &expressions,
std::string_view node_name) {
std::vector<TypedValue> evaluated_expressions;
evaluated_expressions.reserve(expressions.size());
std::transform(expressions.begin(), expressions.end(), std::back_inserter(evaluated_expressions),
[&dba, &v_acc, &node_name](const auto &expression) {
return ComputeExpression(dba, v_acc, std::nullopt, expression, node_name, "");
});
return evaluated_expressions;
}
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view,
const Schemas::Schema &schema) {
std::map<PropertyId, Value> ret;
auto props = acc.Properties(view);
if (props.HasError()) {
spdlog::debug("Encountered an error while trying to get vertex properties.");
return props.GetError();
}
auto &properties = props.GetValue();
std::transform(properties.begin(), properties.end(), std::inserter(ret, ret.begin()),
[](std::pair<const PropertyId, PropertyValue> &pair) {
return std::make_pair(pair.first, FromPropertyValueToValue(std::move(pair.second)));
});
properties.clear();
auto pks = PrimaryKeysFromAccessor(acc, view, schema);
if (pks) {
ret.merge(*pks);
}
return ret;
}
EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbor_rows) {
// Functions to select connecting edges based on uniquness
EdgeUniquenessFunction maybe_filter_based_on_edge_uniquness;
if (only_unique_neighbor_rows) {
maybe_filter_based_on_edge_uniquness = [](EdgeAccessors &&edges,
msgs::EdgeDirection edge_direction) -> EdgeAccessors {
std::function<bool(std::set<const storage::v3::VertexId *, VertexIdCmpr> &, const storage::v3::EdgeAccessor &)>
is_edge_unique;
switch (edge_direction) {
case msgs::EdgeDirection::OUT: {
is_edge_unique = [](std::set<const storage::v3::VertexId *, VertexIdCmpr> &other_vertex_set,
const storage::v3::EdgeAccessor &edge_acc) {
auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.ToVertex());
return insertion_happened;
};
break;
}
case msgs::EdgeDirection::IN: {
is_edge_unique = [](std::set<const storage::v3::VertexId *, VertexIdCmpr> &other_vertex_set,
const storage::v3::EdgeAccessor &edge_acc) {
auto [it, insertion_happened] = other_vertex_set.insert(&edge_acc.FromVertex());
return insertion_happened;
};
break;
}
case msgs::EdgeDirection::BOTH:
MG_ASSERT(false, "This is should never happen, msgs::EdgeDirection::BOTH should not be passed here.");
}
EdgeAccessors ret;
std::set<const storage::v3::VertexId *, VertexIdCmpr> other_vertex_set;
for (const auto &edge : edges) {
if (is_edge_unique(other_vertex_set, edge)) {
ret.emplace_back(edge);
}
}
return ret;
};
} else {
maybe_filter_based_on_edge_uniquness =
[](EdgeAccessors &&edges, msgs::EdgeDirection /*edge_direction*/) -> EdgeAccessors { return std::move(edges); };
}
return maybe_filter_based_on_edge_uniquness;
}
EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req) {
EdgeFiller edge_filler;
if (!req.edge_properties) {
edge_filler = [transaction_id = req.transaction_id.logical_id](
const EdgeAccessor &edge, const bool is_in_edge,
msgs::ExpandOneResultRow &result_row) -> ShardResult<void> {
auto properties_results = edge.Properties(View::NEW);
if (properties_results.HasError()) {
spdlog::debug("Encountered an error while trying to get edge properties. Transaction id: {}", transaction_id);
return properties_results.GetError();
}
std::map<PropertyId, msgs::Value> value_properties;
for (auto &[prop_key, prop_val] : properties_results.GetValue()) {
value_properties.insert(std::make_pair(prop_key, FromPropertyValueToValue(std::move(prop_val))));
}
using EdgeWithAllProperties = msgs::ExpandOneResultRow::EdgeWithAllProperties;
EdgeWithAllProperties edges{ToMsgsVertexId(edge.FromVertex()), msgs::EdgeType{edge.EdgeType()},
edge.Gid().AsUint(), std::move(value_properties)};
if (is_in_edge) {
result_row.in_edges_with_all_properties.push_back(std::move(edges));
} else {
result_row.out_edges_with_all_properties.push_back(std::move(edges));
}
return {};
};
} else {
// TODO(gvolfing) - do we want to set the action_successful here?
edge_filler = [&req](const EdgeAccessor &edge, const bool is_in_edge,
msgs::ExpandOneResultRow &result_row) -> ShardResult<void> {
std::vector<msgs::Value> value_properties;
value_properties.reserve(req.edge_properties.value().size());
for (const auto &edge_prop : req.edge_properties.value()) {
auto property_result = edge.GetProperty(edge_prop, View::NEW);
if (property_result.HasError()) {
spdlog::debug("Encountered an error while trying to get edge properties. Transaction id: {}",
req.transaction_id.logical_id);
return property_result.GetError();
}
value_properties.emplace_back(FromPropertyValueToValue(std::move(property_result.GetValue())));
}
using EdgeWithSpecificProperties = msgs::ExpandOneResultRow::EdgeWithSpecificProperties;
EdgeWithSpecificProperties edges{ToMsgsVertexId(edge.FromVertex()), msgs::EdgeType{edge.EdgeType()},
edge.Gid().AsUint(), std::move(value_properties)};
if (is_in_edge) {
result_row.in_edges_with_specific_properties.push_back(std::move(edges));
} else {
result_row.out_edges_with_specific_properties.push_back(std::move(edges));
}
return {};
};
}
return edge_filler;
}
bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &filters,
const std::string_view node_name, const std::optional<EdgeAccessor> &e_acc) {
return std::ranges::all_of(filters, [&node_name, &dba, &v_acc, &e_acc](const auto &filter_expr) {
TypedValue result;
if (e_acc) {
result = ComputeExpression(dba, v_acc, e_acc, filter_expr, "", node_name);
} else {
result = ComputeExpression(dba, v_acc, e_acc, filter_expr, node_name, "");
}
return result.IsBool() && result.ValueBool();
}); });
return ordered; }
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
const Schemas::Schema &schema) {
/// Fill up source vertex
const auto primary_key = ConvertPropertyVector(src_vertex.second);
auto v_acc = acc.FindVertex(primary_key, View::NEW);
msgs::Vertex source_vertex = {.id = src_vertex};
auto maybe_secondary_labels = FillUpSourceVertexSecondaryLabels(v_acc, req);
if (maybe_secondary_labels.HasError()) {
return maybe_secondary_labels.GetError();
}
source_vertex.labels = std::move(*maybe_secondary_labels);
auto src_vertex_properties = FillUpSourceVertexProperties(v_acc, req, storage::v3::View::NEW, schema);
if (src_vertex_properties.HasError()) {
return src_vertex_properties.GetError();
}
/// Fill up connecting edges
auto fill_up_connecting_edges = FillUpConnectingEdges(v_acc, req, maybe_filter_based_on_edge_uniqueness);
if (fill_up_connecting_edges.HasError()) {
return fill_up_connecting_edges.GetError();
}
auto [in_edges, out_edges] = fill_up_connecting_edges.GetValue();
msgs::ExpandOneResultRow result_row;
result_row.src_vertex = std::move(source_vertex);
result_row.src_vertex_properties = std::move(*src_vertex_properties);
static constexpr bool kInEdges = true;
static constexpr bool kOutEdges = false;
if (const auto fill_edges_res = FillEdges<kInEdges>(in_edges, result_row, edge_filler); fill_edges_res.HasError()) {
return fill_edges_res.GetError();
}
if (const auto fill_edges_res = FillEdges<kOutEdges>(out_edges, result_row, edge_filler); fill_edges_res.HasError()) {
return fill_edges_res.GetError();
}
return result_row;
}
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
VertexAccessor v_acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
std::vector<EdgeAccessor> in_edge_accessors, std::vector<EdgeAccessor> out_edge_accessors,
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
const Schemas::Schema &schema) {
/// Fill up source vertex
msgs::Vertex source_vertex = {.id = src_vertex};
auto maybe_secondary_labels = FillUpSourceVertexSecondaryLabels(v_acc, req);
if (maybe_secondary_labels.HasError()) {
return maybe_secondary_labels.GetError();
}
source_vertex.labels = std::move(*maybe_secondary_labels);
/// Fill up source vertex properties
auto src_vertex_properties = FillUpSourceVertexProperties(v_acc, req, storage::v3::View::NEW, schema);
if (src_vertex_properties.HasError()) {
return src_vertex_properties.GetError();
}
/// Fill up connecting edges
auto in_edges = maybe_filter_based_on_edge_uniqueness(std::move(in_edge_accessors), msgs::EdgeDirection::IN);
auto out_edges = maybe_filter_based_on_edge_uniqueness(std::move(out_edge_accessors), msgs::EdgeDirection::OUT);
msgs::ExpandOneResultRow result_row;
result_row.src_vertex = std::move(source_vertex);
result_row.src_vertex_properties = std::move(*src_vertex_properties);
static constexpr bool kInEdges = true;
static constexpr bool kOutEdges = false;
if (const auto fill_edges_res = FillEdges<kInEdges>(in_edges, result_row, edge_filler); fill_edges_res.HasError()) {
return fill_edges_res.GetError();
}
if (const auto fill_edges_res = FillEdges<kOutEdges>(out_edges, result_row, edge_filler); fill_edges_res.HasError()) {
return fill_edges_res.GetError();
}
return result_row;
} }
std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::OrderBy> &order_by, std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::OrderBy> &order_by,
@ -113,10 +500,10 @@ std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::O
} }
VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable, VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable,
const std::vector<PropertyValue> &start_ids, const View view) { const std::vector<PropertyValue> &primary_key, const View view) {
auto it = vertex_iterable.begin(); auto it = vertex_iterable.begin();
while (it != vertex_iterable.end()) { while (it != vertex_iterable.end()) {
if (const auto &vertex = *it; start_ids <= vertex.PrimaryKey(view).GetValue()) { if (const auto &vertex = *it; primary_key <= vertex.PrimaryKey(view).GetValue()) {
break; break;
} }
++it; ++it;
@ -124,15 +511,77 @@ VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_itera
return it; return it;
} }
std::vector<Element>::const_iterator GetStartOrderedElementsIterator(const std::vector<Element> &ordered_elements, std::vector<Element<VertexAccessor>>::const_iterator GetStartOrderedElementsIterator(
const std::vector<PropertyValue> &start_ids, const std::vector<Element<VertexAccessor>> &ordered_elements, const std::vector<PropertyValue> &primary_key,
const View view) { const View view) {
for (auto it = ordered_elements.begin(); it != ordered_elements.end(); ++it) { for (auto it = ordered_elements.begin(); it != ordered_elements.end(); ++it) {
if (const auto &vertex = it->vertex_acc; start_ids <= vertex.PrimaryKey(view).GetValue()) { if (const auto &vertex = it->object_acc; primary_key <= vertex.PrimaryKey(view).GetValue()) {
return it; return it;
} }
} }
return ordered_elements.end(); return ordered_elements.end();
} }
std::array<std::vector<EdgeAccessor>, 2> GetEdgesFromVertex(const VertexAccessor &vertex_accessor,
const msgs::EdgeDirection direction) {
std::vector<EdgeAccessor> in_edges;
std::vector<EdgeAccessor> out_edges;
switch (direction) {
case memgraph::msgs::EdgeDirection::IN: {
auto edges = vertex_accessor.InEdges(View::OLD);
if (edges.HasValue()) {
in_edges = edges.GetValue();
}
}
case memgraph::msgs::EdgeDirection::OUT: {
auto edges = vertex_accessor.OutEdges(View::OLD);
if (edges.HasValue()) {
out_edges = edges.GetValue();
}
}
case memgraph::msgs::EdgeDirection::BOTH: {
auto maybe_in_edges = vertex_accessor.InEdges(View::OLD);
auto maybe_out_edges = vertex_accessor.OutEdges(View::OLD);
std::vector<EdgeAccessor> edges;
if (maybe_in_edges.HasValue()) {
in_edges = maybe_in_edges.GetValue();
}
if (maybe_out_edges.HasValue()) {
out_edges = maybe_out_edges.GetValue();
}
}
}
return std::array<std::vector<EdgeAccessor>, 2>{std::move(in_edges), std::move(out_edges)};
}
std::vector<Element<EdgeAccessor>> OrderByEdges(DbAccessor &dba, std::vector<EdgeAccessor> &iterable,
std::vector<msgs::OrderBy> &order_by_edges,
const VertexAccessor &vertex_acc) {
std::vector<Ordering> ordering;
ordering.reserve(order_by_edges.size());
std::transform(order_by_edges.begin(), order_by_edges.end(), std::back_inserter(ordering),
[](const auto &order_by) { return ConvertMsgsOrderByToOrdering(order_by.direction); });
std::vector<Element<EdgeAccessor>> ordered;
for (auto it = iterable.begin(); it != iterable.end(); ++it) {
std::vector<TypedValue> properties_order_by;
properties_order_by.reserve(order_by_edges.size());
std::transform(order_by_edges.begin(), order_by_edges.end(), std::back_inserter(properties_order_by),
[&dba, &vertex_acc, &it](const auto &order_by) {
return ComputeExpression(dba, vertex_acc, *it, order_by.expression.expression,
expr::identifier_node_symbol, expr::identifier_edge_symbol);
});
ordered.push_back({std::move(properties_order_by), *it});
}
auto compare_typed_values = TypedValueVectorCompare(ordering);
std::sort(ordered.begin(), ordered.end(), [compare_typed_values](const auto &pair1, const auto &pair2) {
return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by);
});
return ordered;
}
} // namespace memgraph::storage::v3 } // namespace memgraph::storage::v3

View File

@ -9,15 +9,29 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#pragma once
#include <vector> #include <vector>
#include "ast/ast.hpp"
#include "query/v2/requests.hpp" #include "query/v2/requests.hpp"
#include "storage/v3/bindings/ast/ast.hpp"
#include "storage/v3/bindings/pretty_print_ast_to_original_expression.hpp"
#include "storage/v3/bindings/typed_value.hpp" #include "storage/v3/bindings/typed_value.hpp"
#include "storage/v3/edge_accessor.hpp"
#include "storage/v3/expr.hpp"
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
#include "utils/template_utils.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
using EdgeAccessors = std::vector<storage::v3::EdgeAccessor>;
using EdgeUniquenessFunction = std::function<EdgeAccessors(EdgeAccessors &&, msgs::EdgeDirection)>;
using EdgeFiller =
std::function<ShardResult<void>(const EdgeAccessor &edge, bool is_in_edge, msgs::ExpandOneResultRow &result_row)>;
using msgs::Value;
template <typename T>
concept ObjectAccessor = utils::SameAsAnyOf<T, VertexAccessor, EdgeAccessor>;
inline bool TypedValueCompare(const TypedValue &a, const TypedValue &b) { inline bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
// in ordering null comes after everything else // in ordering null comes after everything else
@ -73,6 +87,17 @@ inline bool TypedValueCompare(const TypedValue &a, const TypedValue &b) {
} }
} }
inline Ordering ConvertMsgsOrderByToOrdering(msgs::OrderingDirection ordering) {
switch (ordering) {
case memgraph::msgs::OrderingDirection::ASCENDING:
return memgraph::storage::v3::Ordering::ASC;
case memgraph::msgs::OrderingDirection::DESCENDING:
return memgraph::storage::v3::Ordering::DESC;
default:
LOG_FATAL("Unknown ordering direction");
}
}
class TypedValueVectorCompare final { class TypedValueVectorCompare final {
public: public:
explicit TypedValueVectorCompare(const std::vector<Ordering> &ordering) : ordering_(ordering) {} explicit TypedValueVectorCompare(const std::vector<Ordering> &ordering) : ordering_(ordering) {}
@ -100,9 +125,10 @@ class TypedValueVectorCompare final {
std::vector<Ordering> ordering_; std::vector<Ordering> ordering_;
}; };
template <ObjectAccessor TObjectAccessor>
struct Element { struct Element {
std::vector<TypedValue> properties_order_by; std::vector<TypedValue> properties_order_by;
VertexAccessor vertex_acc; TObjectAccessor object_acc;
}; };
struct GetPropElement { struct GetPropElement {
@ -112,16 +138,81 @@ struct GetPropElement {
std::optional<EdgeAccessor> edge_acc; std::optional<EdgeAccessor> edge_acc;
}; };
std::vector<Element> OrderByElements(Shard::Accessor &acc, DbAccessor &dba, VerticesIterable &vertices_iterable, template <typename T>
std::vector<msgs::OrderBy> &order_bys); concept VerticesIt = utils::SameAsAnyOf<T, VerticesIterable, std::vector<VertexAccessor>>;
template <VerticesIt TIterable>
std::vector<Element<VertexAccessor>> OrderByVertices(DbAccessor &dba, TIterable &iterable,
std::vector<msgs::OrderBy> &order_by_vertices) {
std::vector<Ordering> ordering;
ordering.reserve(order_by_vertices.size());
std::transform(order_by_vertices.begin(), order_by_vertices.end(), std::back_inserter(ordering),
[](const auto &order_by) { return ConvertMsgsOrderByToOrdering(order_by.direction); });
std::vector<Element<VertexAccessor>> ordered;
for (auto it = iterable.begin(); it != iterable.end(); ++it) {
std::vector<TypedValue> properties_order_by;
properties_order_by.reserve(order_by_vertices.size());
std::transform(order_by_vertices.begin(), order_by_vertices.end(), std::back_inserter(properties_order_by),
[&dba, &it](const auto &order_by) {
return ComputeExpression(dba, *it, std::nullopt /*e_acc*/, order_by.expression.expression,
expr::identifier_node_symbol, expr::identifier_edge_symbol);
});
ordered.push_back({std::move(properties_order_by), *it});
}
auto compare_typed_values = TypedValueVectorCompare(ordering);
std::sort(ordered.begin(), ordered.end(), [compare_typed_values](const auto &pair1, const auto &pair2) {
return compare_typed_values(pair1.properties_order_by, pair2.properties_order_by);
});
return ordered;
}
std::vector<Element<EdgeAccessor>> OrderByEdges(DbAccessor &dba, std::vector<EdgeAccessor> &iterable,
std::vector<msgs::OrderBy> &order_by_edges,
const VertexAccessor &vertex_acc);
std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::OrderBy> &order_bys, std::vector<GetPropElement> OrderByElements(DbAccessor &dba, std::vector<msgs::OrderBy> &order_bys,
std::vector<GetPropElement> &&vertices); std::vector<GetPropElement> &&vertices);
VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable, VerticesIterable::Iterator GetStartVertexIterator(VerticesIterable &vertex_iterable,
const std::vector<PropertyValue> &start_ids, View view); const std::vector<PropertyValue> &primary_key, View view);
std::vector<Element>::const_iterator GetStartOrderedElementsIterator(const std::vector<Element> &ordered_elements, std::vector<Element<VertexAccessor>>::const_iterator GetStartOrderedElementsIterator(
const std::vector<PropertyValue> &start_ids, const std::vector<Element<VertexAccessor>> &ordered_elements, const std::vector<PropertyValue> &primary_key,
View view); View view);
std::array<std::vector<EdgeAccessor>, 2> GetEdgesFromVertex(const VertexAccessor &vertex_accessor,
msgs::EdgeDirection direction);
bool FilterOnVertex(DbAccessor &dba, const storage::v3::VertexAccessor &v_acc, const std::vector<std::string> &filters,
const std::string_view node_name, const std::optional<EdgeAccessor> &e_acc = std::nullopt);
std::vector<TypedValue> EvaluateVertexExpressions(DbAccessor &dba, const VertexAccessor &v_acc,
const std::vector<std::string> &expressions,
std::string_view node_name);
ShardResult<std::map<PropertyId, Value>> CollectSpecificPropertiesFromAccessor(const VertexAccessor &acc,
const std::vector<PropertyId> &props,
View view);
ShardResult<std::map<PropertyId, Value>> CollectAllPropertiesFromAccessor(const VertexAccessor &acc, View view,
const Schemas::Schema &schema);
EdgeUniquenessFunction InitializeEdgeUniquenessFunction(bool only_unique_neighbor_rows);
EdgeFiller InitializeEdgeFillerFunction(const msgs::ExpandOneRequest &req);
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
Shard::Accessor &acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
const Schemas::Schema &schema);
ShardResult<msgs::ExpandOneResultRow> GetExpandOneResult(
VertexAccessor v_acc, msgs::VertexId src_vertex, const msgs::ExpandOneRequest &req,
std::vector<EdgeAccessor> in_edge_accessors, std::vector<EdgeAccessor> out_edge_accessors,
const EdgeUniquenessFunction &maybe_filter_based_on_edge_uniqueness, const EdgeFiller &edge_filler,
const Schemas::Schema &schema);
} // namespace memgraph::storage::v3 } // namespace memgraph::storage::v3

View File

@ -11,24 +11,43 @@
#pragma once #pragma once
#include <cstdint>
#include <experimental/source_location>
#include <string>
#include <string_view>
#include <type_traits> #include <type_traits>
#include "common/errors.hpp"
#include "utils/result.hpp" #include "utils/result.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
static_assert(std::is_same_v<uint8_t, unsigned char>); static_assert(std::is_same_v<uint8_t, unsigned char>);
enum class Error : uint8_t { struct ShardError {
SERIALIZATION_ERROR, ShardError(common::ErrorCode code, std::string message, const std::experimental::source_location location)
NONEXISTENT_OBJECT, : code{code}, message{std::move(message)}, source{fmt::format("{}:{}", location.file_name(), location.line())} {}
DELETED_OBJECT,
VERTEX_HAS_EDGES, ShardError(common::ErrorCode code, const std::experimental::source_location location)
PROPERTIES_DISABLED, : code{code}, source{fmt::format("{}:{}", location.file_name(), location.line())} {}
VERTEX_ALREADY_INSERTED
common::ErrorCode code;
std::string message;
std::string source;
inline friend bool operator==(const ShardError &lhs, const ShardError &rhs) { return lhs.code == rhs.code; }
inline friend bool operator==(const ShardError &lhs, const common::ErrorCode rhs) { return lhs.code == rhs; }
}; };
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define SHARD_ERROR(error, ...) \
({ \
using ErrorCode = memgraph::common::ErrorCode; \
memgraph::storage::v3::ShardError(error, GET_MESSAGE(__VA_ARGS__), std::experimental::source_location::current()); \
})
template <class TValue> template <class TValue>
using Result = utils::BasicResult<Error, TValue>; using ShardResult = utils::BasicResult<ShardError, TValue>;
} // namespace memgraph::storage::v3 } // namespace memgraph::storage::v3

View File

@ -16,67 +16,58 @@
#include <ranges> #include <ranges>
#include "common/types.hpp" #include "common/types.hpp"
#include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
bool operator==(const SchemaViolation &lhs, const SchemaViolation &rhs) { SchemaValidator::SchemaValidator(Schemas &schemas, const NameIdMapper &name_id_mapper)
return lhs.status == rhs.status && lhs.label == rhs.label && : schemas_{&schemas}, name_id_mapper_{&name_id_mapper} {}
lhs.violated_schema_property == rhs.violated_schema_property &&
lhs.violated_property_value == rhs.violated_property_value;
}
SchemaViolation::SchemaViolation(ValidationStatus status, LabelId label) : status{status}, label{label} {} ShardResult<void> SchemaValidator::ValidateVertexCreate(LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<PropertyValue> &primary_properties) const {
SchemaViolation::SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property)
: status{status}, label{label}, violated_schema_property{violated_schema_property} {}
SchemaViolation::SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property,
PropertyValue violated_property_value)
: status{status},
label{label},
violated_schema_property{violated_schema_property},
violated_property_value{violated_property_value} {}
SchemaValidator::SchemaValidator(Schemas &schemas) : schemas_{schemas} {}
std::optional<SchemaViolation> SchemaValidator::ValidateVertexCreate(
LabelId primary_label, const std::vector<LabelId> &labels,
const std::vector<PropertyValue> &primary_properties) const {
// Schema on primary label // Schema on primary label
const auto *schema = schemas_.GetSchema(primary_label); const auto *schema = schemas_->GetSchema(primary_label);
if (schema == nullptr) { if (schema == nullptr) {
return SchemaViolation(SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL, primary_label); return SHARD_ERROR(ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL, "Schema not defined for label :{}",
name_id_mapper_->IdToName(primary_label.AsInt()));
} }
// Is there another primary label among secondary labels // Is there another primary label among secondary labels
for (const auto &secondary_label : labels) { for (const auto &secondary_label : labels) {
if (schemas_.GetSchema(secondary_label)) { if (schemas_->GetSchema(secondary_label)) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, secondary_label); return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY,
"Cannot add label :{}, since it is defined as a primary label",
name_id_mapper_->IdToName(secondary_label.AsInt()));
} }
} }
// Quick size check // Quick size check
if (schema->second.size() != primary_properties.size()) { if (schema->second.size() != primary_properties.size()) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, primary_label); return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
"Not all primary properties have been specified for :{} vertex",
name_id_mapper_->IdToName(primary_label.AsInt()));
} }
// Check only properties defined by schema // Check only properties defined by schema
for (size_t i{0}; i < schema->second.size(); ++i) { for (size_t i{0}; i < schema->second.size(); ++i) {
// Check schema property type // Check schema property type
if (auto property_schema_type = PropertyTypeToSchemaType(primary_properties[i]); if (auto property_schema_type = PropertyTypeToSchemaType(primary_properties[i]);
property_schema_type && *property_schema_type != schema->second[i].type) { property_schema_type && *property_schema_type != schema->second[i].type) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, primary_label, return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE,
schema->second[i], primary_properties[i]); "Property {} is of wrong type, expected {}, actual {}",
name_id_mapper_->IdToName(schema->second[i].property_id.AsInt()),
SchemaTypeToString(schema->second[i].type), SchemaTypeToString(*property_schema_type));
} }
} }
return std::nullopt; return {};
} }
std::optional<SchemaViolation> SchemaValidator::ValidatePropertyUpdate(const LabelId primary_label, ShardResult<void> SchemaValidator::ValidatePropertyUpdate(const LabelId primary_label,
const PropertyId property_id) const { const PropertyId property_id) const {
// Verify existence of schema on primary label // Verify existence of schema on primary label
const auto *schema = schemas_.GetSchema(primary_label); const auto *schema = schemas_->GetSchema(primary_label);
MG_ASSERT(schema, "Cannot validate against non existing schema!"); MG_ASSERT(schema, "Cannot validate against non existing schema!");
// Verify that updating property is not part of schema // Verify that updating property is not part of schema
@ -84,34 +75,37 @@ std::optional<SchemaViolation> SchemaValidator::ValidatePropertyUpdate(const Lab
schema->second, schema->second,
[property_id](const auto &schema_property) { return property_id == schema_property.property_id; }); [property_id](const auto &schema_property) { return property_id == schema_property.property_id; });
schema_property != schema->second.end()) { schema_property != schema->second.end()) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, primary_label, return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY,
*schema_property); "Cannot update primary property {} of schema on label :{}",
name_id_mapper_->IdToName(schema_property->property_id.AsInt()),
name_id_mapper_->IdToName(primary_label.AsInt()));
} }
return std::nullopt; return {};
} }
std::optional<SchemaViolation> SchemaValidator::ValidateLabelUpdate(const LabelId label) const { ShardResult<void> SchemaValidator::ValidateLabelUpdate(const LabelId label) const {
const auto *schema = schemas_.GetSchema(label); const auto *schema = schemas_->GetSchema(label);
if (schema) { if (schema) {
return SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label); return SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL, "Cannot add/remove primary label :{}",
name_id_mapper_->IdToName(label.AsInt()));
} }
return std::nullopt; return {};
} }
const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_.GetSchema(label); } const Schemas::Schema *SchemaValidator::GetSchema(LabelId label) const { return schemas_->GetSchema(label); }
VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const LabelId primary_label) VertexValidator::VertexValidator(const SchemaValidator &schema_validator, const LabelId primary_label)
: schema_validator{&schema_validator}, primary_label_{primary_label} {} : schema_validator{&schema_validator}, primary_label_{primary_label} {}
std::optional<SchemaViolation> VertexValidator::ValidatePropertyUpdate(PropertyId property_id) const { ShardResult<void> VertexValidator::ValidatePropertyUpdate(PropertyId property_id) const {
return schema_validator->ValidatePropertyUpdate(primary_label_, property_id); return schema_validator->ValidatePropertyUpdate(primary_label_, property_id);
}; };
std::optional<SchemaViolation> VertexValidator::ValidateAddLabel(LabelId label) const { ShardResult<void> VertexValidator::ValidateAddLabel(LabelId label) const {
return schema_validator->ValidateLabelUpdate(label); return schema_validator->ValidateLabelUpdate(label);
} }
std::optional<SchemaViolation> VertexValidator::ValidateRemoveLabel(LabelId label) const { ShardResult<void> VertexValidator::ValidateRemoveLabel(LabelId label) const {
return schema_validator->ValidateLabelUpdate(label); return schema_validator->ValidateLabelUpdate(label);
} }

View File

@ -11,68 +11,43 @@
#pragma once #pragma once
#include <optional>
#include <variant> #include <variant>
#include "storage/v2/result.hpp"
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp" #include "storage/v3/result.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
struct SchemaViolation {
enum class ValidationStatus : uint8_t {
NO_SCHEMA_DEFINED_FOR_LABEL,
VERTEX_PROPERTY_WRONG_TYPE,
VERTEX_UPDATE_PRIMARY_KEY,
VERTEX_UPDATE_PRIMARY_LABEL,
VERTEX_SECONDARY_LABEL_IS_PRIMARY,
VERTEX_PRIMARY_PROPERTIES_UNDEFINED,
};
SchemaViolation(ValidationStatus status, LabelId label);
SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property);
SchemaViolation(ValidationStatus status, LabelId label, SchemaProperty violated_schema_property,
PropertyValue violated_property_value);
friend bool operator==(const SchemaViolation &lhs, const SchemaViolation &rhs);
ValidationStatus status;
LabelId label;
std::optional<SchemaProperty> violated_schema_property;
std::optional<PropertyValue> violated_property_value;
};
class SchemaValidator { class SchemaValidator {
public: public:
explicit SchemaValidator(Schemas &schemas); explicit SchemaValidator(Schemas &schemas, const NameIdMapper &name_id_mapper);
[[nodiscard]] std::optional<SchemaViolation> ValidateVertexCreate( [[nodiscard]] ShardResult<void> ValidateVertexCreate(LabelId primary_label, const std::vector<LabelId> &labels,
LabelId primary_label, const std::vector<LabelId> &labels, const std::vector<PropertyValue> &primary_properties) const;
const std::vector<PropertyValue> &primary_properties) const;
[[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(LabelId primary_label, [[nodiscard]] ShardResult<void> ValidatePropertyUpdate(LabelId primary_label, PropertyId property_id) const;
PropertyId property_id) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateLabelUpdate(LabelId label) const; [[nodiscard]] ShardResult<void> ValidateLabelUpdate(LabelId label) const;
const Schemas::Schema *GetSchema(LabelId label) const; const Schemas::Schema *GetSchema(LabelId label) const;
private: private:
Schemas &schemas_; Schemas *schemas_;
const NameIdMapper *name_id_mapper_;
}; };
struct VertexValidator { struct VertexValidator {
explicit VertexValidator(const SchemaValidator &schema_validator, LabelId primary_label); explicit VertexValidator(const SchemaValidator &schema_validator, LabelId primary_label);
[[nodiscard]] std::optional<SchemaViolation> ValidatePropertyUpdate(PropertyId property_id) const; [[nodiscard]] ShardResult<void> ValidatePropertyUpdate(PropertyId property_id) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateAddLabel(LabelId label) const; [[nodiscard]] ShardResult<void> ValidateAddLabel(LabelId label) const;
[[nodiscard]] std::optional<SchemaViolation> ValidateRemoveLabel(LabelId label) const; [[nodiscard]] ShardResult<void> ValidateRemoveLabel(LabelId label) const;
const SchemaValidator *schema_validator; const SchemaValidator *schema_validator;

View File

@ -31,9 +31,10 @@
#include "storage/v3/indices.hpp" #include "storage/v3/indices.hpp"
#include "storage/v3/key_store.hpp" #include "storage/v3/key_store.hpp"
#include "storage/v3/mvcc.hpp" #include "storage/v3/mvcc.hpp"
#include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp" #include "storage/v3/schema_validator.hpp"
#include "storage/v3/shard_operation_result.hpp"
#include "storage/v3/transaction.hpp" #include "storage/v3/transaction.hpp"
#include "storage/v3/vertex.hpp" #include "storage/v3/vertex.hpp"
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
@ -327,7 +328,7 @@ Shard::Shard(const LabelId primary_label, const PrimaryKey min_primary_key,
: primary_label_{primary_label}, : primary_label_{primary_label},
min_primary_key_{min_primary_key}, min_primary_key_{min_primary_key},
max_primary_key_{max_primary_key}, max_primary_key_{max_primary_key},
schema_validator_{schemas_}, schema_validator_{schemas_, name_id_mapper_},
vertex_validator_{schema_validator_, primary_label}, vertex_validator_{schema_validator_, primary_label},
indices_{config.items, vertex_validator_}, indices_{config.items, vertex_validator_},
isolation_level_{config.transaction.isolation_level}, isolation_level_{config.transaction.isolation_level},
@ -344,7 +345,7 @@ Shard::~Shard() {}
Shard::Accessor::Accessor(Shard &shard, Transaction &transaction) Shard::Accessor::Accessor(Shard &shard, Transaction &transaction)
: shard_(&shard), transaction_(&transaction), config_(shard_->config_.items) {} : shard_(&shard), transaction_(&transaction), config_(shard_->config_.items) {}
ShardOperationResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate( ShardResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
const std::vector<LabelId> &labels, const std::vector<PropertyValue> &primary_properties, const std::vector<LabelId> &labels, const std::vector<PropertyValue> &primary_properties,
const std::vector<std::pair<PropertyId, PropertyValue>> &properties) { const std::vector<std::pair<PropertyId, PropertyValue>> &properties) {
OOMExceptionEnabler oom_exception; OOMExceptionEnabler oom_exception;
@ -352,8 +353,8 @@ ShardOperationResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
auto maybe_schema_violation = auto maybe_schema_violation =
GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties); GetSchemaValidator().ValidateVertexCreate(shard_->primary_label_, labels, primary_properties);
if (maybe_schema_violation) { if (maybe_schema_violation.HasError()) {
return {std::move(*maybe_schema_violation)}; return {std::move(maybe_schema_violation.GetError())};
} }
auto acc = shard_->vertices_.access(); auto acc = shard_->vertices_.access();
@ -363,7 +364,7 @@ ShardOperationResult<VertexAccessor> Shard::Accessor::CreateVertexAndValidate(
VertexAccessor vertex_acc{&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_}; VertexAccessor vertex_acc{&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_};
if (!inserted) { if (!inserted) {
return {Error::VERTEX_ALREADY_INSERTED}; return SHARD_ERROR(ErrorCode::VERTEX_ALREADY_INSERTED);
} }
MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!"); MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
@ -394,19 +395,19 @@ std::optional<VertexAccessor> Shard::Accessor::FindVertex(std::vector<PropertyVa
return VertexAccessor::Create(&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_, view); return VertexAccessor::Create(&it->vertex, transaction_, &shard_->indices_, config_, shard_->vertex_validator_, view);
} }
Result<std::optional<VertexAccessor>> Shard::Accessor::DeleteVertex(VertexAccessor *vertex) { ShardResult<std::optional<VertexAccessor>> Shard::Accessor::DeleteVertex(VertexAccessor *vertex) {
MG_ASSERT(vertex->transaction_ == transaction_, MG_ASSERT(vertex->transaction_ == transaction_,
"VertexAccessor must be from the same transaction as the storage " "VertexAccessor must be from the same transaction as the storage "
"accessor when deleting a vertex!"); "accessor when deleting a vertex!");
auto *vertex_ptr = vertex->vertex_; auto *vertex_ptr = vertex->vertex_;
if (!PrepareForWrite(transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_ptr->deleted) { if (vertex_ptr->deleted) {
return std::optional<VertexAccessor>{}; return std::optional<VertexAccessor>{};
} }
if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return Error::VERTEX_HAS_EDGES; if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return SHARD_ERROR(ErrorCode::VERTEX_HAS_EDGES);
CreateAndLinkDelta(transaction_, vertex_ptr, Delta::RecreateObjectTag()); CreateAndLinkDelta(transaction_, vertex_ptr, Delta::RecreateObjectTag());
vertex_ptr->deleted = true; vertex_ptr->deleted = true;
@ -415,7 +416,7 @@ Result<std::optional<VertexAccessor>> Shard::Accessor::DeleteVertex(VertexAccess
shard_->vertex_validator_, true); shard_->vertex_validator_, true);
} }
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shard::Accessor::DetachDeleteVertex( ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shard::Accessor::DetachDeleteVertex(
VertexAccessor *vertex) { VertexAccessor *vertex) {
using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>; using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
@ -428,7 +429,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shar
std::vector<Vertex::EdgeLink> out_edges; std::vector<Vertex::EdgeLink> out_edges;
{ {
if (!PrepareForWrite(transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_ptr->deleted) return std::optional<ReturnType>{}; if (vertex_ptr->deleted) return std::optional<ReturnType>{};
@ -443,7 +444,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shar
EdgeAccessor e(edge, edge_type, from_vertex, vertex_id, transaction_, &shard_->indices_, config_); EdgeAccessor e(edge, edge_type, from_vertex, vertex_id, transaction_, &shard_->indices_, config_);
auto ret = DeleteEdge(e.FromVertex(), e.ToVertex(), e.Gid()); auto ret = DeleteEdge(e.FromVertex(), e.ToVertex(), e.Gid());
if (ret.HasError()) { if (ret.HasError()) {
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!"); MG_ASSERT(ret.GetError() == common::ErrorCode::SERIALIZATION_ERROR, "Invalid database state!");
return ret.GetError(); return ret.GetError();
} }
@ -456,7 +457,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shar
EdgeAccessor e(edge, edge_type, vertex_id, to_vertex, transaction_, &shard_->indices_, config_); EdgeAccessor e(edge, edge_type, vertex_id, to_vertex, transaction_, &shard_->indices_, config_);
auto ret = DeleteEdge(e.FromVertex(), e.ToVertex(), e.Gid()); auto ret = DeleteEdge(e.FromVertex(), e.ToVertex(), e.Gid());
if (ret.HasError()) { if (ret.HasError()) {
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!"); MG_ASSERT(ret.GetError() == common::ErrorCode::SERIALIZATION_ERROR, "Invalid database state!");
return ret.GetError(); return ret.GetError();
} }
@ -469,7 +470,7 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shar
// vertex. Some other transaction could have modified the vertex in the // vertex. Some other transaction could have modified the vertex in the
// meantime if we didn't have any edges to delete. // meantime if we didn't have any edges to delete.
if (!PrepareForWrite(transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_ptr)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!"); MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
@ -481,8 +482,8 @@ Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Shar
std::move(deleted_edges)); std::move(deleted_edges));
} }
Result<EdgeAccessor> Shard::Accessor::CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, ShardResult<EdgeAccessor> Shard::Accessor::CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id,
const EdgeTypeId edge_type, const Gid gid) { const EdgeTypeId edge_type, const Gid gid) {
OOMExceptionEnabler oom_exception; OOMExceptionEnabler oom_exception;
Vertex *from_vertex{nullptr}; Vertex *from_vertex{nullptr};
Vertex *to_vertex{nullptr}; Vertex *to_vertex{nullptr};
@ -506,12 +507,12 @@ Result<EdgeAccessor> Shard::Accessor::CreateEdge(VertexId from_vertex_id, Vertex
} }
if (from_is_local) { if (from_is_local) {
if (!PrepareForWrite(transaction_, from_vertex)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, from_vertex)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (from_vertex->deleted) return Error::DELETED_OBJECT; if (from_vertex->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
} }
if (to_is_local && to_vertex != from_vertex) { if (to_is_local && to_vertex != from_vertex) {
if (!PrepareForWrite(transaction_, to_vertex)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, to_vertex)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (to_vertex->deleted) return Error::DELETED_OBJECT; if (to_vertex->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
} }
EdgeRef edge(gid); EdgeRef edge(gid);
@ -540,8 +541,8 @@ Result<EdgeAccessor> Shard::Accessor::CreateEdge(VertexId from_vertex_id, Vertex
&shard_->indices_, config_); &shard_->indices_, config_);
} }
Result<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, ShardResult<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id,
const Gid edge_id) { const Gid edge_id) {
Vertex *from_vertex{nullptr}; Vertex *from_vertex{nullptr};
Vertex *to_vertex{nullptr}; Vertex *to_vertex{nullptr};
@ -566,13 +567,13 @@ Result<std::optional<EdgeAccessor>> Shard::Accessor::DeleteEdge(VertexId from_ve
if (from_is_local) { if (from_is_local) {
if (!PrepareForWrite(transaction_, from_vertex)) { if (!PrepareForWrite(transaction_, from_vertex)) {
return Error::SERIALIZATION_ERROR; return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
} }
MG_ASSERT(!from_vertex->deleted, "Invalid database state!"); MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
} }
if (to_is_local && to_vertex != from_vertex) { if (to_is_local && to_vertex != from_vertex) {
if (!PrepareForWrite(transaction_, to_vertex)) { if (!PrepareForWrite(transaction_, to_vertex)) {
return Error::SERIALIZATION_ERROR; return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
} }
MG_ASSERT(!to_vertex->deleted, "Invalid database state!"); MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
} }

View File

@ -38,7 +38,6 @@
#include "storage/v3/result.hpp" #include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp" #include "storage/v3/schema_validator.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
#include "storage/v3/shard_operation_result.hpp"
#include "storage/v3/transaction.hpp" #include "storage/v3/transaction.hpp"
#include "storage/v3/vertex.hpp" #include "storage/v3/vertex.hpp"
#include "storage/v3/vertex_accessor.hpp" #include "storage/v3/vertex_accessor.hpp"
@ -207,7 +206,7 @@ class Shard final {
public: public:
/// @throw std::bad_alloc /// @throw std::bad_alloc
ShardOperationResult<VertexAccessor> CreateVertexAndValidate( ShardResult<VertexAccessor> CreateVertexAndValidate(
const std::vector<LabelId> &labels, const std::vector<PropertyValue> &primary_properties, const std::vector<LabelId> &labels, const std::vector<PropertyValue> &primary_properties,
const std::vector<std::pair<PropertyId, PropertyValue>> &properties); const std::vector<std::pair<PropertyId, PropertyValue>> &properties);
@ -262,19 +261,19 @@ class Shard final {
/// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise /// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex); ShardResult<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex);
/// @return Accessor to the deleted vertex and deleted edges if a deletion took place, std::nullopt otherwise /// @return Accessor to the deleted vertex and deleted edges if a deletion took place, std::nullopt otherwise
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex( ShardResult<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
VertexAccessor *vertex); VertexAccessor *vertex);
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<EdgeAccessor> CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, EdgeTypeId edge_type, Gid gid); ShardResult<EdgeAccessor> CreateEdge(VertexId from_vertex_id, VertexId to_vertex_id, EdgeTypeId edge_type, Gid gid);
/// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise /// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::optional<EdgeAccessor>> DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, Gid edge_id); ShardResult<std::optional<EdgeAccessor>> DeleteEdge(VertexId from_vertex_id, VertexId to_vertex_id, Gid edge_id);
LabelId NameToLabel(std::string_view name) const; LabelId NameToLabel(std::string_view name) const;

View File

@ -190,6 +190,12 @@ class ShardManager {
}); });
} }
void BlockOnQuiescence() {
for (const auto &worker : workers_) {
worker.BlockOnQuiescence();
}
}
private: private:
io::Io<IoImpl> io_; io::Io<IoImpl> io_;
std::vector<shard_worker::Queue> workers_; std::vector<shard_worker::Queue> workers_;

View File

@ -1,26 +0,0 @@
// 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 <variant>
#include "storage/v3/result.hpp"
#include "storage/v3/schema_validator.hpp"
namespace memgraph::storage::v3 {
using ResultErrorType = std::variant<SchemaViolation, Error>;
template <typename TValue>
using ShardOperationResult = utils::BasicResult<ResultErrorType, TValue>;
} // namespace memgraph::storage::v3

File diff suppressed because it is too large Load Diff

View File

@ -21,9 +21,6 @@
namespace memgraph::storage::v3 { namespace memgraph::storage::v3 {
template <typename>
constexpr auto kAlwaysFalse = false;
class ShardRsm { class ShardRsm {
std::unique_ptr<Shard> shard_; std::unique_ptr<Shard> shard_;

View File

@ -80,6 +80,9 @@ struct QueueInner {
// starvation by sometimes randomizing priorities, rather than following a strict // starvation by sometimes randomizing priorities, rather than following a strict
// prioritization. // prioritization.
std::deque<Message> queue; std::deque<Message> queue;
uint64_t submitted = 0;
uint64_t calls_to_pop = 0;
}; };
/// There are two reasons to implement our own Queue instead of using /// There are two reasons to implement our own Queue instead of using
@ -95,6 +98,8 @@ class Queue {
MG_ASSERT(inner_.use_count() > 0); MG_ASSERT(inner_.use_count() > 0);
std::unique_lock<std::mutex> lock(inner_->mu); std::unique_lock<std::mutex> lock(inner_->mu);
inner_->submitted++;
inner_->queue.emplace_back(std::forward<Message>(message)); inner_->queue.emplace_back(std::forward<Message>(message));
} // lock dropped before notifying condition variable } // lock dropped before notifying condition variable
@ -105,6 +110,9 @@ class Queue {
MG_ASSERT(inner_.use_count() > 0); MG_ASSERT(inner_.use_count() > 0);
std::unique_lock<std::mutex> lock(inner_->mu); std::unique_lock<std::mutex> lock(inner_->mu);
inner_->calls_to_pop++;
inner_->cv.notify_all();
while (inner_->queue.empty()) { while (inner_->queue.empty()) {
inner_->cv.wait(lock); inner_->cv.wait(lock);
} }
@ -114,6 +122,15 @@ class Queue {
return message; return message;
} }
void BlockOnQuiescence() const {
MG_ASSERT(inner_.use_count() > 0);
std::unique_lock<std::mutex> lock(inner_->mu);
while (inner_->calls_to_pop <= inner_->submitted) {
inner_->cv.wait(lock);
}
}
}; };
/// A ShardWorker owns Raft<ShardRsm> instances. receives messages from the ShardManager. /// A ShardWorker owns Raft<ShardRsm> instances. receives messages from the ShardManager.
@ -122,7 +139,6 @@ class ShardWorker {
io::Io<IoImpl> io_; io::Io<IoImpl> io_;
Queue queue_; Queue queue_;
std::priority_queue<std::pair<Time, uuid>, std::vector<std::pair<Time, uuid>>, std::greater<>> cron_schedule_; std::priority_queue<std::pair<Time, uuid>, std::vector<std::pair<Time, uuid>>, std::greater<>> cron_schedule_;
Time next_cron_ = Time::min();
std::map<uuid, ShardRaft<IoImpl>> rsm_map_; std::map<uuid, ShardRaft<IoImpl>> rsm_map_;
bool Process(ShutDown && /* shut_down */) { return false; } bool Process(ShutDown && /* shut_down */) { return false; }
@ -175,10 +191,7 @@ class ShardWorker {
return; return;
} }
auto rsm_io = io_.ForkLocal(); auto rsm_io = io_.ForkLocal(to_init.uuid);
auto io_addr = rsm_io.GetAddress();
io_addr.unique_id = to_init.uuid;
rsm_io.SetAddress(io_addr);
// TODO(tyler) get peers from Coordinator in HeartbeatResponse // TODO(tyler) get peers from Coordinator in HeartbeatResponse
std::vector<Address> rsm_peers = {}; std::vector<Address> rsm_peers = {};
@ -208,15 +221,12 @@ class ShardWorker {
~ShardWorker() = default; ~ShardWorker() = default;
void Run() { void Run() {
while (true) { bool should_continue = true;
while (should_continue) {
Message message = queue_.Pop(); Message message = queue_.Pop();
const bool should_continue = should_continue =
std::visit([&](auto &&msg) { return Process(std::forward<decltype(msg)>(msg)); }, std::move(message)); std::visit([&](auto &&msg) { return Process(std::forward<decltype(msg)>(msg)); }, std::move(message));
if (!should_continue) {
return;
}
} }
} }
}; };

View File

@ -1,34 +0,0 @@
// 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 <vector>
#include <boost/asio/thread_pool.hpp>
#include "storage/v3/shard.hpp"
namespace memgraph::storage::v3 {
class Storage {
public:
explicit Storage(Config config);
// Interface toward shard manipulation
// Shard handler -> will use rsm client
private:
std::vector<Shard> shards_;
boost::asio::thread_pool shard_handlers_;
Config config_;
};
} // namespace memgraph::storage::v3

View File

@ -129,4 +129,27 @@ inline std::vector<Value> ConvertValueVector(const std::vector<v3::PropertyValue
inline msgs::VertexId ToMsgsVertexId(const v3::VertexId &vertex_id) { inline msgs::VertexId ToMsgsVertexId(const v3::VertexId &vertex_id) {
return {msgs::Label{vertex_id.primary_label}, ConvertValueVector(vertex_id.primary_key)}; return {msgs::Label{vertex_id.primary_label}, ConvertValueVector(vertex_id.primary_key)};
} }
inline std::vector<std::pair<v3::PropertyId, v3::PropertyValue>> ConvertPropertyMap(
std::vector<std::pair<v3::PropertyId, Value>> &properties) {
std::vector<std::pair<v3::PropertyId, v3::PropertyValue>> ret;
ret.reserve(properties.size());
std::transform(std::make_move_iterator(properties.begin()), std::make_move_iterator(properties.end()),
std::back_inserter(ret), [](std::pair<v3::PropertyId, Value> &&property) {
return std::make_pair(property.first, ToPropertyValue(std::move(property.second)));
});
return ret;
}
inline std::vector<std::pair<PropertyId, Value>> FromMap(const std::map<PropertyId, Value> &properties) {
std::vector<std::pair<PropertyId, Value>> ret;
ret.reserve(properties.size());
std::transform(properties.begin(), properties.end(), std::back_inserter(ret),
[](const auto &property) { return std::make_pair(property.first, property.second); });
return ret;
}
} // namespace memgraph::storage::conversions } // namespace memgraph::storage::conversions

View File

@ -21,8 +21,8 @@
#include "storage/v3/key_store.hpp" #include "storage/v3/key_store.hpp"
#include "storage/v3/mvcc.hpp" #include "storage/v3/mvcc.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/result.hpp"
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/shard_operation_result.hpp"
#include "storage/v3/vertex.hpp" #include "storage/v3/vertex.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/memory_tracker.hpp" #include "utils/memory_tracker.hpp"
@ -80,12 +80,12 @@ bool VertexAccessor::IsVisible(View view) const {
return exists && (for_deleted_ || !deleted); return exists && (for_deleted_ || !deleted);
} }
Result<bool> VertexAccessor::AddLabel(LabelId label) { ShardResult<bool> VertexAccessor::AddLabel(LabelId label) {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_->deleted) return Error::DELETED_OBJECT; if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false; if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
@ -98,15 +98,15 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
return true; return true;
} }
ShardOperationResult<bool> VertexAccessor::AddLabelAndValidate(LabelId label) { ShardResult<bool> VertexAccessor::AddLabelAndValidate(LabelId label) {
if (const auto maybe_violation_error = vertex_validator_->ValidateAddLabel(label); maybe_violation_error) { if (const auto maybe_violation_error = vertex_validator_->ValidateAddLabel(label); maybe_violation_error.HasError()) {
return {*maybe_violation_error}; return {maybe_violation_error.GetError()};
} }
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR}; if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_->deleted) return {Error::DELETED_OBJECT}; if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false; if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
@ -119,10 +119,10 @@ ShardOperationResult<bool> VertexAccessor::AddLabelAndValidate(LabelId label) {
return true; return true;
} }
Result<bool> VertexAccessor::RemoveLabel(LabelId label) { ShardResult<bool> VertexAccessor::RemoveLabel(LabelId label) {
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_->deleted) return Error::DELETED_OBJECT; if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label); auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
if (it == vertex_->labels.end()) return false; if (it == vertex_->labels.end()) return false;
@ -134,14 +134,15 @@ Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
return true; return true;
} }
ShardOperationResult<bool> VertexAccessor::RemoveLabelAndValidate(LabelId label) { ShardResult<bool> VertexAccessor::RemoveLabelAndValidate(LabelId label) {
if (const auto maybe_violation_error = vertex_validator_->ValidateRemoveLabel(label); maybe_violation_error) { if (const auto maybe_violation_error = vertex_validator_->ValidateRemoveLabel(label);
return {*maybe_violation_error}; maybe_violation_error.HasError()) {
return {maybe_violation_error.GetError()};
} }
if (!PrepareForWrite(transaction_, vertex_)) return {Error::SERIALIZATION_ERROR}; if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_->deleted) return {Error::DELETED_OBJECT}; if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label); auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
if (it == vertex_->labels.end()) return false; if (it == vertex_->labels.end()) return false;
@ -153,9 +154,9 @@ ShardOperationResult<bool> VertexAccessor::RemoveLabelAndValidate(LabelId label)
return true; return true;
} }
Result<bool> VertexAccessor::HasLabel(View view, LabelId label) const { return HasLabel(label, view); } ShardResult<bool> VertexAccessor::HasLabel(View view, LabelId label) const { return HasLabel(label, view); }
Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const { ShardResult<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
bool has_label = false; bool has_label = false;
@ -197,12 +198,12 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return has_label; return has_label;
} }
Result<LabelId> VertexAccessor::PrimaryLabel(const View view) const { ShardResult<LabelId> VertexAccessor::PrimaryLabel(const View view) const {
if (const auto result = CheckVertexExistence(view); result.HasError()) { if (const auto result = CheckVertexExistence(view); result.HasError()) {
return result.GetError(); return result.GetError();
} }
@ -210,21 +211,21 @@ Result<LabelId> VertexAccessor::PrimaryLabel(const View view) const {
return vertex_validator_->primary_label_; return vertex_validator_->primary_label_;
} }
Result<PrimaryKey> VertexAccessor::PrimaryKey(const View view) const { ShardResult<PrimaryKey> VertexAccessor::PrimaryKey(const View view) const {
if (const auto result = CheckVertexExistence(view); result.HasError()) { if (const auto result = CheckVertexExistence(view); result.HasError()) {
return result.GetError(); return result.GetError();
} }
return vertex_->keys.Keys(); return vertex_->keys.Keys();
} }
Result<VertexId> VertexAccessor::Id(View view) const { ShardResult<VertexId> VertexAccessor::Id(View view) const {
if (const auto result = CheckVertexExistence(view); result.HasError()) { if (const auto result = CheckVertexExistence(view); result.HasError()) {
return result.GetError(); return result.GetError();
} }
return VertexId{vertex_validator_->primary_label_, vertex_->keys.Keys()}; return VertexId{vertex_validator_->primary_label_, vertex_->keys.Keys()};
}; };
Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const { ShardResult<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
std::vector<LabelId> labels; std::vector<LabelId> labels;
@ -267,17 +268,17 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return std::move(labels); return std::move(labels);
} }
Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) { ShardResult<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_->deleted) return Error::DELETED_OBJECT; if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
auto current_value = vertex_->properties.GetProperty(property); auto current_value = vertex_->properties.GetProperty(property);
// We could skip setting the value if the previous one is the same to the new // We could skip setting the value if the previous one is the same to the new
@ -294,7 +295,7 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
return std::move(current_value); return std::move(current_value);
} }
Result<void> VertexAccessor::CheckVertexExistence(View view) const { ShardResult<void> VertexAccessor::CheckVertexExistence(View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
Delta *delta = nullptr; Delta *delta = nullptr;
@ -323,27 +324,27 @@ Result<void> VertexAccessor::CheckVertexExistence(View view) const {
} }
}); });
if (!exists) { if (!exists) {
return Error::NONEXISTENT_OBJECT; return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
} }
if (!for_deleted_ && deleted) { if (!for_deleted_ && deleted) {
return Error::DELETED_OBJECT; return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
} }
return {}; return {};
} }
ShardOperationResult<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId property, ShardResult<PropertyValue> VertexAccessor::SetPropertyAndValidate(PropertyId property, const PropertyValue &value) {
const PropertyValue &value) { if (auto maybe_violation_error = vertex_validator_->ValidatePropertyUpdate(property);
if (auto maybe_violation_error = vertex_validator_->ValidatePropertyUpdate(property); maybe_violation_error) { maybe_violation_error.HasError()) {
return {*maybe_violation_error}; return {maybe_violation_error.GetError()};
} }
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
if (!PrepareForWrite(transaction_, vertex_)) { if (!PrepareForWrite(transaction_, vertex_)) {
return {Error::SERIALIZATION_ERROR}; return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
} }
if (vertex_->deleted) { if (vertex_->deleted) {
return {Error::DELETED_OBJECT}; return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
} }
auto current_value = vertex_->properties.GetProperty(property); auto current_value = vertex_->properties.GetProperty(property);
@ -361,10 +362,10 @@ ShardOperationResult<PropertyValue> VertexAccessor::SetPropertyAndValidate(Prope
return std::move(current_value); return std::move(current_value);
} }
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() { ShardResult<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR);
if (vertex_->deleted) return Error::DELETED_OBJECT; if (vertex_->deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
auto properties = vertex_->properties.Properties(); auto properties = vertex_->properties.Properties();
for (const auto &property : properties) { for (const auto &property : properties) {
@ -377,7 +378,7 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
return std::move(properties); return std::move(properties);
} }
Result<PropertyValue> VertexAccessor::GetProperty(View view, PropertyId property) const { ShardResult<PropertyValue> VertexAccessor::GetProperty(View view, PropertyId property) const {
return GetProperty(property, view).GetValue(); return GetProperty(property, view).GetValue();
} }
@ -407,7 +408,7 @@ PropertyValue VertexAccessor::GetPropertyValue(PropertyId property, View view) c
return value; return value;
} }
Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view) const { ShardResult<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
PropertyValue value; PropertyValue value;
@ -442,12 +443,12 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return std::move(value); return std::move(value);
} }
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view) const { ShardResult<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
std::map<PropertyId, PropertyValue> properties; std::map<PropertyId, PropertyValue> properties;
@ -492,13 +493,13 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return std::move(properties); return std::move(properties);
} }
Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::vector<EdgeTypeId> &edge_types, ShardResult<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::vector<EdgeTypeId> &edge_types,
const VertexId *destination_id) const { const VertexId *destination_id) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
std::vector<Vertex::EdgeLink> in_edges; std::vector<Vertex::EdgeLink> in_edges;
@ -564,8 +565,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (deleted) return Error::DELETED_OBJECT; if (deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
std::vector<EdgeAccessor> ret; std::vector<EdgeAccessor> ret;
if (in_edges.empty()) { if (in_edges.empty()) {
return ret; return ret;
@ -579,8 +580,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
return ret; return ret;
} }
Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std::vector<EdgeTypeId> &edge_types, ShardResult<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std::vector<EdgeTypeId> &edge_types,
const VertexId *destination_id) const { const VertexId *destination_id) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
std::vector<Vertex::EdgeLink> out_edges; std::vector<Vertex::EdgeLink> out_edges;
@ -644,8 +645,8 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (deleted) return Error::DELETED_OBJECT; if (deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
std::vector<EdgeAccessor> ret; std::vector<EdgeAccessor> ret;
if (out_edges.empty()) { if (out_edges.empty()) {
return ret; return ret;
@ -659,7 +660,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
return ret; return ret;
} }
Result<size_t> VertexAccessor::InDegree(View view) const { ShardResult<size_t> VertexAccessor::InDegree(View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
size_t degree = 0; size_t degree = 0;
@ -691,12 +692,12 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return degree; return degree;
} }
Result<size_t> VertexAccessor::OutDegree(View view) const { ShardResult<size_t> VertexAccessor::OutDegree(View view) const {
bool exists = true; bool exists = true;
bool deleted = false; bool deleted = false;
size_t degree = 0; size_t degree = 0;
@ -728,8 +729,8 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
break; break;
} }
}); });
if (!exists) return Error::NONEXISTENT_OBJECT; if (!exists) return SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT);
if (!for_deleted_ && deleted) return Error::DELETED_OBJECT; if (!for_deleted_ && deleted) return SHARD_ERROR(ErrorCode::DELETED_OBJECT);
return degree; return degree;
} }

View File

@ -17,7 +17,7 @@
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/key_store.hpp" #include "storage/v3/key_store.hpp"
#include "storage/v3/result.hpp" #include "storage/v3/result.hpp"
#include "storage/v3/shard_operation_result.hpp" #include "storage/v3/schema_validator.hpp"
#include "storage/v3/transaction.hpp" #include "storage/v3/transaction.hpp"
#include "storage/v3/vertex.hpp" #include "storage/v3/vertex.hpp"
#include "storage/v3/vertex_id.hpp" #include "storage/v3/vertex_id.hpp"
@ -55,61 +55,61 @@ class VertexAccessor final {
/// `false` is returned if the label already existed, or SchemaViolation /// `false` is returned if the label already existed, or SchemaViolation
/// if adding the label has violated one of the schema constraints. /// if adding the label has violated one of the schema constraints.
/// @throw std::bad_alloc /// @throw std::bad_alloc
ShardOperationResult<bool> AddLabelAndValidate(LabelId label); ShardResult<bool> AddLabelAndValidate(LabelId label);
/// Remove a label and return `true` if deletion took place. /// Remove a label and return `true` if deletion took place.
/// `false` is returned if the vertex did not have a label already. or SchemaViolation /// `false` is returned if the vertex did not have a label already. or SchemaViolation
/// if adding the label has violated one of the schema constraints. /// if adding the label has violated one of the schema constraints.
/// @throw std::bad_alloc /// @throw std::bad_alloc
ShardOperationResult<bool> RemoveLabelAndValidate(LabelId label); ShardResult<bool> RemoveLabelAndValidate(LabelId label);
Result<bool> HasLabel(View view, LabelId label) const; ShardResult<bool> HasLabel(View view, LabelId label) const;
Result<bool> HasLabel(LabelId label, View view) const; ShardResult<bool> HasLabel(LabelId label, View view) const;
/// @throw std::bad_alloc /// @throw std::bad_alloc
/// @throw std::length_error if the resulting vector exceeds /// @throw std::length_error if the resulting vector exceeds
/// std::vector::max_size(). /// std::vector::max_size().
Result<std::vector<LabelId>> Labels(View view) const; ShardResult<std::vector<LabelId>> Labels(View view) const;
Result<LabelId> PrimaryLabel(View view) const; ShardResult<LabelId> PrimaryLabel(View view) const;
Result<PrimaryKey> PrimaryKey(View view) const; ShardResult<PrimaryKey> PrimaryKey(View view) const;
Result<VertexId> Id(View view) const; ShardResult<VertexId> Id(View view) const;
/// Set a property value and return the old value or error. /// Set a property value and return the old value or error.
/// @throw std::bad_alloc /// @throw std::bad_alloc
ShardOperationResult<PropertyValue> SetPropertyAndValidate(PropertyId property, const PropertyValue &value); ShardResult<PropertyValue> SetPropertyAndValidate(PropertyId property, const PropertyValue &value);
/// Remove all properties and return the values of the removed properties. /// Remove all properties and return the values of the removed properties.
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::map<PropertyId, PropertyValue>> ClearProperties(); ShardResult<std::map<PropertyId, PropertyValue>> ClearProperties();
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<PropertyValue> GetProperty(PropertyId property, View view) const; ShardResult<PropertyValue> GetProperty(PropertyId property, View view) const;
// TODO Remove this // TODO Remove this
Result<PropertyValue> GetProperty(View view, PropertyId property) const; ShardResult<PropertyValue> GetProperty(View view, PropertyId property) const;
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<std::map<PropertyId, PropertyValue>> Properties(View view) const; ShardResult<std::map<PropertyId, PropertyValue>> Properties(View view) const;
/// @throw std::bad_alloc /// @throw std::bad_alloc
/// @throw std::length_error if the resulting vector exceeds /// @throw std::length_error if the resulting vector exceeds
/// std::vector::max_size(). /// std::vector::max_size().
Result<std::vector<EdgeAccessor>> InEdges(View view, const std::vector<EdgeTypeId> &edge_types = {}, ShardResult<std::vector<EdgeAccessor>> InEdges(View view, const std::vector<EdgeTypeId> &edge_types = {},
const VertexId *destination_id = nullptr) const; const VertexId *destination_id = nullptr) const;
/// @throw std::bad_alloc /// @throw std::bad_alloc
/// @throw std::length_error if the resulting vector exceeds /// @throw std::length_error if the resulting vector exceeds
/// std::vector::max_size(). /// std::vector::max_size().
Result<std::vector<EdgeAccessor>> OutEdges(View view, const std::vector<EdgeTypeId> &edge_types = {}, ShardResult<std::vector<EdgeAccessor>> OutEdges(View view, const std::vector<EdgeTypeId> &edge_types = {},
const VertexId *destination_id = nullptr) const; const VertexId *destination_id = nullptr) const;
Result<size_t> InDegree(View view) const; ShardResult<size_t> InDegree(View view) const;
Result<size_t> OutDegree(View view) const; ShardResult<size_t> OutDegree(View view) const;
const SchemaValidator *GetSchemaValidator() const; const SchemaValidator *GetSchemaValidator() const;
@ -122,20 +122,20 @@ class VertexAccessor final {
/// Add a label and return `true` if insertion took place. /// Add a label and return `true` if insertion took place.
/// `false` is returned if the label already existed. /// `false` is returned if the label already existed.
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<bool> AddLabel(LabelId label); ShardResult<bool> AddLabel(LabelId label);
/// Remove a label and return `true` if deletion took place. /// Remove a label and return `true` if deletion took place.
/// `false` is returned if the vertex did not have a label already. /// `false` is returned if the vertex did not have a label already.
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<bool> RemoveLabel(LabelId label); ShardResult<bool> RemoveLabel(LabelId label);
/// Set a property value and return the old value. /// Set a property value and return the old value.
/// @throw std::bad_alloc /// @throw std::bad_alloc
Result<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value); ShardResult<PropertyValue> SetProperty(PropertyId property, const PropertyValue &value);
PropertyValue GetPropertyValue(PropertyId property, View view) const; PropertyValue GetPropertyValue(PropertyId property, View view) const;
Result<void> CheckVertexExistence(View view) const; ShardResult<void> CheckVertexExistence(View view) const;
Vertex *vertex_; Vertex *vertex_;
Transaction *transaction_; Transaction *transaction_;

View File

@ -9,12 +9,11 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#include "storage/v3/storage.hpp" #pragma once
#include "storage/v3/config.hpp" namespace memgraph::utils {
namespace memgraph::storage::v3 { template <typename>
constexpr auto kAlwaysFalse{false};
Storage::Storage(Config config) : config_{config} {} } // namespace memgraph::utils
} // namespace memgraph::storage::v3

View File

@ -9,17 +9,25 @@
// by the Apache License, Version 2.0, included in the file // by the Apache License, Version 2.0, included in the file
// licenses/APL.txt. // licenses/APL.txt.
#include <memory>
#include <thread> #include <thread>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <spdlog/cfg/env.h>
#include "io/message_histogram_collector.hpp"
#include "io/simulator/simulator.hpp" #include "io/simulator/simulator.hpp"
#include "utils/print_helpers.hpp" #include "utils/print_helpers.hpp"
using memgraph::io::Address; using memgraph::io::Address;
using memgraph::io::Io; using memgraph::io::Io;
using memgraph::io::LatencyHistogramSummaries;
using memgraph::io::ResponseFuture; using memgraph::io::ResponseFuture;
using memgraph::io::ResponseResult; using memgraph::io::ResponseResult;
using memgraph::io::simulator::Simulator; using memgraph::io::simulator::Simulator;
using memgraph::io::simulator::SimulatorConfig; using memgraph::io::simulator::SimulatorConfig;
using memgraph::io::simulator::SimulatorStats;
using memgraph::io::simulator::SimulatorTransport; using memgraph::io::simulator::SimulatorTransport;
struct CounterRequest { struct CounterRequest {
@ -40,6 +48,7 @@ void run_server(Io<SimulatorTransport> io) {
std::cout << "[SERVER] Error, continue" << std::endl; std::cout << "[SERVER] Error, continue" << std::endl;
continue; continue;
} }
std::cout << "[SERVER] Got message" << std::endl;
auto request_envelope = request_result.GetValue(); auto request_envelope = request_result.GetValue();
auto req = std::get<CounterRequest>(request_envelope.message); auto req = std::get<CounterRequest>(request_envelope.message);
@ -50,13 +59,7 @@ void run_server(Io<SimulatorTransport> io) {
} }
} }
int main() { std::pair<SimulatorStats, LatencyHistogramSummaries> RunWorkload(SimulatorConfig &config) {
auto config = SimulatorConfig{
.drop_percent = 0,
.perform_timeouts = true,
.scramble_messages = true,
.rng_seed = 0,
};
auto simulator = Simulator(config); auto simulator = Simulator(config);
auto cli_addr = Address::TestAddress(1); auto cli_addr = Address::TestAddress(1);
@ -72,21 +75,47 @@ int main() {
// send request // send request
CounterRequest cli_req; CounterRequest cli_req;
cli_req.proposal = i; cli_req.proposal = i;
spdlog::info("[CLIENT] calling Request");
auto res_f = cli_io.Request<CounterRequest, CounterResponse>(srv_addr, cli_req); auto res_f = cli_io.Request<CounterRequest, CounterResponse>(srv_addr, cli_req);
spdlog::info("[CLIENT] calling Wait");
auto res_rez = std::move(res_f).Wait(); auto res_rez = std::move(res_f).Wait();
spdlog::info("[CLIENT] Wait returned");
if (!res_rez.HasError()) { if (!res_rez.HasError()) {
std::cout << "[CLIENT] Got a valid response" << std::endl; spdlog::info("[CLIENT] Got a valid response");
auto env = res_rez.GetValue(); auto env = res_rez.GetValue();
MG_ASSERT(env.message.highest_seen == i); MG_ASSERT(env.message.highest_seen == i);
std::cout << "response latency: " << env.response_latency.count() << " microseconds" << std::endl; spdlog::info("response latency: {} microseconds", env.response_latency.count());
} else { } else {
std::cout << "[CLIENT] Got an error" << std::endl; spdlog::info("[CLIENT] Got an error");
} }
} }
using memgraph::utils::print_helpers::operator<<;
std::cout << "response latencies: " << cli_io.ResponseLatencies() << std::endl;
simulator.ShutDown(); simulator.ShutDown();
return std::make_pair(simulator.Stats(), cli_io.ResponseLatencies());
}
int main() {
spdlog::cfg::load_env_levels();
auto config = SimulatorConfig{
.drop_percent = 0,
.perform_timeouts = true,
.scramble_messages = true,
.rng_seed = 0,
};
auto [sim_stats_1, latency_stats_1] = RunWorkload(config);
auto [sim_stats_2, latency_stats_2] = RunWorkload(config);
if (sim_stats_1 != sim_stats_2 || latency_stats_1 != latency_stats_2) {
spdlog::error("simulator stats diverged across runs");
spdlog::error("run 1 simulator stats: {}", sim_stats_1);
spdlog::error("run 2 simulator stats: {}", sim_stats_2);
spdlog::error("run 1 latency:\n{}", latency_stats_1.SummaryTable());
spdlog::error("run 2 latency:\n{}", latency_stats_2.SummaryTable());
std::terminate();
}
return 0; return 0;
} }

View File

@ -33,21 +33,31 @@ using io::Time;
using io::simulator::SimulatorConfig; using io::simulator::SimulatorConfig;
using storage::v3::kMaximumCronInterval; using storage::v3::kMaximumCronInterval;
RC_GTEST_PROP(RandomClusterConfig, HappyPath, (ClusterConfig cluster_config, NonEmptyOpVec ops)) { RC_GTEST_PROP(RandomClusterConfig, HappyPath, (ClusterConfig cluster_config, NonEmptyOpVec ops, uint64_t rng_seed)) {
// TODO(tyler) set abort_time to something more restrictive than Time::max()
spdlog::cfg::load_env_levels(); spdlog::cfg::load_env_levels();
SimulatorConfig sim_config{ SimulatorConfig sim_config{
.drop_percent = 0, .drop_percent = 0,
.perform_timeouts = false, .perform_timeouts = false,
.scramble_messages = true, .scramble_messages = true,
.rng_seed = 0, .rng_seed = rng_seed,
.start_time = Time::min(), .start_time = Time::min(),
// TODO(tyler) set abort_time to something more restrictive than Time::max()
.abort_time = Time::max(), .abort_time = Time::max(),
}; };
RunClusterSimulation(sim_config, cluster_config, ops.ops); auto [sim_stats_1, latency_stats_1] = RunClusterSimulation(sim_config, cluster_config, ops.ops);
auto [sim_stats_2, latency_stats_2] = RunClusterSimulation(sim_config, cluster_config, ops.ops);
if (latency_stats_1 != latency_stats_2) {
spdlog::error("simulator stats diverged across runs");
spdlog::error("run 1 simulator stats: {}", sim_stats_1);
spdlog::error("run 2 simulator stats: {}", sim_stats_2);
spdlog::error("run 1 latency:\n{}", latency_stats_1.SummaryTable());
spdlog::error("run 2 latency:\n{}", latency_stats_2.SummaryTable());
RC_ASSERT(latency_stats_1 == latency_stats_2);
RC_ASSERT(sim_stats_1 == sim_stats_2);
}
} }
} // namespace memgraph::tests::simulation } // namespace memgraph::tests::simulation

View File

@ -14,11 +14,15 @@
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <optional> #include <optional>
#include <random>
#include <set> #include <set>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <spdlog/cfg/env.h>
#include "io/address.hpp" #include "io/address.hpp"
#include "io/message_histogram_collector.hpp"
#include "io/rsm/raft.hpp" #include "io/rsm/raft.hpp"
#include "io/rsm/rsm_client.hpp" #include "io/rsm/rsm_client.hpp"
#include "io/simulator/simulator.hpp" #include "io/simulator/simulator.hpp"
@ -27,6 +31,7 @@
using memgraph::io::Address; using memgraph::io::Address;
using memgraph::io::Duration; using memgraph::io::Duration;
using memgraph::io::Io; using memgraph::io::Io;
using memgraph::io::LatencyHistogramSummaries;
using memgraph::io::ResponseEnvelope; using memgraph::io::ResponseEnvelope;
using memgraph::io::ResponseFuture; using memgraph::io::ResponseFuture;
using memgraph::io::ResponseResult; using memgraph::io::ResponseResult;
@ -123,16 +128,7 @@ void RunRaft(Raft<IoImpl, TestState, CasRequest, CasResponse, GetRequest, GetRes
server.Run(); server.Run();
} }
void RunSimulation() { std::pair<SimulatorStats, LatencyHistogramSummaries> RunSimulation(SimulatorConfig &config) {
SimulatorConfig config{
.drop_percent = 5,
.perform_timeouts = true,
.scramble_messages = true,
.rng_seed = 0,
.start_time = Time::min() + std::chrono::microseconds{256 * 1024},
.abort_time = Time::max(),
};
auto simulator = Simulator(config); auto simulator = Simulator(config);
auto cli_addr = Address::TestAddress(1); auto cli_addr = Address::TestAddress(1);
@ -189,6 +185,7 @@ void RunSimulation() {
auto write_cas_response_result = client.SendWriteRequest(cas_req); auto write_cas_response_result = client.SendWriteRequest(cas_req);
if (write_cas_response_result.HasError()) { if (write_cas_response_result.HasError()) {
spdlog::debug("timed out");
// timed out // timed out
continue; continue;
} }
@ -229,6 +226,10 @@ void RunSimulation() {
simulator.ShutDown(); simulator.ShutDown();
srv_thread_1.join();
srv_thread_2.join();
srv_thread_3.join();
SimulatorStats stats = simulator.Stats(); SimulatorStats stats = simulator.Stats();
spdlog::info("total messages: {}", stats.total_messages); spdlog::info("total messages: {}", stats.total_messages);
@ -240,21 +241,51 @@ void RunSimulation() {
spdlog::info("========================== SUCCESS :) =========================="); spdlog::info("========================== SUCCESS :) ==========================");
/* return std::make_pair(simulator.Stats(), cli_io.ResponseLatencies());
this is implicit in jthread's dtor }
srv_thread_1.join();
srv_thread_2.join(); void RunWithSeed(uint64_t seed) {
srv_thread_3.join(); SimulatorConfig config{
*/ .drop_percent = 5,
.perform_timeouts = true,
.scramble_messages = true,
.rng_seed = seed,
.start_time = Time::min() + std::chrono::microseconds{256 * 1024},
.abort_time = Time::min() + std::chrono::seconds{3600},
};
spdlog::error("========================== NEW SIMULATION, replay with RunWithSeed({}) ==========================",
seed);
spdlog::info("\tTime\t\tTerm\tPort\tRole\t\tMessage\n");
auto [sim_stats_1, latency_stats_1] = RunSimulation(config);
spdlog::info("========================== NEW SIMULATION, replay with RunWithSeed({}) ==========================",
seed);
spdlog::info("\tTime\t\tTerm\tPort\tRole\t\tMessage\n");
auto [sim_stats_2, latency_stats_2] = RunSimulation(config);
if (sim_stats_1 != sim_stats_2 || latency_stats_1 != latency_stats_2) {
spdlog::error("simulator stats diverged across runs for test rng_seed: {}", seed);
spdlog::error("run 1 simulator stats: {}", sim_stats_1);
spdlog::error("run 2 simulator stats: {}", sim_stats_2);
spdlog::error("run 1 latency:\n{}", latency_stats_1.SummaryTable());
spdlog::error("run 2 latency:\n{}", latency_stats_2.SummaryTable());
std::terminate();
}
} }
int main() { int main() {
spdlog::cfg::load_env_levels();
std::random_device random_device;
std::mt19937 generator(random_device());
std::uniform_int_distribution<> distribution;
int n_tests = 50; int n_tests = 50;
for (int i = 0; i < n_tests; i++) { for (int i = 0; i < n_tests; i++) {
spdlog::info("========================== NEW SIMULATION {} ==========================", i); uint64_t seed = distribution(generator);
spdlog::info("\tTime\t\tTerm\tPort\tRole\t\tMessage\n"); RunWithSeed(seed);
RunSimulation();
} }
spdlog::info("passed {} tests!", n_tests); spdlog::info("passed {} tests!", n_tests);

View File

@ -137,7 +137,7 @@ void Commit(ShardClient &client, const coordinator::Hlc &transaction_timestamp)
auto write_response_result = write_res.GetValue(); auto write_response_result = write_res.GetValue();
auto write_response = std::get<msgs::CommitResponse>(write_response_result); auto write_response = std::get<msgs::CommitResponse>(write_response_result);
MG_ASSERT(write_response.success, "Commit expected to be successful, but it is failed"); MG_ASSERT(!write_response.error.has_value(), "Commit expected to be successful, but it is failed");
break; break;
} }
@ -156,7 +156,7 @@ bool AttemptToCreateVertex(ShardClient &client, int64_t value) {
create_req.transaction_id.logical_id = GetTransactionId(); create_req.transaction_id.logical_id = GetTransactionId();
auto write_res = client.SendWriteRequest(create_req); auto write_res = client.SendWriteRequest(create_req);
MG_ASSERT(write_res.HasValue() && std::get<msgs::CreateVerticesResponse>(write_res.GetValue()).success, MG_ASSERT(write_res.HasValue() && !std::get<msgs::CreateVerticesResponse>(write_res.GetValue()).error.has_value(),
"Unexpected failure"); "Unexpected failure");
Commit(client, create_req.transaction_id); Commit(client, create_req.transaction_id);
@ -179,23 +179,26 @@ bool AttemptToDeleteVertex(ShardClient &client, int64_t value) {
auto write_response = std::get<msgs::DeleteVerticesResponse>(write_response_result); auto write_response = std::get<msgs::DeleteVerticesResponse>(write_response_result);
Commit(client, delete_req.transaction_id); Commit(client, delete_req.transaction_id);
return write_response.success; return !write_response.error.has_value();
} }
} }
bool AttemptToUpdateVertex(ShardClient &client, int64_t value) { bool AttemptToUpdateVertex(ShardClient &client, int64_t vertex_primary_key, std::vector<LabelId> add_labels = {},
auto vertex_id = GetValuePrimaryKeysWithValue(value)[0]; std::vector<LabelId> remove_labels = {}) {
auto vertex_id = GetValuePrimaryKeysWithValue(vertex_primary_key)[0];
std::vector<std::pair<PropertyId, msgs::Value>> property_updates; std::vector<std::pair<PropertyId, msgs::Value>> property_updates;
auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value(static_cast<int64_t>(10000))); auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value(static_cast<int64_t>(10000)));
auto vertex_prop = msgs::UpdateVertexProp{}; msgs::UpdateVertex update_vertex;
vertex_prop.primary_key = vertex_id; update_vertex.primary_key = vertex_id;
vertex_prop.property_updates = {property_update}; update_vertex.property_updates = {property_update};
update_vertex.add_labels = add_labels;
update_vertex.remove_labels = remove_labels;
auto update_req = msgs::UpdateVerticesRequest{}; msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId(); update_req.transaction_id.logical_id = GetTransactionId();
update_req.new_properties = {vertex_prop}; update_req.update_vertices = {update_vertex};
while (true) { while (true) {
auto write_res = client.SendWriteRequest(update_req); auto write_res = client.SendWriteRequest(update_req);
@ -207,7 +210,38 @@ bool AttemptToUpdateVertex(ShardClient &client, int64_t value) {
auto write_response = std::get<msgs::UpdateVerticesResponse>(write_response_result); auto write_response = std::get<msgs::UpdateVerticesResponse>(write_response_result);
Commit(client, update_req.transaction_id); Commit(client, update_req.transaction_id);
return write_response.success; return !write_response.error.has_value();
}
}
bool AttemptToRemoveVertexProperty(ShardClient &client, int64_t primary_key, std::vector<LabelId> add_labels = {},
std::vector<LabelId> remove_labels = {}) {
auto vertex_id = GetValuePrimaryKeysWithValue(primary_key)[0];
std::vector<std::pair<PropertyId, msgs::Value>> property_updates;
auto property_update = std::make_pair(PropertyId::FromUint(5), msgs::Value());
msgs::UpdateVertex update_vertex;
update_vertex.primary_key = vertex_id;
update_vertex.property_updates = {property_update};
update_vertex.add_labels = add_labels;
update_vertex.remove_labels = remove_labels;
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = {update_vertex};
while (true) {
auto write_res = client.SendWriteRequest(update_req);
if (write_res.HasError()) {
continue;
}
auto write_response_result = write_res.GetValue();
auto write_response = std::get<msgs::UpdateVerticesResponse>(write_response_result);
Commit(client, update_req.transaction_id);
return !write_response.error.has_value();
} }
} }
@ -244,7 +278,7 @@ bool AttemptToAddEdge(ShardClient &client, int64_t value_of_vertex_1, int64_t va
Commit(client, create_req.transaction_id); Commit(client, create_req.transaction_id);
return write_response.success; return !write_response.error.has_value();
} }
return true; return true;
} }
@ -276,7 +310,7 @@ bool AttemptToAddEdgeWithProperties(ShardClient &client, int64_t value_of_vertex
create_req.transaction_id.logical_id = GetTransactionId(); create_req.transaction_id.logical_id = GetTransactionId();
auto write_res = client.SendWriteRequest(create_req); auto write_res = client.SendWriteRequest(create_req);
MG_ASSERT(write_res.HasValue() && std::get<msgs::CreateExpandResponse>(write_res.GetValue()).success, MG_ASSERT(write_res.HasValue() && !std::get<msgs::CreateExpandResponse>(write_res.GetValue()).error.has_value(),
"Unexpected failure"); "Unexpected failure");
Commit(client, create_req.transaction_id); Commit(client, create_req.transaction_id);
@ -316,7 +350,7 @@ bool AttemptToDeleteEdge(ShardClient &client, int64_t value_of_vertex_1, int64_t
auto write_response = std::get<msgs::DeleteEdgesResponse>(write_response_result); auto write_response = std::get<msgs::DeleteEdgesResponse>(write_response_result);
Commit(client, delete_req.transaction_id); Commit(client, delete_req.transaction_id);
return write_response.success; return !write_response.error.has_value();
} }
} }
@ -356,7 +390,7 @@ bool AttemptToUpdateEdge(ShardClient &client, int64_t value_of_vertex_1, int64_t
auto write_response = std::get<msgs::UpdateEdgesResponse>(write_response_result); auto write_response = std::get<msgs::UpdateEdgesResponse>(write_response_result);
Commit(client, update_req.transaction_id); Commit(client, update_req.transaction_id);
return write_response.success; return !write_response.error.has_value();
} }
} }
@ -379,7 +413,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithoutBatchLi
auto write_response_result = read_res.GetValue(); auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result); auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success); MG_ASSERT(write_response.error == std::nullopt);
return {write_response.results.size(), write_response.next_start_id}; return {write_response.results.size(), write_response.next_start_id};
} }
@ -405,7 +439,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithBatchLimit
auto write_response_result = read_res.GetValue(); auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result); auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success); MG_ASSERT(!write_response.error.has_value());
return {write_response.results.size(), write_response.next_start_id}; return {write_response.results.size(), write_response.next_start_id};
} }
@ -439,7 +473,7 @@ std::tuple<size_t, std::optional<msgs::VertexId>> AttemptToScanAllWithExpression
auto write_response_result = read_res.GetValue(); auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result); auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success); MG_ASSERT(!write_response.error.has_value());
MG_ASSERT(!write_response.results.empty(), "There are no results!"); MG_ASSERT(!write_response.results.empty(), "There are no results!");
MG_ASSERT(write_response.results[0].evaluated_vertex_expressions[0].int_v == 4); MG_ASSERT(write_response.results[0].evaluated_vertex_expressions[0].int_v == 4);
return {write_response.results.size(), write_response.next_start_id}; return {write_response.results.size(), write_response.next_start_id};
@ -514,7 +548,7 @@ void AttemptToScanAllWithOrderByOnPrimaryProperty(ShardClient &client, msgs::Ver
auto write_response_result = read_res.GetValue(); auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result); auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success); MG_ASSERT(!write_response.error.has_value());
MG_ASSERT(write_response.results.size() == 5, "Expecting 5 results!"); MG_ASSERT(write_response.results.size() == 5, "Expecting 5 results!");
for (int64_t i{0}; i < 5; ++i) { for (int64_t i{0}; i < 5; ++i) {
const auto expected_primary_key = std::vector{msgs::Value(1023 - i)}; const auto expected_primary_key = std::vector{msgs::Value(1023 - i)};
@ -544,7 +578,7 @@ void AttemptToScanAllWithOrderByOnSecondaryProperty(ShardClient &client, msgs::V
auto write_response_result = read_res.GetValue(); auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result); auto write_response = std::get<msgs::ScanVerticesResponse>(write_response_result);
MG_ASSERT(write_response.success); MG_ASSERT(!write_response.error.has_value());
MG_ASSERT(write_response.results.size() == 5, "Expecting 5 results!"); MG_ASSERT(write_response.results.size() == 5, "Expecting 5 results!");
for (int64_t i{0}; i < 5; ++i) { for (int64_t i{0}; i < 5; ++i) {
const auto expected_prop4 = std::vector{msgs::Value(1023 - i)}; const auto expected_prop4 = std::vector{msgs::Value(1023 - i)};
@ -581,7 +615,8 @@ void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_verte
std::optional<std::vector<PropertyId>> edge_properties = {}; std::optional<std::vector<PropertyId>> edge_properties = {};
std::vector<std::string> expressions; std::vector<std::string> expressions;
std::optional<std::vector<msgs::OrderBy>> order_by = {}; std::vector<msgs::OrderBy> order_by_vertices = {};
std::vector<msgs::OrderBy> order_by_edges = {};
std::optional<size_t> limit = {}; std::optional<size_t> limit = {};
std::vector<std::string> filter = {}; std::vector<std::string> filter = {};
@ -593,7 +628,8 @@ void AttemptToExpandOneWithWrongEdgeType(ShardClient &client, uint64_t src_verte
expand_one_req.vertex_expressions = expressions; expand_one_req.vertex_expressions = expressions;
expand_one_req.filters = filter; expand_one_req.filters = filter;
expand_one_req.limit = limit; expand_one_req.limit = limit;
expand_one_req.order_by = order_by; expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex}; expand_one_req.src_vertices = {src_vertex};
expand_one_req.transaction_id.logical_id = GetTransactionId(); expand_one_req.transaction_id.logical_id = GetTransactionId();
@ -636,7 +672,8 @@ void AttemptToExpandOneSimple(ShardClient &client, uint64_t src_vertex_val, Edge
std::optional<std::vector<PropertyId>> edge_properties = {}; std::optional<std::vector<PropertyId>> edge_properties = {};
std::vector<std::string> expressions; std::vector<std::string> expressions;
std::optional<std::vector<msgs::OrderBy>> order_by = {}; std::vector<msgs::OrderBy> order_by_vertices = {};
std::vector<msgs::OrderBy> order_by_edges = {};
std::optional<size_t> limit = {}; std::optional<size_t> limit = {};
std::vector<std::string> filter = {}; std::vector<std::string> filter = {};
@ -648,7 +685,8 @@ void AttemptToExpandOneSimple(ShardClient &client, uint64_t src_vertex_val, Edge
expand_one_req.vertex_expressions = expressions; expand_one_req.vertex_expressions = expressions;
expand_one_req.filters = filter; expand_one_req.filters = filter;
expand_one_req.limit = limit; expand_one_req.limit = limit;
expand_one_req.order_by = order_by; expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex}; expand_one_req.src_vertices = {src_vertex};
expand_one_req.transaction_id.logical_id = GetTransactionId(); expand_one_req.transaction_id.logical_id = GetTransactionId();
@ -692,7 +730,8 @@ void AttemptToExpandOneWithUniqueEdges(ShardClient &client, uint64_t src_vertex_
std::optional<std::vector<PropertyId>> edge_properties = {}; std::optional<std::vector<PropertyId>> edge_properties = {};
std::vector<std::string> expressions; std::vector<std::string> expressions;
std::optional<std::vector<msgs::OrderBy>> order_by = {}; std::vector<msgs::OrderBy> order_by_vertices = {};
std::vector<msgs::OrderBy> order_by_edges = {};
std::optional<size_t> limit = {}; std::optional<size_t> limit = {};
std::vector<std::string> filter = {}; std::vector<std::string> filter = {};
@ -704,7 +743,8 @@ void AttemptToExpandOneWithUniqueEdges(ShardClient &client, uint64_t src_vertex_
expand_one_req.vertex_expressions = expressions; expand_one_req.vertex_expressions = expressions;
expand_one_req.filters = filter; expand_one_req.filters = filter;
expand_one_req.limit = limit; expand_one_req.limit = limit;
expand_one_req.order_by = order_by; expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex}; expand_one_req.src_vertices = {src_vertex};
expand_one_req.only_unique_neighbor_rows = true; expand_one_req.only_unique_neighbor_rows = true;
@ -730,6 +770,88 @@ void AttemptToExpandOneWithUniqueEdges(ShardClient &client, uint64_t src_vertex_
} }
} }
void AttemptToExpandOneLimitAndOrderBy(ShardClient &client, uint64_t src_vertex_val, uint64_t other_src_vertex_val,
EdgeTypeId edge_type_id) {
// Source vertex
msgs::Label label = {.id = get_primary_label()};
auto src_vertex = std::make_pair(label, GetPrimaryKey(src_vertex_val));
auto other_src_vertex = std::make_pair(label, GetPrimaryKey(other_src_vertex_val));
// Edge type
auto edge_type = msgs::EdgeType{};
edge_type.id = edge_type_id;
// Edge direction
auto edge_direction = msgs::EdgeDirection::OUT;
// Source Vertex properties to look for
std::optional<std::vector<PropertyId>> src_vertex_properties = {};
// Edge properties to look for
std::optional<std::vector<PropertyId>> edge_properties = {};
std::vector<msgs::OrderBy> order_by_vertices = {
{msgs::Expression{"MG_SYMBOL_NODE.prop1"}, msgs::OrderingDirection::ASCENDING}};
std::vector<msgs::OrderBy> order_by_edges = {
{msgs::Expression{"MG_SYMBOL_EDGE.prop4"}, msgs::OrderingDirection::DESCENDING}};
size_t limit = 1;
std::vector<std::string> filters = {"MG_SYMBOL_NODE.prop1 != -1"};
msgs::ExpandOneRequest expand_one_req{};
expand_one_req.direction = edge_direction;
expand_one_req.edge_properties = edge_properties;
expand_one_req.edge_types = {edge_type};
expand_one_req.filters = filters;
expand_one_req.limit = limit;
expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex, other_src_vertex};
expand_one_req.transaction_id.logical_id = GetTransactionId();
while (true) {
auto read_res = client.SendReadRequest(expand_one_req);
if (read_res.HasError()) {
continue;
}
auto write_response_result = read_res.GetValue();
auto write_response = std::get<msgs::ExpandOneResponse>(write_response_result);
// We check that we do not have more results than the limit. Based on the data in the graph, we know that we should
// receive exactly limit responses.
auto expected_number_of_rows = std::min(expand_one_req.src_vertices.size(), limit);
MG_ASSERT(expected_number_of_rows == 1);
MG_ASSERT(write_response.result.size() == expected_number_of_rows);
// We know there are 1 out-going edges from V1->V2
// We know there are 10 out-going edges from V2->V3
// Since we sort on prop1 and limit 1, we will have a single response
// with two edges corresponding to V1->V2 and V1->V3
const auto expected_number_of_edges = 2;
MG_ASSERT(write_response.result[0].out_edges_with_all_properties.size() == expected_number_of_edges);
MG_ASSERT(write_response.result[0]
.out_edges_with_specific_properties.empty()); // We are not asking for specific properties
// We also check that the vertices are ordered by prop1 DESC
auto is_sorted = std::is_sorted(write_response.result.cbegin(), write_response.result.cend(),
[](const auto &vertex, const auto &other_vertex) {
const auto primary_key = vertex.src_vertex.id.second;
const auto other_primary_key = other_vertex.src_vertex.id.second;
MG_ASSERT(primary_key.size() == 1);
MG_ASSERT(other_primary_key.size() == 1);
return primary_key[0].int_v > other_primary_key[0].int_v;
});
MG_ASSERT(is_sorted);
break;
}
}
void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uint64_t src_vertex_val, void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uint64_t src_vertex_val,
EdgeTypeId edge_type_id) { EdgeTypeId edge_type_id) {
// Source vertex // Source vertex
@ -751,7 +873,8 @@ void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uin
std::optional<std::vector<PropertyId>> edge_properties = {}; std::optional<std::vector<PropertyId>> edge_properties = {};
std::vector<std::string> expressions; std::vector<std::string> expressions;
std::optional<std::vector<msgs::OrderBy>> order_by = {}; std::vector<msgs::OrderBy> order_by_vertices = {};
std::vector<msgs::OrderBy> order_by_edges = {};
std::optional<size_t> limit = {}; std::optional<size_t> limit = {};
std::vector<std::string> filter = {}; std::vector<std::string> filter = {};
@ -763,7 +886,8 @@ void AttemptToExpandOneWithSpecifiedSrcVertexProperties(ShardClient &client, uin
expand_one_req.vertex_expressions = expressions; expand_one_req.vertex_expressions = expressions;
expand_one_req.filters = filter; expand_one_req.filters = filter;
expand_one_req.limit = limit; expand_one_req.limit = limit;
expand_one_req.order_by = order_by; expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex}; expand_one_req.src_vertices = {src_vertex};
expand_one_req.transaction_id.logical_id = GetTransactionId(); expand_one_req.transaction_id.logical_id = GetTransactionId();
@ -811,7 +935,8 @@ void AttemptToExpandOneWithSpecifiedEdgeProperties(ShardClient &client, uint64_t
std::optional<std::vector<PropertyId>> edge_properties = {specified_edge_prop}; std::optional<std::vector<PropertyId>> edge_properties = {specified_edge_prop};
std::vector<std::string> expressions; std::vector<std::string> expressions;
std::optional<std::vector<msgs::OrderBy>> order_by = {}; std::vector<msgs::OrderBy> order_by_vertices = {};
std::vector<msgs::OrderBy> order_by_edges = {};
std::optional<size_t> limit = {}; std::optional<size_t> limit = {};
std::vector<std::string> filter = {}; std::vector<std::string> filter = {};
@ -823,7 +948,8 @@ void AttemptToExpandOneWithSpecifiedEdgeProperties(ShardClient &client, uint64_t
expand_one_req.vertex_expressions = expressions; expand_one_req.vertex_expressions = expressions;
expand_one_req.filters = filter; expand_one_req.filters = filter;
expand_one_req.limit = limit; expand_one_req.limit = limit;
expand_one_req.order_by = order_by; expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex}; expand_one_req.src_vertices = {src_vertex};
expand_one_req.transaction_id.logical_id = GetTransactionId(); expand_one_req.transaction_id.logical_id = GetTransactionId();
@ -870,7 +996,8 @@ void AttemptToExpandOneWithFilters(ShardClient &client, uint64_t src_vertex_val,
std::optional<std::vector<PropertyId>> edge_properties = {}; std::optional<std::vector<PropertyId>> edge_properties = {};
std::vector<std::string> expressions; std::vector<std::string> expressions;
std::optional<std::vector<msgs::OrderBy>> order_by = {}; std::vector<msgs::OrderBy> order_by_vertices = {};
std::vector<msgs::OrderBy> order_by_edges = {};
std::optional<size_t> limit = {}; std::optional<size_t> limit = {};
std::vector<std::string> filter = {}; std::vector<std::string> filter = {};
@ -882,7 +1009,8 @@ void AttemptToExpandOneWithFilters(ShardClient &client, uint64_t src_vertex_val,
expand_one_req.vertex_expressions = expressions; expand_one_req.vertex_expressions = expressions;
expand_one_req.filters = {filter_expr1}; expand_one_req.filters = {filter_expr1};
expand_one_req.limit = limit; expand_one_req.limit = limit;
expand_one_req.order_by = order_by; expand_one_req.order_by_vertices = order_by_vertices;
expand_one_req.order_by_edges = order_by_edges;
expand_one_req.src_vertex_properties = src_vertex_properties; expand_one_req.src_vertex_properties = src_vertex_properties;
expand_one_req.src_vertices = {src_vertex}; expand_one_req.src_vertices = {src_vertex};
expand_one_req.transaction_id.logical_id = GetTransactionId(); expand_one_req.transaction_id.logical_id = GetTransactionId();
@ -922,7 +1050,9 @@ void TestCreateAndUpdateVertices(ShardClient &client) {
auto unique_prop_val = GetUniqueInteger(); auto unique_prop_val = GetUniqueInteger();
MG_ASSERT(AttemptToCreateVertex(client, unique_prop_val)); MG_ASSERT(AttemptToCreateVertex(client, unique_prop_val));
MG_ASSERT(AttemptToUpdateVertex(client, unique_prop_val)); MG_ASSERT(AttemptToUpdateVertex(client, unique_prop_val, {LabelId::FromInt(3)}, {}));
MG_ASSERT(AttemptToUpdateVertex(client, unique_prop_val, {}, {LabelId::FromInt(3)}));
MG_ASSERT(AttemptToRemoveVertexProperty(client, unique_prop_val));
} }
void TestCreateEdge(ShardClient &client) { void TestCreateEdge(ShardClient &client) {
@ -1071,6 +1201,9 @@ void TestExpandOneGraphOne(ShardClient &client) {
auto edge_prop_id = GetUniqueInteger(); auto edge_prop_id = GetUniqueInteger();
auto edge_prop_val = GetUniqueInteger(); auto edge_prop_val = GetUniqueInteger();
std::vector<uint64_t> edges_ids(10);
std::generate(edges_ids.begin(), edges_ids.end(), GetUniqueInteger);
// (V1)-[edge_type_id]->(V2) // (V1)-[edge_type_id]->(V2)
MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_1, unique_prop_val_2, edge_gid_1, edge_prop_id, MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_1, unique_prop_val_2, edge_gid_1, edge_prop_id,
edge_prop_val, {edge_type_id})); edge_prop_val, {edge_type_id}));
@ -1078,7 +1211,14 @@ void TestExpandOneGraphOne(ShardClient &client) {
MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_1, unique_prop_val_3, edge_gid_2, edge_prop_id, MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_1, unique_prop_val_3, edge_gid_2, edge_prop_id,
edge_prop_val, {edge_type_id})); edge_prop_val, {edge_type_id}));
// (V2)-[edge_type_id]->(V3) x 10
std::for_each(edges_ids.begin(), edges_ids.end(), [&](const auto &edge_id) {
MG_ASSERT(AttemptToAddEdgeWithProperties(client, unique_prop_val_2, unique_prop_val_3, edge_id, edge_prop_id,
edge_prop_val, {edge_type_id}));
});
AttemptToExpandOneSimple(client, unique_prop_val_1, edge_type_id); AttemptToExpandOneSimple(client, unique_prop_val_1, edge_type_id);
AttemptToExpandOneLimitAndOrderBy(client, unique_prop_val_1, unique_prop_val_2, edge_type_id);
AttemptToExpandOneWithWrongEdgeType(client, unique_prop_val_1, wrong_edge_type_id); AttemptToExpandOneWithWrongEdgeType(client, unique_prop_val_1, wrong_edge_type_id);
AttemptToExpandOneWithSpecifiedSrcVertexProperties(client, unique_prop_val_1, edge_type_id); AttemptToExpandOneWithSpecifiedSrcVertexProperties(client, unique_prop_val_1, edge_type_id);
AttemptToExpandOneWithSpecifiedEdgeProperties(client, unique_prop_val_1, edge_type_id, edge_prop_id); AttemptToExpandOneWithSpecifiedEdgeProperties(client, unique_prop_val_1, edge_type_id, edge_prop_id);
@ -1355,11 +1495,12 @@ int TestMessages() {
ConcreteShardRsm shard_server3(std::move(shard_server_io_3), address_for_3, ShardRsm(std::move(shard_ptr3))); ConcreteShardRsm shard_server3(std::move(shard_server_io_3), address_for_3, ShardRsm(std::move(shard_ptr3)));
auto server_thread1 = std::jthread([&shard_server1]() { shard_server1.Run(); }); auto server_thread1 = std::jthread([&shard_server1]() { shard_server1.Run(); });
auto server_thread2 = std::jthread([&shard_server2]() { shard_server2.Run(); });
auto server_thread3 = std::jthread([&shard_server3]() { shard_server3.Run(); });
simulator.IncrementServerCountAndWaitForQuiescentState(shard_server_1_address); simulator.IncrementServerCountAndWaitForQuiescentState(shard_server_1_address);
auto server_thread2 = std::jthread([&shard_server2]() { shard_server2.Run(); });
simulator.IncrementServerCountAndWaitForQuiescentState(shard_server_2_address); simulator.IncrementServerCountAndWaitForQuiescentState(shard_server_2_address);
auto server_thread3 = std::jthread([&shard_server3]() { shard_server3.Run(); });
simulator.IncrementServerCountAndWaitForQuiescentState(shard_server_3_address); simulator.IncrementServerCountAndWaitForQuiescentState(shard_server_3_address);
std::cout << "Beginning test after servers have become quiescent." << std::endl; std::cout << "Beginning test after servers have become quiescent." << std::endl;

View File

@ -24,6 +24,7 @@
#include "coordinator/shard_map.hpp" #include "coordinator/shard_map.hpp"
#include "generated_operations.hpp" #include "generated_operations.hpp"
#include "io/address.hpp" #include "io/address.hpp"
#include "io/message_histogram_collector.hpp"
#include "io/simulator/simulator.hpp" #include "io/simulator/simulator.hpp"
#include "io/simulator/simulator_config.hpp" #include "io/simulator/simulator_config.hpp"
#include "io/simulator/simulator_transport.hpp" #include "io/simulator/simulator_transport.hpp"
@ -57,6 +58,7 @@ using io::simulator::SimulatorStats;
using io::simulator::SimulatorTransport; using io::simulator::SimulatorTransport;
using machine_manager::MachineConfig; using machine_manager::MachineConfig;
using machine_manager::MachineManager; using machine_manager::MachineManager;
using memgraph::io::LatencyHistogramSummaries;
using msgs::ReadRequests; using msgs::ReadRequests;
using msgs::ReadResponses; using msgs::ReadResponses;
using msgs::WriteRequests; using msgs::WriteRequests;
@ -75,6 +77,8 @@ MachineManager<SimulatorTransport> MkMm(Simulator &simulator, std::vector<Addres
.is_coordinator = true, .is_coordinator = true,
.listen_ip = addr.last_known_ip, .listen_ip = addr.last_known_ip,
.listen_port = addr.last_known_port, .listen_port = addr.last_known_port,
.shard_worker_threads = 4,
.sync_message_handling = true,
}; };
Io<SimulatorTransport> io = simulator.Register(addr); Io<SimulatorTransport> io = simulator.Register(addr);
@ -173,7 +177,7 @@ void ExecuteOp(msgs::ShardRequestManager<SimulatorTransport> &shard_request_mana
auto result = shard_request_manager.Request(state, std::move(new_vertices)); auto result = shard_request_manager.Request(state, std::move(new_vertices));
RC_ASSERT(result.size() == 1); RC_ASSERT(result.size() == 1);
RC_ASSERT(result[0].success); RC_ASSERT(!result[0].error.has_value());
correctness_model.emplace(std::make_pair(create_vertex.first, create_vertex.second)); correctness_model.emplace(std::make_pair(create_vertex.first, create_vertex.second));
} }
@ -210,17 +214,19 @@ struct DetachIfDropped {
} }
}; };
void RunClusterSimulation(const SimulatorConfig &sim_config, const ClusterConfig &cluster_config, std::pair<SimulatorStats, LatencyHistogramSummaries> RunClusterSimulation(const SimulatorConfig &sim_config,
const std::vector<Op> &ops) { const ClusterConfig &cluster_config,
const std::vector<Op> &ops) {
spdlog::info("========================== NEW SIMULATION =========================="); spdlog::info("========================== NEW SIMULATION ==========================");
auto simulator = Simulator(sim_config); auto simulator = Simulator(sim_config);
auto cli_addr = Address::TestAddress(1); auto machine_1_addr = Address::TestAddress(1);
auto machine_1_addr = cli_addr.ForkUniqueAddress(); auto cli_addr = Address::TestAddress(2);
auto cli_addr_2 = Address::TestAddress(3);
Io<SimulatorTransport> cli_io = simulator.Register(cli_addr); Io<SimulatorTransport> cli_io = simulator.Register(cli_addr);
Io<SimulatorTransport> cli_io_2 = simulator.Register(Address::TestAddress(2)); Io<SimulatorTransport> cli_io_2 = simulator.Register(cli_addr_2);
auto coordinator_addresses = std::vector{ auto coordinator_addresses = std::vector{
machine_1_addr, machine_1_addr,
@ -232,6 +238,7 @@ void RunClusterSimulation(const SimulatorConfig &sim_config, const ClusterConfig
Address coordinator_address = mm_1.CoordinatorAddress(); Address coordinator_address = mm_1.CoordinatorAddress();
auto mm_thread_1 = std::jthread(RunMachine, std::move(mm_1)); auto mm_thread_1 = std::jthread(RunMachine, std::move(mm_1));
simulator.IncrementServerCountAndWaitForQuiescentState(machine_1_addr);
auto detach_on_error = DetachIfDropped{.handle = mm_thread_1}; auto detach_on_error = DetachIfDropped{.handle = mm_thread_1};
@ -257,6 +264,8 @@ void RunClusterSimulation(const SimulatorConfig &sim_config, const ClusterConfig
simulator.ShutDown(); simulator.ShutDown();
mm_thread_1.join();
SimulatorStats stats = simulator.Stats(); SimulatorStats stats = simulator.Stats();
spdlog::info("total messages: {}", stats.total_messages); spdlog::info("total messages: {}", stats.total_messages);
@ -268,10 +277,8 @@ void RunClusterSimulation(const SimulatorConfig &sim_config, const ClusterConfig
auto histo = cli_io_2.ResponseLatencies(); auto histo = cli_io_2.ResponseLatencies();
using memgraph::utils::print_helpers::operator<<;
std::cout << "response latencies: " << histo << std::endl;
spdlog::info("========================== SUCCESS :) =========================="); spdlog::info("========================== SUCCESS :) ==========================");
return std::make_pair(stats, histo);
} }
} // namespace memgraph::tests::simulation } // namespace memgraph::tests::simulation

View File

@ -343,6 +343,9 @@ target_link_libraries(${test_prefix}storage_v3_edge mg-storage-v3)
add_unit_test(storage_v3_isolation_level.cpp) add_unit_test(storage_v3_isolation_level.cpp)
target_link_libraries(${test_prefix}storage_v3_isolation_level mg-storage-v3) target_link_libraries(${test_prefix}storage_v3_isolation_level mg-storage-v3)
add_unit_test(storage_v3_shard_rsm.cpp)
target_link_libraries(${test_prefix}storage_v3_shard_rsm mg-storage-v3)
add_unit_test(replication_persistence_helper.cpp) add_unit_test(replication_persistence_helper.cpp)
target_link_libraries(${test_prefix}replication_persistence_helper mg-storage-v2) target_link_libraries(${test_prefix}replication_persistence_helper mg-storage-v2)

View File

@ -187,7 +187,7 @@ void ExecuteOp(msgs::ShardRequestManager<LocalTransport> &shard_request_manager,
auto result = shard_request_manager.Request(state, std::move(new_vertices)); auto result = shard_request_manager.Request(state, std::move(new_vertices));
MG_ASSERT(result.size() == 1); MG_ASSERT(result.size() == 1);
MG_ASSERT(result[0].success); MG_ASSERT(!result[0].error.has_value());
correctness_model.emplace(std::make_pair(create_vertex.first, create_vertex.second)); correctness_model.emplace(std::make_pair(create_vertex.first, create_vertex.second));
} }

View File

@ -131,6 +131,7 @@ void TestCreateVertices(msgs::ShardRequestManagerInterface &shard_request_manage
auto result = shard_request_manager.Request(state, std::move(new_vertices)); auto result = shard_request_manager.Request(state, std::move(new_vertices));
EXPECT_EQ(result.size(), 1); EXPECT_EQ(result.size(), 1);
EXPECT_FALSE(result[0].error.has_value()) << result[0].error->message;
} }
void TestCreateExpand(msgs::ShardRequestManagerInterface &shard_request_manager) { void TestCreateExpand(msgs::ShardRequestManagerInterface &shard_request_manager) {
@ -151,7 +152,7 @@ void TestCreateExpand(msgs::ShardRequestManagerInterface &shard_request_manager)
auto responses = shard_request_manager.Request(state, std::move(new_expands)); auto responses = shard_request_manager.Request(state, std::move(new_expands));
MG_ASSERT(responses.size() == 1); MG_ASSERT(responses.size() == 1);
MG_ASSERT(responses[0].success); MG_ASSERT(!responses[0].error.has_value());
} }
void TestExpandOne(msgs::ShardRequestManagerInterface &shard_request_manager) { void TestExpandOne(msgs::ShardRequestManagerInterface &shard_request_manager) {

View File

@ -21,7 +21,7 @@
#include "query/v2/context.hpp" #include "query/v2/context.hpp"
#include "query/v2/db_accessor.hpp" #include "query/v2/db_accessor.hpp"
#include "query/v2/plan/operator.hpp" #include "query/v2/plan/operator.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "query_v2_query_common.hpp" #include "query_v2_query_common.hpp"

View File

@ -30,7 +30,7 @@
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/vertex.hpp" #include "storage/v3/vertex.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"

View File

@ -21,7 +21,7 @@
#include "query/v2/interpreter.hpp" #include "query/v2/interpreter.hpp"
#include "result_stream_faker.hpp" #include "result_stream_faker.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
DECLARE_bool(query_cost_planner); DECLARE_bool(query_cost_planner);

View File

@ -15,7 +15,7 @@
#include "query/v2/plan/operator.hpp" #include "query/v2/plan/operator.hpp"
#include "query_v2_query_plan_common.hpp" #include "query_v2_query_plan_common.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
namespace memgraph::query::v2::tests { namespace memgraph::query::v2::tests {

View File

@ -16,7 +16,7 @@
#include "glue/v2/communication.hpp" #include "glue/v2/communication.hpp"
#include "query/v2/bindings/typed_value.hpp" #include "query/v2/bindings/typed_value.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "utils/algorithm.hpp" #include "utils/algorithm.hpp"
/** /**

View File

@ -17,6 +17,7 @@
#include <gtest/gtest-death-test.h> #include <gtest/gtest-death-test.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "common/errors.hpp"
#include "coordinator/hybrid_logical_clock.hpp" #include "coordinator/hybrid_logical_clock.hpp"
#include "io/time.hpp" #include "io/time.hpp"
#include "storage/v3/delta.hpp" #include "storage/v3/delta.hpp"
@ -579,7 +580,7 @@ TEST_P(StorageV3, VertexDeleteSerializationError) {
EXPECT_EQ(CountVertices(acc2, View::NEW), 1U); EXPECT_EQ(CountVertices(acc2, View::NEW), 1U);
auto res = acc2.DeleteVertex(&*vertex); auto res = acc2.DeleteVertex(&*vertex);
ASSERT_TRUE(res.HasError()); ASSERT_TRUE(res.HasError());
ASSERT_EQ(res.GetError(), Error::SERIALIZATION_ERROR); ASSERT_EQ(res.GetError(), SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR));
EXPECT_EQ(CountVertices(acc2, View::OLD), 1U); EXPECT_EQ(CountVertices(acc2, View::OLD), 1U);
EXPECT_EQ(CountVertices(acc2, View::NEW), 1U); EXPECT_EQ(CountVertices(acc2, View::NEW), 1U);
acc2.AdvanceCommand(); acc2.AdvanceCommand();
@ -660,12 +661,11 @@ TEST_P(StorageV3, VertexDeleteSpecialCases) {
} }
} }
template <typename TError, typename TResultHolder> template <typename T>
void AssertErrorInVariant(TResultHolder &holder, TError error_type) { void AssertShardErrorEqual(const ShardResult<T> &lhs, const ShardError &rhs) {
ASSERT_TRUE(holder.HasError()); ASSERT_TRUE(lhs.HasError());
const auto error = holder.GetError(); const auto error = lhs.GetError();
ASSERT_TRUE(std::holds_alternative<TError>(error)); ASSERT_EQ(error, rhs);
ASSERT_EQ(std::get<TError>(error), error_type);
} }
// NOLINTNEXTLINE(hicpp-special-member-functions) // NOLINTNEXTLINE(hicpp-special-member-functions)
@ -711,20 +711,20 @@ TEST_P(StorageV3, VertexDeleteLabel) {
// Check whether label 5 exists // Check whether label 5 exists
ASSERT_FALSE(vertex->HasLabel(label5, View::OLD).GetValue()); ASSERT_FALSE(vertex->HasLabel(label5, View::OLD).GetValue());
ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->Labels(View::OLD)->size(), 0); ASSERT_EQ(vertex->Labels(View::OLD)->size(), 0);
ASSERT_EQ(vertex->Labels(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Labels(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
// Try to add the label // Try to add the label
{ {
auto ret = vertex->AddLabelAndValidate(label5); auto ret = vertex->AddLabelAndValidate(label5);
AssertErrorInVariant(ret, Error::DELETED_OBJECT); AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT));
} }
// Try to remove the label // Try to remove the label
{ {
auto ret = vertex->RemoveLabelAndValidate(label5); auto ret = vertex->RemoveLabelAndValidate(label5);
AssertErrorInVariant(ret, Error::DELETED_OBJECT); AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT));
} }
acc.Abort(); acc.Abort();
@ -779,33 +779,33 @@ TEST_P(StorageV3, VertexDeleteLabel) {
// Check whether label 5 exists // Check whether label 5 exists
ASSERT_TRUE(vertex->HasLabel(label5, View::OLD).GetValue()); ASSERT_TRUE(vertex->HasLabel(label5, View::OLD).GetValue());
ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
{ {
auto labels = vertex->Labels(View::OLD).GetValue(); auto labels = vertex->Labels(View::OLD).GetValue();
ASSERT_EQ(labels.size(), 1); ASSERT_EQ(labels.size(), 1);
ASSERT_EQ(labels[0], label5); ASSERT_EQ(labels[0], label5);
} }
ASSERT_EQ(vertex->Labels(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Labels(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
// Advance command // Advance command
acc.AdvanceCommand(); acc.AdvanceCommand();
// Check whether label 5 exists // Check whether label 5 exists
ASSERT_EQ(vertex->HasLabel(label5, View::OLD).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->HasLabel(label5, View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->HasLabel(label5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->Labels(View::OLD).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Labels(View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->Labels(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Labels(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
// Try to add the label // Try to add the label
{ {
auto ret = vertex->AddLabelAndValidate(label5); auto ret = vertex->AddLabelAndValidate(label5);
AssertErrorInVariant(ret, Error::DELETED_OBJECT); AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT));
} }
// Try to remove the label // Try to remove the label
{ {
auto ret = vertex->RemoveLabelAndValidate(label5); auto ret = vertex->RemoveLabelAndValidate(label5);
AssertErrorInVariant(ret, Error::DELETED_OBJECT); AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT));
} }
acc.Abort(); acc.Abort();
@ -855,14 +855,14 @@ TEST_P(StorageV3, VertexDeleteProperty) {
// Check whether label 5 exists // Check whether label 5 exists
ASSERT_TRUE(vertex->GetProperty(property5, View::OLD)->IsNull()); ASSERT_TRUE(vertex->GetProperty(property5, View::OLD)->IsNull());
ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->Properties(View::OLD)->size(), 0); ASSERT_EQ(vertex->Properties(View::OLD)->size(), 0);
ASSERT_EQ(vertex->Properties(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Properties(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
// Try to set the property5 // Try to set the property5
{ {
auto ret = vertex->SetPropertyAndValidate(property5, PropertyValue("haihai")); auto ret = vertex->SetPropertyAndValidate(property5, PropertyValue("haihai"));
AssertErrorInVariant(ret, Error::DELETED_OBJECT); AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT));
} }
acc.Abort(); acc.Abort();
@ -918,27 +918,27 @@ TEST_P(StorageV3, VertexDeleteProperty) {
// Check whether property 5 exists // Check whether property 5 exists
ASSERT_EQ(vertex->GetProperty(property5, View::OLD)->ValueString(), "nandare"); ASSERT_EQ(vertex->GetProperty(property5, View::OLD)->ValueString(), "nandare");
ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
{ {
auto properties = vertex->Properties(View::OLD).GetValue(); auto properties = vertex->Properties(View::OLD).GetValue();
ASSERT_EQ(properties.size(), 1); ASSERT_EQ(properties.size(), 1);
ASSERT_EQ(properties[property5].ValueString(), "nandare"); ASSERT_EQ(properties[property5].ValueString(), "nandare");
} }
ASSERT_EQ(vertex->Properties(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Properties(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
// Advance command // Advance command
acc.AdvanceCommand(); acc.AdvanceCommand();
// Check whether property 5 exists // Check whether property 5 exists
ASSERT_EQ(vertex->GetProperty(property5, View::OLD).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->GetProperty(property5, View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->GetProperty(property5, View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->Properties(View::OLD).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Properties(View::OLD).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex->Properties(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex->Properties(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
// Try to set the property // Try to set the property
{ {
auto ret = vertex->SetPropertyAndValidate(property5, PropertyValue("haihai")); auto ret = vertex->SetPropertyAndValidate(property5, PropertyValue("haihai"));
AssertErrorInVariant(ret, Error::DELETED_OBJECT); AssertShardErrorEqual(ret, SHARD_ERROR(ErrorCode::DELETED_OBJECT));
} }
acc.Abort(); acc.Abort();
@ -1371,7 +1371,7 @@ TEST_P(StorageV3, VertexLabelSerializationError) {
{ {
auto res = vertex->AddLabelAndValidate(label1); auto res = vertex->AddLabelAndValidate(label1);
AssertErrorInVariant(res, Error::SERIALIZATION_ERROR); AssertShardErrorEqual(res, SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR));
} }
} }
@ -1865,7 +1865,7 @@ TEST_P(StorageV3, VertexPropertySerializationError) {
{ {
auto res = vertex->SetPropertyAndValidate(property2, PropertyValue("nandare")); auto res = vertex->SetPropertyAndValidate(property2, PropertyValue("nandare"));
AssertErrorInVariant(res, Error::SERIALIZATION_ERROR); AssertShardErrorEqual(res, SHARD_ERROR(ErrorCode::SERIALIZATION_ERROR));
} }
} }
@ -2255,14 +2255,14 @@ TEST_P(StorageV3, VertexNonexistentLabelPropertyEdgeAPI) {
auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{0}, {}); auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{0}, {});
// Check state before (OLD view). // Check state before (OLD view).
ASSERT_EQ(vertex.Labels(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.Labels(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.Properties(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.Properties(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
// Check state before (NEW view). // Check state before (NEW view).
ASSERT_EQ(vertex.Labels(View::NEW)->size(), 0); ASSERT_EQ(vertex.Labels(View::NEW)->size(), 0);
@ -2282,14 +2282,14 @@ TEST_P(StorageV3, VertexNonexistentLabelPropertyEdgeAPI) {
.HasValue()); .HasValue());
// Check state after (OLD view). // Check state after (OLD view).
ASSERT_EQ(vertex.Labels(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.Labels(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.HasLabel(label1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.Properties(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.Properties(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.GetProperty(property1, View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.InEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.OutEdges(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.InDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), Error::NONEXISTENT_OBJECT); ASSERT_EQ(vertex.OutDegree(View::OLD).GetError(), SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
// Check state after (NEW view). // Check state after (NEW view).
ASSERT_EQ(vertex.Labels(View::NEW)->size(), 1); ASSERT_EQ(vertex.Labels(View::NEW)->size(), 1);
@ -2657,42 +2657,31 @@ TEST_P(StorageV3, TestCreateVertexAndValidate) {
ASSERT_TRUE(vertex2.HasError()); ASSERT_TRUE(vertex2.HasError());
auto error = vertex2.GetError(); auto error = vertex2.GetError();
auto error_ptr = std::get_if<memgraph::storage::v3::Error>(&error); ASSERT_TRUE(error == common::ErrorCode::VERTEX_ALREADY_INSERTED);
ASSERT_TRUE(error_ptr);
ASSERT_TRUE(*error_ptr == storage::v3::Error::VERTEX_ALREADY_INSERTED);
} }
{ {
auto acc = store.Access(GetNextHlc()); auto acc = store.Access(GetNextHlc());
auto vertex = acc.CreateVertexAndValidate({primary_label}, {PropertyValue{0}}, {}); auto vertex = acc.CreateVertexAndValidate({primary_label}, {PropertyValue{0}}, {});
ASSERT_TRUE(vertex.HasError()); ASSERT_TRUE(vertex.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(vertex.GetError())); EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY));
EXPECT_EQ(std::get<SchemaViolation>(vertex.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, primary_label));
} }
{ {
auto acc = store.Access(GetNextHlc()); auto acc = store.Access(GetNextHlc());
auto vertex = acc.CreateVertexAndValidate({primary_label}, {PropertyValue{0}}, {}); auto vertex = acc.CreateVertexAndValidate({primary_label}, {PropertyValue{0}}, {});
ASSERT_TRUE(vertex.HasError()); ASSERT_TRUE(vertex.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(vertex.GetError())); EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY));
EXPECT_EQ(std::get<SchemaViolation>(vertex.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, primary_label));
} }
{ {
auto acc = store.Access(GetNextHlc()); auto acc = store.Access(GetNextHlc());
auto vertex = acc.CreateVertexAndValidate({}, {}, {}); auto vertex = acc.CreateVertexAndValidate({}, {}, {});
ASSERT_TRUE(vertex.HasError()); ASSERT_TRUE(vertex.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(vertex.GetError())); EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED));
EXPECT_EQ(std::get<SchemaViolation>(vertex.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, primary_label));
} }
{ {
auto acc = store.Access(GetNextHlc()); auto acc = store.Access(GetNextHlc());
auto vertex = acc.CreateVertexAndValidate({}, {PropertyValue{"test"}}, {}); auto vertex = acc.CreateVertexAndValidate({}, {PropertyValue{"test"}}, {});
ASSERT_TRUE(vertex.HasError()); ASSERT_TRUE(vertex.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(vertex.GetError())); EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE));
EXPECT_EQ(std::get<SchemaViolation>(vertex.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, primary_label,
{primary_property, common::SchemaType::INT}, PropertyValue("test")));
} }
} }
} // namespace memgraph::storage::v3::tests } // namespace memgraph::storage::v3::tests

View File

@ -18,7 +18,6 @@
#include "storage/v3/name_id_mapper.hpp" #include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/shard.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/shard_operation_result.hpp"
namespace memgraph::storage::v3::tests { namespace memgraph::storage::v3::tests {
using testing::UnorderedElementsAre; using testing::UnorderedElementsAre;
@ -39,7 +38,7 @@ class StorageEdgeTest : public ::testing::TestWithParam<bool> {
return store.NameToEdgeType(edge_type_name); return store.NameToEdgeType(edge_type_name);
} }
static ShardOperationResult<VertexAccessor> CreateVertex(Shard::Accessor &acc, const PropertyValue &key) { static ShardResult<VertexAccessor> CreateVertex(Shard::Accessor &acc, const PropertyValue &key) {
return acc.CreateVertexAndValidate({}, {key}, {}); return acc.CreateVertexAndValidate({}, {key}, {});
} }
@ -3243,7 +3242,7 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
{ {
auto ret = acc.DeleteVertex(&vertex_from.value()); auto ret = acc.DeleteVertex(&vertex_from.value());
ASSERT_TRUE(ret.HasError()); ASSERT_TRUE(ret.HasError());
ASSERT_EQ(ret.GetError(), Error::VERTEX_HAS_EDGES); ASSERT_EQ(ret.GetError(), SHARD_ERROR(ErrorCode::VERTEX_HAS_EDGES));
} }
// Detach delete vertex // Detach delete vertex
@ -3256,8 +3255,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
// Check edges // Check edges
ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0); ASSERT_EQ(vertex_from->InEdges(View::OLD)->size(), 0);
ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0); ASSERT_EQ(*vertex_from->InDegree(View::OLD), 0);
ASSERT_EQ(vertex_from->InEdges(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex_from->InEdges(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex_from->InDegree(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex_from->InDegree(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
{ {
auto ret = vertex_from->OutEdges(View::OLD); auto ret = vertex_from->OutEdges(View::OLD);
ASSERT_TRUE(ret.HasValue()); ASSERT_TRUE(ret.HasValue());
@ -3270,8 +3269,8 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
ASSERT_EQ(e.FromVertex(), from_id); ASSERT_EQ(e.FromVertex(), from_id);
ASSERT_EQ(e.ToVertex(), to_id); ASSERT_EQ(e.ToVertex(), to_id);
} }
ASSERT_EQ(vertex_from->OutEdges(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex_from->OutEdges(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), Error::DELETED_OBJECT); ASSERT_EQ(vertex_from->OutDegree(View::NEW).GetError(), SHARD_ERROR(ErrorCode::DELETED_OBJECT));
{ {
auto ret = vertex_to->InEdges(View::OLD); auto ret = vertex_to->InEdges(View::OLD);
ASSERT_TRUE(ret.HasValue()); ASSERT_TRUE(ret.HasValue());

View File

@ -18,7 +18,7 @@
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/name_id_mapper.hpp" #include "storage/v3/name_id_mapper.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/temporal.hpp" #include "storage/v3/temporal.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"

View File

@ -13,7 +13,7 @@
#include "storage/v3/isolation_level.hpp" #include "storage/v3/isolation_level.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
namespace memgraph::storage::v3::tests { namespace memgraph::storage::v3::tests {
int64_t VerticesCount(Shard::Accessor &accessor) { int64_t VerticesCount(Shard::Accessor &accessor) {

View File

@ -14,16 +14,16 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <optional>
#include <string> #include <string>
#include <vector> #include <vector>
#include "common/errors.hpp"
#include "common/types.hpp" #include "common/types.hpp"
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp" #include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp" #include "storage/v3/schema_validator.hpp"
#include "storage/v3/schemas.hpp" #include "storage/v3/schemas.hpp"
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/temporal.hpp" #include "storage/v3/temporal.hpp"
using testing::Pair; using testing::Pair;
@ -150,7 +150,15 @@ TEST_F(SchemaTest, TestSchemaDrop) {
class SchemaValidatorTest : public testing::Test { class SchemaValidatorTest : public testing::Test {
private: private:
NameIdMapper id_mapper_{{{1, "label1"}, {2, "label2"}, {3, "prop1"}, {4, "prop2"}, {5, "prop3"}}}; NameIdMapper id_mapper_{{{1, "label1"},
{2, "label2"},
{3, "prop1"},
{4, "prop2"},
{5, "prop3"},
{6, "label4"},
{7, "label5"},
{8, "label6"},
{9, "test"}}};
protected: protected:
void SetUp() override { void SetUp() override {
@ -162,9 +170,8 @@ class SchemaValidatorTest : public testing::Test {
PropertyId NameToProperty(const std::string &name) { return PropertyId::FromUint(id_mapper_.NameToId(name)); } PropertyId NameToProperty(const std::string &name) { return PropertyId::FromUint(id_mapper_.NameToId(name)); }
protected:
Schemas schemas; Schemas schemas;
SchemaValidator schema_validator{schemas}; SchemaValidator schema_validator{schemas, id_mapper_};
PropertyId prop_string{NameToProperty("prop1")}; PropertyId prop_string{NameToProperty("prop1")};
PropertyId prop_int{NameToProperty("prop2")}; PropertyId prop_int{NameToProperty("prop2")};
PropertyId prop_duration{NameToProperty("prop3")}; PropertyId prop_duration{NameToProperty("prop3")};
@ -179,100 +186,92 @@ TEST_F(SchemaValidatorTest, TestSchemaValidateVertexCreate) {
// Validate against secondary label // Validate against secondary label
{ {
const auto schema_violation = schema_validator.ValidateVertexCreate(NameToLabel("test"), {}, {PropertyValue(1)}); const auto schema_violation = schema_validator.ValidateVertexCreate(NameToLabel("test"), {}, {PropertyValue(1)});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_NO_SCHEMA_DEFINED_FOR_LABEL));
SchemaViolation(SchemaViolation::ValidationStatus::NO_SCHEMA_DEFINED_FOR_LABEL, NameToLabel("test")));
} }
{ {
const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {}); const auto schema_violation = schema_validator.ValidateVertexCreate(label2, {}, {});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PRIMARY_PROPERTIES_UNDEFINED));
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PRIMARY_PROPERTIES_UNDEFINED, label2));
} }
// Validate wrong secondary label // Validate wrong secondary label
{ {
const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {label1}, {PropertyValue("test")}); const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {label1}, {PropertyValue("test")});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY));
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, label1));
} }
{ {
const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {label2}, {PropertyValue("test")}); const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {label2}, {PropertyValue("test")});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY));
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, label2));
} }
// Validate wrong property type // Validate wrong property type
{ {
const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue(1)}); const auto schema_violation = schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue(1)});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, label1, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE));
schema_prop_string, PropertyValue(1)));
} }
{ {
const auto schema_violation = const auto schema_violation =
schema_validator.ValidateVertexCreate(label2, {}, {PropertyValue("test"), PropertyValue(12), PropertyValue(1)}); schema_validator.ValidateVertexCreate(label2, {}, {PropertyValue("test"), PropertyValue(12), PropertyValue(1)});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, label2, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE));
schema_prop_duration, PropertyValue(1)));
} }
{ {
const auto wrong_prop = PropertyValue(TemporalData(TemporalType::Date, 1234)); const auto wrong_prop = PropertyValue(TemporalData(TemporalType::Date, 1234));
const auto schema_violation = const auto schema_violation =
schema_validator.ValidateVertexCreate(label2, {}, {PropertyValue("test"), PropertyValue(12), wrong_prop}); schema_validator.ValidateVertexCreate(label2, {}, {PropertyValue("test"), PropertyValue(12), wrong_prop});
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_PROPERTY_WRONG_TYPE, label2, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_PROPERTY_WRONG_TYPE));
schema_prop_duration, wrong_prop));
} }
// Passing validations // Passing validations
EXPECT_EQ(schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue("test")}), std::nullopt); EXPECT_FALSE(schema_validator.ValidateVertexCreate(label1, {}, {PropertyValue("test")}).HasError());
EXPECT_EQ(schema_validator.ValidateVertexCreate(label1, {NameToLabel("label3"), NameToLabel("label4")}, EXPECT_FALSE(
{PropertyValue("test")}), schema_validator
std::nullopt); .ValidateVertexCreate(label1, {NameToLabel("label3"), NameToLabel("label4")}, {PropertyValue("test")})
EXPECT_EQ(schema_validator.ValidateVertexCreate( .HasError());
label2, {}, EXPECT_FALSE(schema_validator
{PropertyValue("test"), PropertyValue(122), PropertyValue(TemporalData(TemporalType::Duration, 1234))}), .ValidateVertexCreate(label2, {},
std::nullopt); {PropertyValue("test"), PropertyValue(122),
EXPECT_EQ(schema_validator.ValidateVertexCreate(label2, {NameToLabel("label5"), NameToLabel("label6")}, PropertyValue(TemporalData(TemporalType::Duration, 1234))})
{PropertyValue("test123"), PropertyValue(122221), .HasError());
PropertyValue(TemporalData(TemporalType::Duration, 12344321))}), EXPECT_FALSE(schema_validator
std::nullopt); .ValidateVertexCreate(label2, {NameToLabel("label5"), NameToLabel("label6")},
{PropertyValue("test123"), PropertyValue(122221),
PropertyValue(TemporalData(TemporalType::Duration, 12344321))})
.HasError());
} }
TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdate) { TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdate) {
// Validate updating of primary key // Validate updating of primary key
{ {
const auto schema_violation = schema_validator.ValidatePropertyUpdate(label1, prop_string); const auto schema_violation = schema_validator.ValidatePropertyUpdate(label1, prop_string);
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, label1, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY));
schema_prop_string));
} }
{ {
const auto schema_violation = schema_validator.ValidatePropertyUpdate(label2, prop_duration); const auto schema_violation = schema_validator.ValidatePropertyUpdate(label2, prop_duration);
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, label2, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY));
schema_prop_duration));
} }
EXPECT_EQ(schema_validator.ValidatePropertyUpdate(label1, prop_int), std::nullopt); EXPECT_FALSE(schema_validator.ValidatePropertyUpdate(label1, prop_int).HasError());
EXPECT_EQ(schema_validator.ValidatePropertyUpdate(label1, prop_duration), std::nullopt); EXPECT_FALSE(schema_validator.ValidatePropertyUpdate(label1, prop_duration).HasError());
EXPECT_EQ(schema_validator.ValidatePropertyUpdate(label2, NameToProperty("test")), std::nullopt); EXPECT_FALSE(schema_validator.ValidatePropertyUpdate(label2, NameToProperty("test")).HasError());
} }
TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdateLabel) { TEST_F(SchemaValidatorTest, TestSchemaValidatePropertyUpdateLabel) {
// Validate adding primary label // Validate adding primary label
{ {
const auto schema_violation = schema_validator.ValidateLabelUpdate(label1); const auto schema_violation = schema_validator.ValidateLabelUpdate(label1);
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL));
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label1));
} }
{ {
const auto schema_violation = schema_validator.ValidateLabelUpdate(label2); const auto schema_violation = schema_validator.ValidateLabelUpdate(label2);
ASSERT_NE(schema_violation, std::nullopt); ASSERT_TRUE(schema_violation.HasError());
EXPECT_EQ(*schema_violation, EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL));
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label2));
} }
EXPECT_EQ(schema_validator.ValidateLabelUpdate(NameToLabel("test")), std::nullopt); EXPECT_FALSE(schema_validator.ValidateLabelUpdate(NameToLabel("test")).HasError());
} }
} // namespace memgraph::storage::v3::tests } // namespace memgraph::storage::v3::tests

View File

@ -0,0 +1,316 @@
// 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.
#include <gmock/gmock-matchers.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <fmt/format.h>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include "common/types.hpp"
#include "query/v2/requests.hpp"
#include "storage/v3/id_types.hpp"
#include "storage/v3/property_value.hpp"
#include "storage/v3/schema_validator.hpp"
#include "storage/v3/schemas.hpp"
#include "storage/v3/shard_rsm.hpp"
#include "storage/v3/temporal.hpp"
#include "storage/v3/vertex_id.hpp"
using testing::Pair;
using testing::UnorderedElementsAre;
using SchemaType = memgraph::common::SchemaType;
namespace memgraph::storage::v3::tests {
uint64_t GetTransactionId() {
static uint64_t transaction_id = 0;
return transaction_id++;
}
class ShardRSMTest : public testing::Test {
private:
NameIdMapper id_mapper_{{{1, "primary_label"},
{2, "primary_label2"},
{3, "label"},
{4, "primary_prop1"},
{5, "primary_prop2"},
{6, "prop"}}};
protected:
ShardRSMTest() {
PropertyValue min_pk(static_cast<int64_t>(0));
std::vector<PropertyValue> min_prim_key = {min_pk};
PropertyValue max_pk(static_cast<int64_t>(10000000));
std::vector<PropertyValue> max_prim_key = {max_pk};
auto shard_ptr1 = std::make_unique<Shard>(primary_label, min_prim_key, max_prim_key, std::vector{schema_prop});
shard_ptr1->StoreMapping({{1, "primary_label"},
{2, "primary_label2"},
{3, "label"},
{4, "primary_prop1"},
{5, "primary_prop2"},
{6, "prop"}});
shard_ptr1->CreateSchema(primary_label2, {{primary_property2, SchemaType::INT}});
shard_rsm = std::make_unique<ShardRsm>(std::move(shard_ptr1));
}
LabelId NameToLabel(const std::string &name) { return LabelId::FromUint(id_mapper_.NameToId(name)); }
PropertyId NameToProperty(const std::string &name) { return PropertyId::FromUint(id_mapper_.NameToId(name)); }
auto Commit(const auto &req) {
const coordinator::Hlc commit_timestamp{GetTransactionId()};
msgs::CommitRequest commit_req;
commit_req.transaction_id = req.transaction_id;
commit_req.commit_timestamp = commit_timestamp;
return shard_rsm->Apply(commit_req);
}
void CreateVertex(const msgs::PrimaryKey &primary_key, const std::vector<msgs::Label> labels,
const std::vector<std::pair<msgs::PropertyId, msgs::Value>> &properties) {
msgs::NewVertex vertex = {labels, primary_key, properties};
msgs::CreateVerticesRequest create_req;
create_req.new_vertices = {vertex};
create_req.new_vertices = {vertex};
create_req.transaction_id.logical_id = GetTransactionId();
auto write_res = shard_rsm->Apply(create_req);
ASSERT_TRUE(std::holds_alternative<msgs::CreateVerticesResponse>(write_res));
auto commit_res = Commit(create_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
ASSERT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
}
void AssertVertexExists(const msgs::PrimaryKey &primary_key, const std::vector<msgs::Label> &labels,
const std::vector<std::pair<msgs::PropertyId, msgs::Value>> &properties) {
msgs::ScanVerticesRequest scan_req;
scan_req.props_to_return = std::nullopt;
scan_req.start_id = msgs::VertexId{msgs::Label{.id = primary_label}, primary_key};
scan_req.storage_view = msgs::StorageView::OLD;
scan_req.transaction_id.logical_id = GetTransactionId();
// Make request
auto maybe_read_res = shard_rsm->Read(scan_req);
ASSERT_TRUE(std::holds_alternative<msgs::ScanVerticesResponse>(maybe_read_res));
const auto read_res = std::get<msgs::ScanVerticesResponse>(maybe_read_res);
EXPECT_FALSE(read_res.error.has_value());
EXPECT_EQ(read_res.results.size(), 1);
// Read results
const auto res = read_res.results[0];
const auto vtx_id = msgs::VertexId{msgs::Label{.id = primary_label}, primary_key};
EXPECT_EQ(res.vertex.id, vtx_id);
EXPECT_EQ(res.vertex.labels, labels);
EXPECT_EQ(res.props, properties);
}
LabelId primary_label{NameToLabel("primary_label")};
LabelId primary_label2{NameToLabel("primary_label2")};
LabelId label{NameToLabel("label")};
PropertyId primary_property1{NameToProperty("primary_prop1")};
PropertyId primary_property2{NameToProperty("primary_prop2")};
PropertyId prop{NameToProperty("prop")};
SchemaProperty schema_prop{primary_property1, SchemaType::INT};
std::unique_ptr<ShardRsm> shard_rsm;
};
TEST_F(ShardRSMTest, TestUpdateVertexSecondaryProperty) {
const msgs::Value primary_key_val{static_cast<int64_t>(1)};
const msgs::PrimaryKey pk{primary_key_val};
// Create Vertex
CreateVertex(pk, {}, {});
// Add property prop
static constexpr int64_t updated_vertex_id{10};
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices =
std::vector<msgs::UpdateVertex>{{pk, {}, {}, {{msgs::PropertyId(prop), msgs::Value(updated_vertex_id)}}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_FALSE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
const auto commit_res = Commit(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
EXPECT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}, {prop, msgs::Value(updated_vertex_id)}});
// Update property prop
static constexpr int64_t updated_vertex_id_2{101};
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices =
std::vector<msgs::UpdateVertex>{{pk, {}, {}, {{msgs::PropertyId(prop), msgs::Value(updated_vertex_id_2)}}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_FALSE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
const auto commit_res = Commit(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
EXPECT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}, {prop, msgs::Value(updated_vertex_id_2)}});
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}, {prop, msgs::Value(updated_vertex_id_2)}});
// Remove property prop
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices =
std::vector<msgs::UpdateVertex>{{pk, {}, {}, {{msgs::PropertyId(prop), msgs::Value()}}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_FALSE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
const auto commit_res = Commit(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
EXPECT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}});
}
TEST_F(ShardRSMTest, TestUpdateVertexPrimaryProperty) {
const msgs::Value primary_key_val{static_cast<int64_t>(1)};
const msgs::PrimaryKey pk{primary_key_val};
// Create Vertex
CreateVertex(pk, {}, {});
// Try to update primary property
static constexpr int64_t updated_vertex_id{10};
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = std::vector<msgs::UpdateVertex>{
{pk, {}, {}, {{msgs::PropertyId(primary_property1), msgs::Value(updated_vertex_id)}}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_TRUE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}});
// Try to update primary property of another schema
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = std::vector<msgs::UpdateVertex>{
{pk, {}, {}, {{msgs::PropertyId(primary_property2), msgs::Value(updated_vertex_id)}}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_FALSE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
const auto commit_res = Commit(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
EXPECT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
}
AssertVertexExists(pk, {},
{{primary_property1, primary_key_val}, {primary_property2, msgs::Value(updated_vertex_id)}});
}
TEST_F(ShardRSMTest, TestUpdateSecondaryLabel) {
const msgs::Value primary_key_val{static_cast<int64_t>(1)};
const msgs::PrimaryKey pk{primary_key_val};
// Create Vertex
CreateVertex(pk, {}, {});
// Add label label
const msgs::Label secondary_label{label};
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = std::vector<msgs::UpdateVertex>{{pk, {label}, {}, {}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_FALSE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
const auto commit_res = Commit(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
EXPECT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
}
AssertVertexExists(pk, {secondary_label}, {{primary_property1, primary_key_val}});
// Remove primary label
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = std::vector<msgs::UpdateVertex>{{pk, {}, {label}, {}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_FALSE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
const auto commit_res = Commit(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::CommitResponse>(commit_res));
EXPECT_FALSE(std::get<msgs::CommitResponse>(commit_res).error.has_value());
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}});
}
TEST_F(ShardRSMTest, TestUpdatePrimaryLabel) {
const msgs::Value primary_key_val{static_cast<int64_t>(1)};
const msgs::PrimaryKey pk{primary_key_val};
// Create Vertex
CreateVertex(pk, {}, {});
// Remove primary label
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = std::vector<msgs::UpdateVertex>{{pk, {}, {primary_label}, {}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_TRUE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}});
// Add different primary label
{
msgs::UpdateVerticesRequest update_req;
update_req.transaction_id.logical_id = GetTransactionId();
update_req.update_vertices = std::vector<msgs::UpdateVertex>{{pk, {primary_label2}, {}, {}}};
const auto write_res = shard_rsm->Apply(update_req);
ASSERT_TRUE(std::holds_alternative<msgs::UpdateVerticesResponse>(write_res));
EXPECT_TRUE(std::get<msgs::UpdateVerticesResponse>(write_res).error.has_value());
}
AssertVertexExists(pk, {}, {{primary_property1, primary_key_val}});
}
} // namespace memgraph::storage::v3::tests

View File

@ -11,7 +11,7 @@
#pragma once #pragma once
#include "storage/v3/storage.hpp" #include "storage/v3/shard.hpp"
#include "storage/v3/view.hpp" #include "storage/v3/view.hpp"
namespace memgraph::storage::v3::tests { namespace memgraph::storage::v3::tests {

View File

@ -16,6 +16,7 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "common/errors.hpp"
#include "common/types.hpp" #include "common/types.hpp"
#include "storage/v3/delta.hpp" #include "storage/v3/delta.hpp"
#include "storage/v3/id_types.hpp" #include "storage/v3/id_types.hpp"
@ -76,7 +77,7 @@ TEST_F(StorageV3Accessor, TestPrimaryLabel) {
ASSERT_TRUE(vertex.PrimaryLabel(View::OLD).HasError()); ASSERT_TRUE(vertex.PrimaryLabel(View::OLD).HasError());
const auto error_primary_label = vertex.PrimaryLabel(View::OLD).GetError(); const auto error_primary_label = vertex.PrimaryLabel(View::OLD).GetError();
ASSERT_FALSE(vertex.PrimaryLabel(View::NEW).HasError()); ASSERT_FALSE(vertex.PrimaryLabel(View::NEW).HasError());
EXPECT_EQ(error_primary_label, Error::NONEXISTENT_OBJECT); EXPECT_EQ(error_primary_label, SHARD_ERROR(ErrorCode::NONEXISTENT_OBJECT));
} }
{ {
auto acc = storage.Access(GetNextHlc()); auto acc = storage.Access(GetNextHlc());
@ -127,9 +128,7 @@ TEST_F(StorageV3Accessor, TestAddLabels) {
const auto label1 = NameToLabelId("label"); const auto label1 = NameToLabelId("label");
auto vertex = acc.CreateVertexAndValidate({label1}, {PropertyValue{2}}, {}); auto vertex = acc.CreateVertexAndValidate({label1}, {PropertyValue{2}}, {});
ASSERT_TRUE(vertex.HasError()); ASSERT_TRUE(vertex.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(vertex.GetError())); EXPECT_EQ(vertex.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_SECONDARY_LABEL_IS_PRIMARY));
EXPECT_EQ(std::get<SchemaViolation>(vertex.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_SECONDARY_LABEL_IS_PRIMARY, label1));
} }
{ {
auto acc = storage.Access(GetNextHlc()); auto acc = storage.Access(GetNextHlc());
@ -138,9 +137,7 @@ TEST_F(StorageV3Accessor, TestAddLabels) {
ASSERT_TRUE(vertex.HasValue()); ASSERT_TRUE(vertex.HasValue());
const auto schema_violation = vertex->AddLabelAndValidate(label1); const auto schema_violation = vertex->AddLabelAndValidate(label1);
ASSERT_TRUE(schema_violation.HasError()); ASSERT_TRUE(schema_violation.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(schema_violation.GetError())); EXPECT_EQ(schema_violation.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL));
EXPECT_EQ(std::get<SchemaViolation>(schema_violation.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, label1));
} }
} }
@ -184,9 +181,7 @@ TEST_F(StorageV3Accessor, TestRemoveLabels) {
auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{2}); auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{2});
const auto res1 = vertex.RemoveLabelAndValidate(primary_label); const auto res1 = vertex.RemoveLabelAndValidate(primary_label);
ASSERT_TRUE(res1.HasError()); ASSERT_TRUE(res1.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(res1.GetError())); EXPECT_EQ(res1.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_LABEL));
EXPECT_EQ(std::get<SchemaViolation>(res1.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_LABEL, primary_label));
} }
} }
@ -205,20 +200,14 @@ TEST_F(StorageV3Accessor, TestSetKeysAndProperties) {
auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{1}); auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{1});
const auto res = vertex.SetPropertyAndValidate(primary_property, PropertyValue(1)); const auto res = vertex.SetPropertyAndValidate(primary_property, PropertyValue(1));
ASSERT_TRUE(res.HasError()); ASSERT_TRUE(res.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(res.GetError())); EXPECT_EQ(res.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY));
EXPECT_EQ(std::get<SchemaViolation>(res.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, primary_label,
SchemaProperty{primary_property, common::SchemaType::INT}));
} }
{ {
auto acc = storage.Access(GetNextHlc()); auto acc = storage.Access(GetNextHlc());
auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{2}); auto vertex = CreateVertexAndValidate(acc, {}, PropertyValue{2});
const auto res = vertex.SetPropertyAndValidate(primary_property, PropertyValue()); const auto res = vertex.SetPropertyAndValidate(primary_property, PropertyValue());
ASSERT_TRUE(res.HasError()); ASSERT_TRUE(res.HasError());
ASSERT_TRUE(std::holds_alternative<SchemaViolation>(res.GetError())); EXPECT_EQ(res.GetError(), SHARD_ERROR(ErrorCode::SCHEMA_VERTEX_UPDATE_PRIMARY_KEY));
EXPECT_EQ(std::get<SchemaViolation>(res.GetError()),
SchemaViolation(SchemaViolation::ValidationStatus::VERTEX_UPDATE_PRIMARY_KEY, primary_label,
SchemaProperty{primary_property, common::SchemaType::INT}));
} }
} }