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
This commit is contained in:
parent
9e223cdabd
commit
6e6dff81e0
@ -11,6 +11,7 @@ add_subdirectory(communication)
|
||||
add_subdirectory(stats)
|
||||
add_subdirectory(auth)
|
||||
add_subdirectory(slk)
|
||||
add_subdirectory(storage/v2)
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Common LCP files
|
||||
|
6
src/storage/v2/CMakeLists.txt
Normal file
6
src/storage/v2/CMakeLists.txt
Normal file
@ -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)
|
153
src/storage/v2/storage.cpp
Normal file
153
src/storage/v2/storage.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include "storage/v2/storage.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#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<utils::SpinLock> 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<VertexAccessor> 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<utils::SpinLock> 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<utils::SpinLock> 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
|
@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#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<utils::SpinLock> 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<VertexAccessor> 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<VertexAccessor> 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<utils::SpinLock> 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<utils::SpinLock> 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_;
|
||||
|
301
src/storage/v2/vertex_accessor.cpp
Normal file
301
src/storage/v2/vertex_accessor.cpp
Normal file
@ -0,0 +1,301 @@
|
||||
#include "storage/v2/vertex_accessor.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
|
||||
namespace storage {
|
||||
|
||||
std::optional<VertexAccessor> VertexAccessor::Create(Vertex *vertex,
|
||||
Transaction *transaction,
|
||||
View view) {
|
||||
bool is_visible = true;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<bool> VertexAccessor::Delete() {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{false};
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::RecreateObjectTag());
|
||||
|
||||
vertex_->deleted = true;
|
||||
|
||||
return Result<bool>{true};
|
||||
}
|
||||
|
||||
Result<bool> VertexAccessor::AddLabel(uint64_t label) {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{Error::DELETED_OBJECT};
|
||||
|
||||
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) !=
|
||||
vertex_->labels.end())
|
||||
return Result<bool>{false};
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
|
||||
|
||||
vertex_->labels.push_back(label);
|
||||
return Result<bool>{true};
|
||||
}
|
||||
|
||||
Result<bool> VertexAccessor::RemoveLabel(uint64_t label) {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{Error::DELETED_OBJECT};
|
||||
|
||||
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
|
||||
if (it == vertex_->labels.end()) return Result<bool>{false};
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
|
||||
|
||||
std::swap(*it, *vertex_->labels.rbegin());
|
||||
vertex_->labels.pop_back();
|
||||
return Result<bool>{true};
|
||||
}
|
||||
|
||||
Result<bool> VertexAccessor::HasLabel(uint64_t label, View view) {
|
||||
bool deleted = false;
|
||||
bool has_label = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<bool>{Error::DELETED_OBJECT};
|
||||
return Result<bool>{has_label};
|
||||
}
|
||||
|
||||
Result<std::vector<uint64_t>> VertexAccessor::Labels(View view) {
|
||||
bool deleted = false;
|
||||
std::vector<uint64_t> labels;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<std::vector<uint64_t>>{Error::DELETED_OBJECT};
|
||||
return Result<std::vector<uint64_t>>{std::move(labels)};
|
||||
}
|
||||
|
||||
Result<bool> VertexAccessor::SetProperty(uint64_t property,
|
||||
const PropertyValue &value) {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{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<bool>{existed};
|
||||
}
|
||||
|
||||
Result<PropertyValue> VertexAccessor::GetProperty(uint64_t property,
|
||||
View view) {
|
||||
bool deleted = false;
|
||||
PropertyValue value;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<PropertyValue>{Error::DELETED_OBJECT};
|
||||
return Result<PropertyValue>{std::move(value)};
|
||||
}
|
||||
|
||||
Result<std::unordered_map<uint64_t, PropertyValue>> VertexAccessor::Properties(
|
||||
View view) {
|
||||
std::unordered_map<uint64_t, PropertyValue> properties;
|
||||
bool deleted = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<std::unordered_map<uint64_t, PropertyValue>>{
|
||||
Error::DELETED_OBJECT};
|
||||
}
|
||||
return Result<std::unordered_map<uint64_t, PropertyValue>>{
|
||||
std::move(properties)};
|
||||
}
|
||||
|
||||
} // namespace storage
|
@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#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<VertexAccessor> Create(Vertex *vertex,
|
||||
Transaction *transaction,
|
||||
View view) {
|
||||
bool is_visible = true;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<bool> Delete() {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
Result<bool> Delete();
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
Result<bool> AddLabel(uint64_t label);
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{false};
|
||||
Result<bool> RemoveLabel(uint64_t label);
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::RecreateObjectTag());
|
||||
Result<bool> HasLabel(uint64_t label, View view);
|
||||
|
||||
vertex_->deleted = true;
|
||||
Result<std::vector<uint64_t>> Labels(View view);
|
||||
|
||||
return Result<bool>{true};
|
||||
}
|
||||
Result<bool> SetProperty(uint64_t property, const PropertyValue &value);
|
||||
|
||||
Result<bool> AddLabel(uint64_t label) {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
Result<PropertyValue> GetProperty(uint64_t property, View view);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{Error::DELETED_OBJECT};
|
||||
|
||||
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) !=
|
||||
vertex_->labels.end())
|
||||
return Result<bool>{false};
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
|
||||
|
||||
vertex_->labels.push_back(label);
|
||||
return Result<bool>{true};
|
||||
}
|
||||
|
||||
Result<bool> RemoveLabel(uint64_t label) {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{Error::DELETED_OBJECT};
|
||||
|
||||
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
|
||||
if (it == vertex_->labels.end()) return Result<bool>{false};
|
||||
|
||||
CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
|
||||
|
||||
std::swap(*it, *vertex_->labels.rbegin());
|
||||
vertex_->labels.pop_back();
|
||||
return Result<bool>{true};
|
||||
}
|
||||
|
||||
Result<bool> HasLabel(uint64_t label, View view) {
|
||||
bool deleted = false;
|
||||
bool has_label = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<bool>{Error::DELETED_OBJECT};
|
||||
return Result<bool>{has_label};
|
||||
}
|
||||
|
||||
Result<std::vector<uint64_t>> Labels(View view) {
|
||||
bool deleted = false;
|
||||
std::vector<uint64_t> labels;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<std::vector<uint64_t>>{Error::DELETED_OBJECT};
|
||||
return Result<std::vector<uint64_t>>{std::move(labels)};
|
||||
}
|
||||
|
||||
Result<bool> SetProperty(uint64_t property, const PropertyValue &value) {
|
||||
std::lock_guard<utils::SpinLock> guard(vertex_->lock);
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_))
|
||||
return Result<bool>{Error::SERIALIZATION_ERROR};
|
||||
|
||||
if (vertex_->deleted) return Result<bool>{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<bool>{existed};
|
||||
}
|
||||
|
||||
Result<PropertyValue> GetProperty(uint64_t property, View view) {
|
||||
bool deleted = false;
|
||||
PropertyValue value;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<PropertyValue>{Error::DELETED_OBJECT};
|
||||
return Result<PropertyValue>{std::move(value)};
|
||||
}
|
||||
|
||||
Result<std::unordered_map<uint64_t, PropertyValue>> Properties(View view) {
|
||||
std::unordered_map<uint64_t, PropertyValue> properties;
|
||||
bool deleted = false;
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
std::lock_guard<utils::SpinLock> 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<std::unordered_map<uint64_t, PropertyValue>>{
|
||||
Error::DELETED_OBJECT};
|
||||
}
|
||||
return Result<std::unordered_map<uint64_t, PropertyValue>>{
|
||||
std::move(properties)};
|
||||
}
|
||||
Result<std::unordered_map<uint64_t, PropertyValue>> Properties(View view);
|
||||
|
||||
Gid Gid() const { return vertex_->gid; }
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user