Merge branch 'master' into fix-trigger-for-deleted-api-issue

This commit is contained in:
Antonio Filipovic
2023-09-04 15:51:39 +02:00
committed by GitHub
56 changed files with 1591 additions and 1000 deletions

View File

@@ -67,7 +67,11 @@ jobs:
- name: Run mgbench - name: Run mgbench
run: | run: |
cd tests/mgbench cd tests/mgbench
./benchmark.py vendor-native --num-workers-for-benchmark 12 --export-results benchmark_result.json pokec/medium/*/* ./benchmark.py vendor-native --num-workers-for-benchmark 12 --export-results benchmark_pokec.json pokec/medium/*/*
./benchmark.py vendor-native --num-workers-for-benchmark 1 --export-results benchmark_supernode.json supernode
./benchmark.py vendor-native --num-workers-for-benchmark 1 --export-results benchmark_high_write_set_property.json high_write_set_property
- name: Upload mgbench results - name: Upload mgbench results
run: | run: |
@@ -76,7 +80,19 @@ jobs:
source ve3/bin/activate source ve3/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
./main.py --benchmark-name "mgbench" \ ./main.py --benchmark-name "mgbench" \
--benchmark-results-path "../../tests/mgbench/benchmark_result.json" \ --benchmark-results-path "../../tests/mgbench/benchmark_pokec.json" \
--github-run-id "${{ github.run_id }}" \
--github-run-number "${{ github.run_number }}" \
--head-branch-name "${{ env.BRANCH_NAME }}"
./main.py --benchmark-name "supernode" \
--benchmark-results-path "../../tests/mgbench/benchmark_supernode.json" \
--github-run-id "${{ github.run_id }}" \
--github-run-number "${{ github.run_number }}" \
--head-branch-name "${{ env.BRANCH_NAME }}"
./main.py --benchmark-name "high_write_set_property" \
--benchmark-results-path "../../tests/mgbench/benchmark_high_write_set_property.json" \
--github-run-id "${{ github.run_id }}" \ --github-run-id "${{ github.run_id }}" \
--github-run-number "${{ github.run_number }}" \ --github-run-number "${{ github.run_number }}" \
--head-branch-name "${{ env.BRANCH_NAME }}" --head-branch-name "${{ env.BRANCH_NAME }}"

View File

@@ -151,8 +151,8 @@ storage::Result<communication::bolt::Vertex> ToBoltVertex(const storage::VertexA
properties[db.PropertyToName(prop.first)] = ToBoltValue(prop.second); properties[db.PropertyToName(prop.first)] = ToBoltValue(prop.second);
} }
// Introduced in Bolt v5 (for now just send the ID) // Introduced in Bolt v5 (for now just send the ID)
const auto element_id = std::to_string(id.AsInt()); auto element_id = std::to_string(id.AsInt());
return communication::bolt::Vertex{id, labels, properties, element_id}; return communication::bolt::Vertex{id, std::move(labels), std::move(properties), std::move(element_id)};
} }
storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccessor &edge, const storage::Storage &db, storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccessor &edge, const storage::Storage &db,
@@ -171,7 +171,8 @@ storage::Result<communication::bolt::Edge> ToBoltEdge(const storage::EdgeAccesso
const auto element_id = std::to_string(id.AsInt()); const auto element_id = std::to_string(id.AsInt());
const auto from_element_id = std::to_string(from.AsInt()); const auto from_element_id = std::to_string(from.AsInt());
const auto to_element_id = std::to_string(to.AsInt()); const auto to_element_id = std::to_string(to.AsInt());
return communication::bolt::Edge{id, from, to, type, properties, element_id, from_element_id, to_element_id}; return communication::bolt::Edge{
id, from, to, std::move(type), std::move(properties), element_id, from_element_id, to_element_id};
} }
storage::Result<communication::bolt::Path> ToBoltPath(const query::Path &path, const storage::Storage &db, storage::Result<communication::bolt::Path> ToBoltPath(const query::Path &path, const storage::Storage &db,

View File

@@ -217,16 +217,15 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
/// @throw QueryRuntimeException if an error ocurred. /// @throw QueryRuntimeException if an error ocurred.
void SetReplicationRole(ReplicationQuery::ReplicationRole replication_role, std::optional<int64_t> port) override { void SetReplicationRole(ReplicationQuery::ReplicationRole replication_role, std::optional<int64_t> port) override {
auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_);
if (replication_role == ReplicationQuery::ReplicationRole::MAIN) { if (replication_role == ReplicationQuery::ReplicationRole::MAIN) {
if (!mem_storage->SetMainReplicationRole()) { if (!db_->SetMainReplicationRole()) {
throw QueryRuntimeException("Couldn't set role to main!"); throw QueryRuntimeException("Couldn't set role to main!");
} }
} else { } else {
if (!port || *port < 0 || *port > std::numeric_limits<uint16_t>::max()) { if (!port || *port < 0 || *port > std::numeric_limits<uint16_t>::max()) {
throw QueryRuntimeException("Port number invalid!"); throw QueryRuntimeException("Port number invalid!");
} }
if (!mem_storage->SetReplicaRole( if (!db_->SetReplicaRole(
io::network::Endpoint(storage::replication::kDefaultReplicationServerIp, static_cast<uint16_t>(*port)), io::network::Endpoint(storage::replication::kDefaultReplicationServerIp, static_cast<uint16_t>(*port)),
storage::replication::ReplicationServerConfig{})) { storage::replication::ReplicationServerConfig{})) {
throw QueryRuntimeException("Couldn't set role to replica!"); throw QueryRuntimeException("Couldn't set role to replica!");
@@ -236,7 +235,7 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
/// @throw QueryRuntimeException if an error ocurred. /// @throw QueryRuntimeException if an error ocurred.
ReplicationQuery::ReplicationRole ShowReplicationRole() const override { ReplicationQuery::ReplicationRole ShowReplicationRole() const override {
switch (static_cast<storage::InMemoryStorage *>(db_)->GetReplicationRole()) { switch (db_->GetReplicationRole()) {
case storage::replication::ReplicationRole::MAIN: case storage::replication::ReplicationRole::MAIN:
return ReplicationQuery::ReplicationRole::MAIN; return ReplicationQuery::ReplicationRole::MAIN;
case storage::replication::ReplicationRole::REPLICA: case storage::replication::ReplicationRole::REPLICA:
@@ -249,8 +248,7 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
void RegisterReplica(const std::string &name, const std::string &socket_address, void RegisterReplica(const std::string &name, const std::string &socket_address,
const ReplicationQuery::SyncMode sync_mode, const ReplicationQuery::SyncMode sync_mode,
const std::chrono::seconds replica_check_frequency) override { const std::chrono::seconds replica_check_frequency) override {
auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_); if (db_->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
if (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
// replica can't register another replica // replica can't register another replica
throw QueryRuntimeException("Replica can't register another replica!"); throw QueryRuntimeException("Replica can't register another replica!");
} }
@@ -275,9 +273,9 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
io::network::Endpoint::ParseSocketOrIpAddress(socket_address, storage::replication::kDefaultReplicationPort); io::network::Endpoint::ParseSocketOrIpAddress(socket_address, storage::replication::kDefaultReplicationPort);
if (maybe_ip_and_port) { if (maybe_ip_and_port) {
auto [ip, port] = *maybe_ip_and_port; auto [ip, port] = *maybe_ip_and_port;
auto ret = mem_storage->RegisterReplica( auto ret = db_->RegisterReplica(name, {std::move(ip), port}, repl_mode,
name, {std::move(ip), port}, repl_mode, storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID, storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
{.replica_check_frequency = replica_check_frequency, .ssl = std::nullopt}); {.replica_check_frequency = replica_check_frequency, .ssl = std::nullopt});
if (ret.HasError()) { if (ret.HasError()) {
throw QueryRuntimeException(fmt::format("Couldn't register replica '{}'!", name)); throw QueryRuntimeException(fmt::format("Couldn't register replica '{}'!", name));
} }
@@ -288,26 +286,23 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
/// @throw QueryRuntimeException if an error ocurred. /// @throw QueryRuntimeException if an error ocurred.
void DropReplica(const std::string &replica_name) override { void DropReplica(const std::string &replica_name) override {
auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_); if (db_->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
if (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
// replica can't unregister a replica // replica can't unregister a replica
throw QueryRuntimeException("Replica can't unregister a replica!"); throw QueryRuntimeException("Replica can't unregister a replica!");
} }
if (!mem_storage->UnregisterReplica(replica_name)) { if (!db_->UnregisterReplica(replica_name)) {
throw QueryRuntimeException(fmt::format("Couldn't unregister the replica '{}'", replica_name)); throw QueryRuntimeException(fmt::format("Couldn't unregister the replica '{}'", replica_name));
} }
} }
using Replica = ReplicationQueryHandler::Replica; using Replica = ReplicationQueryHandler::Replica;
std::vector<Replica> ShowReplicas() const override { std::vector<Replica> ShowReplicas() const override {
auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_); if (db_->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
if (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
// replica can't show registered replicas (it shouldn't have any) // replica can't show registered replicas (it shouldn't have any)
throw QueryRuntimeException("Replica can't show registered replicas (it shouldn't have any)!"); throw QueryRuntimeException("Replica can't show registered replicas (it shouldn't have any)!");
} }
auto repl_infos = mem_storage->ReplicasInfo(); auto repl_infos = db_->ReplicasInfo();
std::vector<Replica> replicas; std::vector<Replica> replicas;
replicas.reserve(repl_infos.size()); replicas.reserve(repl_infos.size());
@@ -1333,8 +1328,7 @@ bool IsWriteQueryOnMainMemoryReplica(storage::Storage *storage,
const query::plan::ReadWriteTypeChecker::RWType query_type) { const query::plan::ReadWriteTypeChecker::RWType query_type) {
if (auto storage_mode = storage->GetStorageMode(); storage_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL || if (auto storage_mode = storage->GetStorageMode(); storage_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL ||
storage_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL) { storage_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL) {
auto *mem_storage = static_cast<storage::InMemoryStorage *>(storage); return (storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) &&
return (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) &&
(query_type == RWType::W || query_type == RWType::RW); (query_type == RWType::W || query_type == RWType::RW);
} }
return false; return false;
@@ -1343,8 +1337,7 @@ bool IsWriteQueryOnMainMemoryReplica(storage::Storage *storage,
storage::replication::ReplicationRole GetReplicaRole(storage::Storage *storage) { storage::replication::ReplicationRole GetReplicaRole(storage::Storage *storage) {
if (auto storage_mode = storage->GetStorageMode(); storage_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL || if (auto storage_mode = storage->GetStorageMode(); storage_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL ||
storage_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL) { storage_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL) {
auto *mem_storage = static_cast<storage::InMemoryStorage *>(storage); return storage->GetReplicationRole();
return mem_storage->GetReplicationRole();
} }
return storage::replication::ReplicationRole::MAIN; return storage::replication::ReplicationRole::MAIN;
} }

View File

@@ -5,6 +5,7 @@ find_package(Threads REQUIRED)
add_library(mg-storage-v2 STATIC add_library(mg-storage-v2 STATIC
commit_log.cpp commit_log.cpp
constraints/existence_constraints.cpp constraints/existence_constraints.cpp
constraints/constraints.cpp
temporal.cpp temporal.cpp
durability/durability.cpp durability/durability.cpp
durability/serialization.cpp durability/serialization.cpp

View File

@@ -0,0 +1,32 @@
// Copyright 2023 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 "storage/v2/constraints/constraints.hpp"
#include "storage/v2/disk/unique_constraints.hpp"
#include "storage/v2/inmemory/unique_constraints.hpp"
namespace memgraph::storage {
Constraints::Constraints(const Config &config, StorageMode storage_mode) {
std::invoke([this, config, storage_mode]() {
existence_constraints_ = std::make_unique<ExistenceConstraints>();
switch (storage_mode) {
case StorageMode::IN_MEMORY_TRANSACTIONAL:
case StorageMode::IN_MEMORY_ANALYTICAL:
unique_constraints_ = std::make_unique<InMemoryUniqueConstraints>();
break;
case StorageMode::ON_DISK_TRANSACTIONAL:
unique_constraints_ = std::make_unique<DiskUniqueConstraints>(config);
break;
};
});
}
} // namespace memgraph::storage

View File

@@ -13,27 +13,13 @@
#include "storage/v2/config.hpp" #include "storage/v2/config.hpp"
#include "storage/v2/constraints/existence_constraints.hpp" #include "storage/v2/constraints/existence_constraints.hpp"
#include "storage/v2/disk/unique_constraints.hpp" #include "storage/v2/constraints/unique_constraints.hpp"
#include "storage/v2/inmemory/unique_constraints.hpp"
#include "storage/v2/storage_mode.hpp" #include "storage/v2/storage_mode.hpp"
namespace memgraph::storage { namespace memgraph::storage {
struct Constraints { struct Constraints {
Constraints(const Config &config, StorageMode storage_mode) { Constraints(const Config &config, StorageMode storage_mode);
std::invoke([this, config, storage_mode]() {
existence_constraints_ = std::make_unique<ExistenceConstraints>();
switch (storage_mode) {
case StorageMode::IN_MEMORY_TRANSACTIONAL:
case StorageMode::IN_MEMORY_ANALYTICAL:
unique_constraints_ = std::make_unique<InMemoryUniqueConstraints>();
break;
case StorageMode::ON_DISK_TRANSACTIONAL:
unique_constraints_ = std::make_unique<DiskUniqueConstraints>(config);
break;
};
});
}
Constraints(const Constraints &) = delete; Constraints(const Constraints &) = delete;
Constraints(Constraints &&) = delete; Constraints(Constraints &&) = delete;

View File

@@ -12,13 +12,8 @@
/// TODO: clear dependencies /// TODO: clear dependencies
#include "storage/v2/disk/label_property_index.hpp" #include "storage/v2/disk/label_property_index.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/inmemory/indices_utils.hpp"
#include "storage/v2/property_value.hpp"
#include "utils/disk_utils.hpp" #include "utils/disk_utils.hpp"
#include "utils/exceptions.hpp" #include "utils/rocksdb_serialization.hpp"
#include "utils/file.hpp"
#include "utils/skip_list.hpp"
namespace memgraph::storage { namespace memgraph::storage {

View File

@@ -28,6 +28,8 @@
#include "kvstore/kvstore.hpp" #include "kvstore/kvstore.hpp"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "storage/v2/constraints/unique_constraints.hpp" #include "storage/v2/constraints/unique_constraints.hpp"
#include "storage/v2/disk/label_index.hpp"
#include "storage/v2/disk/label_property_index.hpp"
#include "storage/v2/disk/rocksdb_storage.hpp" #include "storage/v2/disk/rocksdb_storage.hpp"
#include "storage/v2/disk/storage.hpp" #include "storage/v2/disk/storage.hpp"
#include "storage/v2/disk/unique_constraints.hpp" #include "storage/v2/disk/unique_constraints.hpp"

View File

@@ -27,7 +27,9 @@
#include "storage/v2/durability/paths.hpp" #include "storage/v2/durability/paths.hpp"
#include "storage/v2/durability/snapshot.hpp" #include "storage/v2/durability/snapshot.hpp"
#include "storage/v2/durability/wal.hpp" #include "storage/v2/durability/wal.hpp"
#include "storage/v2/indices/label_property_index.hpp" #include "storage/v2/inmemory/label_index.hpp"
#include "storage/v2/inmemory/label_property_index.hpp"
#include "storage/v2/inmemory/unique_constraints.hpp"
#include "utils/event_histogram.hpp" #include "utils/event_histogram.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/memory_tracker.hpp" #include "utils/memory_tracker.hpp"

View File

@@ -1377,7 +1377,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
bool is_visible = true; bool is_visible = true;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(edge.lock); auto guard = std::shared_lock{edge.lock};
is_visible = !edge.deleted; is_visible = !edge.deleted;
delta = edge.delta; delta = edge.delta;
} }

View File

@@ -0,0 +1,28 @@
// Copyright 2023 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
namespace memgraph::storage::durability {
/// Enum used to indicate a global database operation that isn't transactional.
enum class StorageGlobalOperation {
LABEL_INDEX_CREATE,
LABEL_INDEX_DROP,
LABEL_PROPERTY_INDEX_CREATE,
LABEL_PROPERTY_INDEX_DROP,
EXISTENCE_CONSTRAINT_CREATE,
EXISTENCE_CONSTRAINT_DROP,
UNIQUE_CONSTRAINT_CREATE,
UNIQUE_CONSTRAINT_DROP,
};
} // namespace memgraph::storage::durability

View File

@@ -490,7 +490,7 @@ void EncodeDelta(BaseEncoder *encoder, NameIdMapper *name_id_mapper, Config::Ite
// actions. // actions.
encoder->WriteMarker(Marker::SECTION_DELTA); encoder->WriteMarker(Marker::SECTION_DELTA);
encoder->WriteUint(timestamp); encoder->WriteUint(timestamp);
std::lock_guard<utils::SpinLock> guard(vertex.lock); auto guard = std::shared_lock{vertex.lock};
switch (delta.action) { switch (delta.action) {
case Delta::Action::DELETE_DESERIALIZED_OBJECT: case Delta::Action::DELETE_DESERIALIZED_OBJECT:
case Delta::Action::DELETE_OBJECT: case Delta::Action::DELETE_OBJECT:
@@ -546,7 +546,7 @@ void EncodeDelta(BaseEncoder *encoder, NameIdMapper *name_id_mapper, const Delta
// actions. // actions.
encoder->WriteMarker(Marker::SECTION_DELTA); encoder->WriteMarker(Marker::SECTION_DELTA);
encoder->WriteUint(timestamp); encoder->WriteUint(timestamp);
std::lock_guard<utils::SpinLock> guard(edge.lock); auto guard = std::shared_lock{edge.lock};
switch (delta.action) { switch (delta.action) {
case Delta::Action::SET_PROPERTY: { case Delta::Action::SET_PROPERTY: {
encoder->WriteMarker(Marker::DELTA_EDGE_SET_PROPERTY); encoder->WriteMarker(Marker::DELTA_EDGE_SET_PROPERTY);

View File

@@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd. // Copyright 2023 Memgraph Ltd.
// //
// Use of this software is governed by the Business Source License // 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 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,6 +20,7 @@
#include "storage/v2/delta.hpp" #include "storage/v2/delta.hpp"
#include "storage/v2/durability/metadata.hpp" #include "storage/v2/durability/metadata.hpp"
#include "storage/v2/durability/serialization.hpp" #include "storage/v2/durability/serialization.hpp"
#include "storage/v2/durability/storage_global_operation.hpp"
#include "storage/v2/edge.hpp" #include "storage/v2/edge.hpp"
#include "storage/v2/id_types.hpp" #include "storage/v2/id_types.hpp"
#include "storage/v2/name_id_mapper.hpp" #include "storage/v2/name_id_mapper.hpp"
@@ -107,18 +108,6 @@ struct WalDeltaData {
bool operator==(const WalDeltaData &a, const WalDeltaData &b); bool operator==(const WalDeltaData &a, const WalDeltaData &b);
bool operator!=(const WalDeltaData &a, const WalDeltaData &b); bool operator!=(const WalDeltaData &a, const WalDeltaData &b);
/// Enum used to indicate a global database operation that isn't transactional.
enum class StorageGlobalOperation {
LABEL_INDEX_CREATE,
LABEL_INDEX_DROP,
LABEL_PROPERTY_INDEX_CREATE,
LABEL_PROPERTY_INDEX_DROP,
EXISTENCE_CONSTRAINT_CREATE,
EXISTENCE_CONSTRAINT_DROP,
UNIQUE_CONSTRAINT_CREATE,
UNIQUE_CONSTRAINT_DROP,
};
constexpr bool IsWalDeltaDataTypeTransactionEnd(const WalDeltaData::Type type) { constexpr bool IsWalDeltaDataTypeTransactionEnd(const WalDeltaData::Type type) {
switch (type) { switch (type) {
// These delta actions are all found inside transactions so they don't // These delta actions are all found inside transactions so they don't

View File

@@ -17,7 +17,7 @@
#include "storage/v2/id_types.hpp" #include "storage/v2/id_types.hpp"
#include "storage/v2/property_store.hpp" #include "storage/v2/property_store.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
#include "utils/spin_lock.hpp" #include "utils/rw_spin_lock.hpp"
namespace memgraph::storage { namespace memgraph::storage {
@@ -34,7 +34,7 @@ struct Edge {
PropertyStore properties; PropertyStore properties;
mutable utils::SpinLock lock; mutable utils::RWSpinLock lock;
bool deleted; bool deleted;
// uint8_t PAD; // uint8_t PAD;
// uint16_t PAD; // uint16_t PAD;

View File

@@ -32,7 +32,7 @@ bool EdgeAccessor::IsVisible(const View view) const {
if (!config_.properties_on_edges) { if (!config_.properties_on_edges) {
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(from_vertex_->lock); auto guard = std::shared_lock{from_vertex_->lock};
// Initialize deleted by checking if out edges contain edge_ // Initialize deleted by checking if out edges contain edge_
deleted = std::find_if(from_vertex_->out_edges.begin(), from_vertex_->out_edges.end(), [&](const auto &out_edge) { deleted = std::find_if(from_vertex_->out_edges.begin(), from_vertex_->out_edges.end(), [&](const auto &out_edge) {
return std::get<2>(out_edge) == edge_; return std::get<2>(out_edge) == edge_;
@@ -69,7 +69,7 @@ bool EdgeAccessor::IsVisible(const View view) const {
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::shared_lock{edge_.ptr->lock};
deleted = edge_.ptr->deleted; deleted = edge_.ptr->deleted;
delta = edge_.ptr->delta; delta = edge_.ptr->delta;
} }
@@ -118,7 +118,7 @@ Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, co
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 Error::PROPERTIES_DISABLED;
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::unique_lock{edge_.ptr->lock};
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
@@ -142,7 +142,7 @@ Result<bool> EdgeAccessor::InitProperties(const std::map<storage::PropertyId, st
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 Error::PROPERTIES_DISABLED;
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::unique_lock{edge_.ptr->lock};
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
@@ -161,7 +161,7 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> EdgeAc
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 Error::PROPERTIES_DISABLED;
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::unique_lock{edge_.ptr->lock};
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
@@ -179,7 +179,7 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> EdgeAc
Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() { Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() {
if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED; if (!config_.properties_on_edges) return Error::PROPERTIES_DISABLED;
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::unique_lock{edge_.ptr->lock};
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
@@ -202,7 +202,7 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view)
PropertyValue value; PropertyValue value;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::shared_lock{edge_.ptr->lock};
deleted = edge_.ptr->deleted; deleted = edge_.ptr->deleted;
value = edge_.ptr->properties.GetProperty(property); value = edge_.ptr->properties.GetProperty(property);
delta = edge_.ptr->delta; delta = edge_.ptr->delta;
@@ -245,7 +245,7 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view)
std::map<PropertyId, PropertyValue> properties; std::map<PropertyId, PropertyValue> properties;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(edge_.ptr->lock); auto guard = std::shared_lock{edge_.ptr->lock};
deleted = edge_.ptr->deleted; deleted = edge_.ptr->deleted;
properties = edge_.ptr->properties.Properties(); properties = edge_.ptr->properties.Properties();
delta = edge_.ptr->delta; delta = edge_.ptr->delta;

View File

@@ -10,7 +10,10 @@
// licenses/APL.txt. // licenses/APL.txt.
#include "storage/v2/indices/indices.hpp" #include "storage/v2/indices/indices.hpp"
#include "storage/v2/disk/label_index.hpp"
#include "storage/v2/disk/label_property_index.hpp"
#include "storage/v2/inmemory/label_index.hpp" #include "storage/v2/inmemory/label_index.hpp"
#include "storage/v2/inmemory/label_property_index.hpp"
namespace memgraph::storage { namespace memgraph::storage {
@@ -35,4 +38,16 @@ void Indices::UpdateOnSetProperty(PropertyId property, const PropertyValue &valu
label_property_index_->UpdateOnSetProperty(property, value, vertex, tx); label_property_index_->UpdateOnSetProperty(property, value, vertex, tx);
} }
Indices::Indices(Constraints *constraints, const Config &config, StorageMode storage_mode) {
std::invoke([this, constraints, config, storage_mode]() {
if (storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL || storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
label_index_ = std::make_unique<InMemoryLabelIndex>(this, constraints, config);
label_property_index_ = std::make_unique<InMemoryLabelPropertyIndex>(this, constraints, config);
} else {
label_index_ = std::make_unique<DiskLabelIndex>(this, constraints, config);
label_property_index_ = std::make_unique<DiskLabelPropertyIndex>(this, constraints, config);
}
});
}
} // namespace memgraph::storage } // namespace memgraph::storage

View File

@@ -12,28 +12,14 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include "storage/v2/disk/label_index.hpp"
#include "storage/v2/disk/label_property_index.hpp"
#include "storage/v2/indices/label_index.hpp" #include "storage/v2/indices/label_index.hpp"
#include "storage/v2/indices/label_property_index.hpp" #include "storage/v2/indices/label_property_index.hpp"
#include "storage/v2/inmemory/label_index.hpp"
#include "storage/v2/inmemory/label_property_index.hpp"
#include "storage/v2/storage_mode.hpp" #include "storage/v2/storage_mode.hpp"
namespace memgraph::storage { namespace memgraph::storage {
struct Indices { struct Indices {
Indices(Constraints *constraints, const Config &config, StorageMode storage_mode) { Indices(Constraints *constraints, const Config &config, StorageMode storage_mode);
std::invoke([this, constraints, config, storage_mode]() {
if (storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL || storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
label_index_ = std::make_unique<InMemoryLabelIndex>(this, constraints, config);
label_property_index_ = std::make_unique<InMemoryLabelPropertyIndex>(this, constraints, config);
} else {
label_index_ = std::make_unique<DiskLabelIndex>(this, constraints, config);
label_property_index_ = std::make_unique<DiskLabelPropertyIndex>(this, constraints, config);
}
});
}
Indices(const Indices &) = delete; Indices(const Indices &) = delete;
Indices(Indices &&) = delete; Indices(Indices &&) = delete;

View File

@@ -51,7 +51,7 @@ inline bool AnyVersionHasLabel(const Vertex &vertex, LabelId label, uint64_t tim
bool deleted{false}; bool deleted{false};
const Delta *delta = nullptr; const Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex.lock); auto guard = std::shared_lock{vertex.lock};
has_label = utils::Contains(vertex.labels, label); has_label = utils::Contains(vertex.labels, label);
deleted = vertex.deleted; deleted = vertex.deleted;
delta = vertex.delta; delta = vertex.delta;
@@ -105,7 +105,7 @@ inline bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, Prop
bool deleted{false}; bool deleted{false};
const Delta *delta = nullptr; const Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex.lock); auto guard = std::shared_lock{vertex.lock};
has_label = utils::Contains(vertex.labels, label); has_label = utils::Contains(vertex.labels, label);
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value); current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
deleted = vertex.deleted; deleted = vertex.deleted;
@@ -168,7 +168,7 @@ inline bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label,
bool current_value_equal_to_value = value.IsNull(); bool current_value_equal_to_value = value.IsNull();
const Delta *delta = nullptr; const Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex.lock); auto guard = std::shared_lock{vertex.lock};
deleted = vertex.deleted; deleted = vertex.deleted;
has_label = utils::Contains(vertex.labels, label); has_label = utils::Contains(vertex.labels, label);
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value); current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);

View File

@@ -10,7 +10,7 @@
// licenses/APL.txt. // licenses/APL.txt.
#include "storage/v2/inmemory/label_index.hpp" #include "storage/v2/inmemory/label_index.hpp"
#include "storage/v2/inmemory/indices_utils.hpp" #include "storage/v2/indices/indices_utils.hpp"
namespace memgraph::storage { namespace memgraph::storage {

View File

@@ -10,7 +10,7 @@
// licenses/APL.txt. // licenses/APL.txt.
#include "storage/v2/inmemory/label_property_index.hpp" #include "storage/v2/inmemory/label_property_index.hpp"
#include "storage/v2/inmemory/indices_utils.hpp" #include "storage/v2/indices/indices_utils.hpp"
namespace memgraph::storage { namespace memgraph::storage {

View File

@@ -12,6 +12,7 @@
#include "storage/v2/inmemory/replication/replication_client.hpp" #include "storage/v2/inmemory/replication/replication_client.hpp"
#include "storage/v2/durability/durability.hpp" #include "storage/v2/durability/durability.hpp"
#include "storage/v2/inmemory/storage.hpp"
namespace memgraph::storage { namespace memgraph::storage {
@@ -67,212 +68,50 @@ void CurrentWalHandler::AppendBufferData(const uint8_t *buffer, const size_t buf
replication::CurrentWalRes CurrentWalHandler::Finalize() { return stream_.AwaitResponse(); } replication::CurrentWalRes CurrentWalHandler::Finalize() { return stream_.AwaitResponse(); }
////// ReplicationClient Helpers //////
replication::WalFilesRes TransferWalFiles(rpc::Client &client, const std::vector<std::filesystem::path> &wal_files) {
MG_ASSERT(!wal_files.empty(), "Wal files list is empty!");
auto stream = client.Stream<replication::WalFilesRpc>(wal_files.size());
replication::Encoder encoder(stream.GetBuilder());
for (const auto &wal : wal_files) {
spdlog::debug("Sending wal file: {}", wal);
encoder.WriteFile(wal);
}
return stream.AwaitResponse();
}
replication::SnapshotRes TransferSnapshot(rpc::Client &client, const std::filesystem::path &path) {
auto stream = client.Stream<replication::SnapshotRpc>();
replication::Encoder encoder(stream.GetBuilder());
encoder.WriteFile(path);
return stream.AwaitResponse();
}
uint64_t ReplicateCurrentWal(CurrentWalHandler &stream, durability::WalFile const &wal_file) {
stream.AppendFilename(wal_file.Path().filename());
utils::InputFile file;
MG_ASSERT(file.Open(wal_file.Path()), "Failed to open current WAL file!");
const auto [buffer, buffer_size] = wal_file.CurrentFileBuffer();
stream.AppendSize(file.GetSize() + buffer_size);
stream.AppendFileData(&file);
stream.AppendBufferData(buffer, buffer_size);
auto response = stream.Finalize();
return response.current_commit_timestamp;
}
////// ReplicationClient ////// ////// ReplicationClient //////
InMemoryReplicationClient::InMemoryReplicationClient(InMemoryStorage *storage, std::string name, InMemoryReplicationClient::InMemoryReplicationClient(InMemoryStorage *storage, std::string name,
io::network::Endpoint endpoint, replication::ReplicationMode mode, io::network::Endpoint endpoint, replication::ReplicationMode mode,
const replication::ReplicationClientConfig &config) const replication::ReplicationClientConfig &config)
: ReplicationClient{std::move(name), std::move(endpoint), mode, config}, storage_{storage} {} : ReplicationClient{storage, std::move(name), std::move(endpoint), mode, config} {}
void InMemoryReplicationClient::TryInitializeClientAsync() {
thread_pool_.AddTask([this] {
rpc_client_.Abort();
this->TryInitializeClientSync();
});
}
void InMemoryReplicationClient::FrequentCheck() {
const auto is_success = std::invoke([this]() {
try {
auto stream{rpc_client_.Stream<replication::FrequentHeartbeatRpc>()};
const auto response = stream.AwaitResponse();
return response.success;
} catch (const rpc::RpcFailedException &) {
return false;
}
});
// States: READY, REPLICATING, RECOVERY, INVALID
// If success && ready, replicating, recovery -> stay the same because something good is going on.
// If success && INVALID -> [it's possible that replica came back to life] -> TryInitializeClient.
// If fail -> [replica is not reachable at all] -> INVALID state.
// NOTE: TryInitializeClient might return nothing if there is a branching point.
// NOTE: The early return pattern simplified the code, but the behavior should be as explained.
if (!is_success) {
replica_state_.store(replication::ReplicaState::INVALID);
return;
}
if (replica_state_.load() == replication::ReplicaState::INVALID) {
TryInitializeClientAsync();
}
} /// @throws rpc::RpcFailedException
void InMemoryReplicationClient::Start() {
auto const &endpoint = rpc_client_.Endpoint();
spdlog::trace("Replication client started at: {}:{}", endpoint.address, endpoint.port);
TryInitializeClientSync();
// Help the user to get the most accurate replica state possible.
if (replica_check_frequency_ > std::chrono::seconds(0)) {
replica_checker_.Run("Replica Checker", replica_check_frequency_, [this] { FrequentCheck(); });
}
}
void InMemoryReplicationClient::InitializeClient() {
uint64_t current_commit_timestamp{kTimestampInitialId};
const auto &main_epoch = storage_->replication_state_.GetEpoch();
auto stream{rpc_client_.Stream<replication::HeartbeatRpc>(storage_->replication_state_.last_commit_timestamp_,
main_epoch.id)};
const auto replica = stream.AwaitResponse();
std::optional<uint64_t> branching_point;
if (replica.epoch_id != main_epoch.id && replica.current_commit_timestamp != kTimestampInitialId) {
auto const &history = storage_->replication_state_.history;
const auto epoch_info_iter = std::find_if(history.crbegin(), history.crend(), [&](const auto &main_epoch_info) {
return main_epoch_info.first == replica.epoch_id;
});
if (epoch_info_iter == history.crend()) {
branching_point = 0;
} else if (epoch_info_iter->second != replica.current_commit_timestamp) {
branching_point = epoch_info_iter->second;
}
}
if (branching_point) {
spdlog::error(
"You cannot register Replica {} to this Main because at one point "
"Replica {} acted as the Main instance. Both the Main and Replica {} "
"now hold unique data. Please resolve data conflicts and start the "
"replication on a clean instance.",
name_, name_, name_);
return;
}
current_commit_timestamp = replica.current_commit_timestamp;
spdlog::trace("Current timestamp on replica {}: {}", name_, current_commit_timestamp);
spdlog::trace("Current timestamp on main: {}", storage_->replication_state_.last_commit_timestamp_.load());
if (current_commit_timestamp == storage_->replication_state_.last_commit_timestamp_.load()) {
spdlog::debug("Replica '{}' up to date", name_);
std::unique_lock client_guard{client_lock_};
replica_state_.store(replication::ReplicaState::READY);
} else {
spdlog::debug("Replica '{}' is behind", name_);
{
std::unique_lock client_guard{client_lock_};
replica_state_.store(replication::ReplicaState::RECOVERY);
}
thread_pool_.AddTask([=, this] { this->RecoverReplica(current_commit_timestamp); });
}
}
void InMemoryReplicationClient::TryInitializeClientSync() {
try {
InitializeClient();
} catch (const rpc::RpcFailedException &) {
std::unique_lock client_guarde{client_lock_};
replica_state_.store(replication::ReplicaState::INVALID);
spdlog::error(utils::MessageWithLink("Failed to connect to replica {} at the endpoint {}.", name_,
rpc_client_.Endpoint(), "https://memgr.ph/replication"));
}
}
void InMemoryReplicationClient::HandleRpcFailure() {
spdlog::error(utils::MessageWithLink("Couldn't replicate data to {}.", name_, "https://memgr.ph/replication"));
TryInitializeClientAsync();
}
void InMemoryReplicationClient::StartTransactionReplication(const uint64_t current_wal_seq_num) {
std::unique_lock guard(client_lock_);
const auto status = replica_state_.load();
switch (status) {
case replication::ReplicaState::RECOVERY:
spdlog::debug("Replica {} is behind MAIN instance", name_);
return;
case replication::ReplicaState::REPLICATING:
spdlog::debug("Replica {} missed a transaction", name_);
// We missed a transaction because we're still replicating
// the previous transaction so we need to go to RECOVERY
// state to catch up with the missing transaction
// We cannot queue the recovery process here because
// an error can happen while we're replicating the previous
// transaction after which the client should go to
// INVALID state before starting the recovery process
replica_state_.store(replication::ReplicaState::RECOVERY);
return;
case replication::ReplicaState::INVALID:
HandleRpcFailure();
return;
case replication::ReplicaState::READY:
MG_ASSERT(!replica_stream_);
try {
replica_stream_.emplace(
ReplicaStream{this, storage_->replication_state_.last_commit_timestamp_.load(), current_wal_seq_num});
replica_state_.store(replication::ReplicaState::REPLICATING);
} catch (const rpc::RpcFailedException &) {
replica_state_.store(replication::ReplicaState::INVALID);
HandleRpcFailure();
}
return;
}
}
void InMemoryReplicationClient::IfStreamingTransaction(const std::function<void(ReplicaStream &)> &callback) {
// We can only check the state because it guarantees to be only
// valid during a single transaction replication (if the assumption
// that this and other transaction replication functions can only be
// called from a one thread stands)
if (replica_state_ != replication::ReplicaState::REPLICATING) {
return;
}
try {
callback(*replica_stream_);
} catch (const rpc::RpcFailedException &) {
{
std::unique_lock client_guard{client_lock_};
replica_state_.store(replication::ReplicaState::INVALID);
}
HandleRpcFailure();
}
}
bool InMemoryReplicationClient::FinalizeTransactionReplication() {
// We can only check the state because it guarantees to be only
// valid during a single transaction replication (if the assumption
// that this and other transaction replication functions can only be
// called from a one thread stands)
if (replica_state_ != replication::ReplicaState::REPLICATING) {
return false;
}
if (mode_ == replication::ReplicationMode::ASYNC) {
thread_pool_.AddTask([this] { static_cast<void>(this->FinalizeTransactionReplicationInternal()); });
return true;
}
return FinalizeTransactionReplicationInternal();
}
bool InMemoryReplicationClient::FinalizeTransactionReplicationInternal() {
MG_ASSERT(replica_stream_, "Missing stream for transaction deltas");
try {
auto response = replica_stream_->Finalize();
replica_stream_.reset();
std::unique_lock client_guard(client_lock_);
if (!response.success || replica_state_ == replication::ReplicaState::RECOVERY) {
replica_state_.store(replication::ReplicaState::RECOVERY);
thread_pool_.AddTask([&, this] { this->RecoverReplica(response.current_commit_timestamp); });
} else {
replica_state_.store(replication::ReplicaState::READY);
return true;
}
} catch (const rpc::RpcFailedException &) {
replica_stream_.reset();
{
std::unique_lock client_guard(client_lock_);
replica_state_.store(replication::ReplicaState::INVALID);
}
HandleRpcFailure();
}
return false;
}
void InMemoryReplicationClient::RecoverReplica(uint64_t replica_commit) { void InMemoryReplicationClient::RecoverReplica(uint64_t replica_commit) {
spdlog::debug("Starting replica recover"); spdlog::debug("Starting replica recover");
auto *storage = static_cast<InMemoryStorage *>(storage_);
while (true) { while (true) {
auto file_locker = storage_->file_retainer_.AddLocker(); auto file_locker = storage->file_retainer_.AddLocker();
const auto steps = GetRecoverySteps(replica_commit, &file_locker); const auto steps = GetRecoverySteps(replica_commit, &file_locker);
int i = 0; int i = 0;
@@ -284,21 +123,22 @@ void InMemoryReplicationClient::RecoverReplica(uint64_t replica_commit) {
using StepType = std::remove_cvref_t<T>; using StepType = std::remove_cvref_t<T>;
if constexpr (std::is_same_v<StepType, RecoverySnapshot>) { if constexpr (std::is_same_v<StepType, RecoverySnapshot>) {
spdlog::debug("Sending the latest snapshot file: {}", arg); spdlog::debug("Sending the latest snapshot file: {}", arg);
auto response = TransferSnapshot(arg); auto response = TransferSnapshot(rpc_client_, arg);
replica_commit = response.current_commit_timestamp; replica_commit = response.current_commit_timestamp;
} else if constexpr (std::is_same_v<StepType, RecoveryWals>) { } else if constexpr (std::is_same_v<StepType, RecoveryWals>) {
spdlog::debug("Sending the latest wal files"); spdlog::debug("Sending the latest wal files");
auto response = TransferWalFiles(arg); auto response = TransferWalFiles(rpc_client_, arg);
replica_commit = response.current_commit_timestamp; replica_commit = response.current_commit_timestamp;
spdlog::debug("Wal files successfully transferred."); spdlog::debug("Wal files successfully transferred.");
} else if constexpr (std::is_same_v<StepType, RecoveryCurrentWal>) { } else if constexpr (std::is_same_v<StepType, RecoveryCurrentWal>) {
std::unique_lock transaction_guard(storage_->engine_lock_); std::unique_lock transaction_guard(storage->engine_lock_);
if (storage_->wal_file_ && storage_->wal_file_->SequenceNumber() == arg.current_wal_seq_num) { if (storage->wal_file_ && storage->wal_file_->SequenceNumber() == arg.current_wal_seq_num) {
storage_->wal_file_->DisableFlushing(); storage->wal_file_->DisableFlushing();
transaction_guard.unlock(); transaction_guard.unlock();
spdlog::debug("Sending current wal file"); spdlog::debug("Sending current wal file");
replica_commit = ReplicateCurrentWal(); auto streamHandler = CurrentWalHandler{this};
storage_->wal_file_->EnableFlushing(); replica_commit = ReplicateCurrentWal(streamHandler, *storage->wal_file_);
storage->wal_file_->EnableFlushing();
} else { } else {
spdlog::debug("Cannot recover using current wal file"); spdlog::debug("Cannot recover using current wal file");
} }
@@ -328,27 +168,17 @@ void InMemoryReplicationClient::RecoverReplica(uint64_t replica_commit) {
// and we will go to recovery. // and we will go to recovery.
// By adding this lock, we can avoid that, and go to RECOVERY immediately. // By adding this lock, we can avoid that, and go to RECOVERY immediately.
std::unique_lock client_guard{client_lock_}; std::unique_lock client_guard{client_lock_};
const auto last_commit_timestamp = LastCommitTimestamp();
SPDLOG_INFO("Replica timestamp: {}", replica_commit); SPDLOG_INFO("Replica timestamp: {}", replica_commit);
SPDLOG_INFO("Last commit: {}", storage_->replication_state_.last_commit_timestamp_); SPDLOG_INFO("Last commit: {}", last_commit_timestamp);
if (storage_->replication_state_.last_commit_timestamp_.load() == replica_commit) { if (last_commit_timestamp == replica_commit) {
replica_state_.store(replication::ReplicaState::READY); replica_state_.store(replication::ReplicaState::READY);
return; return;
} }
} }
} }
uint64_t InMemoryReplicationClient::ReplicateCurrentWal() {
const auto &wal_file = storage_->wal_file_; /// This method tries to find the optimal path for recoverying a single replica.
auto stream = CurrentWalHandler{this};
stream.AppendFilename(wal_file->Path().filename());
utils::InputFile file;
MG_ASSERT(file.Open(storage_->wal_file_->Path()), "Failed to open current WAL file!");
const auto [buffer, buffer_size] = wal_file->CurrentFileBuffer();
stream.AppendSize(file.GetSize() + buffer_size);
stream.AppendFileData(&file);
stream.AppendBufferData(buffer, buffer_size);
auto response = stream.Finalize();
return response.current_commit_timestamp;
} /// This method tries to find the optimal path for recoverying a single replica.
/// Based on the last commit transfered to replica it tries to update the /// Based on the last commit transfered to replica it tries to update the
/// replica using durability files - WALs and Snapshots. WAL files are much /// replica using durability files - WALs and Snapshots. WAL files are much
/// smaller in size as they contain only the Deltas (changes) made during the /// smaller in size as they contain only the Deltas (changes) made during the
@@ -375,16 +205,17 @@ std::vector<InMemoryReplicationClient::RecoveryStep> InMemoryReplicationClient::
// This lock is also necessary to force the missed transaction to finish. // This lock is also necessary to force the missed transaction to finish.
std::optional<uint64_t> current_wal_seq_num; std::optional<uint64_t> current_wal_seq_num;
std::optional<uint64_t> current_wal_from_timestamp; std::optional<uint64_t> current_wal_from_timestamp;
if (std::unique_lock transtacion_guard(storage_->engine_lock_); storage_->wal_file_) { auto *storage = static_cast<InMemoryStorage *>(storage_);
current_wal_seq_num.emplace(storage_->wal_file_->SequenceNumber()); if (std::unique_lock transtacion_guard(storage->engine_lock_); storage->wal_file_) {
current_wal_from_timestamp.emplace(storage_->wal_file_->FromTimestamp()); current_wal_seq_num.emplace(storage->wal_file_->SequenceNumber());
current_wal_from_timestamp.emplace(storage->wal_file_->FromTimestamp());
} }
auto locker_acc = file_locker->Access(); auto locker_acc = file_locker->Access();
auto wal_files = durability::GetWalFiles(storage_->wal_directory_, storage_->uuid_, current_wal_seq_num); auto wal_files = durability::GetWalFiles(storage->wal_directory_, storage->uuid_, current_wal_seq_num);
MG_ASSERT(wal_files, "Wal files could not be loaded"); MG_ASSERT(wal_files, "Wal files could not be loaded");
auto snapshot_files = durability::GetSnapshotFiles(storage_->snapshot_directory_, storage_->uuid_); auto snapshot_files = durability::GetSnapshotFiles(storage->snapshot_directory_, storage->uuid_);
std::optional<durability::SnapshotDurabilityInfo> latest_snapshot; std::optional<durability::SnapshotDurabilityInfo> latest_snapshot;
if (!snapshot_files.empty()) { if (!snapshot_files.empty()) {
std::sort(snapshot_files.begin(), snapshot_files.end()); std::sort(snapshot_files.begin(), snapshot_files.end());
@@ -513,54 +344,5 @@ std::vector<InMemoryReplicationClient::RecoveryStep> InMemoryReplicationClient::
return recovery_steps; return recovery_steps;
} }
TimestampInfo InMemoryReplicationClient::GetTimestampInfo() {
TimestampInfo info;
info.current_timestamp_of_replica = 0;
info.current_number_of_timestamp_behind_master = 0;
try {
auto stream{rpc_client_.Stream<replication::TimestampRpc>()};
const auto response = stream.AwaitResponse();
const auto is_success = response.success;
if (!is_success) {
replica_state_.store(replication::ReplicaState::INVALID);
HandleRpcFailure();
}
auto main_time_stamp = storage_->replication_state_.last_commit_timestamp_.load();
info.current_timestamp_of_replica = response.current_commit_timestamp;
info.current_number_of_timestamp_behind_master = response.current_commit_timestamp - main_time_stamp;
} catch (const rpc::RpcFailedException &) {
{
std::unique_lock client_guard(client_lock_);
replica_state_.store(replication::ReplicaState::INVALID);
}
HandleRpcFailure(); // mutex already unlocked, if the new enqueued task dispatches immediately it probably won't
// block
}
return info;
}
std::string const &InMemoryReplicationClient::GetEpochId() const { return storage_->replication_state_.GetEpoch().id; }
auto InMemoryReplicationClient::GetStorage() -> Storage * { return storage_; }
replication::SnapshotRes InMemoryReplicationClient::TransferSnapshot(const std::filesystem::path &path) {
auto stream{rpc_client_.Stream<replication::SnapshotRpc>()};
replication::Encoder encoder(stream.GetBuilder());
encoder.WriteFile(path);
return stream.AwaitResponse();
}
replication::WalFilesRes InMemoryReplicationClient::TransferWalFiles(
const std::vector<std::filesystem::path> &wal_files) {
MG_ASSERT(!wal_files.empty(), "Wal files list is empty!");
auto stream{rpc_client_.Stream<replication::WalFilesRpc>(wal_files.size())};
replication::Encoder encoder(stream.GetBuilder());
for (const auto &wal : wal_files) {
spdlog::debug("Sending wal file: {}", wal);
encoder.WriteFile(wal);
}
return stream.AwaitResponse();
}
} // namespace memgraph::storage } // namespace memgraph::storage

View File

@@ -10,42 +10,21 @@
// licenses/APL.txt. // licenses/APL.txt.
#pragma once #pragma once
#include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/replication/replication_client.hpp" #include "storage/v2/replication/replication_client.hpp"
namespace memgraph::storage { namespace memgraph::storage {
class InMemoryStorage;
class InMemoryReplicationClient : public ReplicationClient { class InMemoryReplicationClient : public ReplicationClient {
public: public:
InMemoryReplicationClient(InMemoryStorage *storage, std::string name, io::network::Endpoint endpoint, InMemoryReplicationClient(InMemoryStorage *storage, std::string name, io::network::Endpoint endpoint,
replication::ReplicationMode mode, const replication::ReplicationClientConfig &config = {}); replication::ReplicationMode mode, const replication::ReplicationClientConfig &config = {});
void StartTransactionReplication(uint64_t current_wal_seq_num) override;
// Replication clients can be removed at any point
// so to avoid any complexity of checking if the client was removed whenever
// we want to send part of transaction and to avoid adding some GC logic this
// function will run a callback if, after previously callling
// StartTransactionReplication, stream is created.
void IfStreamingTransaction(const std::function<void(ReplicaStream &)> &callback) override;
auto GetEpochId() const -> std::string const & override; protected:
void RecoverReplica(uint64_t replica_commit) override;
auto GetStorage() -> Storage * override;
void Start() override;
// Return whether the transaction could be finalized on the replication client or not.
[[nodiscard]] bool FinalizeTransactionReplication() override;
TimestampInfo GetTimestampInfo() override;
private:
void TryInitializeClientAsync();
void FrequentCheck();
void InitializeClient();
void TryInitializeClientSync();
void HandleRpcFailure();
[[nodiscard]] bool FinalizeTransactionReplicationInternal();
void RecoverReplica(uint64_t replica_commit);
uint64_t ReplicateCurrentWal();
// TODO: move the GetRecoverySteps stuff below as an internal detail
using RecoverySnapshot = std::filesystem::path; using RecoverySnapshot = std::filesystem::path;
using RecoveryWals = std::vector<std::filesystem::path>; using RecoveryWals = std::vector<std::filesystem::path>;
struct RecoveryCurrentWal { struct RecoveryCurrentWal {
@@ -54,15 +33,6 @@ class InMemoryReplicationClient : public ReplicationClient {
}; };
using RecoveryStep = std::variant<RecoverySnapshot, RecoveryWals, RecoveryCurrentWal>; using RecoveryStep = std::variant<RecoverySnapshot, RecoveryWals, RecoveryCurrentWal>;
std::vector<RecoveryStep> GetRecoverySteps(uint64_t replica_commit, utils::FileRetainer::FileLocker *file_locker); std::vector<RecoveryStep> GetRecoverySteps(uint64_t replica_commit, utils::FileRetainer::FileLocker *file_locker);
// Transfer the snapshot file.
// @param path Path of the snapshot file.
replication::SnapshotRes TransferSnapshot(const std::filesystem::path &path);
// Transfer the WAL files
replication::WalFilesRes TransferWalFiles(const std::vector<std::filesystem::path> &wal_files);
InMemoryStorage *storage_;
}; };
} // namespace memgraph::storage } // namespace memgraph::storage

View File

@@ -14,6 +14,7 @@
#include "storage/v2/durability/snapshot.hpp" #include "storage/v2/durability/snapshot.hpp"
#include "storage/v2/durability/version.hpp" #include "storage/v2/durability/version.hpp"
#include "storage/v2/inmemory/storage.hpp" #include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/inmemory/unique_constraints.hpp"
namespace memgraph::storage { namespace memgraph::storage {
namespace { namespace {
@@ -130,7 +131,7 @@ void InMemoryReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::Bu
MG_ASSERT(maybe_snapshot_path, "Failed to load snapshot!"); MG_ASSERT(maybe_snapshot_path, "Failed to load snapshot!");
spdlog::info("Received snapshot saved to {}", *maybe_snapshot_path); spdlog::info("Received snapshot saved to {}", *maybe_snapshot_path);
std::unique_lock<utils::RWLock> storage_guard(storage_->main_lock_); auto storage_guard = std::unique_lock{storage_->main_lock_};
spdlog::trace("Clearing database since recovering from snapshot."); spdlog::trace("Clearing database since recovering from snapshot.");
// Clear the database // Clear the database
storage_->vertices_.clear(); storage_->vertices_.clear();
@@ -415,7 +416,7 @@ uint64_t InMemoryReplicationServer::ReadAndApplyDelta(InMemoryStorage *storage,
bool is_visible = true; bool is_visible = true;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(edge->lock); auto guard = std::shared_lock{edge->lock};
is_visible = !edge->deleted; is_visible = !edge->deleted;
delta = edge->delta; delta = edge->delta;
} }

View File

@@ -10,19 +10,13 @@
// licenses/APL.txt. // licenses/APL.txt.
#include "storage/v2/inmemory/storage.hpp" #include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/constraints/constraints.hpp"
#include "storage/v2/durability/durability.hpp" #include "storage/v2/durability/durability.hpp"
#include "storage/v2/durability/snapshot.hpp" #include "storage/v2/durability/snapshot.hpp"
#include "storage/v2/durability/wal.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/edge_direction.hpp"
#include "storage/v2/storage_mode.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "utils/stat.hpp"
/// REPLICATION /// /// REPLICATION ///
#include "storage/v2/inmemory/replication/replication_client.hpp" #include "storage/v2/inmemory/replication/replication_client.hpp"
#include "storage/v2/inmemory/replication/replication_server.hpp" #include "storage/v2/inmemory/replication/replication_server.hpp"
#include "storage/v2/inmemory/unique_constraints.hpp"
namespace memgraph::storage { namespace memgraph::storage {
@@ -34,9 +28,7 @@ InMemoryStorage::InMemoryStorage(Config config)
lock_file_path_(config.durability.storage_directory / durability::kLockFile), lock_file_path_(config.durability.storage_directory / durability::kLockFile),
wal_directory_(config.durability.storage_directory / durability::kWalDirectory), wal_directory_(config.durability.storage_directory / durability::kWalDirectory),
uuid_(utils::GenerateUUID()), uuid_(utils::GenerateUUID()),
global_locker_(file_retainer_.AddLocker()), global_locker_(file_retainer_.AddLocker()) {
replication_state_(config_.durability.restore_replication_state_on_startup,
config_.durability.storage_directory) {
if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED || if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED ||
config_.durability.snapshot_on_exit || config_.durability.recover_on_startup) { config_.durability.snapshot_on_exit || config_.durability.recover_on_startup) {
// Create the directory initially to crash the database in case of // Create the directory initially to crash the database in case of
@@ -256,7 +248,7 @@ Result<std::optional<VertexAccessor>> InMemoryStorage::InMemoryAccessor::DeleteV
"accessor when deleting a vertex!"); "accessor when deleting a vertex!");
auto *vertex_ptr = vertex->vertex_; auto *vertex_ptr = vertex->vertex_;
std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock); auto guard = std::unique_lock{vertex_ptr->lock};
if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
@@ -294,7 +286,7 @@ InMemoryStorage::InMemoryAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges; std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock); auto guard = std::unique_lock{vertex_ptr->lock};
if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
@@ -334,7 +326,7 @@ InMemoryStorage::InMemoryAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
} }
} }
std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock); auto guard = std::unique_lock{vertex_ptr->lock};
// We need to check again for serialization errors because we unlocked the // We need to check again for serialization errors because we unlocked the
// vertex. Some other transaction could have modified the vertex in the // vertex. Some other transaction could have modified the vertex in the
@@ -374,8 +366,8 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
auto *to_vertex = to->vertex_; auto *to_vertex = to->vertex_;
// Obtain the locks by `gid` order to avoid lock cycles. // Obtain the locks by `gid` order to avoid lock cycles.
std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock); auto guard_from = std::unique_lock{from_vertex->lock, std::defer_lock};
std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock); auto guard_to = std::unique_lock{to_vertex->lock, std::defer_lock};
if (from_vertex->gid < to_vertex->gid) { if (from_vertex->gid < to_vertex->gid) {
guard_from.lock(); guard_from.lock();
guard_to.lock(); guard_to.lock();
@@ -440,8 +432,8 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdgeEx(VertexAcces
auto *to_vertex = to->vertex_; auto *to_vertex = to->vertex_;
// Obtain the locks by `gid` order to avoid lock cycles. // Obtain the locks by `gid` order to avoid lock cycles.
std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock); auto guard_from = std::unique_lock{from_vertex->lock, std::defer_lock};
std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock); auto guard_to = std::unique_lock{to_vertex->lock, std::defer_lock};
if (from_vertex->gid < to_vertex->gid) { if (from_vertex->gid < to_vertex->gid) {
guard_from.lock(); guard_from.lock();
guard_to.lock(); guard_to.lock();
@@ -508,10 +500,10 @@ Result<std::optional<EdgeAccessor>> InMemoryStorage::InMemoryAccessor::DeleteEdg
auto edge_ref = edge->edge_; auto edge_ref = edge->edge_;
auto edge_type = edge->edge_type_; auto edge_type = edge->edge_type_;
std::unique_lock<utils::SpinLock> guard; std::unique_lock<utils::RWSpinLock> guard;
if (config_.properties_on_edges) { if (config_.properties_on_edges) {
auto *edge_ptr = edge_ref.ptr; auto *edge_ptr = edge_ref.ptr;
guard = std::unique_lock<utils::SpinLock>(edge_ptr->lock); guard = std::unique_lock{edge_ptr->lock};
if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
@@ -522,8 +514,8 @@ Result<std::optional<EdgeAccessor>> InMemoryStorage::InMemoryAccessor::DeleteEdg
auto *to_vertex = edge->to_vertex_; auto *to_vertex = edge->to_vertex_;
// Obtain the locks by `gid` order to avoid lock cycles. // Obtain the locks by `gid` order to avoid lock cycles.
std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock); auto guard_from = std::unique_lock{from_vertex->lock, std::defer_lock};
std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock); auto guard_to = std::unique_lock{to_vertex->lock, std::defer_lock};
if (from_vertex->gid < to_vertex->gid) { if (from_vertex->gid < to_vertex->gid) {
guard_from.lock(); guard_from.lock();
guard_to.lock(); guard_to.lock();
@@ -739,7 +731,7 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
switch (prev.type) { switch (prev.type) {
case PreviousPtr::Type::VERTEX: { case PreviousPtr::Type::VERTEX: {
auto *vertex = prev.vertex; auto *vertex = prev.vertex;
std::lock_guard<utils::SpinLock> guard(vertex->lock); auto guard = std::unique_lock{vertex->lock};
Delta *current = vertex->delta; Delta *current = vertex->delta;
while (current != nullptr && current->timestamp->load(std::memory_order_acquire) == while (current != nullptr && current->timestamp->load(std::memory_order_acquire) ==
transaction_.transaction_id.load(std::memory_order_acquire)) { transaction_.transaction_id.load(std::memory_order_acquire)) {
@@ -827,7 +819,7 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
} }
case PreviousPtr::Type::EDGE: { case PreviousPtr::Type::EDGE: {
auto *edge = prev.edge; auto *edge = prev.edge;
std::lock_guard<utils::SpinLock> guard(edge->lock); auto guard = std::lock_guard{edge->lock};
Delta *current = edge->delta; Delta *current = edge->delta;
while (current != nullptr && current->timestamp->load(std::memory_order_acquire) == while (current != nullptr && current->timestamp->load(std::memory_order_acquire) ==
transaction_.transaction_id.load(std::memory_order_acquire)) { transaction_.transaction_id.load(std::memory_order_acquire)) {
@@ -1272,7 +1264,7 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::RWLock> main_guard)
switch (prev.type) { switch (prev.type) {
case PreviousPtr::Type::VERTEX: { case PreviousPtr::Type::VERTEX: {
Vertex *vertex = prev.vertex; Vertex *vertex = prev.vertex;
std::lock_guard<utils::SpinLock> vertex_guard(vertex->lock); auto vertex_guard = std::unique_lock{vertex->lock};
if (vertex->delta != &delta) { if (vertex->delta != &delta) {
// Something changed, we're not the first delta in the chain // Something changed, we're not the first delta in the chain
// anymore. // anymore.
@@ -1286,7 +1278,7 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::RWLock> main_guard)
} }
case PreviousPtr::Type::EDGE: { case PreviousPtr::Type::EDGE: {
Edge *edge = prev.edge; Edge *edge = prev.edge;
std::lock_guard<utils::SpinLock> edge_guard(edge->lock); auto edge_guard = std::unique_lock{edge->lock};
if (edge->delta != &delta) { if (edge->delta != &delta) {
// Something changed, we're not the first delta in the chain // Something changed, we're not the first delta in the chain
// anymore. // anymore.
@@ -1305,7 +1297,7 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::RWLock> main_guard)
// part of the suffix later. // part of the suffix later.
break; break;
} }
std::unique_lock<utils::SpinLock> guard; std::unique_lock<utils::RWSpinLock> guard;
{ {
// We need to find the parent object in order to be able to use // We need to find the parent object in order to be able to use
// its lock. // its lock.
@@ -1315,10 +1307,10 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::RWLock> main_guard)
} }
switch (parent.type) { switch (parent.type) {
case PreviousPtr::Type::VERTEX: case PreviousPtr::Type::VERTEX:
guard = std::unique_lock<utils::SpinLock>(parent.vertex->lock); guard = std::unique_lock{parent.vertex->lock};
break; break;
case PreviousPtr::Type::EDGE: case PreviousPtr::Type::EDGE:
guard = std::unique_lock<utils::SpinLock>(parent.edge->lock); guard = std::unique_lock{parent.edge->lock};
break; break;
case PreviousPtr::Type::DELTA: case PreviousPtr::Type::DELTA:
case PreviousPtr::Type::NULLPTR: case PreviousPtr::Type::NULLPTR:
@@ -1668,8 +1660,8 @@ bool InMemoryStorage::AppendToWalDataDefinition(durability::StorageGlobalOperati
wal_file_->AppendOperation(operation, label, properties, final_commit_timestamp); wal_file_->AppendOperation(operation, label, properties, final_commit_timestamp);
FinalizeWalFile(); FinalizeWalFile();
return replication_state_.AppendToWalDataDefinition(wal_file_->SequenceNumber(), operation, label, properties, return replication_state_.AppendOperation(wal_file_->SequenceNumber(), operation, label, properties,
final_commit_timestamp); final_commit_timestamp);
} }
utils::BasicResult<InMemoryStorage::CreateSnapshotError> InMemoryStorage::CreateSnapshot( utils::BasicResult<InMemoryStorage::CreateSnapshotError> InMemoryStorage::CreateSnapshot(
@@ -1780,7 +1772,7 @@ auto InMemoryStorage::CreateReplicationClient(std::string name, io::network::End
replication::ReplicationMode mode, replication::ReplicationMode mode,
replication::ReplicationClientConfig const &config) replication::ReplicationClientConfig const &config)
-> std::unique_ptr<ReplicationClient> { -> std::unique_ptr<ReplicationClient> {
return std::make_unique<InMemoryReplicationClient>(this, std::move(name), endpoint, mode, config); return std::make_unique<InMemoryReplicationClient>(this, std::move(name), std::move(endpoint), mode, config);
} }
std::unique_ptr<ReplicationServer> InMemoryStorage::CreateReplicationServer( std::unique_ptr<ReplicationServer> InMemoryStorage::CreateReplicationServer(

View File

@@ -17,15 +17,13 @@
#include "storage/v2/storage.hpp" #include "storage/v2/storage.hpp"
/// REPLICATION /// /// REPLICATION ///
#include "rpc/server.hpp"
#include "storage/v2/replication/config.hpp" #include "storage/v2/replication/config.hpp"
#include "storage/v2/replication/enums.hpp" #include "storage/v2/replication/enums.hpp"
#include "storage/v2/replication/replication.hpp"
#include "storage/v2/replication/replication_persistence_helper.hpp" #include "storage/v2/replication/replication_persistence_helper.hpp"
#include "storage/v2/replication/rpc.hpp" #include "storage/v2/replication/rpc.hpp"
#include "storage/v2/replication/serialization.hpp" #include "storage/v2/replication/serialization.hpp"
#include "storage/v2/replication/replication.hpp"
namespace memgraph::storage { namespace memgraph::storage {
// The storage is based on this paper: // The storage is based on this paper:
@@ -36,7 +34,6 @@ namespace memgraph::storage {
class InMemoryStorage final : public Storage { class InMemoryStorage final : public Storage {
friend class InMemoryReplicationServer; friend class InMemoryReplicationServer;
friend class InMemoryReplicationClient; friend class InMemoryReplicationClient;
friend class ReplicationClient;
public: public:
enum class CreateSnapshotError : uint8_t { enum class CreateSnapshotError : uint8_t {
@@ -363,33 +360,6 @@ class InMemoryStorage final : public Storage {
utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus> DropUniqueConstraint( utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus> DropUniqueConstraint(
LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp) override; LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp) override;
bool SetReplicaRole(io::network::Endpoint endpoint, const replication::ReplicationServerConfig &config) {
return replication_state_.SetReplicaRole(std::move(endpoint), config, this);
}
bool SetMainReplicationRole() { return replication_state_.SetMainReplicationRole(this); }
/// @pre The instance should have a MAIN role
/// @pre Timeout can only be set for SYNC replication
auto RegisterReplica(std::string name, io::network::Endpoint endpoint,
const replication::ReplicationMode replication_mode,
const replication::RegistrationMode registration_mode,
const replication::ReplicationClientConfig &config) {
return replication_state_.RegisterReplica(std::move(name), std::move(endpoint), replication_mode, registration_mode,
config, this);
}
/// @pre The instance should have a MAIN role
bool UnregisterReplica(const std::string &name) { return replication_state_.UnregisterReplica(name); }
replication::ReplicationRole GetReplicationRole() const { return replication_state_.GetRole(); }
auto ReplicasInfo() { return replication_state_.ReplicasInfo(); }
std::optional<replication::ReplicaState> GetReplicaState(std::string_view name) {
return replication_state_.GetReplicaState(name);
}
void FreeMemory(std::unique_lock<utils::RWLock> main_guard) override; void FreeMemory(std::unique_lock<utils::RWLock> main_guard) override;
utils::FileRetainer::FileLockerAccessor::ret_type IsPathLocked(); utils::FileRetainer::FileLockerAccessor::ret_type IsPathLocked();
@@ -435,10 +405,6 @@ class InMemoryStorage final : public Storage {
uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {}); uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
void RestoreReplicationRole() { return replication_state_.RestoreReplicationRole(this); }
void RestoreReplicas() { return replication_state_.RestoreReplicas(this); }
void EstablishNewEpoch() override; void EstablishNewEpoch() override;
// Main object storage // Main object storage
@@ -494,8 +460,6 @@ class InMemoryStorage final : public Storage {
// Flags to inform CollectGarbage that it needs to do the more expensive full scans // Flags to inform CollectGarbage that it needs to do the more expensive full scans
std::atomic<bool> gc_full_scan_vertices_delete_ = false; std::atomic<bool> gc_full_scan_vertices_delete_ = false;
std::atomic<bool> gc_full_scan_edges_delete_ = false; std::atomic<bool> gc_full_scan_edges_delete_ = false;
ReplicationState replication_state_;
}; };
} // namespace memgraph::storage } // namespace memgraph::storage

View File

@@ -50,7 +50,7 @@ bool LastCommittedVersionHasLabelProperty(const Vertex &vertex, LabelId label, c
bool deleted; bool deleted;
bool has_label; bool has_label;
{ {
std::lock_guard<utils::SpinLock> guard(vertex.lock); auto guard = std::shared_lock{vertex.lock};
delta = vertex.delta; delta = vertex.delta;
deleted = vertex.deleted; deleted = vertex.deleted;
has_label = utils::Contains(vertex.labels, label); has_label = utils::Contains(vertex.labels, label);
@@ -136,7 +136,7 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
bool deleted; bool deleted;
Delta *delta; Delta *delta;
{ {
std::lock_guard<utils::SpinLock> guard(vertex.lock); auto guard = std::shared_lock{vertex.lock};
has_label = utils::Contains(vertex.labels, label); has_label = utils::Contains(vertex.labels, label);
deleted = vertex.deleted; deleted = vertex.deleted;
delta = vertex.delta; delta = vertex.delta;

View File

@@ -13,22 +13,9 @@
#include "storage/v2/constraints/constraints.hpp" #include "storage/v2/constraints/constraints.hpp"
#include "storage/v2/durability/durability.hpp" #include "storage/v2/durability/durability.hpp"
#include "storage/v2/durability/snapshot.hpp" #include "storage/v2/durability/snapshot.hpp"
#include "storage/v2/durability/wal.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/edge_direction.hpp"
#include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/storage_mode.hpp"
#include "storage/v2/vertex_accessor.hpp"
#include "utils/stat.hpp"
/// REPLICATION ///
#include "storage/v2/replication/replication_client.hpp" #include "storage/v2/replication/replication_client.hpp"
#include "storage/v2/replication/replication_server.hpp" #include "storage/v2/replication/replication_server.hpp"
#include "storage/v2/replication/rpc.hpp" #include "storage/v2/storage.hpp"
#include "storage/v2/storage_error.hpp"
#include "storage/v2/inmemory/replication/replication_client.hpp"
#include "storage/v2/inmemory/replication/replication_server.hpp"
namespace memgraph::storage { namespace memgraph::storage {
@@ -97,10 +84,9 @@ bool storage::ReplicationState::SetMainReplicationRole(storage::Storage *storage
return true; return true;
} }
bool storage::ReplicationState::AppendToWalDataDefinition(const uint64_t seq_num, bool storage::ReplicationState::AppendOperation(const uint64_t seq_num, durability::StorageGlobalOperation operation,
durability::StorageGlobalOperation operation, LabelId label, LabelId label, const std::set<PropertyId> &properties,
const std::set<PropertyId> &properties, uint64_t final_commit_timestamp) {
uint64_t final_commit_timestamp) {
bool finalized_on_all_replicas = true; bool finalized_on_all_replicas = true;
// TODO Should we return true if not MAIN? // TODO Should we return true if not MAIN?
if (GetRole() == replication::ReplicationRole::MAIN) { if (GetRole() == replication::ReplicationRole::MAIN) {
@@ -199,7 +185,7 @@ utils::BasicResult<ReplicationState::RegisterReplicaError> ReplicationState::Reg
} }
} }
auto client = storage->CreateReplicationClient(std::move(name), endpoint, replication_mode, config); auto client = storage->CreateReplicationClient(std::move(name), std::move(endpoint), replication_mode, config);
client->Start(); client->Start();
if (client->State() == replication::ReplicaState::INVALID) { if (client->State() == replication::ReplicaState::INVALID) {

View File

@@ -11,20 +11,18 @@
#pragma once #pragma once
#include "storage/v2/inmemory/label_index.hpp" #include "kvstore/kvstore.hpp"
#include "storage/v2/inmemory/label_property_index.hpp" #include "storage/v2/durability/storage_global_operation.hpp"
#include "storage/v2/storage.hpp" #include "utils/result.hpp"
/// REPLICATION /// /// REPLICATION ///
#include "rpc/server.hpp"
#include "storage/v2/replication/config.hpp" #include "storage/v2/replication/config.hpp"
#include "storage/v2/replication/enums.hpp" #include "storage/v2/replication/enums.hpp"
#include "storage/v2/replication/global.hpp"
#include "storage/v2/replication/replication_persistence_helper.hpp" #include "storage/v2/replication/replication_persistence_helper.hpp"
#include "storage/v2/replication/rpc.hpp" #include "storage/v2/replication/rpc.hpp"
#include "storage/v2/replication/serialization.hpp" #include "storage/v2/replication/serialization.hpp"
#include "storage/v2/replication/global.hpp"
// TODO use replication namespace // TODO use replication namespace
namespace memgraph::storage { namespace memgraph::storage {
@@ -56,8 +54,8 @@ struct ReplicationState {
void RestoreReplicationRole(Storage *storage); void RestoreReplicationRole(Storage *storage);
// MAIN actually doing the replication // MAIN actually doing the replication
bool AppendToWalDataDefinition(uint64_t seq_num, durability::StorageGlobalOperation operation, LabelId label, bool AppendOperation(uint64_t seq_num, durability::StorageGlobalOperation operation, LabelId label,
const std::set<PropertyId> &properties, uint64_t final_commit_timestamp); const std::set<PropertyId> &properties, uint64_t final_commit_timestamp);
void InitializeTransaction(uint64_t seq_num); void InitializeTransaction(uint64_t seq_num);
void AppendDelta(const Delta &delta, const Vertex &parent, uint64_t timestamp); void AppendDelta(const Delta &delta, const Vertex &parent, uint64_t timestamp);
void AppendDelta(const Delta &delta, const Edge &parent, uint64_t timestamp); void AppendDelta(const Delta &delta, const Edge &parent, uint64_t timestamp);

View File

@@ -15,9 +15,9 @@
#include <type_traits> #include <type_traits>
#include "storage/v2/durability/durability.hpp" #include "storage/v2/durability/durability.hpp"
#include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/replication/config.hpp" #include "storage/v2/replication/config.hpp"
#include "storage/v2/replication/enums.hpp" #include "storage/v2/replication/enums.hpp"
#include "storage/v2/storage.hpp"
#include "storage/v2/transaction.hpp" #include "storage/v2/transaction.hpp"
#include "utils/file_locker.hpp" #include "utils/file_locker.hpp"
#include "utils/logging.hpp" #include "utils/logging.hpp"
@@ -30,18 +30,258 @@ static auto CreateClientContext(const replication::ReplicationClientConfig &conf
: communication::ClientContext{}; : communication::ClientContext{};
} }
ReplicationClient::ReplicationClient(std::string name, memgraph::io::network::Endpoint endpoint, ReplicationClient::ReplicationClient(Storage *storage, std::string name, memgraph::io::network::Endpoint endpoint,
replication::ReplicationMode mode, replication::ReplicationMode mode,
replication::ReplicationClientConfig const &config) replication::ReplicationClientConfig const &config)
: name_{std::move(name)}, : name_{std::move(name)},
rpc_context_{CreateClientContext(config)}, rpc_context_{CreateClientContext(config)},
rpc_client_{std::move(endpoint), &rpc_context_}, rpc_client_{std::move(endpoint), &rpc_context_},
replica_check_frequency_{config.replica_check_frequency}, replica_check_frequency_{config.replica_check_frequency},
mode_{mode} {} mode_{mode},
storage_{storage} {}
ReplicationClient::~ReplicationClient() { ReplicationClient::~ReplicationClient() {
auto endpoint = rpc_client_.Endpoint(); auto endpoint = rpc_client_.Endpoint();
spdlog::trace("Closing replication client on {}:{}", endpoint.address, endpoint.port); spdlog::trace("Closing replication client on {}:{}", endpoint.address, endpoint.port);
thread_pool_.Shutdown();
}
uint64_t ReplicationClient::LastCommitTimestamp() const {
return storage_->replication_state_.last_commit_timestamp_.load();
}
void ReplicationClient::InitializeClient() {
uint64_t current_commit_timestamp{kTimestampInitialId};
const auto &main_epoch = storage_->replication_state_.GetEpoch();
auto stream{rpc_client_.Stream<replication::HeartbeatRpc>(storage_->replication_state_.last_commit_timestamp_,
main_epoch.id)};
const auto replica = stream.AwaitResponse();
std::optional<uint64_t> branching_point;
if (replica.epoch_id != main_epoch.id && replica.current_commit_timestamp != kTimestampInitialId) {
auto const &history = storage_->replication_state_.history;
const auto epoch_info_iter = std::find_if(history.crbegin(), history.crend(), [&](const auto &main_epoch_info) {
return main_epoch_info.first == replica.epoch_id;
});
if (epoch_info_iter == history.crend()) {
branching_point = 0;
} else if (epoch_info_iter->second != replica.current_commit_timestamp) {
branching_point = epoch_info_iter->second;
}
}
if (branching_point) {
spdlog::error(
"You cannot register Replica {} to this Main because at one point "
"Replica {} acted as the Main instance. Both the Main and Replica {} "
"now hold unique data. Please resolve data conflicts and start the "
"replication on a clean instance.",
name_, name_, name_);
return;
}
current_commit_timestamp = replica.current_commit_timestamp;
spdlog::trace("Current timestamp on replica {}: {}", name_, current_commit_timestamp);
spdlog::trace("Current timestamp on main: {}", storage_->replication_state_.last_commit_timestamp_.load());
if (current_commit_timestamp == storage_->replication_state_.last_commit_timestamp_.load()) {
spdlog::debug("Replica '{}' up to date", name_);
std::unique_lock client_guard{client_lock_};
replica_state_.store(replication::ReplicaState::READY);
} else {
spdlog::debug("Replica '{}' is behind", name_);
{
std::unique_lock client_guard{client_lock_};
replica_state_.store(replication::ReplicaState::RECOVERY);
}
thread_pool_.AddTask([=, this] { this->RecoverReplica(current_commit_timestamp); });
}
}
TimestampInfo ReplicationClient::GetTimestampInfo() {
TimestampInfo info;
info.current_timestamp_of_replica = 0;
info.current_number_of_timestamp_behind_master = 0;
try {
auto stream{rpc_client_.Stream<replication::TimestampRpc>()};
const auto response = stream.AwaitResponse();
const auto is_success = response.success;
if (!is_success) {
replica_state_.store(replication::ReplicaState::INVALID);
HandleRpcFailure();
}
auto main_time_stamp = storage_->replication_state_.last_commit_timestamp_.load();
info.current_timestamp_of_replica = response.current_commit_timestamp;
info.current_number_of_timestamp_behind_master = response.current_commit_timestamp - main_time_stamp;
} catch (const rpc::RpcFailedException &) {
{
std::unique_lock client_guard(client_lock_);
replica_state_.store(replication::ReplicaState::INVALID);
}
HandleRpcFailure(); // mutex already unlocked, if the new enqueued task dispatches immediately it probably won't
// block
}
return info;
}
void ReplicationClient::HandleRpcFailure() {
spdlog::error(utils::MessageWithLink("Couldn't replicate data to {}.", name_, "https://memgr.ph/replication"));
TryInitializeClientAsync();
}
void ReplicationClient::TryInitializeClientAsync() {
thread_pool_.AddTask([this] {
rpc_client_.Abort();
this->TryInitializeClientSync();
});
}
void ReplicationClient::TryInitializeClientSync() {
try {
InitializeClient();
} catch (const rpc::RpcFailedException &) {
std::unique_lock client_guarde{client_lock_};
replica_state_.store(replication::ReplicaState::INVALID);
spdlog::error(utils::MessageWithLink("Failed to connect to replica {} at the endpoint {}.", name_,
rpc_client_.Endpoint(), "https://memgr.ph/replication"));
}
}
void ReplicationClient::StartTransactionReplication(const uint64_t current_wal_seq_num) {
std::unique_lock guard(client_lock_);
const auto status = replica_state_.load();
switch (status) {
case replication::ReplicaState::RECOVERY:
spdlog::debug("Replica {} is behind MAIN instance", name_);
return;
case replication::ReplicaState::REPLICATING:
spdlog::debug("Replica {} missed a transaction", name_);
// We missed a transaction because we're still replicating
// the previous transaction so we need to go to RECOVERY
// state to catch up with the missing transaction
// We cannot queue the recovery process here because
// an error can happen while we're replicating the previous
// transaction after which the client should go to
// INVALID state before starting the recovery process
replica_state_.store(replication::ReplicaState::RECOVERY);
return;
case replication::ReplicaState::INVALID:
HandleRpcFailure();
return;
case replication::ReplicaState::READY:
MG_ASSERT(!replica_stream_);
try {
replica_stream_.emplace(
ReplicaStream{this, storage_->replication_state_.last_commit_timestamp_.load(), current_wal_seq_num});
replica_state_.store(replication::ReplicaState::REPLICATING);
} catch (const rpc::RpcFailedException &) {
replica_state_.store(replication::ReplicaState::INVALID);
HandleRpcFailure();
}
return;
}
}
auto ReplicationClient::GetEpochId() const -> std::string const & { return storage_->replication_state_.GetEpoch().id; }
bool ReplicationClient::FinalizeTransactionReplication() {
// We can only check the state because it guarantees to be only
// valid during a single transaction replication (if the assumption
// that this and other transaction replication functions can only be
// called from a one thread stands)
if (replica_state_ != replication::ReplicaState::REPLICATING) {
return false;
}
auto task = [this]() {
MG_ASSERT(replica_stream_, "Missing stream for transaction deltas");
try {
auto response = replica_stream_->Finalize();
replica_stream_.reset();
std::unique_lock client_guard(client_lock_);
if (!response.success || replica_state_ == replication::ReplicaState::RECOVERY) {
replica_state_.store(replication::ReplicaState::RECOVERY);
thread_pool_.AddTask([&, this] { this->RecoverReplica(response.current_commit_timestamp); });
} else {
replica_state_.store(replication::ReplicaState::READY);
return true;
}
} catch (const rpc::RpcFailedException &) {
replica_stream_.reset();
{
std::unique_lock client_guard(client_lock_);
replica_state_.store(replication::ReplicaState::INVALID);
}
HandleRpcFailure();
}
return false;
};
if (mode_ == replication::ReplicationMode::ASYNC) {
thread_pool_.AddTask([=] { (void)task(); });
return true;
}
return task();
}
void ReplicationClient::FrequentCheck() {
const auto is_success = std::invoke([this]() {
try {
auto stream{rpc_client_.Stream<replication::FrequentHeartbeatRpc>()};
const auto response = stream.AwaitResponse();
return response.success;
} catch (const rpc::RpcFailedException &) {
return false;
}
});
// States: READY, REPLICATING, RECOVERY, INVALID
// If success && ready, replicating, recovery -> stay the same because something good is going on.
// If success && INVALID -> [it's possible that replica came back to life] -> TryInitializeClient.
// If fail -> [replica is not reachable at all] -> INVALID state.
// NOTE: TryInitializeClient might return nothing if there is a branching point.
// NOTE: The early return pattern simplified the code, but the behavior should be as explained.
if (!is_success) {
replica_state_.store(replication::ReplicaState::INVALID);
return;
}
if (replica_state_.load() == replication::ReplicaState::INVALID) {
TryInitializeClientAsync();
}
}
void ReplicationClient::Start() {
auto const &endpoint = rpc_client_.Endpoint();
spdlog::trace("Replication client started at: {}:{}", endpoint.address, endpoint.port);
TryInitializeClientSync();
// Help the user to get the most accurate replica state possible.
if (replica_check_frequency_ > std::chrono::seconds(0)) {
replica_checker_.Run("Replica Checker", replica_check_frequency_, [this] { this->FrequentCheck(); });
}
}
void ReplicationClient::IfStreamingTransaction(const std::function<void(ReplicaStream &)> &callback) {
// We can only check the state because it guarantees to be only
// valid during a single transaction replication (if the assumption
// that this and other transaction replication functions can only be
// called from a one thread stands)
if (replica_state_ != replication::ReplicaState::REPLICATING) {
return;
}
try {
callback(*replica_stream_);
} catch (const rpc::RpcFailedException &) {
{
std::unique_lock client_guard{client_lock_};
replica_state_.store(replication::ReplicaState::INVALID);
}
HandleRpcFailure();
}
} }
////// ReplicaStream ////// ////// ReplicaStream //////
@@ -50,6 +290,7 @@ ReplicaStream::ReplicaStream(ReplicationClient *self, const uint64_t previous_co
: self_(self), : self_(self),
stream_(self_->rpc_client_.Stream<replication::AppendDeltasRpc>(previous_commit_timestamp, current_seq_num)) { stream_(self_->rpc_client_.Stream<replication::AppendDeltasRpc>(previous_commit_timestamp, current_seq_num)) {
replication::Encoder encoder{stream_.GetBuilder()}; replication::Encoder encoder{stream_.GetBuilder()};
encoder.WriteString(self_->GetEpochId()); encoder.WriteString(self_->GetEpochId());
} }

View File

@@ -12,23 +12,30 @@
#pragma once #pragma once
#include "rpc/client.hpp" #include "rpc/client.hpp"
#include "storage/v2/durability/wal.hpp" #include "storage/v2/durability/storage_global_operation.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/replication/config.hpp" #include "storage/v2/replication/config.hpp"
#include "storage/v2/replication/enums.hpp" #include "storage/v2/replication/enums.hpp"
#include "storage/v2/replication/global.hpp" #include "storage/v2/replication/global.hpp"
#include "storage/v2/replication/rpc.hpp" #include "storage/v2/replication/rpc.hpp"
#include "storage/v2/vertex.hpp" #include "utils/file_locker.hpp"
#include "utils/file.hpp"
#include "utils/scheduler.hpp" #include "utils/scheduler.hpp"
#include "utils/thread_pool.hpp" #include "utils/thread_pool.hpp"
#include <atomic>
#include <optional>
#include <set>
#include <string>
namespace memgraph::storage { namespace memgraph::storage {
struct Delta;
struct Vertex;
struct Edge;
class Storage; class Storage;
class ReplicationClient; class ReplicationClient;
// Handler used for transfering the current transaction. // Handler used for transferring the current transaction.
class ReplicaStream { class ReplicaStream {
public: public:
explicit ReplicaStream(ReplicationClient *self, uint64_t previous_commit_timestamp, uint64_t current_seq_num); explicit ReplicaStream(ReplicationClient *self, uint64_t previous_commit_timestamp, uint64_t current_seq_num);
@@ -59,8 +66,8 @@ class ReplicationClient {
friend class ReplicaStream; friend class ReplicaStream;
public: public:
ReplicationClient(std::string name, memgraph::io::network::Endpoint endpoint, replication::ReplicationMode mode, ReplicationClient(Storage *storage, std::string name, memgraph::io::network::Endpoint endpoint,
const replication::ReplicationClientConfig &config); replication::ReplicationMode mode, const replication::ReplicationClientConfig &config);
ReplicationClient(ReplicationClient const &) = delete; ReplicationClient(ReplicationClient const &) = delete;
ReplicationClient &operator=(ReplicationClient const &) = delete; ReplicationClient &operator=(ReplicationClient const &) = delete;
@@ -69,24 +76,35 @@ class ReplicationClient {
virtual ~ReplicationClient(); virtual ~ReplicationClient();
virtual void Start() = 0; auto Mode() const -> replication::ReplicationMode { return mode_; }
auto Name() const -> std::string const & { return name_; }
const auto &Name() const { return name_; }
auto State() const { return replica_state_.load(); }
auto Mode() const { return mode_; }
auto Endpoint() const -> io::network::Endpoint const & { return rpc_client_.Endpoint(); } auto Endpoint() const -> io::network::Endpoint const & { return rpc_client_.Endpoint(); }
auto State() const -> replication::ReplicaState { return replica_state_.load(); }
auto GetTimestampInfo() -> TimestampInfo;
virtual void StartTransactionReplication(uint64_t current_wal_seq_num) = 0; void Start();
virtual void IfStreamingTransaction(const std::function<void(ReplicaStream &)> &callback) = 0; void StartTransactionReplication(const uint64_t current_wal_seq_num);
virtual auto GetEpochId() const -> std::string const & = 0; // TODO: make non-virtual once epoch is moved to storage // Replication clients can be removed at any point
virtual auto GetStorage() -> Storage * = 0; // so to avoid any complexity of checking if the client was removed whenever
[[nodiscard]] virtual bool FinalizeTransactionReplication() = 0; // we want to send part of transaction and to avoid adding some GC logic this
virtual TimestampInfo GetTimestampInfo() = 0; // function will run a callback if, after previously callling
// StartTransactionReplication, stream is created.
void IfStreamingTransaction(const std::function<void(ReplicaStream &)> &callback);
// Return whether the transaction could be finalized on the replication client or not.
[[nodiscard]] bool FinalizeTransactionReplication();
protected: protected:
virtual void RecoverReplica(uint64_t replica_commit) = 0;
auto GetStorage() -> Storage * { return storage_; }
auto GetEpochId() const -> std::string const &;
auto LastCommitTimestamp() const -> uint64_t;
void InitializeClient();
void HandleRpcFailure();
void TryInitializeClientAsync();
void TryInitializeClientSync();
void FrequentCheck();
std::string name_; std::string name_;
communication::ClientContext rpc_context_; communication::ClientContext rpc_context_;
rpc::Client rpc_client_; rpc::Client rpc_client_;
@@ -113,6 +131,7 @@ class ReplicationClient {
std::atomic<replication::ReplicaState> replica_state_{replication::ReplicaState::INVALID}; std::atomic<replication::ReplicaState> replica_state_{replication::ReplicaState::INVALID};
utils::Scheduler replica_checker_; utils::Scheduler replica_checker_;
Storage *storage_;
}; };
} // namespace memgraph::storage } // namespace memgraph::storage

View File

@@ -51,7 +51,9 @@ Storage::Storage(Config config, StorageMode storage_mode)
storage_mode_(storage_mode), storage_mode_(storage_mode),
indices_(&constraints_, config, storage_mode), indices_(&constraints_, config, storage_mode),
constraints_(config, storage_mode), constraints_(config, storage_mode),
id_(config.name) {} id_(config.name),
replication_state_(config_.durability.restore_replication_state_on_startup,
config_.durability.storage_directory) {}
Storage::Accessor::Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode) Storage::Accessor::Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode)
: storage_(storage), : storage_(storage),

View File

@@ -26,6 +26,7 @@
#include "storage/v2/mvcc.hpp" #include "storage/v2/mvcc.hpp"
#include "storage/v2/replication/config.hpp" #include "storage/v2/replication/config.hpp"
#include "storage/v2/replication/enums.hpp" #include "storage/v2/replication/enums.hpp"
#include "storage/v2/replication/replication.hpp"
#include "storage/v2/replication/replication_client.hpp" #include "storage/v2/replication/replication_client.hpp"
#include "storage/v2/replication/replication_server.hpp" #include "storage/v2/replication/replication_server.hpp"
#include "storage/v2/storage_error.hpp" #include "storage/v2/storage_error.hpp"
@@ -68,6 +69,9 @@ struct StorageInfo {
}; };
class Storage { class Storage {
friend class ReplicationServer;
friend class ReplicationClient;
public: public:
Storage(Config config, StorageMode storage_mode); Storage(Config config, StorageMode storage_mode);
@@ -293,6 +297,34 @@ class Storage {
replication::ReplicationServerConfig const &config) replication::ReplicationServerConfig const &config)
-> std::unique_ptr<ReplicationServer> = 0; -> std::unique_ptr<ReplicationServer> = 0;
/// REPLICATION
bool SetReplicaRole(io::network::Endpoint endpoint, const replication::ReplicationServerConfig &config) {
return replication_state_.SetReplicaRole(std::move(endpoint), config, this);
}
bool SetMainReplicationRole() { return replication_state_.SetMainReplicationRole(this); }
/// @pre The instance should have a MAIN role
/// @pre Timeout can only be set for SYNC replication
auto RegisterReplica(std::string name, io::network::Endpoint endpoint,
const replication::ReplicationMode replication_mode,
const replication::RegistrationMode registration_mode,
const replication::ReplicationClientConfig &config) {
return replication_state_.RegisterReplica(std::move(name), std::move(endpoint), replication_mode, registration_mode,
config, this);
}
/// @pre The instance should have a MAIN role
bool UnregisterReplica(const std::string &name) { return replication_state_.UnregisterReplica(name); }
replication::ReplicationRole GetReplicationRole() const { return replication_state_.GetRole(); }
auto ReplicasInfo() { return replication_state_.ReplicasInfo(); }
std::optional<replication::ReplicaState> GetReplicaState(std::string_view name) {
return replication_state_.GetReplicaState(name);
}
protected:
void RestoreReplicas() { return replication_state_.RestoreReplicas(this); }
void RestoreReplicationRole() { return replication_state_.RestoreReplicationRole(this); }
public:
// Main storage lock. // Main storage lock.
// Accessors take a shared lock when starting, so it is possible to block // Accessors take a shared lock when starting, so it is possible to block
// creation of new accessors by taking a unique lock. This is used when doing // creation of new accessors by taking a unique lock. This is used when doing
@@ -323,6 +355,9 @@ class Storage {
std::atomic<uint64_t> vertex_id_{0}; std::atomic<uint64_t> vertex_id_{0};
std::atomic<uint64_t> edge_id_{0}; std::atomic<uint64_t> edge_id_{0};
const std::string id_; //!< High-level assigned ID const std::string id_; //!< High-level assigned ID
protected:
ReplicationState replication_state_;
}; };
} // namespace memgraph::storage } // namespace memgraph::storage

View File

@@ -19,7 +19,7 @@
#include "storage/v2/edge_ref.hpp" #include "storage/v2/edge_ref.hpp"
#include "storage/v2/id_types.hpp" #include "storage/v2/id_types.hpp"
#include "storage/v2/property_store.hpp" #include "storage/v2/property_store.hpp"
#include "utils/spin_lock.hpp" #include "utils/rw_spin_lock.hpp"
namespace memgraph::storage { namespace memgraph::storage {
@@ -38,7 +38,7 @@ struct Vertex {
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges; std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges; std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
mutable utils::SpinLock lock; mutable utils::RWSpinLock lock;
bool deleted; bool deleted;
// uint8_t PAD; // uint8_t PAD;
// uint16_t PAD; // uint16_t PAD;

View File

@@ -35,7 +35,7 @@ std::pair<bool, bool> IsVisible(Vertex const *vertex, Transaction const *transac
bool deleted = false; bool deleted = false;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex->lock); auto guard = std::shared_lock{vertex->lock};
deleted = vertex->deleted; deleted = vertex->deleted;
delta = vertex->delta; delta = vertex->delta;
} }
@@ -90,7 +90,7 @@ bool VertexAccessor::IsVisible(View view) const {
Result<bool> VertexAccessor::AddLabel(LabelId label) { Result<bool> VertexAccessor::AddLabel(LabelId label) {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::unique_lock{vertex_->lock};
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
if (vertex_->deleted) return Error::DELETED_OBJECT; if (vertex_->deleted) return Error::DELETED_OBJECT;
@@ -109,7 +109,7 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
/// TODO: move to after update and change naming to vertex after update /// TODO: move to after update and change naming to vertex after update
Result<bool> VertexAccessor::RemoveLabel(LabelId label) { Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::unique_lock{vertex_->lock};
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
if (vertex_->deleted) return Error::DELETED_OBJECT; if (vertex_->deleted) return Error::DELETED_OBJECT;
@@ -135,7 +135,7 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
bool has_label = false; bool has_label = false;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end(); has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end();
delta = vertex_->delta; delta = vertex_->delta;
@@ -182,7 +182,7 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
std::vector<LabelId> labels; std::vector<LabelId> labels;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
labels = vertex_->labels; labels = vertex_->labels;
delta = vertex_->delta; delta = vertex_->delta;
@@ -225,7 +225,7 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) { Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const PropertyValue &value) {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::unique_lock{vertex_->lock};
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
@@ -250,7 +250,7 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
Result<bool> VertexAccessor::InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) { Result<bool> VertexAccessor::InitProperties(const std::map<storage::PropertyId, storage::PropertyValue> &properties) {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::unique_lock{vertex_->lock};
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
@@ -269,7 +269,7 @@ Result<bool> VertexAccessor::InitProperties(const std::map<storage::PropertyId,
Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> VertexAccessor::UpdateProperties( Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> VertexAccessor::UpdateProperties(
std::map<storage::PropertyId, storage::PropertyValue> &properties) const { std::map<storage::PropertyId, storage::PropertyValue> &properties) const {
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception; utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::unique_lock{vertex_->lock};
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
@@ -287,7 +287,7 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> Vertex
} }
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() { Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::unique_lock{vertex_->lock};
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR; if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
@@ -311,7 +311,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
PropertyValue value; PropertyValue value;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
value = vertex_->properties.GetProperty(property); value = vertex_->properties.GetProperty(property);
delta = vertex_->delta; delta = vertex_->delta;
@@ -359,7 +359,7 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view
std::map<PropertyId, PropertyValue> properties; std::map<PropertyId, PropertyValue> properties;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
properties = vertex_->properties.Properties(); properties = vertex_->properties.Properties();
delta = vertex_->delta; delta = vertex_->delta;
@@ -424,7 +424,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
auto in_edges = edge_store{}; auto in_edges = edge_store{};
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
// TODO: a better filter copy // TODO: a better filter copy
if (edge_types.empty() && !destination) { if (edge_types.empty() && !destination) {
@@ -500,7 +500,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
auto out_edges = edge_store{}; auto out_edges = edge_store{};
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
if (edge_types.empty() && !destination) { if (edge_types.empty() && !destination) {
out_edges = vertex_->out_edges; out_edges = vertex_->out_edges;
@@ -558,7 +558,7 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
size_t degree = 0; size_t degree = 0;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
degree = vertex_->in_edges.size(); degree = vertex_->in_edges.size();
delta = vertex_->delta; delta = vertex_->delta;
@@ -606,7 +606,7 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
size_t degree = 0; size_t degree = 0;
Delta *delta = nullptr; Delta *delta = nullptr;
{ {
std::lock_guard<utils::SpinLock> guard(vertex_->lock); auto guard = std::shared_lock{vertex_->lock};
deleted = vertex_->deleted; deleted = vertex_->deleted;
degree = vertex_->out_edges.size(); degree = vertex_->out_edges.size();
delta = vertex_->delta; delta = vertex_->delta;

124
src/utils/rw_spin_lock.hpp Normal file
View File

@@ -0,0 +1,124 @@
// Copyright 2023 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 <time.h>
#include <algorithm>
#include <atomic>
#include <cstdint>
namespace memgraph::utils {
namespace {
/// A helper for RWSpinLock, allows a contended spin lock to yield to another thread.
struct yeilder {
void operator()() noexcept {
#if defined(__i386__) || defined(__x86_64__)
// TODO: make portable
__builtin_ia32_pause();
#endif
++count;
if (count > 8) [[unlikely]] {
count = 0;
nanosleep(&shortpause, nullptr);
// Increase the backoff
shortpause.tv_nsec = std::min<decltype(shortpause.tv_nsec)>(shortpause.tv_nsec << 1, 512);
}
}
private:
uint_fast32_t count{0};
timespec shortpause = {.tv_sec = 0, .tv_nsec = 1};
};
} // namespace
/**
* A reader/writer spin lock.
* Stores in a uint32_t,
* 0x0000'0001 - is the write bit
* rest of the bits hold the count for the number of current writers.
* The lock is friendly to writers.
* - writer lock() will wait for all readers to leave unlock_shared()
* - new reader lock_shared() will wait until writer has unlock()
**/
struct RWSpinLock {
RWSpinLock() = default;
void lock() {
// spin: to grant the UNIQUE_LOCKED bit
while (true) {
// optimistic: assume we will be granted the lock
auto const phase1 = std::atomic_ref{lock_status_}.fetch_or(UNIQUE_LOCKED, std::memory_order_acq_rel);
// check: we were granted UNIQUE_LOCK and no current readers
if (phase1 == 0) [[likely]]
return;
// check: we were granted UNIQUE_LOCK, but need to wait for readers
if ((phase1 & UNIQUE_LOCKED) != UNIQUE_LOCKED) [[likely]]
break;
// spin: to wait for UNIQUE_LOCKED to be available
auto maybe_yield = yeilder{};
while (true) {
auto const phase2 = std::atomic_ref{lock_status_}.load(std::memory_order_relaxed);
// check: we are able to obtain UNIQUE_LOCK
if ((phase2 & UNIQUE_LOCKED) != UNIQUE_LOCKED) [[likely]]
break;
maybe_yield();
}
}
// spin: to wait for readers to leave
auto maybe_yield = yeilder{};
while (true) {
auto const phase3 = std::atomic_ref{lock_status_}.load(std::memory_order_relaxed);
// check: all readers have gone (leaving only the UNIQUE_LOCKED bit set)
if (phase3 == UNIQUE_LOCKED) return;
maybe_yield();
}
}
void unlock() { std::atomic_ref{lock_status_}.fetch_and(~UNIQUE_LOCKED, std::memory_order_release); }
void lock_shared() {
while (true) {
// optimistic: assume we will be granted the lock
auto const phase1 = std::atomic_ref{lock_status_}.fetch_add(READER, std::memory_order_acquire);
// check: we incremented reader count without the UNIQUE_LOCK already being held
if ((phase1 & UNIQUE_LOCKED) != UNIQUE_LOCKED) [[likely]]
return;
// correct for our optimism, we shouldn't have modified the reader count
std::atomic_ref{lock_status_}.fetch_sub(READER, std::memory_order_release);
// spin: to wait for UNIQUE_LOCKED to be available
auto maybe_yield = yeilder{};
while (true) {
auto const phase2 = std::atomic_ref{lock_status_}.load(std::memory_order_relaxed);
// check: UNIQUE_LOCK was released
if ((phase2 & UNIQUE_LOCKED) != UNIQUE_LOCKED) [[likely]]
break;
maybe_yield();
}
}
}
void unlock_shared() { std::atomic_ref{lock_status_}.fetch_sub(READER, std::memory_order_release); }
private:
using status_t = uint32_t;
enum FLAGS : status_t {
UNIQUE_LOCKED = 1,
READER = 2,
};
// TODO: ATM not atomic, just used via atomic_ref, because the type needs to be movable into skip_list
// fix the design flaw and then make RWSpinLock a non-copy/non-move type
status_t lock_status_ = 0;
};
} // namespace memgraph::utils

View File

@@ -1,14 +1,32 @@
template_cluster: &template_cluster args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "batched-procedures-e2e.log" log_file: "batched-procedures-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster
cluster:
main:
args: *args
log_file: "batched-procedures-disk-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
validation_queries: []
workloads: workloads:
- name: "Batched procedures read" - name: "Batched procedures read"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/batched_procedures/procedures/" proc: "tests/e2e/batched_procedures/procedures/"
args: ["batched_procedures/simple_read.py"] args: ["batched_procedures/simple_read.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Disk batched procedures read"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/batched_procedures/procedures/"
args: ["batched_procedures/simple_read.py"]
<<: *disk_cluster

View File

@@ -1,25 +1,38 @@
template_cluster: &template_cluster args: &args
- "--log-level=TRACE"
- "--storage-properties-on-edges=True"
- "--storage-snapshot-interval-sec"
- "300"
- "--storage-wal-enabled=True"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: args: *args
[
"--log-level=TRACE",
"--storage-properties-on-edges=True",
"--storage-snapshot-interval-sec",
"300",
"--storage-wal-enabled=True",
]
log_file: "configuration-check-e2e.log" log_file: "configuration-check-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster
cluster:
main:
args: *args
log_file: "configuration-check-disk-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
validation_queries: []
workloads: workloads:
- name: "Configuration check" - name: "Configuration check"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["configuration/configuration_check.py"] args: ["configuration/configuration_check.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "SHOW STORAGE INFO check" - name: "SHOW STORAGE INFO check"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["configuration/storage_info.py"] args: ["configuration/storage_info.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Disk configuration check"
binary: "tests/e2e/pytest_runner.sh"
args: ["configuration/configuration_check.py"]
<<: *disk_cluster

View File

@@ -1,135 +1,187 @@
bolt_port: &bolt_port "7687" args: &args
create_delete_filtering_cluster: &create_delete_filtering_cluster - "--bolt-port"
cluster: - "7687"
main: - "--log-level=TRACE"
args: ["--bolt-port", "7687", "--log-level=TRACE"]
log_file: "fine_grained_access.log"
setup_queries:
[
"CREATE USER admin IDENTIFIED BY 'test';",
"CREATE USER user IDENTIFIED BY 'test';",
"GRANT ALL PRIVILEGES TO admin;",
"GRANT DATABASE * TO admin;",
"GRANT ALL PRIVILEGES TO user;",
"GRANT DATABASE * TO user;",
]
edge_type_filtering_cluster: &edge_type_filtering_cluster create_delete_filtering_setup_queries: &create_delete_filtering_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin;"
- "GRANT DATABASE * TO admin;"
- "GRANT ALL PRIVILEGES TO user;"
- "GRANT DATABASE * TO user;"
edge_type_filtering_setup_queries: &edge_type_filtering_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin;"
- "GRANT DATABASE * TO admin;"
- "GRANT ALL PRIVILEGES TO user;"
- "GRANT DATABASE * TO user;"
- "GRANT CREATE_DELETE ON LABELS * TO admin;"
- "GRANT CREATE_DELETE ON EDGE_TYPES * TO admin;"
- "MERGE (l1:label1 {name: 'test1'});"
- "MERGE (l2:label2 {name: 'test2'});"
- "MATCH (l1:label1),(l2:label2) WHERE l1.name = 'test1' AND l2.name = 'test2' CREATE (l1)-[r:edgeType1]->(l2);"
- "MERGE (l3:label3 {name: 'test3'});"
- "MATCH (l1:label1),(l3:label3) WHERE l1.name = 'test1' AND l3.name = 'test3' CREATE (l1)-[r:edgeType2]->(l3);"
- "MERGE (mix:label3:label1 {name: 'test4'});"
- "MATCH (l1:label1),(mix:label3) WHERE l1.name = 'test1' AND mix.name = 'test4' CREATE (l1)-[r:edgeType2]->(mix);"
- "CREATE DATABASE clean;"
- "USE DATABASE clean"
- "MATCH (n) DETACH DELETE n;"
- "MERGE (l1:label1 {name: 'test1'});"
- "MERGE (l2:label2 {name: 'test2'});"
- "MATCH (l1:label1),(l2:label2) WHERE l1.name = 'test1' AND l2.name = 'test2' CREATE (l1)-[r:edgeType1]->(l2);"
- "MERGE (l3:label3 {name: 'test3'});"
- "MATCH (l1:label1),(l3:label3) WHERE l1.name = 'test1' AND l3.name = 'test3' CREATE (l1)-[r:edgeType2]->(l3);"
- "MERGE (mix:label3:label1 {name: 'test4'});"
- "MATCH (l1:label1),(mix:label3) WHERE l1.name = 'test1' AND mix.name = 'test4' CREATE (l1)-[r:edgeType2]->(mix);"
- "USE DATABASE memgraph"
path_filtering_setup_queries: &path_filtering_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin;"
- "GRANT DATABASE * TO admin;"
- "GRANT ALL PRIVILEGES TO user;"
- "GRANT DATABASE * TO user;"
- "MERGE (a:label0 {id: 0}) MERGE (b:label1 {id: 1}) CREATE (a)-[:edge_type_1 {weight: 6}]->(b);"
- "MERGE (a:label0 {id: 0}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_1 {weight: 14}]->(b);"
- "MERGE (a:label1 {id: 1}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_2 {weight: 1}]->(b);"
- "MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_2 {weight: 10}]->(b);"
- "MERGE (a:label1 {id: 1}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 5}]->(b);"
- "MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 7}]->(b);"
- "MERGE (a:label3 {id: 3}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);"
- "MERGE (a:label3 {id: 4}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);"
- "MERGE (a:label3 {id: 3}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 14}]->(b);"
- "MERGE (a:label3 {id: 4}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 8}]->(b);"
- "CREATE DATABASE clean;"
- "USE DATABASE clean"
- "MATCH (n) DETACH DELETE n;"
- "MERGE (a:label0 {id: 0}) MERGE (b:label1 {id: 1}) CREATE (a)-[:edge_type_1 {weight: 6}]->(b);"
- "MERGE (a:label0 {id: 0}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_1 {weight: 14}]->(b);"
- "MERGE (a:label1 {id: 1}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_2 {weight: 1}]->(b);"
- "MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_2 {weight: 10}]->(b);"
- "MERGE (a:label1 {id: 1}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 5}]->(b);"
- "MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 7}]->(b);"
- "MERGE (a:label3 {id: 3}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);"
- "MERGE (a:label3 {id: 4}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);"
- "MERGE (a:label3 {id: 3}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 14}]->(b);"
- "MERGE (a:label3 {id: 4}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 8}]->(b);"
- "USE DATABASE memgraph"
show_databases_w_user_setup_queries: &show_databases_w_user_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "CREATE USER user IDENTIFIED BY 'test';"
- "CREATE USER user2 IDENTIFIED BY 'test';"
- "CREATE USER user3 IDENTIFIED BY 'test';"
- "CREATE DATABASE db1;"
- "CREATE DATABASE db2;"
- "GRANT ALL PRIVILEGES TO admin;"
- "GRANT DATABASE * TO admin;"
- "GRANT ALL PRIVILEGES TO user;"
- "GRANT DATABASE db1 TO user;"
- "GRANT ALL PRIVILEGES TO user2;"
- "GRANT DATABASE db2 TO user2;"
- "REVOKE DATABASE memgraph FROM user2;"
- "SET MAIN DATABASE db2 FOR user2"
- "GRANT ALL PRIVILEGES TO user3;"
- "GRANT DATABASE * TO user3;"
- "REVOKE DATABASE memgraph FROM user3;"
- "SET MAIN DATABASE db1 FOR user3"
create_delete_filtering_in_memory_cluster: &create_delete_filtering_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE"] args: *args
log_file: "fine_grained_access.log" log_file: "fine_grained_access.log"
setup_queries: setup_queries: *create_delete_filtering_setup_queries
[
"CREATE USER admin IDENTIFIED BY 'test';",
"CREATE USER user IDENTIFIED BY 'test';",
"GRANT ALL PRIVILEGES TO admin;",
"GRANT DATABASE * TO admin;",
"GRANT ALL PRIVILEGES TO user;",
"GRANT DATABASE * TO user;",
"GRANT CREATE_DELETE ON LABELS * TO admin;",
"GRANT CREATE_DELETE ON EDGE_TYPES * TO admin;",
"MERGE (l1:label1 {name: 'test1'});",
"MERGE (l2:label2 {name: 'test2'});",
"MATCH (l1:label1),(l2:label2) WHERE l1.name = 'test1' AND l2.name = 'test2' CREATE (l1)-[r:edgeType1]->(l2);",
"MERGE (l3:label3 {name: 'test3'});",
"MATCH (l1:label1),(l3:label3) WHERE l1.name = 'test1' AND l3.name = 'test3' CREATE (l1)-[r:edgeType2]->(l3);",
"MERGE (mix:label3:label1 {name: 'test4'});",
"MATCH (l1:label1),(mix:label3) WHERE l1.name = 'test1' AND mix.name = 'test4' CREATE (l1)-[r:edgeType2]->(mix);",
"CREATE DATABASE clean;",
"USE DATABASE clean",
"MATCH (n) DETACH DELETE n;",
"MERGE (l1:label1 {name: 'test1'});",
"MERGE (l2:label2 {name: 'test2'});",
"MATCH (l1:label1),(l2:label2) WHERE l1.name = 'test1' AND l2.name = 'test2' CREATE (l1)-[r:edgeType1]->(l2);",
"MERGE (l3:label3 {name: 'test3'});",
"MATCH (l1:label1),(l3:label3) WHERE l1.name = 'test1' AND l3.name = 'test3' CREATE (l1)-[r:edgeType2]->(l3);",
"MERGE (mix:label3:label1 {name: 'test4'});",
"MATCH (l1:label1),(mix:label3) WHERE l1.name = 'test1' AND mix.name = 'test4' CREATE (l1)-[r:edgeType2]->(mix);",
"USE DATABASE memgraph",
]
validation_queries: [] validation_queries: []
path_filtering_cluster: &path_filtering_cluster create_delete_filtering_disk_cluster: &create_delete_filtering_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "fine_grained_access.log" log_file: "disk_fine_grained_access.log"
setup_queries: setup_queries:
[ - "STORAGE MODE ON_DISK_TRANSACTIONAL"
"CREATE USER admin IDENTIFIED BY 'test';", - *create_delete_filtering_setup_queries
"CREATE USER user IDENTIFIED BY 'test';", validation_queries: []
"GRANT ALL PRIVILEGES TO admin;",
"GRANT DATABASE * TO admin;", edge_type_filtering_in_memory_cluster: &edge_type_filtering_in_memory_cluster
"GRANT ALL PRIVILEGES TO user;", cluster:
"GRANT DATABASE * TO user;", main:
"MERGE (a:label0 {id: 0}) MERGE (b:label1 {id: 1}) CREATE (a)-[:edge_type_1 {weight: 6}]->(b);", args: *args
"MERGE (a:label0 {id: 0}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_1 {weight: 14}]->(b);", log_file: "fine_grained_access.log"
"MERGE (a:label1 {id: 1}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_2 {weight: 1}]->(b);", setup_queries: *edge_type_filtering_setup_queries
"MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_2 {weight: 10}]->(b);", validation_queries: []
"MERGE (a:label1 {id: 1}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 5}]->(b);",
"MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 7}]->(b);", edge_type_filtering_disk_cluster: &edge_type_filtering_disk_cluster
"MERGE (a:label3 {id: 3}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);", cluster:
"MERGE (a:label3 {id: 4}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);", main:
"MERGE (a:label3 {id: 3}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 14}]->(b);", args: *args
"MERGE (a:label3 {id: 4}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 8}]->(b);", log_file: "disk_fine_grained_access.log"
"CREATE DATABASE clean;", setup_queries:
"USE DATABASE clean", - "STORAGE MODE ON_DISK_TRANSACTIONAL"
"MATCH (n) DETACH DELETE n;", - *edge_type_filtering_setup_queries
"MERGE (a:label0 {id: 0}) MERGE (b:label1 {id: 1}) CREATE (a)-[:edge_type_1 {weight: 6}]->(b);", validation_queries: []
"MERGE (a:label0 {id: 0}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_1 {weight: 14}]->(b);",
"MERGE (a:label1 {id: 1}) MERGE (b:label2 {id: 2}) CREATE (a)-[:edge_type_2 {weight: 1}]->(b);", path_filtering_in_memory_cluster: &path_filtering_in_memory_cluster
"MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_2 {weight: 10}]->(b);", cluster:
"MERGE (a:label1 {id: 1}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 5}]->(b);", main:
"MERGE (a:label2 {id: 2}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_3 {weight: 7}]->(b);", args: *args
"MERGE (a:label3 {id: 3}) MERGE (b:label3 {id: 4}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);", log_file: "fine_grained_access.log"
"MERGE (a:label3 {id: 4}) MERGE (b:label3 {id: 3}) CREATE (a)-[:edge_type_4 {weight: 1}]->(b);", setup_queries: *path_filtering_setup_queries
"MERGE (a:label3 {id: 3}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 14}]->(b);",
"MERGE (a:label3 {id: 4}) MERGE (b:label4 {id: 5}) CREATE (a)-[:edge_type_4 {weight: 8}]->(b);", path_filtering_disk_cluster: &path_filtering_disk_cluster
"USE DATABASE memgraph", cluster:
] main:
args: *args
log_file: "disk_fine_grained_access.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
- *path_filtering_setup_queries
show_databases_w_user: &show_databases_w_user show_databases_w_user: &show_databases_w_user
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "fine_grained_access.log" log_file: "fine_grained_access.log"
setup_queries: setup_queries: *show_databases_w_user_setup_queries
[
"CREATE USER admin IDENTIFIED BY 'test';",
"CREATE USER user IDENTIFIED BY 'test';",
"CREATE USER user2 IDENTIFIED BY 'test';",
"CREATE USER user3 IDENTIFIED BY 'test';",
"CREATE DATABASE db1;",
"CREATE DATABASE db2;",
"GRANT ALL PRIVILEGES TO admin;",
"GRANT DATABASE * TO admin;",
"GRANT ALL PRIVILEGES TO user;",
"GRANT DATABASE db1 TO user;",
"GRANT ALL PRIVILEGES TO user2;",
"GRANT DATABASE db2 TO user2;",
"REVOKE DATABASE memgraph FROM user2;",
"SET MAIN DATABASE db2 FOR user2",
"GRANT ALL PRIVILEGES TO user3;",
"GRANT DATABASE * TO user3;",
"REVOKE DATABASE memgraph FROM user3;",
"SET MAIN DATABASE db1 FOR user3",
]
workloads: workloads:
- name: "Create delete filtering" - name: "Create delete filtering"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/create_delete_filtering_tests.py"] args: ["fine_grained_access/create_delete_filtering_tests.py"]
<<: *create_delete_filtering_cluster <<: *create_delete_filtering_in_memory_cluster
- name: "Create delete filtering on disk"
binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/create_delete_filtering_tests.py"]
<<: *create_delete_filtering_disk_cluster
- name: "EdgeType filtering" - name: "EdgeType filtering"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/edge_type_filtering_tests.py"] args: ["fine_grained_access/edge_type_filtering_tests.py"]
<<: *edge_type_filtering_cluster <<: *edge_type_filtering_in_memory_cluster
- name: "EdgeType filtering on disk"
binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/edge_type_filtering_tests.py"]
<<: *edge_type_filtering_disk_cluster
- name: "Path filtering" - name: "Path filtering"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/path_filtering_tests.py"] args: ["fine_grained_access/path_filtering_tests.py"]
<<: *path_filtering_cluster <<: *path_filtering_in_memory_cluster
- name: "Show databases with users" - name: "Show databases with users"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/show_db.py"] args: ["fine_grained_access/show_db.py"]
<<: *show_databases_w_user <<: *show_databases_w_user
- name: "Path filtering on disk"
binary: "tests/e2e/pytest_runner.sh"
args: ["fine_grained_access/path_filtering_tests.py"]
<<: *path_filtering_disk_cluster

View File

@@ -1,22 +1,26 @@
init_file_cluster: &init_file_cluster init_file_args: &init_file_args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
- "--init-file=init_file_flags/init_file.cypherl"
init_data_file_args: &init_data_file_args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
- "--init-data-file=init_file_flags/init_file.cypherl"
init_file_in_memory_cluster: &init_file_in_memory_cluster
cluster: cluster:
main: main:
args: [ args: *init_file_args
"--bolt-port", "7687",
"--log-level=TRACE",
"--init-file=init_file_flags/init_file.cypherl"
]
log_file: "init-file-flags-e2e.log" log_file: "init-file-flags-e2e.log"
validation_queries: [] validation_queries: []
init_data_file_cluster: &init_data_file_cluster init_data_file_in_memory_cluster: &init_data_file_in_memory_cluster
cluster: cluster:
main: main:
args: [ args: *init_data_file_args
"--bolt-port", "7687",
"--log-level=TRACE",
"--init-data-file=init_file_flags/init_file.cypherl"
]
log_file: "init-data-file-flags-e2e.log" log_file: "init-data-file-flags-e2e.log"
validation_queries: [] validation_queries: []
@@ -24,9 +28,9 @@ workloads:
- name: "Init file flags" - name: "Init file flags"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["init_file_flags/init_file_setup.py"] args: ["init_file_flags/init_file_setup.py"]
<<: *init_file_cluster <<: *init_file_in_memory_cluster
- name: "Init data file flags" - name: "Init data file flags"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["init_file_flags/init_data_file_setup.py"] args: ["init_file_flags/init_data_file_setup.py"]
<<: *init_data_file_cluster <<: *init_data_file_in_memory_cluster

View File

@@ -1,24 +1,31 @@
bolt_port: &bolt_port "7687" bolt_port: &bolt_port "7687"
template_cluster: &template_cluster
args: &args
- "--bolt-port"
- *bolt_port
- "--log-level=TRACE"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE"] args: *args
log_file: "isolation-levels-e2e.log" log_file: "isolation-levels-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster disk_cluster: &disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE"] args: *args
log_file: "isolation-levels-disk-e2e.log" log_file: "isolation-levels-disk-e2e.log"
setup_queries: ["storage mode on_disk_transactional"] setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
validation_queries: [] validation_queries: []
workloads: workloads:
- name: "Isolation levels" - name: "Isolation levels"
binary: "tests/e2e/isolation_levels/memgraph__e2e__isolation_levels" binary: "tests/e2e/isolation_levels/memgraph__e2e__isolation_levels"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
<<: *template_cluster <<: *in_memory_cluster
- name: "Isolation levels for disk storage" - name: "Isolation levels for disk storage"
binary: "tests/e2e/isolation_levels/memgraph__e2e__isolation_levels" binary: "tests/e2e/isolation_levels/memgraph__e2e__isolation_levels"

View File

@@ -1,108 +1,152 @@
read_query_modules_cluster: &read_query_modules_cluster args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
query_modules_setup_queries: &query_modules_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin"
- "GRANT DATABASE * TO admin"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO user"
- "GRANT DATABASE * TO user"
show_privileges_setup_queries: &show_privileges_setup_queries
- "Create User Josip;"
- "Grant Read On Labels :Label1 to Josip;"
- "Grant Nothing On Labels :Label2 to Josip;"
- "Grant Update On Labels :Label3 to Josip;"
- "Grant Read On Labels :Label4 to Josip;"
- "Grant Create_Delete On Labels :Label5 to Josip;"
- "Grant Update On Labels :Label6 to Josip;"
- "Grant Create_Delete On Labels :Label7 to Josip;"
- "Grant Nothing On Labels :Label7 to Josip;"
- "Create User Boris;"
- "Grant Auth to Boris;"
- "Grant Read On Labels :Label1 to Boris;"
- "Grant Nothing On Labels :Label2 to Boris;"
- "Grant Update On Labels :Label3 to Boris;"
- "Grant Read On Labels :Label4 to Boris;"
- "Grant Create_Delete On Labels :Label5 to Boris;"
- "Grant Update On Labels :Label6 to Boris;"
- "Grant Create_Delete On Labels :Label7 to Boris;"
- "Grant Nothing On Labels :Label7 to Boris;"
- "Create User Niko;"
- "Grant Auth to Niko;"
- "Grant Create_Delete On Labels * to Niko;"
- "Grant Read On Labels * to Niko;"
- "Create User Bruno;"
- "Grant Auth to Bruno;"
- "Grant Update On Labels * to Bruno;"
read_query_modules_in_memory_cluster: &read_query_modules_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "lba-e2e.log" log_file: "lba-e2e.log"
setup_queries: setup_queries: *query_modules_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin"
- "GRANT DATABASE * TO admin"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO user"
- "GRANT DATABASE * TO user"
validation_queries: [] validation_queries: []
update_query_modules_cluster: &update_query_modules_cluster read_query_modules_disk_cluster: &read_query_modules_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "lba-e2e.log" log_file: "disk-lba-e2e.log"
setup_queries: setup_queries:
- "CREATE USER admin IDENTIFIED BY 'test';" - "STORAGE MODE ON_DISK_TRANSACTIONAL"
- "GRANT ALL PRIVILEGES TO admin" - *query_modules_setup_queries
- "GRANT DATABASE * TO admin"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO user"
- "GRANT DATABASE * TO user"
validation_queries: [] validation_queries: []
show_privileges_cluster: &show_privileges_cluster update_query_modules_in_memory_cluster: &update_query_modules_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "lba-e2e.log" log_file: "lba-e2e.log"
setup_queries: setup_queries: *query_modules_setup_queries
- "Create User Josip;"
- "Grant Read On Labels :Label1 to Josip;"
- "Grant Nothing On Labels :Label2 to Josip;"
- "Grant Update On Labels :Label3 to Josip;"
- "Grant Read On Labels :Label4 to Josip;"
- "Grant Create_Delete On Labels :Label5 to Josip;"
- "Grant Update On Labels :Label6 to Josip;"
- "Grant Create_Delete On Labels :Label7 to Josip;"
- "Grant Nothing On Labels :Label7 to Josip;"
- "Create User Boris;"
- "Grant Auth to Boris;"
- "Grant Read On Labels :Label1 to Boris;"
- "Grant Nothing On Labels :Label2 to Boris;"
- "Grant Update On Labels :Label3 to Boris;"
- "Grant Read On Labels :Label4 to Boris;"
- "Grant Create_Delete On Labels :Label5 to Boris;"
- "Grant Update On Labels :Label6 to Boris;"
- "Grant Create_Delete On Labels :Label7 to Boris;"
- "Grant Nothing On Labels :Label7 to Boris;"
- "Create User Niko;"
- "Grant Auth to Niko;"
- "Grant Create_Delete On Labels * to Niko"
- "Grant Read On Labels * to Niko"
- "Create User Bruno;"
- "Grant Auth to Bruno;"
- "Grant Update On Labels * to Bruno"
validation_queries: [] validation_queries: []
read_permission_queries: &read_permission_queries update_query_modules_disk_cluster: &update_query_modules_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "lba-e2e.log" log_file: "disk-lba-e2e.log"
setup_queries: setup_queries:
- "CREATE USER admin IDENTIFIED BY 'test';" - "STORAGE MODE ON_DISK_TRANSACTIONAL"
- "GRANT ALL PRIVILEGES TO admin" - *query_modules_setup_queries
- "GRANT DATABASE * TO admin"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO user"
- "GRANT DATABASE * TO user"
validation_queries: [] validation_queries: []
create_delete_query_modules_cluster: &create_delete_query_modules_cluster show_privileges_in_memory_cluster: &show_privileges_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "lba-e2e.log" log_file: "lba-e2e.log"
setup_queries: setup_queries: *show_privileges_setup_queries
- "CREATE USER admin IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO admin;"
- "GRANT DATABASE * TO admin"
- "CREATE USER user IDENTIFIED BY 'test';"
- "GRANT ALL PRIVILEGES TO user;"
- "GRANT DATABASE * TO user"
validation_queries: [] validation_queries: []
update_permission_queries_cluster: &update_permission_queries_cluster show_privileges_disk_cluster: &show_privileges_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "lba-e2e.log" log_file: "disk-lba-e2e.log"
setup_queries: setup_queries:
- "CREATE USER admin IDENTIFIED BY 'test';" - "STORAGE MODE ON_DISK_TRANSACTIONAL"
- "GRANT ALL PRIVILEGES TO admin;" - *show_privileges_setup_queries
- "GRANT DATABASE * TO admin" validation_queries: []
- "CREATE USER user IDENTIFIED BY 'test'"
- "GRANT ALL PRIVILEGES TO user;" read_permission_in_memory_queries: &read_permission_in_memory_queries
- "GRANT DATABASE * TO user" cluster:
main:
args: *args
log_file: "lba-e2e.log"
setup_queries: *query_modules_setup_queries
validation_queries: []
read_permission_disk_queries: &read_permission_disk_queries
cluster:
main:
args: *args
log_file: "disk-lba-e2e.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
- *query_modules_setup_queries
validation_queries: []
create_delete_query_modules_in_memory_cluster: &create_delete_query_modules_in_memory_cluster
cluster:
main:
args: *args
log_file: "lba-e2e.log"
setup_queries: *query_modules_setup_queries
validation_queries: []
create_delete_query_modules_disk_cluster: &create_delete_query_modules_disk_cluster
cluster:
main:
args: *args
log_file: "disk-lba-e2e.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
- *query_modules_setup_queries
validation_queries: []
update_permission_queries_in_memory_cluster: &update_permission_queries_in_memory_cluster
cluster:
main:
args: *args
log_file: "lba-e2e.log"
setup_queries: *query_modules_setup_queries
validation_queries: []
update_permission_queries_disk_cluster: &update_permission_queries_disk_cluster
cluster:
main:
args: *args
log_file: "disk-lba-e2e.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
- *query_modules_setup_queries
validation_queries: [] validation_queries: []
workloads: workloads:
@@ -110,34 +154,70 @@ workloads:
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/" proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/read_query_modules.py"] args: ["lba_procedures/read_query_modules.py"]
<<: *read_query_modules_cluster <<: *read_query_modules_in_memory_cluster
- name: "read-query-modules on disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/read_query_modules.py"]
<<: *read_query_modules_disk_cluster
- name: "update-query-modules" - name: "update-query-modules"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/" proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/update_query_modules.py"] args: ["lba_procedures/update_query_modules.py"]
<<: *update_query_modules_cluster <<: *update_query_modules_in_memory_cluster
- name: "update-query-modules on disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/update_query_modules.py"]
<<: *update_query_modules_disk_cluster
- name: "create-delete-query-modules" - name: "create-delete-query-modules"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/" proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/create_delete_query_modules.py"] args: ["lba_procedures/create_delete_query_modules.py"]
<<: *create_delete_query_modules_cluster <<: *create_delete_query_modules_in_memory_cluster
- name: "create-delete-query-modules on disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/create_delete_query_modules.py"]
<<: *create_delete_query_modules_disk_cluster
- name: "show-privileges" - name: "show-privileges"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/" proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/show_privileges.py"] args: ["lba_procedures/show_privileges.py"]
<<: *show_privileges_cluster <<: *show_privileges_in_memory_cluster
- name: "show-privileges on disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/show_privileges.py"]
<<: *show_privileges_disk_cluster
- name: "read-permission-queries" - name: "read-permission-queries"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/" proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/read_permission_queries.py"] args: ["lba_procedures/read_permission_queries.py"]
<<: *read_permission_queries <<: *read_permission_in_memory_queries
- name: "read-permission-queries on disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/read_permission_queries.py"]
<<: *read_permission_disk_queries
- name: "update-permission-queries" - name: "update-permission-queries"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/" proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/update_permission_queries.py"] args: ["lba_procedures/update_permission_queries.py"]
<<: *update_permission_queries_cluster <<: *update_permission_queries_in_memory_cluster
- name: "update-permission-queries on disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/lba_procedures/procedures/"
args: ["lba_procedures/update_permission_queries.py"]
<<: *update_permission_queries_disk_cluster

View File

@@ -1,16 +1,42 @@
nullif_cluster: &nullif_cluster args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
nullif_in_memory_cluster: &nullif_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "load_csv_log_file.txt" log_file: "load_csv_log_file.txt"
setup_queries: []
validation_queries: [] validation_queries: []
load_csv_cluster: &load_csv_cluster nullif_disk_cluster: &nullif_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "load_csv_log_file.txt" log_file: "load_csv_log_file.txt"
setup_queries: setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: []
load_csv_in_memory_cluster: &load_csv_in_memory_cluster
cluster:
main:
args: *args
log_file: "load_csv_log_file.txt"
setup_queries:
- "CREATE (n {prop: 1});"
- "CREATE (n {prop: 2});"
validation_queries: []
load_csv_disk_cluster: &load_csv_disk_cluster
cluster:
main:
args: *args
log_file: "load_csv_log_file.txt"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
- "CREATE (n {prop: 1});" - "CREATE (n {prop: 1});"
- "CREATE (n {prop: 2});" - "CREATE (n {prop: 2});"
validation_queries: [] validation_queries: []
@@ -19,8 +45,19 @@ workloads:
- name: "LOAD CSV nullif" - name: "LOAD CSV nullif"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["load_csv/load_csv_nullif.py"] args: ["load_csv/load_csv_nullif.py"]
<<: *nullif_cluster <<: *nullif_in_memory_cluster
- name: "LOAD CSV nullif on disk"
binary: "tests/e2e/pytest_runner.sh"
args: ["load_csv/load_csv_nullif.py"]
<<: *nullif_disk_cluster
- name: "MATCH + LOAD CSV" - name: "MATCH + LOAD CSV"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
args: ["load_csv/load_csv.py"] args: ["load_csv/load_csv.py"]
<<: *load_csv_cluster <<: *load_csv_in_memory_cluster
- name: "MATCH + LOAD CSV on disk"
binary: "tests/e2e/pytest_runner.sh"
args: ["load_csv/load_csv.py"]
<<: *load_csv_disk_cluster

View File

@@ -1,14 +1,20 @@
template_cluster: &template_cluster args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "magic-functions-e2e.log" log_file: "magic-functions-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster disk_cluster: &disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "magic-functions-e2e.log" log_file: "magic-functions-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"] setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
validation_queries: [] validation_queries: []
@@ -18,7 +24,7 @@ workloads:
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/magic_functions/functions/" proc: "tests/e2e/magic_functions/functions/"
args: ["magic_functions/function_example.py"] args: ["magic_functions/function_example.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Magic functions runner for disk storage" - name: "Magic functions runner for disk storage"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"

View File

@@ -70,8 +70,12 @@ class MemgraphInstanceRunner:
conn = mgclient.connect(host=self.host, port=self.bolt_port, sslmode=self.ssl) conn = mgclient.connect(host=self.host, port=self.bolt_port, sslmode=self.ssl)
conn.autocommit = True conn.autocommit = True
cursor = conn.cursor() cursor = conn.cursor()
for query in setup_queries: for query_coll in setup_queries:
cursor.execute(query) if isinstance(query_coll, str):
cursor.execute(query_coll)
elif isinstance(query_coll, list):
for query in query_coll:
cursor.execute(query)
cursor.close() cursor.close()
conn.close() conn.close()

View File

@@ -1,17 +1,26 @@
bolt_port: &bolt_port "7687" bolt_port: &bolt_port "7687"
template_cluster: &template_cluster args: &args
- "--bolt-port"
- *bolt_port
- "--memory-limit=1000"
- "--storage-gc-cycle-sec=180"
- "--log-level=TRACE"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--memory-limit=1000", "--storage-gc-cycle-sec=180", "--log-level=TRACE"] args: *args
log_file: "memory-e2e.log" log_file: "memory-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster disk_cluster: &disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--memory-limit=1000", "--storage-gc-cycle-sec=180", "--log-level=TRACE"] args: *args
log_file: "memory-e2e.log" log_file: "memory-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"] setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: [] validation_queries: []
@@ -19,33 +28,45 @@ workloads:
- name: "Memory control" - name: "Memory control"
binary: "tests/e2e/memory/memgraph__e2e__memory__control" binary: "tests/e2e/memory/memgraph__e2e__memory__control"
args: ["--bolt-port", *bolt_port, "--timeout", "180"] args: ["--bolt-port", *bolt_port, "--timeout", "180"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Memory control multi database" - name: "Memory control multi database"
binary: "tests/e2e/memory/memgraph__e2e__memory__control" binary: "tests/e2e/memory/memgraph__e2e__memory__control"
args: ["--bolt-port", *bolt_port, "--timeout", "180", "--multi-db", "true"] args: ["--bolt-port", *bolt_port, "--timeout", "180", "--multi-db", "true"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Memory limit for modules upon loading" - name: "Memory limit for modules upon loading"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc" binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc"
args: ["--bolt-port", *bolt_port, "--timeout", "180"] args: ["--bolt-port", *bolt_port, "--timeout", "180"]
proc: "tests/e2e/memory/procedures/" proc: "tests/e2e/memory/procedures/"
<<: *template_cluster <<: *in_memory_cluster
- name: "Memory limit for modules upon loading multi database" - name: "Memory limit for modules upon loading multi database"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc" binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc"
args: ["--bolt-port", *bolt_port, "--timeout", "180", "--multi-db", "true"] args: ["--bolt-port", *bolt_port, "--timeout", "180", "--multi-db", "true"]
proc: "tests/e2e/memory/procedures/" proc: "tests/e2e/memory/procedures/"
<<: *template_cluster <<: *in_memory_cluster
- name: "Memory limit for modules inside a procedure" - name: "Memory limit for modules inside a procedure"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc_proc" binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc_proc"
args: ["--bolt-port", *bolt_port, "--timeout", "180"] args: ["--bolt-port", *bolt_port, "--timeout", "180"]
proc: "tests/e2e/memory/procedures/" proc: "tests/e2e/memory/procedures/"
<<: *template_cluster <<: *in_memory_cluster
- name: "Memory limit for modules inside a procedure multi database" - name: "Memory limit for modules inside a procedure multi database"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc_proc" binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc_proc"
args: ["--bolt-port", *bolt_port, "--timeout", "180", "--multi-db", "true"] args: ["--bolt-port", *bolt_port, "--timeout", "180", "--multi-db", "true"]
proc: "tests/e2e/memory/procedures/" proc: "tests/e2e/memory/procedures/"
<<: *template_cluster <<: *in_memory_cluster
- name: "Memory limit for modules upon loading for on-disk storage"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc"
args: ["--bolt-port", *bolt_port, "--timeout", "180"]
proc: "tests/e2e/memory/procedures/"
<<: *disk_cluster
- name: "Memory limit for modules inside a procedure for on-disk storage"
binary: "tests/e2e/memory/memgraph__e2e__memory__limit_global_alloc_proc"
args: ["--bolt-port", *bolt_port, "--timeout", "180"]
proc: "tests/e2e/memory/procedures/"
<<: *disk_cluster

View File

@@ -1,166 +1,108 @@
compare_mock: &compare_mock args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
- "--also-log-to-stderr"
mock_setup_queries: &mock_setup_queries
- "CREATE INDEX ON :__mg_vertex__(__mg_id__);"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 0, `name`: 'Peter', `surname`: 'Yang'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 1, `name`: 'Engineering'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 2, `name`: 'Memgraph'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 3, `name`: 'MAGE'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 4, `name`: 'GQLAlchemy'});"
- "CREATE (:__mg_vertex__:`Company`:`Startup` {__mg_id__: 5, `name`: 'Memgraph'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 6, `name`: 'welcome_to_engineering.txt'});"
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 7, `name`: 'Google Drive'});"
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 8, `name`: 'Notion'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 9, `name`: 'welcome_to_memgraph.txt'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 10, `name`: 'Carl'});"
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 11, `name`: 'engineering_folder'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 12, `name`: 'Anna'});"
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 13, `name`: 'operations_folder'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 14, `name`: 'Operations'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 15, `name`: 'operations101.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 16, `name`: 'expenses2022.csv'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 17, `name`: 'salaries2022.csv'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 18, `name`: 'engineering101.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 19, `name`: 'working_with_github.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 20, `name`: 'working_with_notion.txt'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 21, `name`: 'Marketing'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 22, `name`: 'Julie'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 23, `name`: 'Facebook'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 24, `name`: 'LinkedIn'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 25, `name`: 'HackerNews'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 26, `name`: 'welcome_to_marketing.txt'});"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 0}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 1}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 2}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 3}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 4}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 5}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 4 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 6}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 6 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 7}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 11 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 8}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 9}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 21 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 10}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 14 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 11}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 12}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 13}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 12 CREATE (u)-[:`CREATED_BY` {`permanent_id`: 14}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 15}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 16}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 17}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 7 CREATE(u)-[:`IS_STORED_IN` {`permanent_id`: 18}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 18 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 19}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 19 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 20}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 20 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 21}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 22}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 15 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 23}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 16 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 24}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 17 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 25}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 26}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 27}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 23 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 28}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 24 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 29}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 25 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 30}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 26 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 31}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 21 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 32}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 33}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 34}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 35}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 36}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM_2` {`importance`: 'HIGH', `permanent_id`: 37}]->(v);"
- "DROP INDEX ON :__mg_vertex__(__mg_id__);"
- "MATCH (u) SET u.permanent_id = u.__mg_id__;"
- "MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;"
compare_mock_in_memory_cluster: &compare_mock_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"] args: *args
log_file: "test-compare-mock-e2e.log" log_file: "test-compare-mock-e2e.log"
setup_queries: setup_queries: *mock_setup_queries
- "CREATE INDEX ON :__mg_vertex__(__mg_id__);"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 0, `name`: 'Peter', `surname`: 'Yang'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 1, `name`: 'Engineering'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 2, `name`: 'Memgraph'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 3, `name`: 'MAGE'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 4, `name`: 'GQLAlchemy'});"
- "CREATE (:__mg_vertex__:`Company`:`Startup` {__mg_id__: 5, `name`: 'Memgraph'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 6, `name`: 'welcome_to_engineering.txt'});"
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 7, `name`: 'Google Drive'});"
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 8, `name`: 'Notion'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 9, `name`: 'welcome_to_memgraph.txt'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 10, `name`: 'Carl'});"
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 11, `name`: 'engineering_folder'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 12, `name`: 'Anna'});"
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 13, `name`: 'operations_folder'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 14, `name`: 'Operations'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 15, `name`: 'operations101.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 16, `name`: 'expenses2022.csv'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 17, `name`: 'salaries2022.csv'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 18, `name`: 'engineering101.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 19, `name`: 'working_with_github.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 20, `name`: 'working_with_notion.txt'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 21, `name`: 'Marketing'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 22, `name`: 'Julie'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 23, `name`: 'Facebook'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 24, `name`: 'LinkedIn'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 25, `name`: 'HackerNews'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 26, `name`: 'welcome_to_marketing.txt'});"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 0}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 1}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 2}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 3}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 4}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 5}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 4 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 6}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 6 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 7}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 11 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 8}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 9}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 21 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 10}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 14 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 11}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 12}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 13}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 12 CREATE (u)-[:`CREATED_BY` {`permanent_id`: 14}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 15}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 16}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 17}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 7 CREATE(u)-[:`IS_STORED_IN` {`permanent_id`: 18}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 18 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 19}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 19 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 20}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 20 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 21}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 22}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 15 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 23}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 16 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 24}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 17 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 25}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 26}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 27}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 23 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 28}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 24 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 29}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 25 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 30}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 26 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 31}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 21 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 32}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 33}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 34}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 35}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 36}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM_2` {`importance`: 'HIGH', `permanent_id`: 37}]->(v);"
- "DROP INDEX ON :__mg_vertex__(__mg_id__);"
- "MATCH (u) SET u.permanent_id = u.__mg_id__;"
- "MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;"
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster
compare_mock_disk_cluster: &compare_mock_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"] args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"]
log_file: "test-compare-mock-e2e.log" log_file: "test-compare-mock-e2e.log"
setup_queries: setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL;" - "STORAGE MODE ON_DISK_TRANSACTIONAL"
- "CREATE INDEX ON :__mg_vertex__(__mg_id__);" - *mock_setup_queries
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 0, `name`: 'Peter', `surname`: 'Yang'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 1, `name`: 'Engineering'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 2, `name`: 'Memgraph'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 3, `name`: 'MAGE'});"
- "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 4, `name`: 'GQLAlchemy'});"
- "CREATE (:__mg_vertex__:`Company`:`Startup` {__mg_id__: 5, `name`: 'Memgraph'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 6, `name`: 'welcome_to_engineering.txt'});"
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 7, `name`: 'Google Drive'});"
- "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 8, `name`: 'Notion'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 9, `name`: 'welcome_to_memgraph.txt'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 10, `name`: 'Carl'});"
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 11, `name`: 'engineering_folder'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 12, `name`: 'Anna'});"
- "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 13, `name`: 'operations_folder'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 14, `name`: 'Operations'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 15, `name`: 'operations101.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 16, `name`: 'expenses2022.csv'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 17, `name`: 'salaries2022.csv'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 18, `name`: 'engineering101.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 19, `name`: 'working_with_github.txt'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 20, `name`: 'working_with_notion.txt'});"
- "CREATE (:__mg_vertex__:`Team` {__mg_id__: 21, `name`: 'Marketing'});"
- "CREATE (:__mg_vertex__:`Person` {__mg_id__: 22, `name`: 'Julie'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 23, `name`: 'Facebook'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 24, `name`: 'LinkedIn'});"
- "CREATE (:__mg_vertex__:`Account` {__mg_id__: 25, `name`: 'HackerNews'});"
- "CREATE (:__mg_vertex__:`File` {__mg_id__: 26, `name`: 'welcome_to_marketing.txt'});"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 0}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 1}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 2}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 3}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 4}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 5}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 4 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 6}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 6 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 7}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 11 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 8}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 9}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 21 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 10}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 14 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 11}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 12}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 13}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 12 CREATE (u)-[:`CREATED_BY` {`permanent_id`: 14}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 15}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 16}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 17}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 7 CREATE(u)-[:`IS_STORED_IN` {`permanent_id`: 18}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 18 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 19}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 19 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 20}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 20 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 21}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 22}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 15 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 23}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 16 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 24}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 17 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 25}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 26}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 27}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 23 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 28}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 24 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 29}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 25 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 30}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 26 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 31}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 21 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 32}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 33}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 34}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 35}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 36}]->(v);"
- "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM_2` {`importance`: 'HIGH', `permanent_id`: 37}]->(v);"
- "DROP INDEX ON :__mg_vertex__(__mg_id__);"
- "MATCH (u) SET u.permanent_id = u.__mg_id__;"
- "MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;"
validation_queries: [] validation_queries: []
workloads: workloads:
- name: "test-compare-mock on disk" # should be the same as the python file - name: "test-compare-mock"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/mock_api/procedures/" proc: "tests/e2e/mock_api/procedures/"
args: ["mock_api/test_compare_mock.py"] args: ["mock_api/test_compare_mock.py"]
<<: *disk_cluster <<: *compare_mock_in_memory_cluster
- name: "test-compare-mock" # should be the same as the python file - name: "test-compare-mock on disk"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/mock_api/procedures/" proc: "tests/e2e/mock_api/procedures/"
args: ["mock_api/test_compare_mock.py"] args: ["mock_api/test_compare_mock.py"]
<<: *compare_mock <<: *compare_mock_disk_cluster

View File

@@ -1,19 +1,38 @@
bolt_port: &bolt_port "7687" bolt_port: &bolt_port "7687"
template_cluster: &template_cluster args: &args
- "--bolt-port"
- *bolt_port
- "--log-level=TRACE"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE"] args: *args
log_file: "module-file-manager-e2e.log" log_file: "module-file-manager-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster
cluster:
main:
args: *args
log_file: "module-file-manager-e2e.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: []
workloads: workloads:
- name: "Module File Manager" - name: "Module File Manager"
binary: "tests/e2e/module_file_manager/memgraph__e2e__module_file_manager" binary: "tests/e2e/module_file_manager/memgraph__e2e__module_file_manager"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
<<: *template_cluster <<: *in_memory_cluster
- name: "Module File Manager multi database" - name: "Module File Manager multi database"
binary: "tests/e2e/module_file_manager/memgraph__e2e__module_file_manager" binary: "tests/e2e/module_file_manager/memgraph__e2e__module_file_manager"
args: ["--bolt-port", *bolt_port, "--multi-db", "true"] args: ["--bolt-port", *bolt_port, "--multi-db", "true"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Module File Manager on disk"
binary: "tests/e2e/module_file_manager/memgraph__e2e__module_file_manager"
args: ["--bolt-port", *bolt_port]
<<: *disk_cluster

View File

@@ -1,27 +1,35 @@
test_reload_query_module: &test_reload_query_module args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
- "--also-log-to-stderr"
test_reload_query_module_in_memory_cluster: &test_reload_query_module_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"] args: *args
log_file: "py-query-modules-reloading-e2e.log" log_file: "py-query-modules-reloading-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_querie: []
disk_test_reload_query_module: &disk_test_reload_query_module
disk_test_reload_query_module_disk_cluster: &disk_test_reload_query_module_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"] args: *args
log_file: "py-query-modules-reloading-e2e.log" log_file: "py-query-modules-reloading-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"] setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: [] validation_queries: []
workloads: workloads:
- name: "test-reload-query-module" # should be the same as the python file - name: "test-reload-query-module"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/python_query_modules_reloading/procedures/" proc: "tests/e2e/python_query_modules_reloading/procedures/"
args: ["python_query_modules_reloading/test_reload_query_module.py"] args: ["python_query_modules_reloading/test_reload_query_module.py"]
<<: *test_reload_query_module <<: *test_reload_query_module_in_memory_cluster
- name: "test-reload-query-module on disk" # should be the same as the python file
- name: "test-reload-query-module on disk"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/python_query_modules_reloading/procedures/" proc: "tests/e2e/python_query_modules_reloading/procedures/"
args: ["python_query_modules_reloading/test_reload_query_module.py"] args: ["python_query_modules_reloading/test_reload_query_module.py"]
<<: *disk_test_reload_query_module <<: *disk_test_reload_query_module_disk_cluster

View File

@@ -1,24 +1,62 @@
template_cluster: &template_cluster stream_args: &stream_args
- "--bolt-port"
- "7687"
- "--log-level=DEBUG"
- "--kafka-bootstrap-servers=localhost:9092"
- "--query-execution-timeout-sec=0"
- "--pulsar-service-url=pulsar://127.0.0.1:6650"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=DEBUG", "--kafka-bootstrap-servers=localhost:9092", "--query-execution-timeout-sec=0", "--pulsar-service-url=pulsar://127.0.0.1:6650"] args: *stream_args
log_file: "streams-e2e.log" log_file: "streams-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_cluster: &disk_cluster
cluster:
main:
args: *stream_args
log_file: "streams-e2e.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: []
workloads: workloads:
- name: "Kafka streams start, stop and show" - name: "Kafka streams start, stop and show"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/streams/transformations/" proc: "tests/e2e/streams/transformations/"
args: ["streams/kafka_streams_tests.py"] args: ["streams/kafka_streams_tests.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Streams with users" - name: "Streams with users"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/streams/transformations/" proc: "tests/e2e/streams/transformations/"
args: ["streams/streams_owner_tests.py"] args: ["streams/streams_owner_tests.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Pulsar streams start, stop and show" - name: "Pulsar streams start, stop and show"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/streams/transformations/" proc: "tests/e2e/streams/transformations/"
args: ["streams/pulsar_streams_tests.py"] args: ["streams/pulsar_streams_tests.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Kafka streams start, stop and show for on-disk storage"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/streams/transformations/"
args: ["streams/kafka_streams_tests.py"]
<<: *disk_cluster
- name: "Streams with users for on-disk storage"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/streams/transformations/"
args: ["streams/streams_owner_tests.py"]
<<: *disk_cluster
- name: "Pulsar streams start, stop and show for on-disk storage"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/streams/transformations/"
args: ["streams/pulsar_streams_tests.py"]
<<: *disk_cluster

View File

@@ -1,17 +1,24 @@
bolt_port: &bolt_port "7687" bolt_port: &bolt_port "7687"
args: &args
- "--bolt_port"
- *bolt_port
- "--log-level=TRACE"
template_cluster: &template_cluster template_cluster: &template_cluster
cluster: cluster:
main: main:
args: ["--bolt_port", *bolt_port, "--log-level=TRACE"] args: *args
log_file: "temporal-types-e2e.log" log_file: "temporal-types-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_template_cluster: &disk_template_cluster disk_template_cluster: &disk_template_cluster
cluster: cluster:
main: main:
args: ["--bolt_port", *bolt_port, "--log-level=TRACE"] args: *args
log_file: "temporal-types-e2e.log" log_file: "temporal-types-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"] setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: [] validation_queries: []
@@ -20,6 +27,7 @@ workloads:
binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip" binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip"
args: ["--bolt_port", *bolt_port] args: ["--bolt_port", *bolt_port]
<<: *template_cluster <<: *template_cluster
- name: "Temporal on disk" - name: "Temporal on disk"
binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip" binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip"
args: ["--bolt_port", *bolt_port] args: ["--bolt_port", *bolt_port]

View File

@@ -1,14 +1,34 @@
test_transaction_queue: &test_transaction_queue args: &args
- "--bolt-port"
- "7687"
- "--log-level=TRACE"
test_transaction_queue_in_memory_cluster: &test_transaction_queue_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"] args: *args
log_file: "transaction_queue.log" log_file: "transaction_queue.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
test_transaction_queue_disk_cluster: &test_transaction_queue_disk_cluster
cluster:
main:
args: *args
log_file: "transaction_queue.log"
setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: []
workloads: workloads:
- name: "test-transaction-queue" # should be the same as the python file - name: "test-transaction-queue"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/transaction_queue/procedures/" proc: "tests/e2e/transaction_queue/procedures/"
args: ["transaction_queue/test_transaction_queue.py"] args: ["transaction_queue/test_transaction_queue.py"]
<<: *test_transaction_queue <<: *test_transaction_queue_in_memory_cluster
- name: "test-transaction-queue for on-disk"
binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/transaction_queue/procedures/"
args: ["transaction_queue/test_transaction_queue.py"]
<<: *test_transaction_queue_disk_cluster

View File

@@ -1,80 +1,106 @@
bolt_port: &bolt_port "7687" bolt_port: &bolt_port "7687"
template_cluster: &template_cluster args_properties_false: &args_properties_false
- "--bolt-port"
- *bolt_port
- "--log-level=TRACE"
- "--storage-properties-on-edges=False"
args_properties_true: &args_properties_true
- "--bolt-port"
- *bolt_port
- "--log-level=TRACE"
- "--storage-properties-on-edges=True"
storage_properties_edges_true_in_memory_cluster: &storage_properties_edges_true_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--storage-properties-on-edges=True"] args: *args_properties_true
log_file: "triggers-e2e.log" log_file: "triggers-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
storage_properties_edges_false: &storage_properties_edges_false
storage_properties_edges_false_in_memory_cluster: &storage_properties_edges_false_in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--also-log-to-stderr", "--storage-properties-on-edges=False"] args: *args_properties_false
log_file: "triggers-e2e.log" log_file: "triggers-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_template_cluster: &disk_template_cluster
storage_properties_edges_true_disk_cluster: &storage_properties_edges_true_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--storage-properties-on-edges=True"] args: *args_properties_true
log_file: "triggers-e2e-disk.log" log_file: "triggers-e2e.log"
setup_queries: ["storage mode on_disk_transactional"] setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: [] validation_queries: []
disk_storage_properties_edges_false: &disk_storage_properties_edges_false
storage_properties_edges_false_disk_cluster: &storage_properties_edges_false_disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--also-log-to-stderr", "--storage-properties-on-edges=False"] args: *args_properties_false
log_file: "triggers-e2e-disk.log" log_file: "triggers-e2e.log"
setup_queries: ["storage mode on_disk_transactional"] setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: [] validation_queries: []
workloads: workloads:
- name: "ON CREATE Triggers" - name: "ON CREATE Triggers"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_create" binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_create"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
<<: *template_cluster <<: *storage_properties_edges_true_in_memory_cluster
- name: "ON UPDATE Triggers" - name: "ON UPDATE Triggers"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_update" binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_update"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
<<: *template_cluster <<: *storage_properties_edges_true_in_memory_cluster
- name: "ON DELETE Triggers Storage Properties On Edges True" - name: "ON DELETE Triggers Storage Properties On Edges True"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete" binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
<<: *template_cluster <<: *storage_properties_edges_true_in_memory_cluster
- name: "Triggers privilege check" - name: "Triggers privilege check"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges" binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
<<: *template_cluster <<: *storage_properties_edges_true_in_memory_cluster
- name: "ON DELETE Triggers Storage Properties On Edges False" # should be the same as the python file
- name: "ON DELETE Triggers Storage Properties On Edges False"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
args: ["triggers/triggers_properties_false.py"] args: ["triggers/triggers_properties_false.py"]
<<: *storage_properties_edges_false <<: *storage_properties_edges_false_in_memory_cluster
- name: "ON CREATE Triggers on disk" - name: "ON CREATE Triggers for disk storage"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_create" binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_create"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
<<: *disk_template_cluster <<: *storage_properties_edges_true_disk_cluster
- name: "ON UPDATE Triggers on disk"
- name: "ON UPDATE Triggers for disk storage"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_update" binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_update"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
<<: *disk_template_cluster <<: *storage_properties_edges_true_disk_cluster
- name: "ON DELETE Triggers Storage Properties On Edges True On Disk"
- name: "ON DELETE Triggers Storage Properties On Edges True for disk storage"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete" binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
<<: *disk_template_cluster <<: *storage_properties_edges_true_disk_cluster
- name: "Triggers privilege check on disk"
- name: "Triggers privilege check for disk storage"
binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges" binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges"
args: ["--bolt-port", *bolt_port] args: ["--bolt-port", *bolt_port]
<<: *disk_template_cluster <<: *storage_properties_edges_true_disk_cluster
- name: "ON DELETE Triggers Storage Properties On Edges False On Disk" # should be the same as the python file
- name: "ON DELETE Triggers Storage Properties On Edges False for disk storage"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/triggers/procedures/" proc: "tests/e2e/triggers/procedures/"
args: ["triggers/triggers_properties_false.py"] args: ["triggers/triggers_properties_false.py"]
<<: *disk_storage_properties_edges_false <<: *storage_properties_edges_false_disk_cluster

View File

@@ -1,16 +1,24 @@
template_cluster: &template_cluster bolt_port: &bolt_port "7687"
args: &args
- "--bolt_port"
- *bolt_port
- "--log-level=TRACE"
in_memory_cluster: &in_memory_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "write-procedures-e2e.log" log_file: "write-procedures-e2e.log"
setup_queries: [] setup_queries: []
validation_queries: [] validation_queries: []
disk_template_cluster: &disk_template_cluster
disk_cluster: &disk_cluster
cluster: cluster:
main: main:
args: ["--bolt-port", "7687", "--log-level=TRACE"] args: *args
log_file: "write-procedures-e2e.log" log_file: "write-procedures-e2e.log"
setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"] setup_queries:
- "STORAGE MODE ON_DISK_TRANSACTIONAL"
validation_queries: [] validation_queries: []
workloads: workloads:
@@ -18,19 +26,22 @@ workloads:
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/write_procedures/procedures/" proc: "tests/e2e/write_procedures/procedures/"
args: ["write_procedures/simple_write.py"] args: ["write_procedures/simple_write.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Graph projection procedures" - name: "Graph projection procedures"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/write_procedures/procedures/" proc: "tests/e2e/write_procedures/procedures/"
args: ["write_procedures/read_subgraph.py"] args: ["write_procedures/read_subgraph.py"]
<<: *template_cluster <<: *in_memory_cluster
- name: "Write procedures simple on disk" - name: "Write procedures simple on disk"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/write_procedures/procedures/" proc: "tests/e2e/write_procedures/procedures/"
args: ["write_procedures/simple_write.py"] args: ["write_procedures/simple_write.py"]
<<: *disk_template_cluster <<: *disk_cluster
- name: "Graph projection procedures on disk" - name: "Graph projection procedures on disk"
binary: "tests/e2e/pytest_runner.sh" binary: "tests/e2e/pytest_runner.sh"
proc: "tests/e2e/write_procedures/procedures/" proc: "tests/e2e/write_procedures/procedures/"
args: ["write_procedures/read_subgraph.py"] args: ["write_procedures/read_subgraph.py"]
<<: *disk_template_cluster <<: *disk_cluster

View File

@@ -0,0 +1,72 @@
# Copyright 2023 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.
from workloads.base import Workload
class HighWriteSetProperty(Workload):
NAME = "high_write_set_property"
CARDINALITY = 100000
def indexes_generator(self):
return [
("CREATE INDEX ON :Node;", {}),
("CREATE INDEX ON :Node(prop1);", {}),
("CREATE INDEX ON :Node(prop2);", {}),
("CREATE INDEX ON :Node(prop3);", {}),
("CREATE INDEX ON :Node(prop4);", {}),
("CREATE INDEX ON :Node(prop5);", {}),
("CREATE INDEX ON :Node(prop6);", {}),
("CREATE INDEX ON :Node(prop7);", {}),
("CREATE INDEX ON :Node(prop8);", {}),
("CREATE INDEX ON :Node(prop9);", {}),
("CREATE INDEX ON :Node(prop10);", {}),
("CREATE INDEX ON :Node(prop11);", {}),
("CREATE INDEX ON :Node(prop12);", {}),
("CREATE INDEX ON :Node(prop13);", {}),
("CREATE INDEX ON :Node(prop14);", {}),
("CREATE INDEX ON :Node(prop15);", {}),
("CREATE INDEX ON :Node(prop16);", {}),
("CREATE INDEX ON :Node(prop17);", {}),
("CREATE INDEX ON :Node(prop18);", {}),
("CREATE INDEX ON :Node(prop19);", {}),
("CREATE INDEX ON :Node(prop20);", {}),
]
def dataset_generator(self):
queries = []
for i in range(0, HighWriteSetProperty.CARDINALITY):
queries.append(
(
"""CREATE
(:Node {prop1: $id, prop2: $id, prop3: $id,
prop4: $id, prop5: $id, prop6: $id, prop7: $id, prop8: $id,
prop9: $id, prop10: $id, prop11: $id, prop12: $id, prop13: $id,
prop14: $id, prop15: $id, prop16: $id, prop17: $id, prop18: $id,
prop19: $id, prop20: $id});
""",
{"id": i},
)
)
return queries
def benchmark__test__write_20_attributes_set_property(self):
return (
"MATCH (n:Node) SET n.prop1 = n.prop2, n.prop2 = n.prop3, n.prop3 = n.prop4, n.prop4 = n.prop5, n.prop5 = n.prop6, n.prop6 = n.prop7, n.prop7 = n.prop8, n.prop8 = n.prop9, n.prop9 = n.prop10, n.prop10 = n.prop11, n.prop11 = n.prop12, n.prop12 = n.prop13, n.prop13 = n.prop14, n.prop14 = n.prop15, n.prop15 = n.prop16, n.prop16 = n.prop17, n.prop17 = n.prop18, n.prop18 = n.prop19, n.prop19 = n.prop20, n.prop20 = n.prop1;",
{},
)
def benchmark__test__write_20_attributes_set_properties(self):
return (
"MATCH (n:Node) SET n += {prop1: n.prop2, prop2: n.prop3, prop3: n.prop4, prop4: n.prop5, prop5: n.prop6, prop6: n.prop7, prop7: n.prop8, prop8: n.prop9, prop9: n.prop10, prop10: n.prop11, prop11: n.prop12, prop12: n.prop13, prop13: n.prop14, prop14: n.prop15, prop15: n.prop16, prop16: n.prop17, prop17: n.prop18, prop18: n.prop19, prop19: n.prop20, prop20: n.prop1};",
{},
)

View File

@@ -0,0 +1,43 @@
# Copyright 2023 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.
from workloads.base import Workload
class Supernode(Workload):
NAME = "supernode"
CARDINALITY = 50000
def indexes_generator(self):
return [
("CREATE INDEX ON :Supernode;", {}),
("CREATE INDEX ON :Supernode(id);", {}),
("CREATE INDEX ON :Node;", {}),
("CREATE INDEX ON :Node(id);", {}),
]
def dataset_generator(self):
queries = []
queries.append(("CREATE (:Supernode {id: $id});", {"id": 1}))
for i in range(0, Supernode.CARDINALITY):
queries.append(("CREATE (:Node {id: $id});", {"id": i}))
queries.append(("MATCH (s:Supernode), (n:Node) CREATE (s)<-[:EDGE]-(n)", {}))
return queries
def benchmark__test__merge_supernode_edges(self):
return ("MATCH (s:Supernode), (n:Node) MERGE (s)<-[:EDGE]-(n);", {})
def benchmark__test__merge_supernode_edges_other_way(self):
return ("MATCH (s:Supernode), (n:Node) MERGE (n)-[:EDGE]->(s);", {})
def benchmark__test__unwind_supernode_with_writes(self):
return (f"UNWIND range(1, {Supernode.CARDINALITY}) AS x MATCH (s:Supernode) SET s.prop = x", {})

View File

@@ -15,6 +15,8 @@
#include <gtest/internal/gtest-type-util.h> #include <gtest/internal/gtest-type-util.h>
#include "disk_test_utils.hpp" #include "disk_test_utils.hpp"
#include "storage/v2/disk/label_index.hpp"
#include "storage/v2/disk/label_property_index.hpp"
#include "storage/v2/disk/storage.hpp" #include "storage/v2/disk/storage.hpp"
#include "storage/v2/inmemory/storage.hpp" #include "storage/v2/inmemory/storage.hpp"
#include "storage/v2/property_value.hpp" #include "storage/v2/property_value.hpp"