From 6e6dff81e0336861255b7aeb49ebfcc8893c534e Mon Sep 17 00:00:00 2001 From: Matej Ferencevic Date: Wed, 3 Jul 2019 15:32:03 +0200 Subject: [PATCH] Split storage and vertex accessor into cpp in storage v2 Reviewers: mtomic, teon.banek Reviewed By: mtomic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2172 --- src/CMakeLists.txt | 1 + src/storage/v2/CMakeLists.txt | 6 + src/storage/v2/storage.cpp | 153 +++++++++++++++ src/storage/v2/storage.hpp | 147 +------------- src/storage/v2/vertex_accessor.cpp | 301 +++++++++++++++++++++++++++++ src/storage/v2/vertex_accessor.hpp | 289 +-------------------------- tests/unit/CMakeLists.txt | 2 +- 7 files changed, 480 insertions(+), 419 deletions(-) create mode 100644 src/storage/v2/CMakeLists.txt create mode 100644 src/storage/v2/storage.cpp create mode 100644 src/storage/v2/vertex_accessor.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ec8d748a4..f62ef47fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ add_subdirectory(communication) add_subdirectory(stats) add_subdirectory(auth) add_subdirectory(slk) +add_subdirectory(storage/v2) # ---------------------------------------------------------------------------- # Common LCP files diff --git a/src/storage/v2/CMakeLists.txt b/src/storage/v2/CMakeLists.txt new file mode 100644 index 000000000..bdc7207ae --- /dev/null +++ b/src/storage/v2/CMakeLists.txt @@ -0,0 +1,6 @@ +set(storage_v2_src_files + vertex_accessor.cpp + storage.cpp) + +add_library(mg-storage-v2 STATIC ${storage_v2_src_files}) +target_link_libraries(mg-storage-v2 Threads::Threads mg-utils glog gflags) diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp new file mode 100644 index 000000000..65f927862 --- /dev/null +++ b/src/storage/v2/storage.cpp @@ -0,0 +1,153 @@ +#include "storage/v2/storage.hpp" + +#include + +#include + +#include "storage/v2/mvcc.hpp" + +namespace storage { + +Storage::Accessor::Accessor(Storage *storage) + : storage_(storage), is_transaction_starter_(true) { + // We acquire the storage lock here because we access (and modify) the + // transaction engine variables (`transaction_id` and `timestamp`) below. + std::lock_guard guard(storage_->lock_); + auto acc = storage_->transactions_.access(); + auto [it, inserted] = acc.insert( + Transaction{storage_->transaction_id_++, storage_->timestamp_++}); + CHECK(inserted) << "The Transaction must be inserted here!"; + CHECK(it != acc.end()) << "Invalid Transaction iterator!"; + transaction_ = &*it; +} + +Storage::Accessor::Accessor(Accessor &&other) noexcept + : storage_(other.storage_), + transaction_(other.transaction_), + is_transaction_starter_(true) { + CHECK(other.is_transaction_starter_) << "The original accessor isn't valid!"; + // Don't allow the other accessor to abort our transaction. + other.is_transaction_starter_ = false; +} + +// This operator isn't `noexcept` because the `Abort` function isn't +// `noexcept`. +Storage::Accessor &Storage::Accessor::operator=(Accessor &&other) { + if (this == &other) return *this; + + if (is_transaction_starter_ && transaction_->is_active) { + Abort(); + } + + storage_ = other.storage_; + transaction_ = other.transaction_; + is_transaction_starter_ = true; + + CHECK(other.is_transaction_starter_) << "The original accessor isn't valid!"; + // Don't allow the other accessor to abort our transaction. + other.is_transaction_starter_ = false; + + return *this; +} + +Storage::Accessor::~Accessor() { + if (is_transaction_starter_ && transaction_->is_active) { + Abort(); + } +} + +VertexAccessor Storage::Accessor::CreateVertex() { + auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel); + auto acc = storage_->vertices_.access(); + auto delta = CreateDeleteObjectDelta(transaction_); + auto [it, inserted] = acc.insert(Vertex{storage::Gid::FromUint(gid), delta}); + CHECK(inserted) << "The vertex must be inserted here!"; + CHECK(it != acc.end()) << "Invalid Vertex accessor!"; + transaction_->modified_vertices.push_back(&*it); + return VertexAccessor::Create(&*it, transaction_, View::NEW).value(); +} + +std::optional Storage::Accessor::FindVertex(Gid gid, + View view) { + auto acc = storage_->vertices_.access(); + auto it = acc.find(gid); + if (it == acc.end()) return std::nullopt; + return VertexAccessor::Create(&*it, transaction_, view); +} + +void Storage::Accessor::AdvanceCommand() { ++transaction_->command_id; } + +void Storage::Accessor::Commit() { + CHECK(!transaction_->must_abort) << "The transaction can't be committed!"; + CHECK(transaction_->is_active) << "The transaction is already terminated!"; + if (transaction_->deltas.empty()) { + transaction_->commit_timestamp.store(transaction_->start_timestamp, + std::memory_order_release); + } else { + std::lock_guard guard(storage_->lock_); + transaction_->commit_timestamp.store(storage_->timestamp_++, + std::memory_order_release); + // TODO: release lock, and update all deltas to have an in-memory copy + // of the commit id + } + transaction_->is_active = false; +} + +void Storage::Accessor::Abort() { + CHECK(transaction_->is_active) << "The transaction is already terminated!"; + for (Vertex *vertex : transaction_->modified_vertices) { + std::lock_guard guard(vertex->lock); + Delta *current = vertex->delta; + while (current != nullptr && + current->timestamp->load(std::memory_order_acquire) == + transaction_->transaction_id) { + switch (current->action) { + case Delta::Action::REMOVE_LABEL: { + auto it = std::find(vertex->labels.begin(), vertex->labels.end(), + current->label); + CHECK(it != vertex->labels.end()) << "Invalid database state!"; + std::swap(*it, *vertex->labels.rbegin()); + vertex->labels.pop_back(); + break; + } + case Delta::Action::ADD_LABEL: { + auto it = std::find(vertex->labels.begin(), vertex->labels.end(), + current->label); + CHECK(it == vertex->labels.end()) << "Invalid database state!"; + vertex->labels.push_back(current->label); + break; + } + case Delta::Action::SET_PROPERTY: { + auto it = vertex->properties.find(current->property.key); + if (it != vertex->properties.end()) { + if (current->property.value.IsNull()) { + // remove the property + vertex->properties.erase(it); + } else { + // set the value + it->second = current->property.value; + } + } else if (!current->property.value.IsNull()) { + vertex->properties.emplace(current->property.key, + current->property.value); + } + break; + } + case Delta::Action::DELETE_OBJECT: { + auto acc = storage_->vertices_.access(); + CHECK(acc.remove(vertex->gid)) << "Invalid database state!"; + break; + } + case Delta::Action::RECREATE_OBJECT: { + vertex->deleted = false; + break; + } + } + current = current->next.load(std::memory_order_acquire); + } + vertex->delta = current; + } + transaction_->is_active = false; +} + +} // namespace storage diff --git a/src/storage/v2/storage.hpp b/src/storage/v2/storage.hpp index b083e88fb..9f0bc014a 100644 --- a/src/storage/v2/storage.hpp +++ b/src/storage/v2/storage.hpp @@ -1,10 +1,7 @@ #pragma once -#include #include -#include - #include "utils/skip_list.hpp" #include "storage/v2/transaction.hpp" @@ -25,154 +22,28 @@ class Storage final { public: class Accessor final { public: - explicit Accessor(Storage *storage) - : storage_(storage), is_transaction_starter_(true) { - // We acquire the storage lock here because we access (and modify) the - // transaction engine variables (`transaction_id` and `timestamp`) below. - std::lock_guard guard(storage_->lock_); - auto acc = storage_->transactions_.access(); - auto [it, inserted] = acc.insert( - Transaction{storage_->transaction_id_++, storage_->timestamp_++}); - CHECK(inserted) << "The Transaction must be inserted here!"; - CHECK(it != acc.end()) << "Invalid Transaction iterator!"; - transaction_ = &*it; - } + explicit Accessor(Storage *storage); Accessor(const Accessor &) = delete; Accessor &operator=(const Accessor &) = delete; - Accessor(Accessor &&other) noexcept - : storage_(other.storage_), - transaction_(other.transaction_), - is_transaction_starter_(true) { - CHECK(other.is_transaction_starter_) - << "The original accessor isn't valid!"; - // Don't allow the other accessor to abort our transaction. - other.is_transaction_starter_ = false; - } + Accessor(Accessor &&other) noexcept; // This operator isn't `noexcept` because the `Abort` function isn't // `noexcept`. - Accessor &operator=(Accessor &&other) { - if (this == &other) return *this; + Accessor &operator=(Accessor &&other); - if (is_transaction_starter_ && transaction_->is_active) { - Abort(); - } + ~Accessor(); - storage_ = other.storage_; - transaction_ = other.transaction_; - is_transaction_starter_ = true; + VertexAccessor CreateVertex(); - CHECK(other.is_transaction_starter_) - << "The original accessor isn't valid!"; - // Don't allow the other accessor to abort our transaction. - other.is_transaction_starter_ = false; + std::optional FindVertex(Gid gid, View view); - return *this; - } + void AdvanceCommand(); - ~Accessor() { - if (is_transaction_starter_ && transaction_->is_active) { - Abort(); - } - } + void Commit(); - VertexAccessor CreateVertex() { - auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel); - auto acc = storage_->vertices_.access(); - auto delta = CreateDeleteObjectDelta(transaction_); - auto [it, inserted] = - acc.insert(Vertex{storage::Gid::FromUint(gid), delta}); - CHECK(inserted) << "The vertex must be inserted here!"; - CHECK(it != acc.end()) << "Invalid Vertex accessor!"; - transaction_->modified_vertices.push_back(&*it); - return VertexAccessor::Create(&*it, transaction_, View::NEW).value(); - } - - std::optional FindVertex(Gid gid, View view) { - auto acc = storage_->vertices_.access(); - auto it = acc.find(gid); - if (it == acc.end()) return std::nullopt; - return VertexAccessor::Create(&*it, transaction_, view); - } - - void AdvanceCommand() { ++transaction_->command_id; } - - void Commit() { - CHECK(!transaction_->must_abort) << "The transaction can't be committed!"; - CHECK(transaction_->is_active) - << "The transaction is already terminated!"; - if (transaction_->deltas.empty()) { - transaction_->commit_timestamp.store(transaction_->start_timestamp, - std::memory_order_release); - } else { - std::lock_guard guard(storage_->lock_); - transaction_->commit_timestamp.store(storage_->timestamp_++, - std::memory_order_release); - // TODO: release lock, and update all deltas to have an in-memory copy - // of the commit id - } - transaction_->is_active = false; - } - - void Abort() { - CHECK(transaction_->is_active) - << "The transaction is already terminated!"; - for (Vertex *vertex : transaction_->modified_vertices) { - std::lock_guard guard(vertex->lock); - Delta *current = vertex->delta; - while (current != nullptr && - current->timestamp->load(std::memory_order_acquire) == - transaction_->transaction_id) { - switch (current->action) { - case Delta::Action::REMOVE_LABEL: { - auto it = std::find(vertex->labels.begin(), vertex->labels.end(), - current->label); - CHECK(it != vertex->labels.end()) << "Invalid database state!"; - std::swap(*it, *vertex->labels.rbegin()); - vertex->labels.pop_back(); - break; - } - case Delta::Action::ADD_LABEL: { - auto it = std::find(vertex->labels.begin(), vertex->labels.end(), - current->label); - CHECK(it == vertex->labels.end()) << "Invalid database state!"; - vertex->labels.push_back(current->label); - break; - } - case Delta::Action::SET_PROPERTY: { - auto it = vertex->properties.find(current->property.key); - if (it != vertex->properties.end()) { - if (current->property.value.IsNull()) { - // remove the property - vertex->properties.erase(it); - } else { - // set the value - it->second = current->property.value; - } - } else if (!current->property.value.IsNull()) { - vertex->properties.emplace(current->property.key, - current->property.value); - } - break; - } - case Delta::Action::DELETE_OBJECT: { - auto acc = storage_->vertices_.access(); - CHECK(acc.remove(vertex->gid)) << "Invalid database state!"; - break; - } - case Delta::Action::RECREATE_OBJECT: { - vertex->deleted = false; - break; - } - } - current = current->next.load(std::memory_order_acquire); - } - vertex->delta = current; - } - transaction_->is_active = false; - } + void Abort(); private: Storage *storage_; diff --git a/src/storage/v2/vertex_accessor.cpp b/src/storage/v2/vertex_accessor.cpp new file mode 100644 index 000000000..b1e35ea08 --- /dev/null +++ b/src/storage/v2/vertex_accessor.cpp @@ -0,0 +1,301 @@ +#include "storage/v2/vertex_accessor.hpp" + +#include + +#include "storage/v2/mvcc.hpp" + +namespace storage { + +std::optional VertexAccessor::Create(Vertex *vertex, + Transaction *transaction, + View view) { + bool is_visible = true; + Delta *delta = nullptr; + { + std::lock_guard guard(vertex->lock); + is_visible = !vertex->deleted; + delta = vertex->delta; + } + ApplyDeltasForRead(transaction, delta, view, + [&is_visible](const Delta &delta) { + switch (delta.action) { + case Delta::Action::ADD_LABEL: + case Delta::Action::REMOVE_LABEL: + case Delta::Action::SET_PROPERTY: + break; + case Delta::Action::RECREATE_OBJECT: { + is_visible = true; + break; + } + case Delta::Action::DELETE_OBJECT: { + is_visible = false; + break; + } + } + }); + if (!is_visible) return std::nullopt; + return VertexAccessor{vertex, transaction}; +} + +Result VertexAccessor::Delete() { + std::lock_guard guard(vertex_->lock); + + if (!PrepareForWrite(transaction_, vertex_)) + return Result{Error::SERIALIZATION_ERROR}; + + if (vertex_->deleted) return Result{false}; + + CreateAndLinkDelta(transaction_, vertex_, Delta::RecreateObjectTag()); + + vertex_->deleted = true; + + return Result{true}; +} + +Result VertexAccessor::AddLabel(uint64_t label) { + std::lock_guard guard(vertex_->lock); + + if (!PrepareForWrite(transaction_, vertex_)) + return Result{Error::SERIALIZATION_ERROR}; + + if (vertex_->deleted) return Result{Error::DELETED_OBJECT}; + + if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != + vertex_->labels.end()) + return Result{false}; + + CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label); + + vertex_->labels.push_back(label); + return Result{true}; +} + +Result VertexAccessor::RemoveLabel(uint64_t label) { + std::lock_guard guard(vertex_->lock); + + if (!PrepareForWrite(transaction_, vertex_)) + return Result{Error::SERIALIZATION_ERROR}; + + if (vertex_->deleted) return Result{Error::DELETED_OBJECT}; + + auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label); + if (it == vertex_->labels.end()) return Result{false}; + + CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label); + + std::swap(*it, *vertex_->labels.rbegin()); + vertex_->labels.pop_back(); + return Result{true}; +} + +Result VertexAccessor::HasLabel(uint64_t label, View view) { + bool deleted = false; + bool has_label = false; + Delta *delta = nullptr; + { + std::lock_guard guard(vertex_->lock); + deleted = vertex_->deleted; + has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), + label) != vertex_->labels.end(); + delta = vertex_->delta; + } + ApplyDeltasForRead(transaction_, delta, view, + [&deleted, &has_label, label](const Delta &delta) { + switch (delta.action) { + case Delta::Action::REMOVE_LABEL: { + if (delta.label == label) { + CHECK(has_label) << "Invalid database state!"; + has_label = false; + } + break; + } + case Delta::Action::ADD_LABEL: { + if (delta.label == label) { + CHECK(!has_label) << "Invalid database state!"; + has_label = true; + } + break; + } + case Delta::Action::DELETE_OBJECT: { + LOG(FATAL) << "Invalid accessor!"; + break; + } + case Delta::Action::RECREATE_OBJECT: { + deleted = false; + break; + } + case Delta::Action::SET_PROPERTY: + break; + } + }); + if (deleted) return Result{Error::DELETED_OBJECT}; + return Result{has_label}; +} + +Result> VertexAccessor::Labels(View view) { + bool deleted = false; + std::vector labels; + Delta *delta = nullptr; + { + std::lock_guard guard(vertex_->lock); + deleted = vertex_->deleted; + labels = vertex_->labels; + delta = vertex_->delta; + } + ApplyDeltasForRead( + transaction_, delta, view, [&deleted, &labels](const Delta &delta) { + switch (delta.action) { + case Delta::Action::REMOVE_LABEL: { + // Remove the label because we don't see the addition. + auto it = std::find(labels.begin(), labels.end(), delta.label); + CHECK(it != labels.end()) << "Invalid database state!"; + std::swap(*it, *labels.rbegin()); + labels.pop_back(); + break; + } + case Delta::Action::ADD_LABEL: { + // Add the label because we don't see the removal. + auto it = std::find(labels.begin(), labels.end(), delta.label); + CHECK(it == labels.end()) << "Invalid database state!"; + labels.push_back(delta.label); + break; + } + case Delta::Action::DELETE_OBJECT: { + LOG(FATAL) << "Invalid accessor!"; + break; + } + case Delta::Action::RECREATE_OBJECT: { + deleted = false; + break; + } + case Delta::Action::SET_PROPERTY: + break; + } + }); + if (deleted) return Result>{Error::DELETED_OBJECT}; + return Result>{std::move(labels)}; +} + +Result VertexAccessor::SetProperty(uint64_t property, + const PropertyValue &value) { + std::lock_guard guard(vertex_->lock); + + if (!PrepareForWrite(transaction_, vertex_)) + return Result{Error::SERIALIZATION_ERROR}; + + if (vertex_->deleted) return Result{Error::DELETED_OBJECT}; + + auto it = vertex_->properties.find(property); + bool existed = it != vertex_->properties.end(); + if (it != vertex_->properties.end()) { + CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, + it->second); + if (value.IsNull()) { + // remove the property + vertex_->properties.erase(it); + } else { + // set the value + it->second = value; + } + } else { + CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, + PropertyValue()); + if (!value.IsNull()) { + vertex_->properties.emplace(property, value); + } + } + + return Result{existed}; +} + +Result VertexAccessor::GetProperty(uint64_t property, + View view) { + bool deleted = false; + PropertyValue value; + Delta *delta = nullptr; + { + std::lock_guard guard(vertex_->lock); + deleted = vertex_->deleted; + auto it = vertex_->properties.find(property); + if (it != vertex_->properties.end()) { + value = it->second; + } + delta = vertex_->delta; + } + ApplyDeltasForRead(transaction_, delta, view, + [&deleted, &value, property](const Delta &delta) { + switch (delta.action) { + case Delta::Action::SET_PROPERTY: { + if (delta.property.key == property) { + value = delta.property.value; + } + break; + } + case Delta::Action::DELETE_OBJECT: { + LOG(FATAL) << "Invalid accessor!"; + break; + } + case Delta::Action::RECREATE_OBJECT: { + deleted = false; + break; + } + case Delta::Action::ADD_LABEL: + case Delta::Action::REMOVE_LABEL: + break; + } + }); + if (deleted) return Result{Error::DELETED_OBJECT}; + return Result{std::move(value)}; +} + +Result> VertexAccessor::Properties( + View view) { + std::unordered_map properties; + bool deleted = false; + Delta *delta = nullptr; + { + std::lock_guard guard(vertex_->lock); + deleted = vertex_->deleted; + properties = vertex_->properties; + delta = vertex_->delta; + } + ApplyDeltasForRead( + transaction_, delta, view, [&deleted, &properties](const Delta &delta) { + switch (delta.action) { + case Delta::Action::SET_PROPERTY: { + auto it = properties.find(delta.property.key); + if (it != properties.end()) { + if (delta.property.value.IsNull()) { + // remove the property + properties.erase(it); + } else { + // set the value + it->second = delta.property.value; + } + } else if (!delta.property.value.IsNull()) { + properties.emplace(delta.property.key, delta.property.value); + } + break; + } + case Delta::Action::DELETE_OBJECT: { + LOG(FATAL) << "Invalid accessor!"; + break; + } + case Delta::Action::RECREATE_OBJECT: { + deleted = false; + break; + } + case Delta::Action::ADD_LABEL: + case Delta::Action::REMOVE_LABEL: + break; + } + }); + if (deleted) { + return Result>{ + Error::DELETED_OBJECT}; + } + return Result>{ + std::move(properties)}; +} + +} // namespace storage diff --git a/src/storage/v2/vertex_accessor.hpp b/src/storage/v2/vertex_accessor.hpp index 0af3a32e6..9d10ec00d 100644 --- a/src/storage/v2/vertex_accessor.hpp +++ b/src/storage/v2/vertex_accessor.hpp @@ -1,11 +1,9 @@ #pragma once -#include #include #include "storage/v2/vertex.hpp" -#include "storage/v2/mvcc.hpp" #include "storage/v2/result.hpp" #include "storage/v2/transaction.hpp" #include "storage/v2/view.hpp" @@ -20,292 +18,23 @@ class VertexAccessor final { public: static std::optional Create(Vertex *vertex, Transaction *transaction, - View view) { - bool is_visible = true; - Delta *delta = nullptr; - { - std::lock_guard guard(vertex->lock); - is_visible = !vertex->deleted; - delta = vertex->delta; - } - ApplyDeltasForRead(transaction, delta, view, - [&is_visible](const Delta &delta) { - switch (delta.action) { - case Delta::Action::ADD_LABEL: - case Delta::Action::REMOVE_LABEL: - case Delta::Action::SET_PROPERTY: - break; - case Delta::Action::RECREATE_OBJECT: { - is_visible = true; - break; - } - case Delta::Action::DELETE_OBJECT: { - is_visible = false; - break; - } - } - }); - if (!is_visible) return std::nullopt; - return VertexAccessor{vertex, transaction}; - } + View view); - Result Delete() { - std::lock_guard guard(vertex_->lock); + Result Delete(); - if (!PrepareForWrite(transaction_, vertex_)) - return Result{Error::SERIALIZATION_ERROR}; + Result AddLabel(uint64_t label); - if (vertex_->deleted) return Result{false}; + Result RemoveLabel(uint64_t label); - CreateAndLinkDelta(transaction_, vertex_, Delta::RecreateObjectTag()); + Result HasLabel(uint64_t label, View view); - vertex_->deleted = true; + Result> Labels(View view); - return Result{true}; - } + Result SetProperty(uint64_t property, const PropertyValue &value); - Result AddLabel(uint64_t label) { - std::lock_guard guard(vertex_->lock); + Result GetProperty(uint64_t property, View view); - if (!PrepareForWrite(transaction_, vertex_)) - return Result{Error::SERIALIZATION_ERROR}; - - if (vertex_->deleted) return Result{Error::DELETED_OBJECT}; - - if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != - vertex_->labels.end()) - return Result{false}; - - CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label); - - vertex_->labels.push_back(label); - return Result{true}; - } - - Result RemoveLabel(uint64_t label) { - std::lock_guard guard(vertex_->lock); - - if (!PrepareForWrite(transaction_, vertex_)) - return Result{Error::SERIALIZATION_ERROR}; - - if (vertex_->deleted) return Result{Error::DELETED_OBJECT}; - - auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label); - if (it == vertex_->labels.end()) return Result{false}; - - CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label); - - std::swap(*it, *vertex_->labels.rbegin()); - vertex_->labels.pop_back(); - return Result{true}; - } - - Result HasLabel(uint64_t label, View view) { - bool deleted = false; - bool has_label = false; - Delta *delta = nullptr; - { - std::lock_guard guard(vertex_->lock); - deleted = vertex_->deleted; - has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), - label) != vertex_->labels.end(); - delta = vertex_->delta; - } - ApplyDeltasForRead(transaction_, delta, view, - [&deleted, &has_label, label](const Delta &delta) { - switch (delta.action) { - case Delta::Action::REMOVE_LABEL: { - if (delta.label == label) { - CHECK(has_label) << "Invalid database state!"; - has_label = false; - } - break; - } - case Delta::Action::ADD_LABEL: { - if (delta.label == label) { - CHECK(!has_label) << "Invalid database state!"; - has_label = true; - } - break; - } - case Delta::Action::DELETE_OBJECT: { - LOG(FATAL) << "Invalid accessor!"; - break; - } - case Delta::Action::RECREATE_OBJECT: { - deleted = false; - break; - } - case Delta::Action::SET_PROPERTY: - break; - } - }); - if (deleted) return Result{Error::DELETED_OBJECT}; - return Result{has_label}; - } - - Result> Labels(View view) { - bool deleted = false; - std::vector labels; - Delta *delta = nullptr; - { - std::lock_guard guard(vertex_->lock); - deleted = vertex_->deleted; - labels = vertex_->labels; - delta = vertex_->delta; - } - ApplyDeltasForRead( - transaction_, delta, view, [&deleted, &labels](const Delta &delta) { - switch (delta.action) { - case Delta::Action::REMOVE_LABEL: { - // Remove the label because we don't see the addition. - auto it = std::find(labels.begin(), labels.end(), delta.label); - CHECK(it != labels.end()) << "Invalid database state!"; - std::swap(*it, *labels.rbegin()); - labels.pop_back(); - break; - } - case Delta::Action::ADD_LABEL: { - // Add the label because we don't see the removal. - auto it = std::find(labels.begin(), labels.end(), delta.label); - CHECK(it == labels.end()) << "Invalid database state!"; - labels.push_back(delta.label); - break; - } - case Delta::Action::DELETE_OBJECT: { - LOG(FATAL) << "Invalid accessor!"; - break; - } - case Delta::Action::RECREATE_OBJECT: { - deleted = false; - break; - } - case Delta::Action::SET_PROPERTY: - break; - } - }); - if (deleted) return Result>{Error::DELETED_OBJECT}; - return Result>{std::move(labels)}; - } - - Result SetProperty(uint64_t property, const PropertyValue &value) { - std::lock_guard guard(vertex_->lock); - - if (!PrepareForWrite(transaction_, vertex_)) - return Result{Error::SERIALIZATION_ERROR}; - - if (vertex_->deleted) return Result{Error::DELETED_OBJECT}; - - auto it = vertex_->properties.find(property); - bool existed = it != vertex_->properties.end(); - if (it != vertex_->properties.end()) { - CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), - property, it->second); - if (value.IsNull()) { - // remove the property - vertex_->properties.erase(it); - } else { - // set the value - it->second = value; - } - } else { - CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), - property, PropertyValue()); - if (!value.IsNull()) { - vertex_->properties.emplace(property, value); - } - } - - return Result{existed}; - } - - Result GetProperty(uint64_t property, View view) { - bool deleted = false; - PropertyValue value; - Delta *delta = nullptr; - { - std::lock_guard guard(vertex_->lock); - deleted = vertex_->deleted; - auto it = vertex_->properties.find(property); - if (it != vertex_->properties.end()) { - value = it->second; - } - delta = vertex_->delta; - } - ApplyDeltasForRead(transaction_, delta, view, - [&deleted, &value, property](const Delta &delta) { - switch (delta.action) { - case Delta::Action::SET_PROPERTY: { - if (delta.property.key == property) { - value = delta.property.value; - } - break; - } - case Delta::Action::DELETE_OBJECT: { - LOG(FATAL) << "Invalid accessor!"; - break; - } - case Delta::Action::RECREATE_OBJECT: { - deleted = false; - break; - } - case Delta::Action::ADD_LABEL: - case Delta::Action::REMOVE_LABEL: - break; - } - }); - if (deleted) return Result{Error::DELETED_OBJECT}; - return Result{std::move(value)}; - } - - Result> Properties(View view) { - std::unordered_map properties; - bool deleted = false; - Delta *delta = nullptr; - { - std::lock_guard guard(vertex_->lock); - deleted = vertex_->deleted; - properties = vertex_->properties; - delta = vertex_->delta; - } - ApplyDeltasForRead( - transaction_, delta, view, [&deleted, &properties](const Delta &delta) { - switch (delta.action) { - case Delta::Action::SET_PROPERTY: { - auto it = properties.find(delta.property.key); - if (it != properties.end()) { - if (delta.property.value.IsNull()) { - // remove the property - properties.erase(it); - } else { - // set the value - it->second = delta.property.value; - } - } else if (!delta.property.value.IsNull()) { - properties.emplace(delta.property.key, delta.property.value); - } - break; - } - case Delta::Action::DELETE_OBJECT: { - LOG(FATAL) << "Invalid accessor!"; - break; - } - case Delta::Action::RECREATE_OBJECT: { - deleted = false; - break; - } - case Delta::Action::ADD_LABEL: - case Delta::Action::REMOVE_LABEL: - break; - } - }); - if (deleted) { - return Result>{ - Error::DELETED_OBJECT}; - } - return Result>{ - std::move(properties)}; - } + Result> Properties(View view); Gid Gid() const { return vertex_->gid; } diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 6f5551ee1..b5cd46045 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -383,7 +383,7 @@ add_unit_test(property_value_v2.cpp) target_link_libraries(${test_prefix}property_value_v2 mg-utils) add_unit_test(storage_v2.cpp) -target_link_libraries(${test_prefix}storage_v2 mg-utils) +target_link_libraries(${test_prefix}storage_v2 mg-storage-v2) # Test LCP