Add edge import mode into the on-disk storage (#1157)
This commit is contained in:
parent
09fd5939da
commit
b5413c6f82
@ -158,9 +158,12 @@ class ExplicitTransactionUsageException : public QueryRuntimeException {
|
|||||||
using QueryRuntimeException::QueryRuntimeException;
|
using QueryRuntimeException::QueryRuntimeException;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
class WriteVertexOperationInEdgeImportModeException : public QueryException {
|
||||||
* An exception for serialization error
|
public:
|
||||||
*/
|
WriteVertexOperationInEdgeImportModeException()
|
||||||
|
: QueryException("Write operations on vertices are forbidden while the edge import mode is active.") {}
|
||||||
|
};
|
||||||
|
|
||||||
class TransactionSerializationException : public QueryException {
|
class TransactionSerializationException : public QueryException {
|
||||||
public:
|
public:
|
||||||
using QueryException::QueryException;
|
using QueryException::QueryException;
|
||||||
@ -271,6 +274,12 @@ class StorageModeModificationInMulticommandTxException : public QueryException {
|
|||||||
: QueryException("Storage mode cannot be modified in multicommand transactions.") {}
|
: QueryException("Storage mode cannot be modified in multicommand transactions.") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EdgeImportModeModificationInMulticommandTxException : public QueryException {
|
||||||
|
public:
|
||||||
|
EdgeImportModeModificationInMulticommandTxException()
|
||||||
|
: QueryException("Edge import mode cannot be modified in multicommand transactions.") {}
|
||||||
|
};
|
||||||
|
|
||||||
class CreateSnapshotInMulticommandTxException final : public QueryException {
|
class CreateSnapshotInMulticommandTxException final : public QueryException {
|
||||||
public:
|
public:
|
||||||
CreateSnapshotInMulticommandTxException()
|
CreateSnapshotInMulticommandTxException()
|
||||||
@ -282,6 +291,12 @@ class CreateSnapshotDisabledOnDiskStorage final : public QueryException {
|
|||||||
CreateSnapshotDisabledOnDiskStorage() : QueryException("In the on-disk storage mode data is already persistent.") {}
|
CreateSnapshotDisabledOnDiskStorage() : QueryException("In the on-disk storage mode data is already persistent.") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EdgeImportModeQueryDisabledOnDiskStorage final : public QueryException {
|
||||||
|
public:
|
||||||
|
EdgeImportModeQueryDisabledOnDiskStorage()
|
||||||
|
: QueryException("Edge import mode is only allowed for on-disk storage mode.") {}
|
||||||
|
};
|
||||||
|
|
||||||
class SettingConfigInMulticommandTxException final : public QueryException {
|
class SettingConfigInMulticommandTxException final : public QueryException {
|
||||||
public:
|
public:
|
||||||
SettingConfigInMulticommandTxException()
|
SettingConfigInMulticommandTxException()
|
||||||
|
@ -285,4 +285,8 @@ constexpr utils::TypeInfo query::MultiDatabaseQuery::kType{utils::TypeId::AST_MU
|
|||||||
|
|
||||||
constexpr utils::TypeInfo query::ShowDatabasesQuery::kType{utils::TypeId::AST_SHOW_DATABASES, "ShowDatabasesQuery",
|
constexpr utils::TypeInfo query::ShowDatabasesQuery::kType{utils::TypeId::AST_SHOW_DATABASES, "ShowDatabasesQuery",
|
||||||
&query::Query::kType};
|
&query::Query::kType};
|
||||||
|
|
||||||
|
constexpr utils::TypeInfo query::EdgeImportModeQuery::kType{utils::TypeId::AST_EDGE_IMPORT_MODE_QUERY,
|
||||||
|
"EdgeImportModeQuery", &query::Query::kType};
|
||||||
|
|
||||||
} // namespace memgraph
|
} // namespace memgraph
|
||||||
|
@ -3003,6 +3003,29 @@ class ReplicationQuery : public memgraph::query::Query {
|
|||||||
friend class AstStorage;
|
friend class AstStorage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EdgeImportModeQuery : public memgraph::query::Query {
|
||||||
|
public:
|
||||||
|
static const utils::TypeInfo kType;
|
||||||
|
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||||
|
|
||||||
|
enum class Status { ACTIVE, INACTIVE };
|
||||||
|
|
||||||
|
EdgeImportModeQuery() = default;
|
||||||
|
|
||||||
|
DEFVISITABLE(QueryVisitor<void>);
|
||||||
|
|
||||||
|
memgraph::query::EdgeImportModeQuery::Status status_;
|
||||||
|
|
||||||
|
EdgeImportModeQuery *Clone(AstStorage *storage) const override {
|
||||||
|
auto *object = storage->Create<EdgeImportModeQuery>();
|
||||||
|
object->status_ = status_;
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class AstStorage;
|
||||||
|
};
|
||||||
|
|
||||||
class LockPathQuery : public memgraph::query::Query {
|
class LockPathQuery : public memgraph::query::Query {
|
||||||
public:
|
public:
|
||||||
static const utils::TypeInfo kType;
|
static const utils::TypeInfo kType;
|
||||||
|
@ -105,6 +105,7 @@ class TransactionQueueQuery;
|
|||||||
class Exists;
|
class Exists;
|
||||||
class MultiDatabaseQuery;
|
class MultiDatabaseQuery;
|
||||||
class ShowDatabasesQuery;
|
class ShowDatabasesQuery;
|
||||||
|
class EdgeImportModeQuery;
|
||||||
|
|
||||||
using TreeCompositeVisitor = utils::CompositeVisitor<
|
using TreeCompositeVisitor = utils::CompositeVisitor<
|
||||||
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
||||||
@ -143,6 +144,6 @@ class QueryVisitor
|
|||||||
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
|
ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery, FreeMemoryQuery, TriggerQuery,
|
||||||
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery,
|
IsolationLevelQuery, CreateSnapshotQuery, StreamQuery, SettingQuery, VersionQuery,
|
||||||
ShowConfigQuery, TransactionQueueQuery, StorageModeQuery, AnalyzeGraphQuery,
|
ShowConfigQuery, TransactionQueueQuery, StorageModeQuery, AnalyzeGraphQuery,
|
||||||
MultiDatabaseQuery, ShowDatabasesQuery> {};
|
MultiDatabaseQuery, ShowDatabasesQuery, EdgeImportModeQuery> {};
|
||||||
|
|
||||||
} // namespace memgraph::query
|
} // namespace memgraph::query
|
||||||
|
@ -269,6 +269,17 @@ antlrcpp::Any CypherMainVisitor::visitReplicationQuery(MemgraphCypher::Replicati
|
|||||||
return replication_query;
|
return replication_query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
antlrcpp::Any CypherMainVisitor::visitEdgeImportModeQuery(MemgraphCypher::EdgeImportModeQueryContext *ctx) {
|
||||||
|
auto *edge_import_mode_query = storage_->Create<EdgeImportModeQuery>();
|
||||||
|
if (ctx->ACTIVE()) {
|
||||||
|
edge_import_mode_query->status_ = EdgeImportModeQuery::Status::ACTIVE;
|
||||||
|
} else {
|
||||||
|
edge_import_mode_query->status_ = EdgeImportModeQuery::Status::INACTIVE;
|
||||||
|
}
|
||||||
|
query_ = edge_import_mode_query;
|
||||||
|
return edge_import_mode_query;
|
||||||
|
}
|
||||||
|
|
||||||
antlrcpp::Any CypherMainVisitor::visitSetReplicationRole(MemgraphCypher::SetReplicationRoleContext *ctx) {
|
antlrcpp::Any CypherMainVisitor::visitSetReplicationRole(MemgraphCypher::SetReplicationRoleContext *ctx) {
|
||||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||||
replication_query->action_ = ReplicationQuery::Action::SET_REPLICATION_ROLE;
|
replication_query->action_ = ReplicationQuery::Action::SET_REPLICATION_ROLE;
|
||||||
|
@ -196,6 +196,11 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
|||||||
*/
|
*/
|
||||||
antlrcpp::Any visitReplicationQuery(MemgraphCypher::ReplicationQueryContext *ctx) override;
|
antlrcpp::Any visitReplicationQuery(MemgraphCypher::ReplicationQueryContext *ctx) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return EdgeImportMode*
|
||||||
|
*/
|
||||||
|
antlrcpp::Any visitEdgeImportModeQuery(MemgraphCypher::EdgeImportModeQueryContext *ctx) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ReplicationQuery*
|
* @return ReplicationQuery*
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,7 @@ options { tokenVocab=MemgraphCypherLexer; }
|
|||||||
import Cypher ;
|
import Cypher ;
|
||||||
|
|
||||||
memgraphCypherKeyword : cypherKeyword
|
memgraphCypherKeyword : cypherKeyword
|
||||||
|
| ACTIVE
|
||||||
| AFTER
|
| AFTER
|
||||||
| ALTER
|
| ALTER
|
||||||
| ANALYZE
|
| ANALYZE
|
||||||
@ -48,6 +49,7 @@ memgraphCypherKeyword : cypherKeyword
|
|||||||
| DENY
|
| DENY
|
||||||
| DROP
|
| DROP
|
||||||
| DUMP
|
| DUMP
|
||||||
|
| EDGE
|
||||||
| EDGE_TYPES
|
| EDGE_TYPES
|
||||||
| EXECUTE
|
| EXECUTE
|
||||||
| FOR
|
| FOR
|
||||||
@ -60,9 +62,11 @@ memgraphCypherKeyword : cypherKeyword
|
|||||||
| HEADER
|
| HEADER
|
||||||
| IDENTIFIED
|
| IDENTIFIED
|
||||||
| NULLIF
|
| NULLIF
|
||||||
| ISOLATION
|
| IMPORT
|
||||||
|
| INACTIVE
|
||||||
| IN_MEMORY_ANALYTICAL
|
| IN_MEMORY_ANALYTICAL
|
||||||
| IN_MEMORY_TRANSACTIONAL
|
| IN_MEMORY_TRANSACTIONAL
|
||||||
|
| ISOLATION
|
||||||
| KAFKA
|
| KAFKA
|
||||||
| LABELS
|
| LABELS
|
||||||
| LEVEL
|
| LEVEL
|
||||||
@ -143,6 +147,7 @@ query : cypherQuery
|
|||||||
| transactionQueueQuery
|
| transactionQueueQuery
|
||||||
| multiDatabaseQuery
|
| multiDatabaseQuery
|
||||||
| showDatabases
|
| showDatabases
|
||||||
|
| edgeImportModeQuery
|
||||||
;
|
;
|
||||||
|
|
||||||
authQuery : createRole
|
authQuery : createRole
|
||||||
@ -475,3 +480,5 @@ useDatabase : USE DATABASE databaseName ;
|
|||||||
dropDatabase : DROP DATABASE databaseName ;
|
dropDatabase : DROP DATABASE databaseName ;
|
||||||
|
|
||||||
showDatabases: SHOW DATABASES ;
|
showDatabases: SHOW DATABASES ;
|
||||||
|
|
||||||
|
edgeImportModeQuery : EDGE IMPORT MODE ( ACTIVE | INACTIVE ) ;
|
||||||
|
@ -23,6 +23,7 @@ lexer grammar MemgraphCypherLexer ;
|
|||||||
|
|
||||||
import CypherLexer ;
|
import CypherLexer ;
|
||||||
|
|
||||||
|
ACTIVE : A C T I V E ;
|
||||||
AFTER : A F T E R ;
|
AFTER : A F T E R ;
|
||||||
ALTER : A L T E R ;
|
ALTER : A L T E R ;
|
||||||
ANALYZE : A N A L Y Z E ;
|
ANALYZE : A N A L Y Z E ;
|
||||||
@ -55,6 +56,7 @@ DIRECTORY : D I R E C T O R Y ;
|
|||||||
DROP : D R O P ;
|
DROP : D R O P ;
|
||||||
DUMP : D U M P ;
|
DUMP : D U M P ;
|
||||||
DURABILITY : D U R A B I L I T Y ;
|
DURABILITY : D U R A B I L I T Y ;
|
||||||
|
EDGE : E D G E ;
|
||||||
EDGE_TYPES : E D G E UNDERSCORE T Y P E S ;
|
EDGE_TYPES : E D G E UNDERSCORE T Y P E S ;
|
||||||
EXECUTE : E X E C U T E ;
|
EXECUTE : E X E C U T E ;
|
||||||
FOR : F O R ;
|
FOR : F O R ;
|
||||||
@ -69,9 +71,11 @@ GRANTS : G R A N T S ;
|
|||||||
HEADER : H E A D E R ;
|
HEADER : H E A D E R ;
|
||||||
IDENTIFIED : I D E N T I F I E D ;
|
IDENTIFIED : I D E N T I F I E D ;
|
||||||
IGNORE : I G N O R E ;
|
IGNORE : I G N O R E ;
|
||||||
ISOLATION : I S O L A T I O N ;
|
IMPORT : I M P O R T ;
|
||||||
|
INACTIVE : I N A C T I V E ;
|
||||||
IN_MEMORY_ANALYTICAL : I N UNDERSCORE M E M O R Y UNDERSCORE A N A L Y T I C A L ;
|
IN_MEMORY_ANALYTICAL : I N UNDERSCORE M E M O R Y UNDERSCORE A N A L Y T I C A L ;
|
||||||
IN_MEMORY_TRANSACTIONAL : I N UNDERSCORE M E M O R Y UNDERSCORE T R A N S A C T I O N A L ;
|
IN_MEMORY_TRANSACTIONAL : I N UNDERSCORE M E M O R Y UNDERSCORE T R A N S A C T I O N A L ;
|
||||||
|
ISOLATION : I S O L A T I O N ;
|
||||||
KAFKA : K A F K A ;
|
KAFKA : K A F K A ;
|
||||||
LABELS : L A B E L S ;
|
LABELS : L A B E L S ;
|
||||||
LEVEL : L E V E L ;
|
LEVEL : L E V E L ;
|
||||||
|
@ -87,6 +87,8 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
|||||||
|
|
||||||
void Visit(TransactionQueueQuery & /*transaction_queue_query*/) override {}
|
void Visit(TransactionQueueQuery & /*transaction_queue_query*/) override {}
|
||||||
|
|
||||||
|
void Visit(EdgeImportModeQuery & /*edge_import_mode_query*/) override {}
|
||||||
|
|
||||||
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
|
void Visit(VersionQuery & /*version_query*/) override { AddPrivilege(AuthQuery::Privilege::STATS); }
|
||||||
|
|
||||||
void Visit(MultiDatabaseQuery &query) override {
|
void Visit(MultiDatabaseQuery &query) override {
|
||||||
|
@ -218,7 +218,7 @@ const trie::Trie kKeywords = {"union",
|
|||||||
"data",
|
"data",
|
||||||
"directory",
|
"directory",
|
||||||
"lock",
|
"lock",
|
||||||
"unlock"
|
"unlock",
|
||||||
"build"};
|
"build"};
|
||||||
|
|
||||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "storage/v2/disk/storage.hpp"
|
#include "storage/v2/disk/storage.hpp"
|
||||||
#include "storage/v2/edge.hpp"
|
#include "storage/v2/edge.hpp"
|
||||||
|
#include "storage/v2/edge_import_mode.hpp"
|
||||||
#include "storage/v2/id_types.hpp"
|
#include "storage/v2/id_types.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"
|
||||||
@ -2459,6 +2460,13 @@ constexpr auto ToStorageMode(const StorageModeQuery::StorageMode storage_mode) n
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr auto ToEdgeImportMode(const EdgeImportModeQuery::Status status) noexcept {
|
||||||
|
if (status == EdgeImportModeQuery::Status::ACTIVE) {
|
||||||
|
return storage::EdgeImportMode::ACTIVE;
|
||||||
|
}
|
||||||
|
return storage::EdgeImportMode::INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
bool SwitchingFromInMemoryToDisk(storage::StorageMode current_mode, storage::StorageMode next_mode) {
|
bool SwitchingFromInMemoryToDisk(storage::StorageMode current_mode, storage::StorageMode next_mode) {
|
||||||
return (current_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL ||
|
return (current_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL ||
|
||||||
current_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL) &&
|
current_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL) &&
|
||||||
@ -2602,6 +2610,37 @@ PreparedQuery PrepareStorageModeQuery(ParsedQuery parsed_query, const bool in_ex
|
|||||||
RWType::NONE};
|
RWType::NONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PreparedQuery PrepareEdgeImportModeQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
|
||||||
|
InterpreterContext *interpreter_context) {
|
||||||
|
if (in_explicit_transaction) {
|
||||||
|
throw EdgeImportModeModificationInMulticommandTxException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interpreter_context->db->GetStorageMode() != storage::StorageMode::ON_DISK_TRANSACTIONAL) {
|
||||||
|
throw EdgeImportModeQueryDisabledOnDiskStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *edge_import_mode_query = utils::Downcast<EdgeImportModeQuery>(parsed_query.query);
|
||||||
|
MG_ASSERT(edge_import_mode_query);
|
||||||
|
const auto requested_status = ToEdgeImportMode(edge_import_mode_query->status_);
|
||||||
|
|
||||||
|
auto callback = [requested_status, interpreter_context]() -> std::function<void()> {
|
||||||
|
return [interpreter_context, requested_status] {
|
||||||
|
auto *disk_storage = static_cast<storage::DiskStorage *>(interpreter_context->db.get());
|
||||||
|
disk_storage->SetEdgeImportMode(requested_status);
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
return PreparedQuery{{},
|
||||||
|
std::move(parsed_query.required_privileges),
|
||||||
|
[callback = std::move(callback)](AnyStream * /*stream*/,
|
||||||
|
std::optional<int> /*n*/) -> std::optional<QueryHandlerResult> {
|
||||||
|
callback();
|
||||||
|
return QueryHandlerResult::COMMIT;
|
||||||
|
},
|
||||||
|
RWType::NONE};
|
||||||
|
}
|
||||||
|
|
||||||
PreparedQuery PrepareCreateSnapshotQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
PreparedQuery PrepareCreateSnapshotQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||||
InterpreterContext *interpreter_context) {
|
InterpreterContext *interpreter_context) {
|
||||||
if (in_explicit_transaction) {
|
if (in_explicit_transaction) {
|
||||||
@ -3645,6 +3684,9 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
|||||||
} else if (utils::Downcast<ShowDatabasesQuery>(parsed_query.query)) {
|
} else if (utils::Downcast<ShowDatabasesQuery>(parsed_query.query)) {
|
||||||
prepared_query =
|
prepared_query =
|
||||||
PrepareShowDatabasesQuery(std::move(parsed_query), interpreter_context_, session_uuid, username_);
|
PrepareShowDatabasesQuery(std::move(parsed_query), interpreter_context_, session_uuid, username_);
|
||||||
|
} else if (utils::Downcast<EdgeImportModeQuery>(parsed_query.query)) {
|
||||||
|
prepared_query =
|
||||||
|
PrepareEdgeImportModeQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
|
||||||
} else {
|
} else {
|
||||||
LOG_FATAL("Should not get here -- unknown query type!");
|
LOG_FATAL("Should not get here -- unknown query type!");
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ add_library(mg-storage-v2 STATIC
|
|||||||
inmemory/label_index.cpp
|
inmemory/label_index.cpp
|
||||||
inmemory/label_property_index.cpp
|
inmemory/label_property_index.cpp
|
||||||
inmemory/unique_constraints.cpp
|
inmemory/unique_constraints.cpp
|
||||||
|
disk/edge_import_mode_cache.cpp
|
||||||
disk/storage.cpp
|
disk/storage.cpp
|
||||||
disk/rocksdb_storage.cpp
|
disk/rocksdb_storage.cpp
|
||||||
disk/label_index.cpp
|
disk/label_index.cpp
|
||||||
|
84
src/storage/v2/disk/edge_import_mode_cache.cpp
Normal file
84
src/storage/v2/disk/edge_import_mode_cache.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// 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/disk//edge_import_mode_cache.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include "storage/v2/disk/label_property_index.hpp"
|
||||||
|
#include "storage/v2/indices/indices.hpp"
|
||||||
|
#include "storage/v2/inmemory/label_index.hpp"
|
||||||
|
#include "storage/v2/mvcc.hpp"
|
||||||
|
#include "storage/v2/storage_mode.hpp"
|
||||||
|
#include "storage/v2/transaction.hpp"
|
||||||
|
#include "utils/algorithm.hpp"
|
||||||
|
#include "utils/disk_utils.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::storage {
|
||||||
|
|
||||||
|
EdgeImportModeCache::EdgeImportModeCache(const Config &config)
|
||||||
|
: in_memory_indices_(Indices(config, StorageMode::IN_MEMORY_TRANSACTIONAL)) {}
|
||||||
|
|
||||||
|
InMemoryLabelIndex::Iterable EdgeImportModeCache::Vertices(LabelId label, View view, Transaction *transaction,
|
||||||
|
Constraints *constraints) const {
|
||||||
|
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(in_memory_indices_.label_index_.get());
|
||||||
|
return mem_label_index->Vertices(label, view, transaction, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
InMemoryLabelPropertyIndex::Iterable EdgeImportModeCache::Vertices(
|
||||||
|
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
|
||||||
|
Constraints *constraints) const {
|
||||||
|
auto *mem_label_property_index =
|
||||||
|
static_cast<InMemoryLabelPropertyIndex *>(in_memory_indices_.label_property_index_.get());
|
||||||
|
return mem_label_property_index->Vertices(label, property, lower_bound, upper_bound, view, transaction, constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeImportModeCache::CreateIndex(LabelId label, PropertyId property,
|
||||||
|
const std::optional<ParallelizedIndexCreationInfo> ¶llel_exec_info) {
|
||||||
|
auto *mem_label_property_index =
|
||||||
|
static_cast<InMemoryLabelPropertyIndex *>(in_memory_indices_.label_property_index_.get());
|
||||||
|
bool res = mem_label_property_index->CreateIndex(label, property, vertices_.access(), parallel_exec_info);
|
||||||
|
if (res) {
|
||||||
|
scanned_label_properties_.insert({label, property});
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeImportModeCache::CreateIndex(LabelId label,
|
||||||
|
const std::optional<ParallelizedIndexCreationInfo> ¶llel_exec_info) {
|
||||||
|
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(in_memory_indices_.label_index_.get());
|
||||||
|
bool res = mem_label_index->CreateIndex(label, vertices_.access(), parallel_exec_info);
|
||||||
|
if (res) {
|
||||||
|
scanned_labels_.insert(label);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeImportModeCache::VerticesWithLabelPropertyScanned(LabelId label, PropertyId property) const {
|
||||||
|
return VerticesWithLabelScanned(label) || utils::Contains(scanned_label_properties_, std::make_pair(label, property));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeImportModeCache::VerticesWithLabelScanned(LabelId label) const {
|
||||||
|
return AllVerticesScanned() || utils::Contains(scanned_labels_, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeImportModeCache::AllVerticesScanned() const { return scanned_all_vertices_; }
|
||||||
|
|
||||||
|
utils::SkipList<Vertex>::Accessor EdgeImportModeCache::AccessToVertices() { return vertices_.access(); }
|
||||||
|
|
||||||
|
utils::SkipList<Edge>::Accessor EdgeImportModeCache::AccessToEdges() { return edges_.access(); }
|
||||||
|
|
||||||
|
void EdgeImportModeCache::SetScannedAllVertices() { scanned_all_vertices_ = true; }
|
||||||
|
|
||||||
|
utils::Synchronized<std::list<Transaction>, utils::SpinLock> &EdgeImportModeCache::GetCommittedTransactions() {
|
||||||
|
return committed_transactions_;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace memgraph::storage
|
74
src/storage/v2/disk/edge_import_mode_cache.hpp
Normal file
74
src/storage/v2/disk/edge_import_mode_cache.hpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// 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 "storage/v2/delta.hpp"
|
||||||
|
#include "storage/v2/disk/label_index.hpp"
|
||||||
|
#include "storage/v2/disk/label_property_index.hpp"
|
||||||
|
#include "storage/v2/id_types.hpp"
|
||||||
|
#include "storage/v2/indices/indices.hpp"
|
||||||
|
#include "storage/v2/inmemory/label_index.hpp"
|
||||||
|
#include "storage/v2/inmemory/label_property_index.hpp"
|
||||||
|
#include "storage/v2/transaction.hpp"
|
||||||
|
#include "storage/v2/vertex.hpp"
|
||||||
|
#include "utils/skip_list.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::storage {
|
||||||
|
|
||||||
|
class EdgeImportModeCache final {
|
||||||
|
public:
|
||||||
|
explicit EdgeImportModeCache(const Config &config);
|
||||||
|
|
||||||
|
EdgeImportModeCache(const EdgeImportModeCache &) = delete;
|
||||||
|
EdgeImportModeCache &operator=(const EdgeImportModeCache &) = delete;
|
||||||
|
EdgeImportModeCache(EdgeImportModeCache &&) = delete;
|
||||||
|
EdgeImportModeCache &operator=(EdgeImportModeCache &&) = delete;
|
||||||
|
~EdgeImportModeCache() = default;
|
||||||
|
|
||||||
|
InMemoryLabelIndex::Iterable Vertices(LabelId label, View view, Transaction *transaction,
|
||||||
|
Constraints *constraints) const;
|
||||||
|
|
||||||
|
InMemoryLabelPropertyIndex::Iterable Vertices(LabelId label, PropertyId property,
|
||||||
|
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound,
|
||||||
|
View view, Transaction *transaction, Constraints *constraints) const;
|
||||||
|
|
||||||
|
bool CreateIndex(LabelId label, PropertyId property,
|
||||||
|
const std::optional<ParallelizedIndexCreationInfo> ¶llel_exec_info = {});
|
||||||
|
|
||||||
|
bool CreateIndex(LabelId label, const std::optional<ParallelizedIndexCreationInfo> ¶llel_exec_info = {});
|
||||||
|
|
||||||
|
bool VerticesWithLabelPropertyScanned(LabelId label, PropertyId property) const;
|
||||||
|
|
||||||
|
bool VerticesWithLabelScanned(LabelId label) const;
|
||||||
|
|
||||||
|
bool AllVerticesScanned() const;
|
||||||
|
|
||||||
|
utils::SkipList<Vertex>::Accessor AccessToVertices();
|
||||||
|
|
||||||
|
utils::SkipList<Edge>::Accessor AccessToEdges();
|
||||||
|
|
||||||
|
void SetScannedAllVertices();
|
||||||
|
|
||||||
|
utils::Synchronized<std::list<Transaction>, utils::SpinLock> &GetCommittedTransactions();
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::SkipList<Vertex> vertices_;
|
||||||
|
utils::SkipList<Edge> edges_;
|
||||||
|
Indices in_memory_indices_;
|
||||||
|
bool scanned_all_vertices_{false};
|
||||||
|
std::set<LabelId> scanned_labels_;
|
||||||
|
std::set<std::pair<LabelId, PropertyId>> scanned_label_properties_;
|
||||||
|
utils::Synchronized<std::list<Transaction>, utils::SpinLock> committed_transactions_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace memgraph::storage
|
@ -45,8 +45,7 @@ bool CommitWithTimestamp(rocksdb::Transaction *disk_transaction, uint64_t commit
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DiskLabelIndex::DiskLabelIndex(Indices *indices, Constraints *constraints, const Config &config)
|
DiskLabelIndex::DiskLabelIndex(Indices *indices, const Config &config) : LabelIndex(indices, config) {
|
||||||
: LabelIndex(indices, constraints, config) {
|
|
||||||
utils::EnsureDirOrDie(config.disk.label_index_directory);
|
utils::EnsureDirOrDie(config.disk.label_index_directory);
|
||||||
kvstore_ = std::make_unique<RocksDBStorage>();
|
kvstore_ = std::make_unique<RocksDBStorage>();
|
||||||
kvstore_->options_.create_if_missing = true;
|
kvstore_->options_.create_if_missing = true;
|
||||||
@ -216,4 +215,6 @@ void DiskLabelIndex::LoadIndexInfo(const std::vector<std::string> &labels) {
|
|||||||
|
|
||||||
RocksDBStorage *DiskLabelIndex::GetRocksDBStorage() const { return kvstore_.get(); }
|
RocksDBStorage *DiskLabelIndex::GetRocksDBStorage() const { return kvstore_.get(); }
|
||||||
|
|
||||||
|
std::unordered_set<LabelId> DiskLabelIndex::GetInfo() const { return index_; }
|
||||||
|
|
||||||
} // namespace memgraph::storage
|
} // namespace memgraph::storage
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
// by the Apache License, Version 2.0, included in the file
|
// by the Apache License, Version 2.0, included in the file
|
||||||
// licenses/APL.txt.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <rocksdb/iterator.h>
|
#include <rocksdb/iterator.h>
|
||||||
#include <rocksdb/utilities/transaction.h>
|
#include <rocksdb/utilities/transaction.h>
|
||||||
|
|
||||||
@ -21,7 +23,7 @@
|
|||||||
namespace memgraph::storage {
|
namespace memgraph::storage {
|
||||||
class DiskLabelIndex : public storage::LabelIndex {
|
class DiskLabelIndex : public storage::LabelIndex {
|
||||||
public:
|
public:
|
||||||
DiskLabelIndex(Indices *indices, Constraints *constraints, const Config &config);
|
DiskLabelIndex(Indices *indices, const Config &config);
|
||||||
|
|
||||||
[[nodiscard]] bool CreateIndex(LabelId label, const std::vector<std::pair<std::string, std::string>> &vertices);
|
[[nodiscard]] bool CreateIndex(LabelId label, const std::vector<std::pair<std::string, std::string>> &vertices);
|
||||||
|
|
||||||
@ -53,6 +55,8 @@ class DiskLabelIndex : public storage::LabelIndex {
|
|||||||
|
|
||||||
void LoadIndexInfo(const std::vector<std::string> &labels);
|
void LoadIndexInfo(const std::vector<std::string> &labels);
|
||||||
|
|
||||||
|
std::unordered_set<LabelId> GetInfo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
utils::Synchronized<std::map<uint64_t, std::map<Gid, std::vector<LabelId>>>> entries_for_deletion;
|
utils::Synchronized<std::map<uint64_t, std::map<Gid, std::vector<LabelId>>>> entries_for_deletion;
|
||||||
std::unordered_set<LabelId> index_;
|
std::unordered_set<LabelId> index_;
|
||||||
|
@ -49,8 +49,8 @@ bool CommitWithTimestamp(rocksdb::Transaction *disk_transaction, uint64_t commit
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DiskLabelPropertyIndex::DiskLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config)
|
DiskLabelPropertyIndex::DiskLabelPropertyIndex(Indices *indices, const Config &config)
|
||||||
: LabelPropertyIndex(indices, constraints, config) {
|
: LabelPropertyIndex(indices, config) {
|
||||||
utils::EnsureDirOrDie(config.disk.label_property_index_directory);
|
utils::EnsureDirOrDie(config.disk.label_property_index_directory);
|
||||||
kvstore_ = std::make_unique<RocksDBStorage>();
|
kvstore_ = std::make_unique<RocksDBStorage>();
|
||||||
kvstore_->options_.create_if_missing = true;
|
kvstore_->options_.create_if_missing = true;
|
||||||
@ -223,4 +223,6 @@ void DiskLabelPropertyIndex::LoadIndexInfo(const std::vector<std::string> &keys)
|
|||||||
|
|
||||||
RocksDBStorage *DiskLabelPropertyIndex::GetRocksDBStorage() const { return kvstore_.get(); }
|
RocksDBStorage *DiskLabelPropertyIndex::GetRocksDBStorage() const { return kvstore_.get(); }
|
||||||
|
|
||||||
|
std::set<std::pair<LabelId, PropertyId>> DiskLabelPropertyIndex::GetInfo() const { return index_; }
|
||||||
|
|
||||||
} // namespace memgraph::storage
|
} // namespace memgraph::storage
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
// by the Apache License, Version 2.0, included in the file
|
// by the Apache License, Version 2.0, included in the file
|
||||||
// licenses/APL.txt.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "storage/v2/disk/rocksdb_storage.hpp"
|
#include "storage/v2/disk/rocksdb_storage.hpp"
|
||||||
#include "storage/v2/indices/label_property_index.hpp"
|
#include "storage/v2/indices/label_property_index.hpp"
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ using ParallelizedIndexCreationInfo =
|
|||||||
|
|
||||||
class DiskLabelPropertyIndex : public storage::LabelPropertyIndex {
|
class DiskLabelPropertyIndex : public storage::LabelPropertyIndex {
|
||||||
public:
|
public:
|
||||||
DiskLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config);
|
DiskLabelPropertyIndex(Indices *indices, const Config &config);
|
||||||
|
|
||||||
bool CreateIndex(LabelId label, PropertyId property,
|
bool CreateIndex(LabelId label, PropertyId property,
|
||||||
const std::vector<std::pair<std::string, std::string>> &vertices);
|
const std::vector<std::pair<std::string, std::string>> &vertices);
|
||||||
@ -61,6 +63,8 @@ class DiskLabelPropertyIndex : public storage::LabelPropertyIndex {
|
|||||||
|
|
||||||
void LoadIndexInfo(const std::vector<std::string> &keys);
|
void LoadIndexInfo(const std::vector<std::string> &keys);
|
||||||
|
|
||||||
|
std::set<std::pair<LabelId, PropertyId>> GetInfo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
utils::Synchronized<std::map<uint64_t, std::map<Gid, std::vector<std::pair<LabelId, PropertyId>>>>>
|
utils::Synchronized<std::map<uint64_t, std::map<Gid, std::vector<std::pair<LabelId, PropertyId>>>>>
|
||||||
entries_for_deletion;
|
entries_for_deletion;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
// licenses/APL.txt.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
#include "rocksdb_storage.hpp"
|
#include "rocksdb_storage.hpp"
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "utils/rocksdb_serialization.hpp"
|
#include "utils/rocksdb_serialization.hpp"
|
||||||
|
|
||||||
@ -80,15 +81,6 @@ int ComparatorWithU64TsImpl::CompareTimestamp(const rocksdb::Slice &ts1, const r
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DiskEdgeKey::DiskEdgeKey(storage::EdgeAccessor *edge_acc) {
|
|
||||||
auto from_gid = utils::SerializeIdType(edge_acc->FromVertex().Gid());
|
|
||||||
auto to_gid = utils::SerializeIdType(edge_acc->ToVertex().Gid());
|
|
||||||
auto edge_type = utils::SerializeIdType(edge_acc->EdgeType());
|
|
||||||
auto edge_gid = utils::SerializeIdType(edge_acc->Gid());
|
|
||||||
|
|
||||||
key = fmt::format("{}|{}|{}|{}|{}", from_gid, to_gid, utils::outEdgeDirection, edge_type, edge_gid);
|
|
||||||
}
|
|
||||||
|
|
||||||
DiskEdgeKey::DiskEdgeKey(storage::Gid src_vertex_gid, storage::Gid dest_vertex_gid, storage::EdgeTypeId edge_type_id,
|
DiskEdgeKey::DiskEdgeKey(storage::Gid src_vertex_gid, storage::Gid dest_vertex_gid, storage::EdgeTypeId edge_type_id,
|
||||||
const storage::EdgeRef edge_ref, bool properties_on_edges) {
|
const storage::EdgeRef edge_ref, bool properties_on_edges) {
|
||||||
auto from_gid = utils::SerializeIdType(src_vertex_gid);
|
auto from_gid = utils::SerializeIdType(src_vertex_gid);
|
||||||
@ -105,6 +97,10 @@ DiskEdgeKey::DiskEdgeKey(storage::Gid src_vertex_gid, storage::Gid dest_vertex_g
|
|||||||
key = fmt::format("{}|{}|{}|{}|{}", from_gid, to_gid, utils::outEdgeDirection, edge_type, edge_gid);
|
key = fmt::format("{}|{}|{}|{}|{}", from_gid, to_gid, utils::outEdgeDirection, edge_type, edge_gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DiskEdgeKey::DiskEdgeKey(const ModifiedEdgeInfo &edge_info, bool properties_on_edges)
|
||||||
|
: DiskEdgeKey(edge_info.src_vertex_gid, edge_info.dest_vertex_gid, edge_info.edge_type_id, edge_info.edge_ref,
|
||||||
|
properties_on_edges) {}
|
||||||
|
|
||||||
std::string DiskEdgeKey::GetVertexOutGid() const { return key.substr(0, key.find('|')); }
|
std::string DiskEdgeKey::GetVertexOutGid() const { return key.substr(0, key.find('|')); }
|
||||||
|
|
||||||
std::string DiskEdgeKey::GetVertexInGid() const {
|
std::string DiskEdgeKey::GetVertexInGid() const {
|
||||||
|
@ -18,9 +18,10 @@
|
|||||||
#include <rocksdb/status.h>
|
#include <rocksdb/status.h>
|
||||||
#include <rocksdb/utilities/transaction_db.h>
|
#include <rocksdb/utilities/transaction_db.h>
|
||||||
|
|
||||||
#include "storage/v2/edge_accessor.hpp"
|
|
||||||
#include "storage/v2/edge_direction.hpp"
|
#include "storage/v2/edge_direction.hpp"
|
||||||
|
#include "storage/v2/edge_ref.hpp"
|
||||||
#include "storage/v2/id_types.hpp"
|
#include "storage/v2/id_types.hpp"
|
||||||
|
#include "storage/v2/modified_edge.hpp"
|
||||||
#include "storage/v2/property_store.hpp"
|
#include "storage/v2/property_store.hpp"
|
||||||
#include "utils/logging.hpp"
|
#include "utils/logging.hpp"
|
||||||
|
|
||||||
@ -79,14 +80,14 @@ class ComparatorWithU64TsImpl : public rocksdb::Comparator {
|
|||||||
struct DiskEdgeKey {
|
struct DiskEdgeKey {
|
||||||
DiskEdgeKey(const std::string_view keyView) : key(keyView) {}
|
DiskEdgeKey(const std::string_view keyView) : key(keyView) {}
|
||||||
|
|
||||||
DiskEdgeKey(EdgeAccessor *edge_acc);
|
|
||||||
|
|
||||||
/// @tparam src_vertex_gid, dest_vertex_gid: Gid of the source and destination vertices
|
/// @tparam src_vertex_gid, dest_vertex_gid: Gid of the source and destination vertices
|
||||||
/// @tparam edge_type_id: EdgeTypeId of the edge
|
/// @tparam edge_type_id: EdgeTypeId of the edge
|
||||||
/// @tparam edge_ref: Edge to be serialized
|
/// @tparam edge_ref: Edge to be serialized
|
||||||
DiskEdgeKey(Gid src_vertex_gid, storage::Gid dest_vertex_gid, storage::EdgeTypeId edge_type_id,
|
DiskEdgeKey(Gid src_vertex_gid, storage::Gid dest_vertex_gid, storage::EdgeTypeId edge_type_id,
|
||||||
const EdgeRef edge_ref, bool properties_on_edges);
|
const EdgeRef edge_ref, bool properties_on_edges);
|
||||||
|
|
||||||
|
DiskEdgeKey(const ModifiedEdgeInfo &edge_info, bool properties_on_edges);
|
||||||
|
|
||||||
std::string GetSerializedKey() const { return key; }
|
std::string GetSerializedKey() const { return key; }
|
||||||
|
|
||||||
std::string GetVertexOutGid() const;
|
std::string GetVertexOutGid() const;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
#include "kvstore/kvstore.hpp"
|
#include "kvstore/kvstore.hpp"
|
||||||
#include "storage/v2/constraints/constraint_violation.hpp"
|
#include "storage/v2/constraints/constraint_violation.hpp"
|
||||||
|
#include "storage/v2/disk/edge_import_mode_cache.hpp"
|
||||||
#include "storage/v2/disk/rocksdb_storage.hpp"
|
#include "storage/v2/disk/rocksdb_storage.hpp"
|
||||||
|
#include "storage/v2/edge_import_mode.hpp"
|
||||||
#include "storage/v2/id_types.hpp"
|
#include "storage/v2/id_types.hpp"
|
||||||
#include "storage/v2/isolation_level.hpp"
|
#include "storage/v2/isolation_level.hpp"
|
||||||
#include "storage/v2/property_store.hpp"
|
#include "storage/v2/property_store.hpp"
|
||||||
@ -44,6 +46,55 @@ class DiskStorage final : public Storage {
|
|||||||
|
|
||||||
explicit DiskAccessor(DiskStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
|
explicit DiskAccessor(DiskStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
|
||||||
|
|
||||||
|
/// TODO: const methods?
|
||||||
|
void LoadVerticesToMainMemoryCache();
|
||||||
|
|
||||||
|
void LoadVerticesFromMainStorageToEdgeImportCache();
|
||||||
|
|
||||||
|
void HandleMainLoadingForEdgeImportCache();
|
||||||
|
|
||||||
|
void LoadVerticesFromLabelIndexStorageToEdgeImportCache(LabelId label);
|
||||||
|
|
||||||
|
void HandleLoadingLabelForEdgeImportCache(LabelId label);
|
||||||
|
|
||||||
|
void LoadVerticesFromLabelPropertyIndexStorageToEdgeImportCache(LabelId label, PropertyId property);
|
||||||
|
|
||||||
|
void HandleLoadingLabelPropertyForEdgeImportCache(LabelId label, PropertyId property);
|
||||||
|
|
||||||
|
std::unordered_set<Gid> MergeVerticesFromMainCacheWithLabelIndexCache(LabelId label, View view,
|
||||||
|
std::list<Delta> &index_deltas,
|
||||||
|
utils::SkipList<Vertex> *indexed_vertices);
|
||||||
|
|
||||||
|
void LoadVerticesFromDiskLabelIndex(LabelId label, const std::unordered_set<storage::Gid> &gids,
|
||||||
|
std::list<Delta> &index_deltas, utils::SkipList<Vertex> *indexed_vertices);
|
||||||
|
|
||||||
|
std::unordered_set<Gid> MergeVerticesFromMainCacheWithLabelPropertyIndexCache(
|
||||||
|
LabelId label, PropertyId property, View view, std::list<Delta> &index_deltas,
|
||||||
|
utils::SkipList<Vertex> *indexed_vertices, const auto &label_property_filter);
|
||||||
|
|
||||||
|
void LoadVerticesFromDiskLabelPropertyIndex(LabelId label, PropertyId property,
|
||||||
|
const std::unordered_set<storage::Gid> &gids,
|
||||||
|
std::list<Delta> &index_deltas,
|
||||||
|
utils::SkipList<Vertex> *indexed_vertices,
|
||||||
|
const auto &label_property_filter);
|
||||||
|
|
||||||
|
void LoadVerticesFromDiskLabelPropertyIndexWithPointValueLookup(LabelId label, PropertyId property,
|
||||||
|
const std::unordered_set<storage::Gid> &gids,
|
||||||
|
const PropertyValue &value,
|
||||||
|
std::list<Delta> &index_deltas,
|
||||||
|
utils::SkipList<Vertex> *indexed_vertices);
|
||||||
|
|
||||||
|
std::unordered_set<Gid> MergeVerticesFromMainCacheWithLabelPropertyIndexCacheForIntervalSearch(
|
||||||
|
LabelId label, PropertyId property, View view, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, std::list<Delta> &index_deltas,
|
||||||
|
utils::SkipList<Vertex> *indexed_vertices);
|
||||||
|
|
||||||
|
void LoadVerticesFromDiskLabelPropertyIndexForIntervalSearch(
|
||||||
|
LabelId label, PropertyId property, const std::unordered_set<storage::Gid> &gids,
|
||||||
|
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, std::list<Delta> &index_deltas,
|
||||||
|
utils::SkipList<Vertex> *indexed_vertices);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DiskAccessor(const DiskAccessor &) = delete;
|
DiskAccessor(const DiskAccessor &) = delete;
|
||||||
DiskAccessor &operator=(const DiskAccessor &) = delete;
|
DiskAccessor &operator=(const DiskAccessor &) = delete;
|
||||||
@ -61,48 +112,14 @@ class DiskStorage final : public Storage {
|
|||||||
|
|
||||||
VerticesIterable Vertices(LabelId label, View view) override;
|
VerticesIterable Vertices(LabelId label, View view) override;
|
||||||
|
|
||||||
std::unordered_set<Gid> MergeVerticesFromMainCacheWithLabelIndexCache(LabelId label, View view,
|
|
||||||
std::list<Delta> &index_deltas,
|
|
||||||
utils::SkipList<Vertex> *indexed_vertices);
|
|
||||||
|
|
||||||
void LoadVerticesFromDiskLabelIndex(LabelId label, const std::unordered_set<storage::Gid> &gids,
|
|
||||||
std::list<Delta> &index_deltas, utils::SkipList<Vertex> *indexed_vertices);
|
|
||||||
|
|
||||||
VerticesIterable Vertices(LabelId label, PropertyId property, View view) override;
|
VerticesIterable Vertices(LabelId label, PropertyId property, View view) override;
|
||||||
|
|
||||||
std::unordered_set<Gid> MergeVerticesFromMainCacheWithLabelPropertyIndexCache(
|
|
||||||
LabelId label, PropertyId property, View view, std::list<Delta> &index_deltas,
|
|
||||||
utils::SkipList<Vertex> *indexed_vertices, const auto &label_property_filter);
|
|
||||||
|
|
||||||
void LoadVerticesFromDiskLabelPropertyIndex(LabelId label, PropertyId property,
|
|
||||||
const std::unordered_set<storage::Gid> &gids,
|
|
||||||
std::list<Delta> &index_deltas,
|
|
||||||
utils::SkipList<Vertex> *indexed_vertices,
|
|
||||||
const auto &label_property_filter);
|
|
||||||
|
|
||||||
VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view) override;
|
VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view) override;
|
||||||
|
|
||||||
void LoadVerticesFromDiskLabelPropertyIndexWithPointValueLookup(LabelId label, PropertyId property,
|
|
||||||
const std::unordered_set<storage::Gid> &gids,
|
|
||||||
const PropertyValue &value,
|
|
||||||
std::list<Delta> &index_deltas,
|
|
||||||
utils::SkipList<Vertex> *indexed_vertices);
|
|
||||||
|
|
||||||
VerticesIterable Vertices(LabelId label, PropertyId property,
|
VerticesIterable Vertices(LabelId label, PropertyId property,
|
||||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) override;
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) override;
|
||||||
|
|
||||||
std::unordered_set<Gid> MergeVerticesFromMainCacheWithLabelPropertyIndexCacheForIntervalSearch(
|
|
||||||
LabelId label, PropertyId property, View view, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
|
||||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, std::list<Delta> &index_deltas,
|
|
||||||
utils::SkipList<Vertex> *indexed_vertices);
|
|
||||||
|
|
||||||
void LoadVerticesFromDiskLabelPropertyIndexForIntervalSearch(
|
|
||||||
LabelId label, PropertyId property, const std::unordered_set<storage::Gid> &gids,
|
|
||||||
const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
|
||||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, std::list<Delta> &index_deltas,
|
|
||||||
utils::SkipList<Vertex> *indexed_vertices);
|
|
||||||
|
|
||||||
uint64_t ApproximateVertexCount() const override;
|
uint64_t ApproximateVertexCount() const override;
|
||||||
|
|
||||||
uint64_t ApproximateVertexCount(LabelId /*label*/) const override { return 10; }
|
uint64_t ApproximateVertexCount(LabelId /*label*/) const override { return 10; }
|
||||||
@ -200,14 +217,13 @@ class DiskStorage final : public Storage {
|
|||||||
void FinalizeTransaction() override;
|
void FinalizeTransaction() override;
|
||||||
|
|
||||||
std::optional<storage::VertexAccessor> LoadVertexToLabelIndexCache(
|
std::optional<storage::VertexAccessor> LoadVertexToLabelIndexCache(
|
||||||
LabelId indexing_label, std::string &&key, std::string &&value, Delta *index_delta,
|
std::string &&key, std::string &&value, Delta *index_delta,
|
||||||
utils::SkipList<storage::Vertex>::Accessor index_accessor);
|
utils::SkipList<storage::Vertex>::Accessor index_accessor);
|
||||||
|
|
||||||
std::optional<storage::VertexAccessor> LoadVertexToMainMemoryCache(const std::string &key, const std::string &value,
|
std::optional<storage::VertexAccessor> LoadVertexToMainMemoryCache(std::string &&key, std::string &&value,
|
||||||
const std::string &ts);
|
std::string &&ts);
|
||||||
|
|
||||||
std::optional<storage::VertexAccessor> LoadVertexToLabelPropertyIndexCache(
|
std::optional<storage::VertexAccessor> LoadVertexToLabelPropertyIndexCache(
|
||||||
LabelId indexing_label, std::string &&key, std::string &&value, Delta *index_delta,
|
std::string &&key, std::string &&value, Delta *index_delta,
|
||||||
utils::SkipList<storage::Vertex>::Accessor index_accessor);
|
utils::SkipList<storage::Vertex>::Accessor index_accessor);
|
||||||
|
|
||||||
std::optional<storage::EdgeAccessor> DeserializeEdge(const rocksdb::Slice &key, const rocksdb::Slice &value,
|
std::optional<storage::EdgeAccessor> DeserializeEdge(const rocksdb::Slice &key, const rocksdb::Slice &value,
|
||||||
@ -222,31 +238,41 @@ class DiskStorage final : public Storage {
|
|||||||
void PrefetchEdges(const VertexAccessor &vertex_acc, EdgeDirection edge_direction);
|
void PrefetchEdges(const VertexAccessor &vertex_acc, EdgeDirection edge_direction);
|
||||||
|
|
||||||
Result<EdgeAccessor> CreateEdgeFromDisk(const VertexAccessor *from, const VertexAccessor *to, EdgeTypeId edge_type,
|
Result<EdgeAccessor> CreateEdgeFromDisk(const VertexAccessor *from, const VertexAccessor *to, EdgeTypeId edge_type,
|
||||||
storage::Gid gid, std::string_view properties,
|
storage::Gid gid, std::string_view properties, std::string &&old_disk_key,
|
||||||
const std::string &old_disk_key, const std::string &ts);
|
std::string &&ts);
|
||||||
/// Flushes vertices and edges to the disk with the commit timestamp.
|
/// Flushes vertices and edges to the disk with the commit timestamp.
|
||||||
/// At the time of calling, the commit_timestamp_ must already exist.
|
/// At the time of calling, the commit_timestamp_ must already exist.
|
||||||
/// After this method, the vertex and edge caches are cleared.
|
/// After this method, the vertex and edge caches are cleared.
|
||||||
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushMainMemoryCache();
|
|
||||||
|
|
||||||
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushIndexCache();
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushIndexCache();
|
||||||
|
|
||||||
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushDeletedVertices();
|
||||||
|
|
||||||
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushDeletedEdges();
|
||||||
|
|
||||||
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushVertices(
|
||||||
|
const auto &vertex_acc, std::vector<std::vector<PropertyValue>> &unique_storage);
|
||||||
|
|
||||||
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushModifiedEdges(const auto &edge_acc);
|
||||||
|
|
||||||
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> ClearDanglingVertices();
|
||||||
|
|
||||||
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> CheckVertexConstraintsBeforeCommit(
|
[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> CheckVertexConstraintsBeforeCommit(
|
||||||
const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage) const;
|
const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage) const;
|
||||||
|
|
||||||
bool WriteVertexToDisk(const Vertex &vertex);
|
bool WriteVertexToDisk(const Vertex &vertex);
|
||||||
bool WriteEdgeToDisk(EdgeRef edge, const std::string &serializedEdgeKey);
|
bool WriteEdgeToDisk(const std::string &serialized_edge_key, const std::string &serialized_edge_value);
|
||||||
bool DeleteVertexFromDisk(const std::string &vertex);
|
bool DeleteVertexFromDisk(const std::string &vertex);
|
||||||
bool DeleteEdgeFromDisk(const std::string &edge);
|
bool DeleteEdgeFromDisk(const std::string &edge);
|
||||||
|
|
||||||
/// Main storage
|
/// Main storage
|
||||||
utils::SkipList<storage::Vertex> vertices_;
|
utils::SkipList<Vertex> vertices_;
|
||||||
std::vector<std::unique_ptr<utils::SkipList<storage::Vertex>>> index_storage_;
|
std::vector<std::unique_ptr<utils::SkipList<Vertex>>> index_storage_;
|
||||||
|
|
||||||
/// We need them because query context for indexed reading is cleared after the query is done not after the
|
/// We need them because query context for indexed reading is cleared after the query is done not after the
|
||||||
/// transaction is done
|
/// transaction is done
|
||||||
std::vector<std::list<Delta>> index_deltas_storage_;
|
std::vector<std::list<Delta>> index_deltas_storage_;
|
||||||
utils::SkipList<storage::Edge> edges_;
|
utils::SkipList<Edge> edges_;
|
||||||
Config::Items config_;
|
Config::Items config_;
|
||||||
std::unordered_set<std::string> edges_to_delete_;
|
std::unordered_set<std::string> edges_to_delete_;
|
||||||
std::vector<std::pair<std::string, std::string>> vertices_to_delete_;
|
std::vector<std::pair<std::string, std::string>> vertices_to_delete_;
|
||||||
@ -290,6 +316,10 @@ class DiskStorage final : public Storage {
|
|||||||
|
|
||||||
Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) override;
|
Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) override;
|
||||||
|
|
||||||
|
void SetEdgeImportMode(EdgeImportMode edge_import_status);
|
||||||
|
|
||||||
|
EdgeImportMode GetEdgeImportMode() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadIndexInfoIfExists() const;
|
void LoadIndexInfoIfExists() const;
|
||||||
|
|
||||||
@ -339,9 +369,12 @@ class DiskStorage final : public Storage {
|
|||||||
|
|
||||||
void FreeMemory(std::unique_lock<utils::RWLock> /*lock*/) override {}
|
void FreeMemory(std::unique_lock<utils::RWLock> /*lock*/) override {}
|
||||||
|
|
||||||
|
void EstablishNewEpoch() override { throw utils::BasicException("Disk storage mode does not support replication."); }
|
||||||
|
|
||||||
uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
|
uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
|
||||||
|
|
||||||
void EstablishNewEpoch() override { throw utils::BasicException("Disk storage mode does not support replication."); }
|
EdgeImportMode edge_import_status_{EdgeImportMode::INACTIVE};
|
||||||
|
std::unique_ptr<EdgeImportModeCache> edge_import_mode_cache_{nullptr};
|
||||||
|
|
||||||
auto CreateReplicationClient(std::string name, io::network::Endpoint endpoint, replication::ReplicationMode mode,
|
auto CreateReplicationClient(std::string name, io::network::Endpoint endpoint, replication::ReplicationMode mode,
|
||||||
const replication::ReplicationClientConfig &config)
|
const replication::ReplicationClientConfig &config)
|
||||||
|
@ -126,6 +126,11 @@ Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, co
|
|||||||
CreateAndLinkDelta(transaction_, edge_.ptr, Delta::SetPropertyTag(), property, current_value);
|
CreateAndLinkDelta(transaction_, edge_.ptr, Delta::SetPropertyTag(), property, current_value);
|
||||||
edge_.ptr->properties.SetProperty(property, value);
|
edge_.ptr->properties.SetProperty(property, value);
|
||||||
|
|
||||||
|
if (transaction_->IsDiskStorage()) {
|
||||||
|
ModifiedEdgeInfo modified_edge(Delta::Action::SET_PROPERTY, from_vertex_->gid, to_vertex_->gid, edge_type_, edge_);
|
||||||
|
transaction_->AddModifiedEdge(Gid(), modified_edge);
|
||||||
|
}
|
||||||
|
|
||||||
return std::move(current_value);
|
return std::move(current_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,9 +79,8 @@ class EdgeAccessor final {
|
|||||||
Gid Gid() const noexcept {
|
Gid Gid() const noexcept {
|
||||||
if (config_.properties_on_edges) {
|
if (config_.properties_on_edges) {
|
||||||
return edge_.ptr->gid;
|
return edge_.ptr->gid;
|
||||||
} else {
|
|
||||||
return edge_.gid;
|
|
||||||
}
|
}
|
||||||
|
return edge_.gid;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsCycle() const { return from_vertex_ == to_vertex_; }
|
bool IsCycle() const { return from_vertex_ == to_vertex_; }
|
||||||
|
28
src/storage/v2/edge_import_mode.hpp
Normal file
28
src/storage/v2/edge_import_mode.hpp
Normal 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
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace memgraph::storage {
|
||||||
|
|
||||||
|
enum class EdgeImportMode : std::uint8_t { ACTIVE, INACTIVE };
|
||||||
|
|
||||||
|
constexpr const char *EdgeImportModeToString(memgraph::storage::EdgeImportMode edge_import_mode) {
|
||||||
|
if (edge_import_mode == EdgeImportMode::INACTIVE) {
|
||||||
|
return "INACTIVE";
|
||||||
|
}
|
||||||
|
return "ACTIVE";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace memgraph::storage
|
@ -38,14 +38,14 @@ 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) {
|
Indices::Indices(const Config &config, StorageMode storage_mode) {
|
||||||
std::invoke([this, constraints, config, storage_mode]() {
|
std::invoke([this, config, storage_mode]() {
|
||||||
if (storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL || storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
|
if (storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL || storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
|
||||||
label_index_ = std::make_unique<InMemoryLabelIndex>(this, constraints, config);
|
label_index_ = std::make_unique<InMemoryLabelIndex>(this, config);
|
||||||
label_property_index_ = std::make_unique<InMemoryLabelPropertyIndex>(this, constraints, config);
|
label_property_index_ = std::make_unique<InMemoryLabelPropertyIndex>(this, config);
|
||||||
} else {
|
} else {
|
||||||
label_index_ = std::make_unique<DiskLabelIndex>(this, constraints, config);
|
label_index_ = std::make_unique<DiskLabelIndex>(this, config);
|
||||||
label_property_index_ = std::make_unique<DiskLabelPropertyIndex>(this, constraints, config);
|
label_property_index_ = std::make_unique<DiskLabelPropertyIndex>(this, config);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
namespace memgraph::storage {
|
namespace memgraph::storage {
|
||||||
|
|
||||||
struct Indices {
|
struct Indices {
|
||||||
Indices(Constraints *constraints, const Config &config, StorageMode storage_mode);
|
Indices(const Config &config, StorageMode storage_mode);
|
||||||
|
|
||||||
Indices(const Indices &) = delete;
|
Indices(const Indices &) = delete;
|
||||||
Indices(Indices &&) = delete;
|
Indices(Indices &&) = delete;
|
||||||
|
@ -19,8 +19,7 @@ namespace memgraph::storage {
|
|||||||
|
|
||||||
class LabelIndex {
|
class LabelIndex {
|
||||||
public:
|
public:
|
||||||
LabelIndex(Indices *indices, Constraints *constraints, const Config &config)
|
LabelIndex(Indices *indices, const Config &config) : indices_(indices), config_(config) {}
|
||||||
: indices_(indices), constraints_(constraints), config_(config) {}
|
|
||||||
|
|
||||||
LabelIndex(const LabelIndex &) = delete;
|
LabelIndex(const LabelIndex &) = delete;
|
||||||
LabelIndex(LabelIndex &&) = delete;
|
LabelIndex(LabelIndex &&) = delete;
|
||||||
@ -44,7 +43,6 @@ class LabelIndex {
|
|||||||
protected:
|
protected:
|
||||||
/// TODO: andi maybe no need for have those in abstract class if disk storage isn't using it
|
/// TODO: andi maybe no need for have those in abstract class if disk storage isn't using it
|
||||||
Indices *indices_;
|
Indices *indices_;
|
||||||
Constraints *constraints_;
|
|
||||||
Config config_;
|
Config config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,8 +19,7 @@ namespace memgraph::storage {
|
|||||||
|
|
||||||
class LabelPropertyIndex {
|
class LabelPropertyIndex {
|
||||||
public:
|
public:
|
||||||
LabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config)
|
LabelPropertyIndex(Indices *indices, const Config &config) : indices_(indices), config_(config) {}
|
||||||
: indices_(indices), constraints_(constraints), config_(config) {}
|
|
||||||
|
|
||||||
LabelPropertyIndex(const LabelPropertyIndex &) = delete;
|
LabelPropertyIndex(const LabelPropertyIndex &) = delete;
|
||||||
LabelPropertyIndex(LabelPropertyIndex &&) = delete;
|
LabelPropertyIndex(LabelPropertyIndex &&) = delete;
|
||||||
@ -52,7 +51,6 @@ class LabelPropertyIndex {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
Indices *indices_;
|
Indices *indices_;
|
||||||
Constraints *constraints_;
|
|
||||||
Config config_;
|
Config config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
// licenses/APL.txt.
|
// licenses/APL.txt.
|
||||||
|
|
||||||
#include "storage/v2/inmemory/label_index.hpp"
|
#include "storage/v2/inmemory/label_index.hpp"
|
||||||
|
#include "storage/v2/constraints/constraints.hpp"
|
||||||
#include "storage/v2/indices/indices_utils.hpp"
|
#include "storage/v2/indices/indices_utils.hpp"
|
||||||
|
|
||||||
namespace memgraph::storage {
|
namespace memgraph::storage {
|
||||||
|
|
||||||
InMemoryLabelIndex::InMemoryLabelIndex(Indices *indices, Constraints *constraints, Config config)
|
InMemoryLabelIndex::InMemoryLabelIndex(Indices *indices, Config config) : LabelIndex(indices, config) {}
|
||||||
: LabelIndex(indices, constraints, config) {}
|
|
||||||
|
|
||||||
void InMemoryLabelIndex::UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) {
|
void InMemoryLabelIndex::UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) {
|
||||||
auto it = index_.find(added_label);
|
auto it = index_.find(added_label);
|
||||||
@ -151,10 +151,11 @@ void InMemoryLabelIndex::RunGC() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InMemoryLabelIndex::Iterable InMemoryLabelIndex::Vertices(LabelId label, View view, Transaction *transaction) {
|
InMemoryLabelIndex::Iterable InMemoryLabelIndex::Vertices(LabelId label, View view, Transaction *transaction,
|
||||||
|
Constraints *constraints) {
|
||||||
const auto it = index_.find(label);
|
const auto it = index_.find(label);
|
||||||
MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
|
MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
|
||||||
return {it->second.access(), label, view, transaction, indices_, constraints_, config_};
|
return {it->second.access(), label, view, transaction, indices_, constraints, config_};
|
||||||
}
|
}
|
||||||
|
|
||||||
void InMemoryLabelIndex::SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats) {
|
void InMemoryLabelIndex::SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats) {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "storage/v2/constraints/constraints.hpp"
|
||||||
#include "storage/v2/indices/label_index.hpp"
|
#include "storage/v2/indices/label_index.hpp"
|
||||||
#include "storage/v2/vertex.hpp"
|
#include "storage/v2/vertex.hpp"
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ class InMemoryLabelIndex : public storage::LabelIndex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InMemoryLabelIndex(Indices *indices, Constraints *constraints, Config config);
|
InMemoryLabelIndex(Indices *indices, Config config);
|
||||||
|
|
||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) override;
|
void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) override;
|
||||||
@ -99,7 +100,7 @@ class InMemoryLabelIndex : public storage::LabelIndex {
|
|||||||
|
|
||||||
void RunGC();
|
void RunGC();
|
||||||
|
|
||||||
Iterable Vertices(LabelId label, View view, Transaction *transaction);
|
Iterable Vertices(LabelId label, View view, Transaction *transaction, Constraints *constraints);
|
||||||
|
|
||||||
void SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats);
|
void SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats);
|
||||||
|
|
||||||
|
@ -10,6 +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/constraints/constraints.hpp"
|
||||||
#include "storage/v2/indices/indices_utils.hpp"
|
#include "storage/v2/indices/indices_utils.hpp"
|
||||||
|
|
||||||
namespace memgraph::storage {
|
namespace memgraph::storage {
|
||||||
@ -32,12 +33,13 @@ bool InMemoryLabelPropertyIndex::Entry::operator<(const PropertyValue &rhs) cons
|
|||||||
|
|
||||||
bool InMemoryLabelPropertyIndex::Entry::operator==(const PropertyValue &rhs) const { return value == rhs; }
|
bool InMemoryLabelPropertyIndex::Entry::operator==(const PropertyValue &rhs) const { return value == rhs; }
|
||||||
|
|
||||||
InMemoryLabelPropertyIndex::InMemoryLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config)
|
InMemoryLabelPropertyIndex::InMemoryLabelPropertyIndex(Indices *indices, const Config &config)
|
||||||
: LabelPropertyIndex(indices, constraints, config) {}
|
: LabelPropertyIndex(indices, config) {}
|
||||||
|
|
||||||
bool InMemoryLabelPropertyIndex::CreateIndex(LabelId label, PropertyId property,
|
bool InMemoryLabelPropertyIndex::CreateIndex(LabelId label, PropertyId property,
|
||||||
utils::SkipList<Vertex>::Accessor vertices,
|
utils::SkipList<Vertex>::Accessor vertices,
|
||||||
const std::optional<ParallelizedIndexCreationInfo> ¶llel_exec_info) {
|
const std::optional<ParallelizedIndexCreationInfo> ¶llel_exec_info) {
|
||||||
|
spdlog::trace("Vertices size when creating index: {}", vertices.size());
|
||||||
auto create_index_seq = [this](LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor &vertices,
|
auto create_index_seq = [this](LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor &vertices,
|
||||||
std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>>::iterator it) {
|
std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>>::iterator it) {
|
||||||
using IndexAccessor = decltype(it->second.access());
|
using IndexAccessor = decltype(it->second.access());
|
||||||
@ -426,11 +428,12 @@ void InMemoryLabelPropertyIndex::RunGC() {
|
|||||||
|
|
||||||
InMemoryLabelPropertyIndex::Iterable InMemoryLabelPropertyIndex::Vertices(
|
InMemoryLabelPropertyIndex::Iterable InMemoryLabelPropertyIndex::Vertices(
|
||||||
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction) {
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
|
||||||
|
Constraints *constraints) {
|
||||||
auto it = index_.find({label, property});
|
auto it = index_.find({label, property});
|
||||||
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
|
MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
|
||||||
return {it->second.access(), label, property, lower_bound, upper_bound, view,
|
return {it->second.access(), label, property, lower_bound, upper_bound, view,
|
||||||
transaction, indices_, constraints_, config_};
|
transaction, indices_, constraints, config_};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace memgraph::storage
|
} // namespace memgraph::storage
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "storage/v2/constraints/constraints.hpp"
|
||||||
#include "storage/v2/indices/label_property_index.hpp"
|
#include "storage/v2/indices/label_property_index.hpp"
|
||||||
|
|
||||||
namespace memgraph::storage {
|
namespace memgraph::storage {
|
||||||
@ -39,7 +40,7 @@ class InMemoryLabelPropertyIndex : public storage::LabelPropertyIndex {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InMemoryLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config);
|
InMemoryLabelPropertyIndex(Indices *indices, const Config &config);
|
||||||
|
|
||||||
/// @throw std::bad_alloc
|
/// @throw std::bad_alloc
|
||||||
bool CreateIndex(LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor vertices,
|
bool CreateIndex(LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor vertices,
|
||||||
@ -131,7 +132,8 @@ class InMemoryLabelPropertyIndex : public storage::LabelPropertyIndex {
|
|||||||
void RunGC();
|
void RunGC();
|
||||||
|
|
||||||
Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction);
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
|
||||||
|
Constraints *constraints);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>> index_;
|
std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>> index_;
|
||||||
|
@ -139,10 +139,9 @@ void InMemoryReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::Bu
|
|||||||
|
|
||||||
storage_->constraints_.existence_constraints_ = std::make_unique<ExistenceConstraints>();
|
storage_->constraints_.existence_constraints_ = std::make_unique<ExistenceConstraints>();
|
||||||
storage_->constraints_.unique_constraints_ = std::make_unique<InMemoryUniqueConstraints>();
|
storage_->constraints_.unique_constraints_ = std::make_unique<InMemoryUniqueConstraints>();
|
||||||
storage_->indices_.label_index_ =
|
storage_->indices_.label_index_ = std::make_unique<InMemoryLabelIndex>(&storage_->indices_, storage_->config_);
|
||||||
std::make_unique<InMemoryLabelIndex>(&storage_->indices_, &storage_->constraints_, storage_->config_);
|
|
||||||
storage_->indices_.label_property_index_ =
|
storage_->indices_.label_property_index_ =
|
||||||
std::make_unique<InMemoryLabelPropertyIndex>(&storage_->indices_, &storage_->constraints_, storage_->config_);
|
std::make_unique<InMemoryLabelPropertyIndex>(&storage_->indices_, storage_->config_);
|
||||||
try {
|
try {
|
||||||
spdlog::debug("Loading snapshot");
|
spdlog::debug("Loading snapshot");
|
||||||
auto &epoch =
|
auto &epoch =
|
||||||
|
@ -1083,14 +1083,14 @@ InMemoryStorage::DropUniqueConstraint(LabelId label, const std::set<PropertyId>
|
|||||||
|
|
||||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, View view) {
|
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, View view) {
|
||||||
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get());
|
auto *mem_label_index = static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get());
|
||||||
return VerticesIterable(mem_label_index->Vertices(label, view, &transaction_));
|
return VerticesIterable(mem_label_index->Vertices(label, view, &transaction_, &storage_->constraints_));
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property, View view) {
|
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property, View view) {
|
||||||
auto *mem_label_property_index =
|
auto *mem_label_property_index =
|
||||||
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
||||||
return VerticesIterable(
|
return VerticesIterable(mem_label_property_index->Vertices(label, property, std::nullopt, std::nullopt, view,
|
||||||
mem_label_property_index->Vertices(label, property, std::nullopt, std::nullopt, view, &transaction_));
|
&transaction_, &storage_->constraints_));
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property,
|
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property,
|
||||||
@ -1098,7 +1098,8 @@ VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, Prop
|
|||||||
auto *mem_label_property_index =
|
auto *mem_label_property_index =
|
||||||
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
||||||
return VerticesIterable(mem_label_property_index->Vertices(label, property, utils::MakeBoundInclusive(value),
|
return VerticesIterable(mem_label_property_index->Vertices(label, property, utils::MakeBoundInclusive(value),
|
||||||
utils::MakeBoundInclusive(value), view, &transaction_));
|
utils::MakeBoundInclusive(value), view, &transaction_,
|
||||||
|
&storage_->constraints_));
|
||||||
}
|
}
|
||||||
|
|
||||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
|
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
|
||||||
@ -1106,8 +1107,8 @@ VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
|
|||||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
|
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
|
||||||
auto *mem_label_property_index =
|
auto *mem_label_property_index =
|
||||||
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
|
||||||
return VerticesIterable(
|
return VerticesIterable(mem_label_property_index->Vertices(label, property, lower_bound, upper_bound, view,
|
||||||
mem_label_property_index->Vertices(label, property, lower_bound, upper_bound, view, &transaction_));
|
&transaction_, &storage_->constraints_));
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction InMemoryStorage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
|
Transaction InMemoryStorage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
|
||||||
@ -1131,7 +1132,7 @@ Transaction InMemoryStorage::CreateTransaction(IsolationLevel isolation_level, S
|
|||||||
start_timestamp = timestamp_++;
|
start_timestamp = timestamp_++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {transaction_id, start_timestamp, isolation_level, storage_mode};
|
return {transaction_id, start_timestamp, isolation_level, storage_mode, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool force>
|
template <bool force>
|
||||||
|
39
src/storage/v2/modified_edge.hpp
Normal file
39
src/storage/v2/modified_edge.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// 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 <unordered_map>
|
||||||
|
|
||||||
|
#include "storage/v2/delta.hpp"
|
||||||
|
#include "storage/v2/edge_ref.hpp"
|
||||||
|
#include "storage/v2/id_types.hpp"
|
||||||
|
|
||||||
|
namespace memgraph::storage {
|
||||||
|
|
||||||
|
struct ModifiedEdgeInfo {
|
||||||
|
ModifiedEdgeInfo(Delta::Action delta, Gid from_vertex, Gid to_vertex, EdgeTypeId edge_type, const EdgeRef &edge)
|
||||||
|
: delta_action(delta),
|
||||||
|
src_vertex_gid(from_vertex),
|
||||||
|
dest_vertex_gid(to_vertex),
|
||||||
|
edge_type_id(edge_type),
|
||||||
|
edge_ref(edge) {}
|
||||||
|
|
||||||
|
Delta::Action delta_action;
|
||||||
|
Gid src_vertex_gid;
|
||||||
|
Gid dest_vertex_gid;
|
||||||
|
EdgeTypeId edge_type_id;
|
||||||
|
EdgeRef edge_ref;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ModifiedEdgesMap = std::unordered_map<Gid, ModifiedEdgeInfo>;
|
||||||
|
|
||||||
|
} // namespace memgraph::storage
|
@ -12,11 +12,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#include "storage/v2/property_value.hpp"
|
#include "storage/v2/property_value.hpp"
|
||||||
#include "storage/v2/transaction.hpp"
|
#include "storage/v2/transaction.hpp"
|
||||||
#include "storage/v2/view.hpp"
|
#include "storage/v2/view.hpp"
|
||||||
|
#include "utils/rocksdb_serialization.hpp"
|
||||||
|
|
||||||
namespace memgraph::storage {
|
namespace memgraph::storage {
|
||||||
|
|
||||||
@ -68,6 +70,7 @@ inline std::size_t ApplyDeltasForRead(Transaction const *transaction, const Delt
|
|||||||
// of the database.
|
// of the database.
|
||||||
if (view == View::OLD && ts == commit_timestamp &&
|
if (view == View::OLD && ts == commit_timestamp &&
|
||||||
(cid < transaction->command_id ||
|
(cid < transaction->command_id ||
|
||||||
|
// This check is used for on-disk storage. The vertex is valid only if it was deserialized in this transaction.
|
||||||
(cid == transaction->command_id && delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT))) {
|
(cid == transaction->command_id && delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT))) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -113,23 +116,38 @@ inline Delta *CreateDeleteObjectDelta(Transaction *transaction) {
|
|||||||
transaction->command_id);
|
transaction->command_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Delta *CreateDeleteObjectDelta(Transaction *transaction, std::list<Delta> *deltas) {
|
||||||
|
if (transaction->storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
transaction->EnsureCommitTimestampExists();
|
||||||
|
return &deltas->emplace_back(Delta::DeleteObjectTag(), transaction->commit_timestamp.get(), transaction->command_id);
|
||||||
|
}
|
||||||
|
|
||||||
/// TODO: what if in-memory analytical
|
/// TODO: what if in-memory analytical
|
||||||
inline Delta *CreateDeleteDeserializedObjectDelta(Transaction *transaction, std::optional<std::string> old_disk_key,
|
inline Delta *CreateDeleteDeserializedObjectDelta(Transaction *transaction, std::optional<std::string> old_disk_key,
|
||||||
const std::string &ts) {
|
std::string &&ts) {
|
||||||
// Should use utils::DecodeFixed64(ts.c_str()) once we will move to RocksDB real timestamps
|
// Should use utils::DecodeFixed64(ts.c_str()) once we will move to RocksDB real timestamps
|
||||||
|
transaction->EnsureCommitTimestampExists();
|
||||||
return &transaction->deltas.emplace_back(Delta::DeleteDeserializedObjectTag(), std::stoull(ts), old_disk_key);
|
return &transaction->deltas.emplace_back(Delta::DeleteDeserializedObjectTag(), std::stoull(ts), old_disk_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Delta *CreateDeleteDeserializedIndexObjectDelta(Transaction *transaction, std::list<Delta> &deltas,
|
inline Delta *CreateDeleteDeserializedObjectDelta(std::list<Delta> *deltas, std::optional<std::string> old_disk_key,
|
||||||
|
std::string &&ts) {
|
||||||
|
// Should use utils::DecodeFixed64(ts.c_str()) once we will move to RocksDB real timestamps
|
||||||
|
return &deltas->emplace_back(Delta::DeleteDeserializedObjectTag(), std::stoull(ts), old_disk_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Delta *CreateDeleteDeserializedIndexObjectDelta(std::list<Delta> &deltas,
|
||||||
std::optional<std::string> old_disk_key, const uint64_t ts) {
|
std::optional<std::string> old_disk_key, const uint64_t ts) {
|
||||||
return &deltas.emplace_back(Delta::DeleteDeserializedObjectTag(), ts, old_disk_key);
|
return &deltas.emplace_back(Delta::DeleteDeserializedObjectTag(), ts, old_disk_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: what if in-memory analytical
|
/// TODO: what if in-memory analytical
|
||||||
inline Delta *CreateDeleteDeserializedIndexObjectDelta(Transaction *transaction, std::list<Delta> &deltas,
|
inline Delta *CreateDeleteDeserializedIndexObjectDelta(std::list<Delta> &deltas,
|
||||||
std::optional<std::string> old_disk_key, const std::string &ts) {
|
std::optional<std::string> old_disk_key, const std::string &ts) {
|
||||||
// Should use utils::DecodeFixed64(ts.c_str()) once we will move to RocksDB real timestamps
|
// Should use utils::DecodeFixed64(ts.c_str()) once we will move to RocksDB real timestamps
|
||||||
return CreateDeleteDeserializedIndexObjectDelta(transaction, deltas, old_disk_key, std::stoull(ts));
|
return CreateDeleteDeserializedIndexObjectDelta(deltas, old_disk_key, std::stoull(ts));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function creates a delta in the transaction for the object and links
|
/// This function creates a delta in the transaction for the object and links
|
||||||
|
@ -49,7 +49,7 @@ Storage::Storage(Config config, StorageMode storage_mode)
|
|||||||
config_(config),
|
config_(config),
|
||||||
isolation_level_(config.transaction.isolation_level),
|
isolation_level_(config.transaction.isolation_level),
|
||||||
storage_mode_(storage_mode),
|
storage_mode_(storage_mode),
|
||||||
indices_(&constraints_, config, storage_mode),
|
indices_(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,
|
replication_state_(config_.durability.restore_replication_state_on_startup,
|
||||||
@ -99,10 +99,10 @@ void Storage::SetStorageMode(StorageMode storage_mode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IsolationLevel Storage::GetIsolationLevel() const noexcept { return isolation_level_; }
|
|
||||||
|
|
||||||
StorageMode Storage::GetStorageMode() const { return storage_mode_; }
|
StorageMode Storage::GetStorageMode() const { return storage_mode_; }
|
||||||
|
|
||||||
|
IsolationLevel Storage::GetIsolationLevel() const noexcept { return isolation_level_; }
|
||||||
|
|
||||||
utils::BasicResult<Storage::SetIsolationLevelError> Storage::SetIsolationLevel(IsolationLevel isolation_level) {
|
utils::BasicResult<Storage::SetIsolationLevelError> Storage::SetIsolationLevel(IsolationLevel isolation_level) {
|
||||||
std::unique_lock main_guard{main_lock_};
|
std::unique_lock main_guard{main_lock_};
|
||||||
if (storage_mode_ == storage::StorageMode::IN_MEMORY_ANALYTICAL) {
|
if (storage_mode_ == storage::StorageMode::IN_MEMORY_ANALYTICAL) {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "storage/v2/delta.hpp"
|
#include "storage/v2/delta.hpp"
|
||||||
#include "storage/v2/edge.hpp"
|
#include "storage/v2/edge.hpp"
|
||||||
#include "storage/v2/isolation_level.hpp"
|
#include "storage/v2/isolation_level.hpp"
|
||||||
|
#include "storage/v2/modified_edge.hpp"
|
||||||
#include "storage/v2/property_value.hpp"
|
#include "storage/v2/property_value.hpp"
|
||||||
#include "storage/v2/storage_mode.hpp"
|
#include "storage/v2/storage_mode.hpp"
|
||||||
#include "storage/v2/vertex.hpp"
|
#include "storage/v2/vertex.hpp"
|
||||||
@ -34,13 +35,14 @@ const uint64_t kTransactionInitialId = 1ULL << 63U;
|
|||||||
|
|
||||||
struct Transaction {
|
struct Transaction {
|
||||||
Transaction(uint64_t transaction_id, uint64_t start_timestamp, IsolationLevel isolation_level,
|
Transaction(uint64_t transaction_id, uint64_t start_timestamp, IsolationLevel isolation_level,
|
||||||
StorageMode storage_mode)
|
StorageMode storage_mode, bool edge_import_mode_active)
|
||||||
: transaction_id(transaction_id),
|
: transaction_id(transaction_id),
|
||||||
start_timestamp(start_timestamp),
|
start_timestamp(start_timestamp),
|
||||||
command_id(0),
|
command_id(0),
|
||||||
must_abort(false),
|
must_abort(false),
|
||||||
isolation_level(isolation_level),
|
isolation_level(isolation_level),
|
||||||
storage_mode(storage_mode) {}
|
storage_mode(storage_mode),
|
||||||
|
edge_import_mode_active(edge_import_mode_active) {}
|
||||||
|
|
||||||
Transaction(Transaction &&other) noexcept
|
Transaction(Transaction &&other) noexcept
|
||||||
: transaction_id(other.transaction_id.load(std::memory_order_acquire)),
|
: transaction_id(other.transaction_id.load(std::memory_order_acquire)),
|
||||||
@ -51,6 +53,7 @@ struct Transaction {
|
|||||||
must_abort(other.must_abort),
|
must_abort(other.must_abort),
|
||||||
isolation_level(other.isolation_level),
|
isolation_level(other.isolation_level),
|
||||||
storage_mode(other.storage_mode),
|
storage_mode(other.storage_mode),
|
||||||
|
edge_import_mode_active(other.edge_import_mode_active),
|
||||||
manyDeltasCache{std::move(other.manyDeltasCache)} {}
|
manyDeltasCache{std::move(other.manyDeltasCache)} {}
|
||||||
|
|
||||||
Transaction(const Transaction &) = delete;
|
Transaction(const Transaction &) = delete;
|
||||||
@ -59,12 +62,22 @@ struct Transaction {
|
|||||||
|
|
||||||
~Transaction() {}
|
~Transaction() {}
|
||||||
|
|
||||||
|
bool IsDiskStorage() const { return storage_mode == StorageMode::ON_DISK_TRANSACTIONAL; }
|
||||||
|
|
||||||
/// @throw std::bad_alloc if failed to create the `commit_timestamp`
|
/// @throw std::bad_alloc if failed to create the `commit_timestamp`
|
||||||
void EnsureCommitTimestampExists() {
|
void EnsureCommitTimestampExists() {
|
||||||
if (commit_timestamp != nullptr) return;
|
if (commit_timestamp != nullptr) return;
|
||||||
commit_timestamp = std::make_unique<std::atomic<uint64_t>>(transaction_id.load(std::memory_order_relaxed));
|
commit_timestamp = std::make_unique<std::atomic<uint64_t>>(transaction_id.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddModifiedEdge(Gid gid, ModifiedEdgeInfo modified_edge) {
|
||||||
|
if (IsDiskStorage()) {
|
||||||
|
modified_edges_.emplace(gid, modified_edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveModifiedEdge(const Gid &gid) { modified_edges_.erase(gid); }
|
||||||
|
|
||||||
std::atomic<uint64_t> transaction_id;
|
std::atomic<uint64_t> transaction_id;
|
||||||
uint64_t start_timestamp;
|
uint64_t start_timestamp;
|
||||||
// The `Transaction` object is stack allocated, but the `commit_timestamp`
|
// The `Transaction` object is stack allocated, but the `commit_timestamp`
|
||||||
@ -77,11 +90,15 @@ struct Transaction {
|
|||||||
bool must_abort;
|
bool must_abort;
|
||||||
IsolationLevel isolation_level;
|
IsolationLevel isolation_level;
|
||||||
StorageMode storage_mode;
|
StorageMode storage_mode;
|
||||||
|
bool edge_import_mode_active{false};
|
||||||
|
|
||||||
// A cache which is consistent to the current transaction_id + command_id.
|
// A cache which is consistent to the current transaction_id + command_id.
|
||||||
// Used to speedup getting info about a vertex when there is a long delta
|
// Used to speedup getting info about a vertex when there is a long delta
|
||||||
// chain involved in rebuilding that info.
|
// chain involved in rebuilding that info.
|
||||||
mutable VertexInfoCache manyDeltasCache;
|
mutable VertexInfoCache manyDeltasCache;
|
||||||
|
|
||||||
|
// Store modified edges GID mapped to changed Delta and serialized edge key
|
||||||
|
ModifiedEdgesMap modified_edges_;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool operator==(const Transaction &first, const Transaction &second) {
|
inline bool operator==(const Transaction &first, const Transaction &second) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "query/exceptions.hpp"
|
||||||
#include "storage/v2/edge_accessor.hpp"
|
#include "storage/v2/edge_accessor.hpp"
|
||||||
#include "storage/v2/id_types.hpp"
|
#include "storage/v2/id_types.hpp"
|
||||||
#include "storage/v2/indices/indices.hpp"
|
#include "storage/v2/indices/indices.hpp"
|
||||||
@ -89,6 +90,9 @@ bool VertexAccessor::IsVisible(View view) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<bool> VertexAccessor::AddLabel(LabelId label) {
|
Result<bool> VertexAccessor::AddLabel(LabelId label) {
|
||||||
|
if (transaction_->edge_import_mode_active) {
|
||||||
|
throw query::WriteVertexOperationInEdgeImportModeException();
|
||||||
|
}
|
||||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||||
auto guard = std::unique_lock{vertex_->lock};
|
auto guard = std::unique_lock{vertex_->lock};
|
||||||
|
|
||||||
@ -109,6 +113,9 @@ 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) {
|
||||||
|
if (transaction_->edge_import_mode_active) {
|
||||||
|
throw query::WriteVertexOperationInEdgeImportModeException();
|
||||||
|
}
|
||||||
auto guard = std::unique_lock{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;
|
||||||
@ -224,6 +231,10 @@ 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) {
|
||||||
|
if (transaction_->edge_import_mode_active) {
|
||||||
|
throw query::WriteVertexOperationInEdgeImportModeException();
|
||||||
|
}
|
||||||
|
|
||||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||||
auto guard = std::unique_lock{vertex_->lock};
|
auto guard = std::unique_lock{vertex_->lock};
|
||||||
|
|
||||||
@ -249,6 +260,10 @@ 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) {
|
||||||
|
if (transaction_->edge_import_mode_active) {
|
||||||
|
throw query::WriteVertexOperationInEdgeImportModeException();
|
||||||
|
}
|
||||||
|
|
||||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||||
auto guard = std::unique_lock{vertex_->lock};
|
auto guard = std::unique_lock{vertex_->lock};
|
||||||
|
|
||||||
@ -268,6 +283,10 @@ 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 {
|
||||||
|
if (transaction_->edge_import_mode_active) {
|
||||||
|
throw query::WriteVertexOperationInEdgeImportModeException();
|
||||||
|
}
|
||||||
|
|
||||||
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
|
||||||
auto guard = std::unique_lock{vertex_->lock};
|
auto guard = std::unique_lock{vertex_->lock};
|
||||||
|
|
||||||
@ -287,6 +306,9 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> Vertex
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
|
Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
|
||||||
|
if (transaction_->edge_import_mode_active) {
|
||||||
|
throw query::WriteVertexOperationInEdgeImportModeException();
|
||||||
|
}
|
||||||
auto guard = std::unique_lock{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;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "storage/v2/delta.hpp"
|
#include "storage/v2/delta.hpp"
|
||||||
|
#include "utils/skip_list.hpp"
|
||||||
|
|
||||||
namespace memgraph::utils {
|
namespace memgraph::utils {
|
||||||
|
|
||||||
@ -25,6 +26,11 @@ inline std::optional<std::string> GetOldDiskKeyOrNull(storage::Delta *head) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TSkipListAccessor>
|
||||||
|
inline bool ObjectExistsInCache(TSkipListAccessor &accessor, storage::Gid gid) {
|
||||||
|
return accessor.find(gid) != accessor.end();
|
||||||
|
}
|
||||||
|
|
||||||
inline uint64_t GetEarliestTimestamp(storage::Delta *head) {
|
inline uint64_t GetEarliestTimestamp(storage::Delta *head) {
|
||||||
if (head == nullptr) return 0;
|
if (head == nullptr) return 0;
|
||||||
while (head->next != nullptr) {
|
while (head->next != nullptr) {
|
||||||
|
@ -237,13 +237,18 @@ inline std::string SerializeVertexAsValueForLabelIndex(storage::LabelId indexing
|
|||||||
return SerializeVertexAsValueForAuxiliaryStorages(indexing_label, vertex_labels, property_store);
|
return SerializeVertexAsValueForAuxiliaryStorages(indexing_label, vertex_labels, property_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<storage::LabelId> DeserializeLabelsFromIndexStorage(const std::string &value) {
|
inline std::vector<storage::LabelId> DeserializeLabelsFromIndexStorage(const std::string &key,
|
||||||
std::string labels = value.substr(0, value.find('|'));
|
const std::string &value) {
|
||||||
return TransformFromStringLabels(utils::Split(labels, ","));
|
std::string labels_str{GetViewOfFirstPartOfSplit(value, '|')};
|
||||||
|
std::vector<storage::LabelId> labels{TransformFromStringLabels(utils::Split(labels_str, ","))};
|
||||||
|
std::string indexing_label = key.substr(0, key.find('|'));
|
||||||
|
labels.emplace_back(storage::LabelId::FromUint(std::stoull(indexing_label)));
|
||||||
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<storage::LabelId> DeserializeLabelsFromLabelIndexStorage(const std::string &value) {
|
inline std::vector<storage::LabelId> DeserializeLabelsFromLabelIndexStorage(const std::string &key,
|
||||||
return DeserializeLabelsFromIndexStorage(value);
|
const std::string &value) {
|
||||||
|
return DeserializeLabelsFromIndexStorage(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline storage::PropertyStore DeserializePropertiesFromLabelIndexStorage(const std::string &value) {
|
inline storage::PropertyStore DeserializePropertiesFromLabelIndexStorage(const std::string &value) {
|
||||||
@ -272,8 +277,9 @@ inline std::string ExtractGidFromLabelPropertyIndexStorage(const std::string &ke
|
|||||||
return std::string(GetViewOfThirdPartOfSplit(key, '|'));
|
return std::string(GetViewOfThirdPartOfSplit(key, '|'));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::vector<storage::LabelId> DeserializeLabelsFromLabelPropertyIndexStorage(const std::string &value) {
|
inline std::vector<storage::LabelId> DeserializeLabelsFromLabelPropertyIndexStorage(const std::string &key,
|
||||||
return DeserializeLabelsFromIndexStorage(value);
|
const std::string &value) {
|
||||||
|
return DeserializeLabelsFromIndexStorage(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline storage::PropertyStore DeserializePropertiesFromLabelPropertyIndexStorage(const std::string &value) {
|
inline storage::PropertyStore DeserializePropertiesFromLabelPropertyIndexStorage(const std::string &value) {
|
||||||
|
@ -186,6 +186,7 @@ enum class TypeId : uint64_t {
|
|||||||
AST_CALL_SUBQUERY,
|
AST_CALL_SUBQUERY,
|
||||||
AST_MULTI_DATABASE_QUERY,
|
AST_MULTI_DATABASE_QUERY,
|
||||||
AST_SHOW_DATABASES,
|
AST_SHOW_DATABASES,
|
||||||
|
AST_EDGE_IMPORT_MODE_QUERY,
|
||||||
// Symbol
|
// Symbol
|
||||||
SYMBOL,
|
SYMBOL,
|
||||||
};
|
};
|
||||||
|
@ -61,6 +61,7 @@ add_subdirectory(load_csv)
|
|||||||
add_subdirectory(init_file_flags)
|
add_subdirectory(init_file_flags)
|
||||||
add_subdirectory(analytical_mode)
|
add_subdirectory(analytical_mode)
|
||||||
add_subdirectory(batched_procedures)
|
add_subdirectory(batched_procedures)
|
||||||
|
add_subdirectory(import_mode)
|
||||||
add_subdirectory(concurrent_query_modules)
|
add_subdirectory(concurrent_query_modules)
|
||||||
add_subdirectory(set_properties)
|
add_subdirectory(set_properties)
|
||||||
|
|
||||||
|
6
tests/e2e/import_mode/CMakeLists.txt
Normal file
6
tests/e2e/import_mode/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function(copy_import_mode_e2e_python_files FILE_NAME)
|
||||||
|
copy_e2e_python_files(import_mode ${FILE_NAME})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
copy_import_mode_e2e_python_files(common.py)
|
||||||
|
copy_import_mode_e2e_python_files(test_command.py)
|
25
tests/e2e/import_mode/common.py
Normal file
25
tests/e2e/import_mode/common.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Copyright 2022 Memgraph Ltd.
|
||||||
|
#
|
||||||
|
# Use of this software is governed by the Business Source License
|
||||||
|
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
|
# License, and you may not use this file except in compliance with the Business Source License.
|
||||||
|
#
|
||||||
|
# As of the Change Date specified in that file, in accordance with
|
||||||
|
# the Business Source License, use of this software will be governed
|
||||||
|
# by the Apache License, Version 2.0, included in the file
|
||||||
|
# licenses/APL.txt.
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import mgclient
|
||||||
|
|
||||||
|
|
||||||
|
def execute_and_fetch_all(cursor: mgclient.Cursor, query: str, params: dict = {}) -> typing.List[tuple]:
|
||||||
|
cursor.execute(query, params)
|
||||||
|
return cursor.fetchall()
|
||||||
|
|
||||||
|
|
||||||
|
def connect(**kwargs) -> mgclient.Connection:
|
||||||
|
connection = mgclient.connect(host="localhost", port=7687, **kwargs)
|
||||||
|
connection.autocommit = True
|
||||||
|
return connection
|
208
tests/e2e/import_mode/test_command.py
Normal file
208
tests/e2e/import_mode/test_command.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# Copyright 2022 Memgraph Ltd.
|
||||||
|
#
|
||||||
|
# Use of this software is governed by the Business Source License
|
||||||
|
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||||
|
# License, and you may not use this file except in compliance with the Business Source License.
|
||||||
|
#
|
||||||
|
# As of the Change Date specified in that file, in accordance with
|
||||||
|
# the Business Source License, use of this software will be governed
|
||||||
|
# by the Apache License, Version 2.0, included in the file
|
||||||
|
# licenses/APL.txt.
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from common import connect, execute_and_fetch_all
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_mode_disabled_for_in_memory_storages():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_mode_on_off():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
|
||||||
|
|
||||||
|
def test_creating_vertices():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n) RETURN n"))) == 2
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n) RETURN n"))) == 2
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_creating_edges():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User {id: 1}), (m:User {id: 2}) CREATE (n)-[r:FRIENDS {id: 3}]->(m)")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_label_index_vertices_loading():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE INDEX ON :User")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_label_index_edges_creation():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE INDEX ON :User")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User {id: 1}), (m:User {id: 2}) CREATE (n)-[r:FRIENDS {id: 3}]->(m)")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_label_property_index_vertices_loading():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE INDEX ON :User(id)")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) WHERE n.id IS NOT NULL RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) WHERE n.id IS NOT NULL RETURN n"))) == 2
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) WHERE n.id IS NOT NULL RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) WHERE n.id IS NOT NULL RETURN n"))) == 2
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_label_property_index_edges_creation():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE INDEX ON :User(id)")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User {id: 1}), (m:User {id: 2}) CREATE (n)-[r:FRIENDS {id: 3}]->(m)")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n:User) RETURN n"))) == 2
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_edge_deletion_in_edge_import_mode():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE INDEX ON :User(id)")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User {id: 1}), (m:User {id: 2}) CREATE (n)-[r:FRIENDS {id: 3}]->(m)")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 1
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n)-[r:FRIENDS {id: 3}]->(m) DELETE r")
|
||||||
|
assert len(list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN n, r, m"))) == 0
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_modification_of_edge_properties_in_edge_import_mode():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE INDEX ON :User(id)")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 2})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(
|
||||||
|
cursor, "MATCH (n:User {id: 1}), (m:User {id: 2}) CREATE (n)-[r:FRIENDS {id: 3, balance: 1000}]->(m)"
|
||||||
|
)
|
||||||
|
assert list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN r.balance"))[0][0] == 1000
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n)-[r:FRIENDS {id: 3}]->(m) SET r.balance = 2000")
|
||||||
|
assert list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN r.balance"))[0][0] == 2000
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
assert list(execute_and_fetch_all(cursor, "MATCH (n)-[r]->(m) RETURN r.balance"))[0][0] == 2000
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_throw_on_vertex_add_label_during_edge_import_mode():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (u:User {id: 1}) SET u:User:Person RETURN u")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_throw_on_vertex_remove_label_during_edge_import_mode():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User:Person {id: 1})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (u:User {id: 1}) REMOVE u:Person RETURN u")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_throw_on_vertex_set_property_during_edge_import_mode():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1, balance: 1000})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (u:User {id: 1}) SET u.balance = 2000")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_throw_on_vertex_create_vertex_during_edge_import_mode():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1, balance: 1000})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (m:Mother {id: 10})")
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE INACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
def test_throw_changing_import_mode_while_in_explicit_tx():
|
||||||
|
cursor = connect().cursor()
|
||||||
|
execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
|
||||||
|
execute_and_fetch_all(cursor, "CREATE (u:User {id: 1, balance: 1000})")
|
||||||
|
execute_and_fetch_all(cursor, "BEGIN")
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
execute_and_fetch_all(cursor, "EDGE IMPORT MODE ACTIVE")
|
||||||
|
execute_and_fetch_all(cursor, "MATCH (n:User) DETACH DELETE n")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(pytest.main([__file__, "-rA"]))
|
13
tests/e2e/import_mode/workloads.yaml
Normal file
13
tests/e2e/import_mode/workloads.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import_mode_cluster: &import_mode_cluster
|
||||||
|
cluster:
|
||||||
|
main:
|
||||||
|
args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"]
|
||||||
|
log_file: "transaction_queue.log"
|
||||||
|
setup_queries: []
|
||||||
|
validation_queries: []
|
||||||
|
|
||||||
|
workloads:
|
||||||
|
- name: "Import mode"
|
||||||
|
binary: "tests/e2e/pytest_runner.sh"
|
||||||
|
args: ["import_mode/test_command.py"]
|
||||||
|
<<: *import_mode_cluster
|
@ -60,7 +60,7 @@ class DeltaGenerator final {
|
|||||||
explicit Transaction(DeltaGenerator *gen)
|
explicit Transaction(DeltaGenerator *gen)
|
||||||
: gen_(gen),
|
: gen_(gen),
|
||||||
transaction_(gen->transaction_id_++, gen->timestamp_++, memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION,
|
transaction_(gen->transaction_id_++, gen->timestamp_++, memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION,
|
||||||
gen->storage_mode_) {}
|
gen->storage_mode_, false) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
memgraph::storage::Vertex *CreateVertex() {
|
memgraph::storage::Vertex *CreateVertex() {
|
||||||
|
Loading…
Reference in New Issue
Block a user