Vertex/edge compaction: delete + delta, smaller propertystore + lock, smaller vector
This commit is contained in:
parent
61b9bb0f59
commit
7627dab16b
libs
src
dbms/inmemory
query
storage/v2
@ -182,6 +182,7 @@ import_external_library(rocksdb STATIC
|
||||
-DGFLAGS_NOTHREADS=OFF
|
||||
-DCMAKE_INSTALL_LIBDIR=lib
|
||||
-DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=true
|
||||
-DPORTABLE=1
|
||||
BUILD_COMMAND $(MAKE) rocksdb)
|
||||
|
||||
# Setup libbcrypt
|
||||
|
@ -630,8 +630,8 @@ uint64_t InMemoryReplicationHandlers::ReadAndApplyDelta(storage::InMemoryStorage
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{edge->lock};
|
||||
is_visible = !edge->deleted;
|
||||
delta = edge->delta;
|
||||
is_visible = !edge->deleted();
|
||||
delta = edge->delta();
|
||||
}
|
||||
ApplyDeltasForRead(&transaction->GetTransaction(), delta, View::NEW, [&is_visible](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
|
@ -324,7 +324,7 @@ mgp_value_type FromTypedValueType(memgraph::query::TypedValue::Type type) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool IsDeleted(const mgp_vertex *vertex) { return vertex->getImpl().impl_.vertex_->deleted; }
|
||||
bool IsDeleted(const mgp_vertex *vertex) { return vertex->getImpl().impl_.vertex_->deleted(); }
|
||||
|
||||
bool IsDeleted(const mgp_edge *edge) { return edge->impl.IsDeleted(); }
|
||||
|
||||
|
@ -386,12 +386,12 @@ bool TypedValue::ContainsDeleted() const {
|
||||
case Type::Map:
|
||||
return std::ranges::any_of(map_v, [](const auto &item) { return item.second.ContainsDeleted(); });
|
||||
case Type::Vertex:
|
||||
return vertex_v.impl_.vertex_->deleted;
|
||||
return vertex_v.impl_.vertex_->deleted();
|
||||
case Type::Edge:
|
||||
return edge_v.IsDeleted();
|
||||
case Type::Path:
|
||||
return std::ranges::any_of(path_v.vertices(),
|
||||
[](auto &vertex_acc) { return vertex_acc.impl_.vertex_->deleted; }) ||
|
||||
[](auto &vertex_acc) { return vertex_acc.impl_.vertex_->deleted(); }) ||
|
||||
std::ranges::any_of(path_v.edges(), [](auto &edge_acc) { return edge_acc.IsDeleted(); });
|
||||
default:
|
||||
throw TypedValueException("Value of unknown type");
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -58,7 +58,7 @@ void ExistenceConstraints::LoadExistenceConstraints(const std::vector<std::strin
|
||||
|
||||
[[nodiscard]] std::optional<ConstraintViolation> ExistenceConstraints::ValidateVertexOnConstraint(
|
||||
const Vertex &vertex, const LabelId &label, const PropertyId &property) {
|
||||
if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
|
||||
if (!vertex.deleted() && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
|
||||
return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
|
||||
}
|
||||
return std::nullopt;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -80,7 +80,7 @@ std::unique_ptr<rocksdb::Transaction> DiskLabelIndex::CreateAllReadingRocksDBTra
|
||||
bool DiskLabelIndex::SyncVertexToLabelIndexStorage(const Vertex &vertex, uint64_t commit_timestamp) const {
|
||||
auto disk_transaction = CreateRocksDBTransaction();
|
||||
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta()); maybe_old_disk_key.has_value()) {
|
||||
if (!disk_transaction->Delete(maybe_old_disk_key.value()).ok()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -87,7 +87,7 @@ bool DiskLabelPropertyIndex::SyncVertexToLabelPropertyIndexStorage(const Vertex
|
||||
uint64_t commit_timestamp) const {
|
||||
auto disk_transaction = CreateRocksDBTransaction();
|
||||
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta()); maybe_old_disk_key.has_value()) {
|
||||
if (!disk_transaction->Delete(maybe_old_disk_key.value()).ok()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ constexpr const char *kExistenceConstraintsStr = "existence_constraints";
|
||||
/// TODO: (andi) Maybe a better way of checking would be if the first delta is DELETE_DESERIALIZED
|
||||
/// then we now that the vertex has only been deserialized and nothing more has been done on it.
|
||||
bool VertexNeedsToBeSerialized(const Vertex &vertex) {
|
||||
Delta *head = vertex.delta;
|
||||
Delta *head = vertex.delta();
|
||||
while (head != nullptr) {
|
||||
if (head->action == Delta::Action::ADD_LABEL || head->action == Delta::Action::REMOVE_LABEL ||
|
||||
head->action == Delta::Action::DELETE_OBJECT || head->action == Delta::Action::RECREATE_OBJECT ||
|
||||
@ -131,9 +131,9 @@ bool VertexNeedsToBeSerialized(const Vertex &vertex) {
|
||||
}
|
||||
|
||||
bool VertexHasLabel(const Vertex &vertex, LabelId label, Transaction *transaction, View view) {
|
||||
bool deleted = vertex.deleted;
|
||||
bool deleted = vertex.deleted();
|
||||
bool has_label = std::find(vertex.labels.begin(), vertex.labels.end(), label) != vertex.labels.end();
|
||||
Delta *delta = vertex.delta;
|
||||
Delta *delta = vertex.delta();
|
||||
ApplyDeltasForRead(transaction, delta, view, [&deleted, &has_label, label](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::REMOVE_LABEL: {
|
||||
@ -170,9 +170,9 @@ bool VertexHasLabel(const Vertex &vertex, LabelId label, Transaction *transactio
|
||||
}
|
||||
|
||||
PropertyValue GetVertexProperty(const Vertex &vertex, PropertyId property, Transaction *transaction, View view) {
|
||||
bool deleted = vertex.deleted;
|
||||
bool deleted = vertex.deleted();
|
||||
PropertyValue value = vertex.properties.GetProperty(property);
|
||||
Delta *delta = vertex.delta;
|
||||
Delta *delta = vertex.delta();
|
||||
ApplyDeltasForRead(transaction, delta, view, [&deleted, &value, property](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
case Delta::Action::SET_PROPERTY: {
|
||||
@ -625,7 +625,7 @@ std::unordered_set<Gid> DiskStorage::MergeVerticesFromMainCacheWithLabelIndexCac
|
||||
gids.insert(vertex.gid);
|
||||
if (VertexHasLabel(vertex, label, transaction, view)) {
|
||||
spdlog::trace("Loaded vertex with gid: {} from main index storage to label index", vertex.gid.ToString());
|
||||
uint64_t ts = utils::GetEarliestTimestamp(vertex.delta);
|
||||
uint64_t ts = utils::GetEarliestTimestamp(vertex.delta());
|
||||
/// TODO: here are doing serialization and then later deserialization again -> expensive
|
||||
LoadVertexToLabelIndexCache(transaction, utils::SerializeVertexAsKeyForLabelIndex(label, vertex.gid),
|
||||
utils::SerializeVertexAsValueForLabelIndex(label, vertex.labels, vertex.properties),
|
||||
@ -676,7 +676,7 @@ std::unordered_set<Gid> DiskStorage::MergeVerticesFromMainCacheWithLabelProperty
|
||||
for (const auto &vertex : main_cache_acc) {
|
||||
gids.insert(vertex.gid);
|
||||
if (label_property_filter(vertex, label, property, view)) {
|
||||
uint64_t ts = utils::GetEarliestTimestamp(vertex.delta);
|
||||
uint64_t ts = utils::GetEarliestTimestamp(vertex.delta());
|
||||
LoadVertexToLabelPropertyIndexCache(
|
||||
transaction, utils::SerializeVertexAsKeyForLabelPropertyIndex(label, property, vertex.gid),
|
||||
utils::SerializeVertexAsValueForLabelPropertyIndex(label, vertex.labels, vertex.properties),
|
||||
@ -761,7 +761,7 @@ std::unordered_set<Gid> DiskStorage::MergeVerticesFromMainCacheWithLabelProperty
|
||||
auto prop_value = GetVertexProperty(vertex, property, transaction, view);
|
||||
if (VertexHasLabel(vertex, label, transaction, view) &&
|
||||
IsPropertyValueWithinInterval(prop_value, lower_bound, upper_bound)) {
|
||||
uint64_t ts = utils::GetEarliestTimestamp(vertex.delta);
|
||||
uint64_t ts = utils::GetEarliestTimestamp(vertex.delta());
|
||||
LoadVertexToLabelPropertyIndexCache(
|
||||
transaction, utils::SerializeVertexAsKeyForLabelPropertyIndex(label, property, vertex.gid),
|
||||
utils::SerializeVertexAsValueForLabelPropertyIndex(label, vertex.labels, vertex.properties),
|
||||
@ -946,7 +946,7 @@ Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from,
|
||||
auto *from_vertex = from->vertex_;
|
||||
auto *to_vertex = to->vertex_;
|
||||
|
||||
if (from_vertex->deleted || to_vertex->deleted) return Error::DELETED_OBJECT;
|
||||
if (from_vertex->deleted() || to_vertex->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
auto gid = storage::Gid::FromUint(disk_storage->edge_id_.fetch_add(1, std::memory_order_acq_rel));
|
||||
@ -1189,12 +1189,12 @@ bool DiskStorage::DeleteEdgeFromConnectivityIndex(Transaction *transaction, cons
|
||||
return check_result.GetError();
|
||||
}
|
||||
|
||||
if (vertex.deleted) {
|
||||
if (vertex.deleted()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/// NOTE: this deletion has to come before writing, otherwise RocksDB thinks that all entries are deleted
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta()); maybe_old_disk_key.has_value()) {
|
||||
if (!DeleteVertexFromDisk(transaction, vertex.gid.ToString(), maybe_old_disk_key.value())) {
|
||||
return StorageManipulationError{SerializationError{}};
|
||||
}
|
||||
@ -1431,7 +1431,7 @@ std::vector<EdgeAccessor> DiskStorage::OutEdges(const VertexAccessor *src_vertex
|
||||
const VertexAccessor *destination, Transaction *transaction,
|
||||
View view) {
|
||||
/// Check whether the vertex is deleted in the current tx only if View::NEW is requested
|
||||
if (view == View::NEW && src_vertex->vertex_->deleted) return {};
|
||||
if (view == View::NEW && src_vertex->vertex_->deleted()) return {};
|
||||
|
||||
const std::string src_vertex_gid = src_vertex->Gid().ToString();
|
||||
rocksdb::ReadOptions ro;
|
||||
@ -1468,9 +1468,9 @@ std::vector<EdgeAccessor> DiskStorage::OutEdges(const VertexAccessor *src_vertex
|
||||
auto dst_vertex_gid = utils::ExtractDstVertexGidFromEdgeValue(edge_val_str);
|
||||
if (!destination) {
|
||||
auto dst_vertex = FindVertex(dst_vertex_gid, transaction, view);
|
||||
/// TODO: (andi) I think dst_vertex->deleted should be unnecessary
|
||||
/// TODO: (andi) I think dst_vertex->deleted() should be unnecessary
|
||||
/// Check whether the vertex is deleted in the current tx only if View::NEW is requested
|
||||
if (!dst_vertex.has_value() || (view == View::NEW && dst_vertex->vertex_->deleted))
|
||||
if (!dst_vertex.has_value() || (view == View::NEW && dst_vertex->vertex_->deleted()))
|
||||
return std::optional<EdgeAccessor>{};
|
||||
|
||||
return CreateEdgeFromDisk(src_vertex, &*dst_vertex, transaction, edge_type_id, edge_gid, properties_str,
|
||||
@ -1478,7 +1478,7 @@ std::vector<EdgeAccessor> DiskStorage::OutEdges(const VertexAccessor *src_vertex
|
||||
}
|
||||
/// This is needed for filtering
|
||||
/// Second check not needed I think
|
||||
if (dst_vertex_gid != destination->Gid() || destination->vertex_->deleted) {
|
||||
if (dst_vertex_gid != destination->Gid() || destination->vertex_->deleted()) {
|
||||
return std::optional<EdgeAccessor>{};
|
||||
}
|
||||
|
||||
@ -1495,7 +1495,7 @@ std::vector<EdgeAccessor> DiskStorage::InEdges(const VertexAccessor *dst_vertex,
|
||||
const std::vector<EdgeTypeId> &edge_types, const VertexAccessor *source,
|
||||
Transaction *transaction, View view) {
|
||||
/// Check whether the vertex is deleted in the current tx only if View::NEW is requested
|
||||
if (view == View::NEW && dst_vertex->vertex_->deleted) return {};
|
||||
if (view == View::NEW && dst_vertex->vertex_->deleted()) return {};
|
||||
|
||||
const std::string dst_vertex_gid = dst_vertex->Gid().ToString();
|
||||
rocksdb::ReadOptions ro;
|
||||
@ -1533,14 +1533,14 @@ std::vector<EdgeAccessor> DiskStorage::InEdges(const VertexAccessor *dst_vertex,
|
||||
auto src_vertex = FindVertex(src_vertex_gid, transaction, view);
|
||||
/// Check whether the vertex is deleted in the current tx only if View::NEW is requested
|
||||
/// TODO: (andi) Second check I think isn't necessary
|
||||
if (!src_vertex.has_value() || (view == View::NEW && src_vertex->vertex_->deleted))
|
||||
if (!src_vertex.has_value() || (view == View::NEW && src_vertex->vertex_->deleted()))
|
||||
return std::optional<EdgeAccessor>{};
|
||||
|
||||
return CreateEdgeFromDisk(&*src_vertex, dst_vertex, transaction, edge_type_id, edge_gid, properties_str,
|
||||
edge_gid_str, kDeserializeTimestamp);
|
||||
}
|
||||
/// TODO: (andi) 2nd check not needed I think
|
||||
if (src_vertex_gid != source->Gid() || source->vertex_->deleted) {
|
||||
if (src_vertex_gid != source->Gid() || source->vertex_->deleted()) {
|
||||
return std::optional<EdgeAccessor>{};
|
||||
}
|
||||
return CreateEdgeFromDisk(source, dst_vertex, transaction, edge_type_id, edge_gid, properties_str, edge_gid_str,
|
||||
@ -1817,7 +1817,7 @@ void DiskStorage::DiskAccessor::UpdateObjectsCountOnAbort() {
|
||||
switch (prev.type) {
|
||||
case PreviousPtr::Type::VERTEX: {
|
||||
auto *vertex = prev.vertex;
|
||||
Delta *current = vertex->delta;
|
||||
Delta *current = vertex->delta();
|
||||
while (current != nullptr && current->timestamp->load(std::memory_order_acquire) == transaction_id) {
|
||||
switch (current->action) {
|
||||
case Delta::Action::DELETE_DESERIALIZED_OBJECT:
|
||||
@ -1847,7 +1847,7 @@ void DiskStorage::DiskAccessor::UpdateObjectsCountOnAbort() {
|
||||
}
|
||||
current = current->next.load(std::memory_order_acquire);
|
||||
}
|
||||
vertex->delta = current;
|
||||
vertex->delta(current);
|
||||
if (current != nullptr) {
|
||||
current->prev.Set(vertex);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -216,7 +216,7 @@ bool DiskUniqueConstraints::SyncVertexToUniqueConstraintsStorage(const Vertex &v
|
||||
auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
|
||||
kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
|
||||
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
|
||||
if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta()); maybe_old_disk_key.has_value()) {
|
||||
spdlog::trace("Found old disk key {} for vertex {}", maybe_old_disk_key.value(), vertex.gid.ToString());
|
||||
if (auto status = disk_transaction->Delete(maybe_old_disk_key.value()); !status.ok()) {
|
||||
return false;
|
||||
|
@ -1881,8 +1881,8 @@ void CreateSnapshot(Storage *storage, Transaction *transaction, const std::files
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{edge.lock};
|
||||
is_visible = !edge.deleted;
|
||||
delta = edge.delta;
|
||||
is_visible = !edge.deleted();
|
||||
delta = edge.delta();
|
||||
}
|
||||
ApplyDeltasForRead(transaction, delta, View::OLD, [&is_visible](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -16,6 +16,7 @@
|
||||
#include "storage/v2/delta.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_store.hpp"
|
||||
#include "storage/v2/tco_delta.hpp"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/rw_spin_lock.hpp"
|
||||
|
||||
@ -24,7 +25,7 @@ namespace memgraph::storage {
|
||||
struct Vertex;
|
||||
|
||||
struct Edge {
|
||||
Edge(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
|
||||
Edge(Gid gid, Delta *delta) : gid(gid), tco_delta(delta) {
|
||||
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT ||
|
||||
delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT,
|
||||
"Edge must be created with an initial DELETE_OBJECT delta!");
|
||||
@ -32,14 +33,21 @@ struct Edge {
|
||||
|
||||
Gid gid;
|
||||
|
||||
mutable utils::RWSpinLock lock;
|
||||
PropertyStore properties;
|
||||
|
||||
mutable utils::RWSpinLock lock;
|
||||
bool deleted;
|
||||
// bool deleted;
|
||||
// uint8_t PAD;
|
||||
// uint16_t PAD;
|
||||
|
||||
Delta *delta;
|
||||
// Delta *delta;
|
||||
|
||||
TcoDelta tco_delta;
|
||||
|
||||
bool deleted() const { return tco_delta.deleted(); }
|
||||
void deleted(bool in) { tco_delta.deleted(in); }
|
||||
Delta *delta() const { return tco_delta.delta(); }
|
||||
void delta(Delta *in) { tco_delta.delta(in); }
|
||||
};
|
||||
|
||||
static_assert(alignof(Edge) >= 8, "The Edge should be aligned to at least 8!");
|
||||
|
@ -31,7 +31,7 @@ bool EdgeAccessor::IsDeleted() const {
|
||||
if (!storage_->config_.salient.items.properties_on_edges) {
|
||||
return false;
|
||||
}
|
||||
return edge_.ptr->deleted;
|
||||
return edge_.ptr->deleted();
|
||||
}
|
||||
|
||||
bool EdgeAccessor::IsVisible(const View view) const {
|
||||
@ -47,7 +47,7 @@ bool EdgeAccessor::IsVisible(const View view) const {
|
||||
deleted = std::find_if(from_vertex_->out_edges.begin(), from_vertex_->out_edges.end(), [&](const auto &out_edge) {
|
||||
return std::get<2>(out_edge) == edge_;
|
||||
}) == from_vertex_->out_edges.end();
|
||||
delta = from_vertex_->delta;
|
||||
delta = from_vertex_->delta();
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -80,8 +80,8 @@ bool EdgeAccessor::IsVisible(const View view) const {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{edge_.ptr->lock};
|
||||
deleted = edge_.ptr->deleted;
|
||||
delta = edge_.ptr->delta;
|
||||
deleted = edge_.ptr->deleted();
|
||||
delta = edge_.ptr->delta();
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -112,11 +112,11 @@ VertexAccessor EdgeAccessor::FromVertex() const { return VertexAccessor{from_ver
|
||||
VertexAccessor EdgeAccessor::ToVertex() const { return VertexAccessor{to_vertex_, storage_, transaction_}; }
|
||||
|
||||
VertexAccessor EdgeAccessor::DeletedEdgeFromVertex() const {
|
||||
return VertexAccessor{from_vertex_, storage_, transaction_, for_deleted_ && from_vertex_->deleted};
|
||||
return VertexAccessor{from_vertex_, storage_, transaction_, for_deleted_ && from_vertex_->deleted()};
|
||||
}
|
||||
|
||||
VertexAccessor EdgeAccessor::DeletedEdgeToVertex() const {
|
||||
return VertexAccessor{to_vertex_, storage_, transaction_, for_deleted_ && to_vertex_->deleted};
|
||||
return VertexAccessor{to_vertex_, storage_, transaction_, for_deleted_ && to_vertex_->deleted()};
|
||||
}
|
||||
|
||||
Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, const PropertyValue &value) {
|
||||
@ -127,7 +127,7 @@ Result<storage::PropertyValue> EdgeAccessor::SetProperty(PropertyId property, co
|
||||
|
||||
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_.ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
using ReturnType = decltype(edge_.ptr->properties.GetProperty(property));
|
||||
std::optional<ReturnType> current_value;
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
@ -160,7 +160,7 @@ Result<bool> EdgeAccessor::InitProperties(const std::map<storage::PropertyId, st
|
||||
|
||||
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_.ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
if (!edge_.ptr->properties.InitProperties(properties)) return false;
|
||||
utils::AtomicMemoryBlock atomic_memory_block{[&properties, transaction_ = transaction_, edge_ = edge_]() {
|
||||
@ -182,7 +182,7 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> EdgeAc
|
||||
|
||||
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_.ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
using ReturnType = decltype(edge_.ptr->properties.UpdateProperties(properties));
|
||||
std::optional<ReturnType> id_old_new_change;
|
||||
@ -205,7 +205,7 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::ClearProperties() {
|
||||
|
||||
if (!PrepareForWrite(transaction_, edge_.ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_.ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_.ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
using ReturnType = decltype(edge_.ptr->properties.Properties());
|
||||
std::optional<ReturnType> properties;
|
||||
@ -230,9 +230,9 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view)
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{edge_.ptr->lock};
|
||||
deleted = edge_.ptr->deleted;
|
||||
deleted = edge_.ptr->deleted();
|
||||
value.emplace(edge_.ptr->properties.GetProperty(property));
|
||||
delta = edge_.ptr->delta;
|
||||
delta = edge_.ptr->delta();
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &value, property](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
@ -269,7 +269,7 @@ Result<uint64_t> EdgeAccessor::GetPropertySize(PropertyId property, View view) c
|
||||
if (!storage_->config_.salient.items.properties_on_edges) return 0;
|
||||
|
||||
auto guard = std::shared_lock{edge_.ptr->lock};
|
||||
Delta *delta = edge_.ptr->delta;
|
||||
Delta *delta = edge_.ptr->delta();
|
||||
if (!delta) {
|
||||
return edge_.ptr->properties.PropertySize(property);
|
||||
}
|
||||
@ -294,9 +294,9 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view)
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{edge_.ptr->lock};
|
||||
deleted = edge_.ptr->deleted;
|
||||
deleted = edge_.ptr->deleted();
|
||||
properties = edge_.ptr->properties.Properties();
|
||||
delta = edge_.ptr->delta;
|
||||
delta = edge_.ptr->delta();
|
||||
}
|
||||
ApplyDeltasForRead(transaction_, delta, view, [&exists, &deleted, &properties](const Delta &delta) {
|
||||
switch (delta.action) {
|
||||
|
@ -60,8 +60,8 @@ inline bool AnyVersionHasLabel(const Vertex &vertex, LabelId label, uint64_t tim
|
||||
{
|
||||
auto guard = std::shared_lock{vertex.lock};
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
deleted = vertex.deleted;
|
||||
delta = vertex.delta;
|
||||
deleted = vertex.deleted();
|
||||
delta = vertex.delta();
|
||||
}
|
||||
if (!deleted && has_label) {
|
||||
return true;
|
||||
@ -116,8 +116,8 @@ inline bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, Prop
|
||||
bool current_value_equal_to_value;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex.lock};
|
||||
delta = vertex.delta;
|
||||
deleted = vertex.deleted;
|
||||
delta = vertex.delta();
|
||||
deleted = vertex.deleted();
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
// Avoid IsPropertyEqual if already not possible
|
||||
if (delta == nullptr && (deleted || !has_label)) return false;
|
||||
@ -184,10 +184,10 @@ inline bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label,
|
||||
const Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex.lock};
|
||||
deleted = vertex.deleted;
|
||||
deleted = vertex.deleted();
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
|
||||
delta = vertex.delta;
|
||||
delta = vertex.delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -233,7 +233,7 @@ inline bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label,
|
||||
|
||||
template <typename TIndexAccessor>
|
||||
inline void TryInsertLabelIndex(Vertex &vertex, LabelId label, TIndexAccessor &index_accessor) {
|
||||
if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
|
||||
if (vertex.deleted() || !utils::Contains(vertex.labels, label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -243,7 +243,7 @@ inline void TryInsertLabelIndex(Vertex &vertex, LabelId label, TIndexAccessor &i
|
||||
template <typename TIndexAccessor>
|
||||
inline void TryInsertLabelPropertyIndex(Vertex &vertex, std::pair<LabelId, PropertyId> label_property_pair,
|
||||
TIndexAccessor &index_accessor) {
|
||||
if (vertex.deleted || !utils::Contains(vertex.labels, label_property_pair.first)) {
|
||||
if (vertex.deleted() || !utils::Contains(vertex.labels, label_property_pair.first)) {
|
||||
return;
|
||||
}
|
||||
auto value = vertex.properties.GetProperty(label_property_pair.second);
|
||||
|
@ -315,11 +315,11 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
if (from_vertex->deleted) return Error::DELETED_OBJECT;
|
||||
if (from_vertex->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
if (to_vertex != from_vertex) {
|
||||
if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
if (to_vertex->deleted) return Error::DELETED_OBJECT;
|
||||
if (to_vertex->deleted()) return Error::DELETED_OBJECT;
|
||||
}
|
||||
|
||||
if (storage_->config_.salient.items.enable_schema_metadata) {
|
||||
@ -404,11 +404,11 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdgeEx(VertexAcces
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
if (from_vertex->deleted) return Error::DELETED_OBJECT;
|
||||
if (from_vertex->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
if (to_vertex != from_vertex) {
|
||||
if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
if (to_vertex->deleted) return Error::DELETED_OBJECT;
|
||||
if (to_vertex->deleted()) return Error::DELETED_OBJECT;
|
||||
}
|
||||
|
||||
if (storage_->config_.salient.items.enable_schema_metadata) {
|
||||
@ -482,7 +482,7 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeSetFrom(EdgeAccessor
|
||||
|
||||
if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
}
|
||||
|
||||
std::unique_lock<utils::RWSpinLock> guard_old_from(old_from_vertex->lock, std::defer_lock);
|
||||
@ -508,14 +508,14 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeSetFrom(EdgeAccessor
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, old_from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!old_from_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!old_from_vertex->deleted(), "Invalid database state!");
|
||||
|
||||
if (!PrepareForWrite(&transaction_, new_from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!new_from_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!new_from_vertex->deleted(), "Invalid database state!");
|
||||
|
||||
if (to_vertex != old_from_vertex && to_vertex != new_from_vertex) {
|
||||
if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!to_vertex->deleted(), "Invalid database state!");
|
||||
}
|
||||
|
||||
auto delete_edge_from_storage = [&edge_type, &edge_ref, this](auto *vertex, auto *edges) {
|
||||
@ -587,7 +587,7 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeSetTo(EdgeAccessor *
|
||||
|
||||
if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (edge_ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
}
|
||||
|
||||
std::unique_lock<utils::RWSpinLock> guard_from(from_vertex->lock, std::defer_lock);
|
||||
@ -613,14 +613,14 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeSetTo(EdgeAccessor *
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, old_to_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!old_to_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!old_to_vertex->deleted(), "Invalid database state!");
|
||||
|
||||
if (!PrepareForWrite(&transaction_, new_to_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!new_to_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!new_to_vertex->deleted(), "Invalid database state!");
|
||||
|
||||
if (from_vertex != old_to_vertex && from_vertex != new_to_vertex) {
|
||||
if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!from_vertex->deleted(), "Invalid database state!");
|
||||
}
|
||||
|
||||
auto delete_edge_from_storage = [&edge_type, &edge_ref, this](auto *vertex, auto *edges) {
|
||||
@ -684,7 +684,7 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeChangeType(EdgeAcces
|
||||
guard = std::unique_lock{edge_ptr->lock};
|
||||
|
||||
if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
if (edge_ptr->deleted) return Error::DELETED_OBJECT;
|
||||
if (edge_ptr->deleted()) return Error::DELETED_OBJECT;
|
||||
}
|
||||
|
||||
auto *from_vertex = edge->from_vertex_;
|
||||
@ -705,10 +705,10 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::EdgeChangeType(EdgeAcces
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!from_vertex->deleted(), "Invalid database state!");
|
||||
|
||||
if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!to_vertex->deleted(), "Invalid database state!");
|
||||
|
||||
auto change_edge_type_in_storage = [&edge_type, &edge_ref, &new_edge_type, this](auto *vertex, auto *edges) {
|
||||
std::tuple<EdgeTypeId, Vertex *, EdgeRef> link(edge_type, vertex, edge_ref);
|
||||
@ -901,8 +901,8 @@ void InMemoryStorage::InMemoryAccessor::FastDiscardOfDeltas(uint64_t oldest_acti
|
||||
case PreviousPtr::Type::VERTEX: {
|
||||
// safe because no other txn can be reading this while we have engine lock
|
||||
auto &vertex = *prev.vertex;
|
||||
vertex.delta = nullptr;
|
||||
if (vertex.deleted) {
|
||||
vertex.delta(nullptr);
|
||||
if (vertex.deleted()) {
|
||||
DMG_ASSERT(delta.action == Delta::Action::RECREATE_OBJECT);
|
||||
current_deleted_vertices.push_back(vertex.gid);
|
||||
}
|
||||
@ -911,8 +911,8 @@ void InMemoryStorage::InMemoryAccessor::FastDiscardOfDeltas(uint64_t oldest_acti
|
||||
case PreviousPtr::Type::EDGE: {
|
||||
// safe because no other txn can be reading this while we have engine lock
|
||||
auto &edge = *prev.edge;
|
||||
edge.delta = nullptr;
|
||||
if (edge.deleted) {
|
||||
edge.delta(nullptr);
|
||||
if (edge.deleted()) {
|
||||
DMG_ASSERT(delta.action == Delta::Action::RECREATE_OBJECT);
|
||||
current_deleted_edges.push_back(edge.gid);
|
||||
}
|
||||
@ -1006,7 +1006,7 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
|
||||
case PreviousPtr::Type::VERTEX: {
|
||||
auto *vertex = prev.vertex;
|
||||
auto guard = std::unique_lock{vertex->lock};
|
||||
Delta *current = vertex->delta;
|
||||
Delta *current = vertex->delta();
|
||||
while (current != nullptr &&
|
||||
current->timestamp->load(std::memory_order_acquire) == transaction_.transaction_id) {
|
||||
switch (current->action) {
|
||||
@ -1103,18 +1103,18 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
|
||||
}
|
||||
case Delta::Action::DELETE_DESERIALIZED_OBJECT:
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
vertex->deleted = true;
|
||||
vertex->deleted(true);
|
||||
my_deleted_vertices.push_back(vertex->gid);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
vertex->deleted = false;
|
||||
vertex->deleted(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
current = current->next.load(std::memory_order_acquire);
|
||||
}
|
||||
vertex->delta = current;
|
||||
vertex->delta(current);
|
||||
if (current != nullptr) {
|
||||
current->prev.Set(vertex);
|
||||
}
|
||||
@ -1124,7 +1124,7 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
|
||||
case PreviousPtr::Type::EDGE: {
|
||||
auto *edge = prev.edge;
|
||||
auto guard = std::lock_guard{edge->lock};
|
||||
Delta *current = edge->delta;
|
||||
Delta *current = edge->delta();
|
||||
while (current != nullptr &&
|
||||
current->timestamp->load(std::memory_order_acquire) == transaction_.transaction_id) {
|
||||
switch (current->action) {
|
||||
@ -1134,12 +1134,12 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
|
||||
}
|
||||
case Delta::Action::DELETE_DESERIALIZED_OBJECT:
|
||||
case Delta::Action::DELETE_OBJECT: {
|
||||
edge->deleted = true;
|
||||
edge->deleted(true);
|
||||
my_deleted_edges.push_back(edge->gid);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::RECREATE_OBJECT: {
|
||||
edge->deleted = false;
|
||||
edge->deleted(false);
|
||||
break;
|
||||
}
|
||||
case Delta::Action::REMOVE_LABEL:
|
||||
@ -1154,7 +1154,7 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
|
||||
}
|
||||
current = current->next.load(std::memory_order_acquire);
|
||||
}
|
||||
edge->delta = current;
|
||||
edge->delta(current);
|
||||
if (current != nullptr) {
|
||||
current->prev.Set(edge);
|
||||
}
|
||||
@ -1572,13 +1572,13 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::ResourceLock> main_
|
||||
case PreviousPtr::Type::VERTEX: {
|
||||
Vertex *vertex = prev.vertex;
|
||||
auto vertex_guard = std::unique_lock{vertex->lock};
|
||||
if (vertex->delta != &delta) {
|
||||
if (vertex->delta() != &delta) {
|
||||
// Something changed, we're not the first delta in the chain
|
||||
// anymore.
|
||||
continue;
|
||||
}
|
||||
vertex->delta = nullptr;
|
||||
if (vertex->deleted) {
|
||||
vertex->delta(nullptr);
|
||||
if (vertex->deleted()) {
|
||||
DMG_ASSERT(delta.action == memgraph::storage::Delta::Action::RECREATE_OBJECT);
|
||||
current_deleted_vertices.push_back(vertex->gid);
|
||||
}
|
||||
@ -1587,13 +1587,13 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::ResourceLock> main_
|
||||
case PreviousPtr::Type::EDGE: {
|
||||
Edge *edge = prev.edge;
|
||||
auto edge_guard = std::unique_lock{edge->lock};
|
||||
if (edge->delta != &delta) {
|
||||
if (edge->delta() != &delta) {
|
||||
// Something changed, we're not the first delta in the chain
|
||||
// anymore.
|
||||
continue;
|
||||
}
|
||||
edge->delta = nullptr;
|
||||
if (edge->deleted) {
|
||||
edge->delta(nullptr);
|
||||
if (edge->deleted()) {
|
||||
DMG_ASSERT(delta.action == memgraph::storage::Delta::Action::RECREATE_OBJECT);
|
||||
current_deleted_edges.push_back(edge->gid);
|
||||
}
|
||||
@ -1729,14 +1729,14 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::ResourceLock> main_
|
||||
|
||||
// EXPENSIVE full scan, is only run if an IN_MEMORY_ANALYTICAL transaction involved any deletions
|
||||
// TODO: implement a fast internal iteration inside the skip_list (to avoid unnecessary find_node calls),
|
||||
// accessor.remove_if([](auto const & item){ return item.delta == nullptr && item.deleted;});
|
||||
// accessor.remove_if([](auto const & item){ return item.delta == nullptr && item.deleted();});
|
||||
// alternatively, an auxiliary data structure within skip_list to track these, hence a full scan wouldn't be needed
|
||||
// we will wait for evidence that this is needed before doing so.
|
||||
if (need_full_scan_vertices) {
|
||||
auto vertex_acc = vertices_.access();
|
||||
for (auto &vertex : vertex_acc) {
|
||||
// a deleted vertex which as no deltas must have come from IN_MEMORY_ANALYTICAL deletion
|
||||
if (vertex.delta == nullptr && vertex.deleted) {
|
||||
if (vertex.delta() == nullptr && vertex.deleted()) {
|
||||
vertex_acc.remove(vertex);
|
||||
}
|
||||
}
|
||||
@ -1747,7 +1747,7 @@ void InMemoryStorage::CollectGarbage(std::unique_lock<utils::ResourceLock> main_
|
||||
auto edge_acc = edges_.access();
|
||||
for (auto &edge : edge_acc) {
|
||||
// a deleted edge which as no deltas must have come from IN_MEMORY_ANALYTICAL deletion
|
||||
if (edge.delta == nullptr && edge.deleted) {
|
||||
if (edge.delta() == nullptr && edge.deleted()) {
|
||||
edge_acc.remove(edge);
|
||||
}
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ bool LastCommittedVersionHasLabelProperty(const Vertex &vertex, LabelId label, c
|
||||
bool has_label;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex.lock};
|
||||
delta = vertex.delta;
|
||||
deleted = vertex.deleted;
|
||||
delta = vertex.delta();
|
||||
deleted = vertex.deleted();
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
|
||||
size_t i = 0;
|
||||
@ -145,8 +145,8 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
|
||||
{
|
||||
auto guard = std::shared_lock{vertex.lock};
|
||||
has_label = utils::Contains(vertex.labels, label);
|
||||
deleted = vertex.deleted;
|
||||
delta = vertex.delta;
|
||||
deleted = vertex.deleted();
|
||||
delta = vertex.delta();
|
||||
|
||||
// Avoid IsPropertyEqual if already not possible
|
||||
if (delta == nullptr && (deleted || !has_label)) return false;
|
||||
@ -331,7 +331,7 @@ bool InMemoryUniqueConstraints::SingleThreadConstraintValidation::operator()(
|
||||
std::optional<ConstraintViolation> InMemoryUniqueConstraints::DoValidate(
|
||||
const Vertex &vertex, utils::SkipList<Entry>::Accessor &constraint_accessor, const LabelId &label,
|
||||
const std::set<PropertyId> &properties) {
|
||||
if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
|
||||
if (vertex.deleted() || !utils::Contains(vertex.labels, label)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
auto values = vertex.properties.ExtractPropertyValues(properties);
|
||||
@ -440,7 +440,7 @@ bool InMemoryUniqueConstraints::ConstraintExists(LabelId label, const std::set<P
|
||||
|
||||
std::optional<ConstraintViolation> InMemoryUniqueConstraints::Validate(const Vertex &vertex, const Transaction &tx,
|
||||
uint64_t commit_timestamp) const {
|
||||
if (vertex.deleted) {
|
||||
if (vertex.deleted()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
@ -94,8 +94,8 @@ inline std::size_t ApplyDeltasForRead(Transaction const *transaction, const Delt
|
||||
/// proceed with a write operation.
|
||||
template <typename TObj>
|
||||
inline bool PrepareForWrite(Transaction *transaction, TObj *object) {
|
||||
if (object->delta == nullptr) return true;
|
||||
auto ts = object->delta->timestamp->load(std::memory_order_acquire);
|
||||
if (object->delta() == nullptr) return true;
|
||||
auto ts = object->delta()->timestamp->load(std::memory_order_acquire);
|
||||
if (ts == transaction->transaction_id || ts < transaction->start_timestamp) {
|
||||
return true;
|
||||
}
|
||||
@ -174,21 +174,21 @@ inline void CreateAndLinkDelta(Transaction *transaction, TObj *object, Args &&..
|
||||
// concurrently (as well as other execution threads).
|
||||
|
||||
// 1. We need to set the next delta of the new delta to the existing delta.
|
||||
delta->next.store(object->delta, std::memory_order_release);
|
||||
delta->next.store(object->delta(), std::memory_order_release);
|
||||
// 2. We need to set the previous delta of the new delta to the object.
|
||||
delta->prev.Set(object);
|
||||
// 3. We need to set the previous delta of the existing delta to the new
|
||||
// delta. After this point the garbage collector will be able to see the new
|
||||
// delta but won't modify it until we are done with all of our modifications.
|
||||
if (object->delta) {
|
||||
object->delta->prev.Set(delta);
|
||||
if (object->delta()) {
|
||||
object->delta()->prev.Set(delta);
|
||||
}
|
||||
// 4. Finally, we need to set the object's delta to the new delta. The garbage
|
||||
// collector and other transactions will acquire the object lock to read the
|
||||
// delta from the object. Because the lock is held during the whole time this
|
||||
// modification is being done, everybody else will wait until we are fully
|
||||
// done with our modification before they read the object's delta value.
|
||||
object->delta = delta;
|
||||
object->delta(delta);
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
@ -78,7 +78,7 @@ namespace {
|
||||
// ++ -> size of property ID (2 bits)
|
||||
// ++ -> size of payload OR size of payload size indicator (2 bits)
|
||||
//
|
||||
// When encoding integers (`int64_t` and `uint64_t`) they are compressed so that
|
||||
// When encoding integers (`int64_t` and `uint32_t`) they are compressed so that
|
||||
// they are stored into 1, 2, 4 or 8 bytes depending on their value.
|
||||
//
|
||||
// The size of the metadata field is very important because it is encoded with
|
||||
@ -93,7 +93,7 @@ enum class Size : uint8_t {
|
||||
INT64 = 0x03,
|
||||
};
|
||||
|
||||
uint64_t SizeToByteSize(Size size) {
|
||||
uint32_t SizeToByteSize(Size size) {
|
||||
switch (size) {
|
||||
case Size::INT8:
|
||||
return 1;
|
||||
@ -143,13 +143,13 @@ const uint8_t kShiftIdSize = 2;
|
||||
// - encoded value
|
||||
// * STRING
|
||||
// - type; payload size is used to indicate whether the string size is
|
||||
// encoded as `uint8_t`, `uint16_t`, `uint32_t` or `uint64_t`
|
||||
// encoded as `uint8_t`, `uint16_t`, `uint32_t` or `uint32_t`
|
||||
// - encoded property ID
|
||||
// - encoded string size
|
||||
// - string data
|
||||
// * LIST
|
||||
// - type; payload size is used to indicate whether the list size is encoded
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint64_t`
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint32_t`
|
||||
// - encoded property ID
|
||||
// - encoded list size
|
||||
// - list items
|
||||
@ -159,12 +159,12 @@ const uint8_t kShiftIdSize = 2;
|
||||
// + encoded item data
|
||||
// * MAP
|
||||
// - type; payload size is used to indicate whether the map size is encoded
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint64_t`
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint32_t`
|
||||
// - encoded property ID
|
||||
// - encoded map size
|
||||
// - map items
|
||||
// + type; id size is used to indicate whether the key size is encoded as
|
||||
// `uint8_t`, `uint16_t`, `uint32_t` or `uint64_t`; payload size is used
|
||||
// `uint8_t`, `uint16_t`, `uint32_t` or `uint32_t`; payload size is used
|
||||
// as described above for the inner payload type
|
||||
// + encoded key size
|
||||
// + encoded key data
|
||||
@ -175,9 +175,9 @@ const uint8_t kShiftIdSize = 2;
|
||||
// - encoded property ID
|
||||
// - value saved as Metadata
|
||||
// + type; id size is used to indicate whether the temporal data type is encoded
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint64_t`; payload size used to
|
||||
// as `uint8_t`, `uint16_t`, `uint32_t` or `uint32_t`; payload size used to
|
||||
// indicate whether the microseconds are encoded as `uint8_t`, `uint16_t, `uint32_t
|
||||
// or `uint64_t`
|
||||
// or `uint32_t`
|
||||
// + encoded temporal data type value
|
||||
// + encoded microseconds value
|
||||
|
||||
@ -210,7 +210,7 @@ class Writer {
|
||||
|
||||
Writer() = default;
|
||||
|
||||
Writer(uint8_t *data, uint64_t size) : data_(data), size_(size) {}
|
||||
Writer(uint8_t *data, uint32_t size) : data_(data), size_(size) {}
|
||||
|
||||
std::optional<MetadataHandle> WriteMetadata() {
|
||||
if (data_ && pos_ + 1 > size_) return std::nullopt;
|
||||
@ -250,19 +250,19 @@ class Writer {
|
||||
|
||||
std::optional<Size> WriteDouble(double value) { return WriteUint(utils::MemcpyCast<uint64_t>(value)); }
|
||||
|
||||
bool WriteBytes(const uint8_t *data, uint64_t size) {
|
||||
bool WriteBytes(const uint8_t *data, uint32_t size) {
|
||||
if (data_ && pos_ + size > size_) return false;
|
||||
if (data_) memcpy(data_ + pos_, data, size);
|
||||
pos_ += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteBytes(const char *data, uint64_t size) {
|
||||
bool WriteBytes(const char *data, uint32_t size) {
|
||||
static_assert(std::is_same_v<uint8_t, unsigned char>);
|
||||
return WriteBytes(reinterpret_cast<const uint8_t *>(data), size);
|
||||
}
|
||||
|
||||
uint64_t Written() const { return pos_; }
|
||||
uint32_t Written() const { return pos_; }
|
||||
|
||||
private:
|
||||
template <typename T, typename V>
|
||||
@ -279,14 +279,14 @@ class Writer {
|
||||
}
|
||||
|
||||
uint8_t *data_{nullptr};
|
||||
uint64_t size_{0};
|
||||
uint64_t pos_{0};
|
||||
uint32_t size_{0};
|
||||
uint32_t pos_{0};
|
||||
};
|
||||
|
||||
// Helper class used to read data from the binary stream.
|
||||
class Reader {
|
||||
public:
|
||||
Reader(const uint8_t *data, uint64_t size) : data_(data), size_(size), pos_(0) {}
|
||||
Reader(const uint8_t *data, uint32_t size) : data_(data), size_(size), pos_(0) {}
|
||||
|
||||
std::optional<Metadata> ReadMetadata() {
|
||||
if (pos_ + 1 > size_) return std::nullopt;
|
||||
@ -366,33 +366,33 @@ class Reader {
|
||||
return utils::MemcpyCast<double>(*value);
|
||||
}
|
||||
|
||||
bool ReadBytes(uint8_t *data, uint64_t size) {
|
||||
bool ReadBytes(uint8_t *data, uint32_t size) {
|
||||
if (pos_ + size > size_) return false;
|
||||
memcpy(data, data_ + pos_, size);
|
||||
pos_ += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadBytes(char *data, uint64_t size) { return ReadBytes(reinterpret_cast<uint8_t *>(data), size); }
|
||||
bool ReadBytes(char *data, uint32_t size) { return ReadBytes(reinterpret_cast<uint8_t *>(data), size); }
|
||||
|
||||
bool VerifyBytes(const uint8_t *data, uint64_t size) {
|
||||
bool VerifyBytes(const uint8_t *data, uint32_t size) {
|
||||
if (pos_ + size > size_) return false;
|
||||
if (memcmp(data, data_ + pos_, size) != 0) return false;
|
||||
pos_ += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VerifyBytes(const char *data, uint64_t size) {
|
||||
bool VerifyBytes(const char *data, uint32_t size) {
|
||||
return VerifyBytes(reinterpret_cast<const uint8_t *>(data), size);
|
||||
}
|
||||
|
||||
bool SkipBytes(uint64_t size) {
|
||||
bool SkipBytes(uint32_t size) {
|
||||
if (pos_ + size > size_) return false;
|
||||
pos_ += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t GetPosition() const { return pos_; }
|
||||
uint32_t GetPosition() const { return pos_; }
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
@ -405,8 +405,8 @@ class Reader {
|
||||
}
|
||||
|
||||
const uint8_t *data_;
|
||||
uint64_t size_;
|
||||
uint64_t pos_;
|
||||
uint32_t size_;
|
||||
uint32_t pos_;
|
||||
};
|
||||
|
||||
// Function used to encode a PropertyValue into a byte stream.
|
||||
@ -499,8 +499,8 @@ std::optional<TemporalData> DecodeTemporalData(Reader &reader) {
|
||||
return TemporalData{static_cast<TemporalType>(*type_value), *microseconds_value};
|
||||
}
|
||||
|
||||
std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
uint64_t temporal_data_size = 0;
|
||||
std::optional<uint32_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
uint32_t temporal_data_size = 0;
|
||||
|
||||
auto metadata = reader.ReadMetadata();
|
||||
if (!metadata || metadata->type != Type::TEMPORAL_DATA) return std::nullopt;
|
||||
@ -567,7 +567,7 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
if (!size) return false;
|
||||
std::vector<PropertyValue> list;
|
||||
list.reserve(*size);
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
for (uint32_t i = 0; i < *size; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
PropertyValue item;
|
||||
@ -581,7 +581,7 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
auto size = reader->ReadUint(payload_size);
|
||||
if (!size) return false;
|
||||
std::map<std::string, PropertyValue> map;
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
for (uint32_t i = 0; i < *size; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
auto key_size = reader->ReadUint(metadata->id_size);
|
||||
@ -606,7 +606,7 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool DecodePropertyValueSize(Reader *reader, Type type, Size payload_size, uint64_t &property_size) {
|
||||
[[nodiscard]] bool DecodePropertyValueSize(Reader *reader, Type type, Size payload_size, uint32_t &property_size) {
|
||||
switch (type) {
|
||||
case Type::EMPTY: {
|
||||
return false;
|
||||
@ -640,9 +640,9 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
auto size = reader->ReadUint(payload_size);
|
||||
if (!size) return false;
|
||||
|
||||
uint64_t list_property_size = SizeToByteSize(payload_size);
|
||||
uint32_t list_property_size = SizeToByteSize(payload_size);
|
||||
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
for (uint32_t i = 0; i < *size; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
|
||||
@ -657,9 +657,9 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
auto size = reader->ReadUint(payload_size);
|
||||
if (!size) return false;
|
||||
|
||||
uint64_t map_property_size = SizeToByteSize(payload_size);
|
||||
uint32_t map_property_size = SizeToByteSize(payload_size);
|
||||
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
for (uint32_t i = 0; i < *size; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
|
||||
@ -720,7 +720,7 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
auto const size = reader->ReadUint(payload_size);
|
||||
if (!size) return false;
|
||||
auto size_val = *size;
|
||||
for (uint64_t i = 0; i != size_val; ++i) {
|
||||
for (uint32_t i = 0; i != size_val; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
if (!SkipPropertyValue(reader, metadata->type, metadata->payload_size)) return false;
|
||||
@ -731,7 +731,7 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
auto const size = reader->ReadUint(payload_size);
|
||||
if (!size) return false;
|
||||
auto size_val = *size;
|
||||
for (uint64_t i = 0; i != size_val; ++i) {
|
||||
for (uint32_t i = 0; i != size_val; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
auto key_size = reader->ReadUint(metadata->id_size);
|
||||
@ -811,7 +811,7 @@ std::optional<uint64_t> DecodeTemporalDataSize(Reader &reader) {
|
||||
auto size = reader->ReadUint(payload_size);
|
||||
if (!size) return false;
|
||||
if (*size != list.size()) return false;
|
||||
for (uint64_t i = 0; i < *size; ++i) {
|
||||
for (uint32_t i = 0; i < *size; ++i) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return false;
|
||||
if (!ComparePropertyValue(reader, metadata->type, metadata->payload_size, list[i])) return false;
|
||||
@ -909,7 +909,7 @@ enum class ExpectedPropertyStatus {
|
||||
}
|
||||
|
||||
[[nodiscard]] ExpectedPropertyStatus DecodeExpectedPropertySize(Reader *reader, PropertyId expected_property,
|
||||
uint64_t &size) {
|
||||
uint32_t &size) {
|
||||
auto metadata = reader->ReadMetadata();
|
||||
if (!metadata) return ExpectedPropertyStatus::MISSING_DATA;
|
||||
|
||||
@ -1016,7 +1016,7 @@ enum class ExpectedPropertyStatus {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ExpectedPropertyStatus FindSpecificPropertySize(Reader *reader, PropertyId property, uint64_t &size) {
|
||||
[[nodiscard]] ExpectedPropertyStatus FindSpecificPropertySize(Reader *reader, PropertyId property, uint32_t &size) {
|
||||
ExpectedPropertyStatus ret = ExpectedPropertyStatus::SMALLER;
|
||||
while ((ret = DecodeExpectedPropertySize(reader, property, size)) == ExpectedPropertyStatus::SMALLER) {
|
||||
}
|
||||
@ -1043,12 +1043,12 @@ enum class ExpectedPropertyStatus {
|
||||
|
||||
// Struct used to return info about the property position and buffer size.
|
||||
struct SpecificPropertyAndBufferInfo {
|
||||
uint64_t property_begin;
|
||||
uint64_t property_end;
|
||||
uint64_t property_size;
|
||||
uint64_t all_begin;
|
||||
uint64_t all_end;
|
||||
uint64_t all_size;
|
||||
uint32_t property_begin;
|
||||
uint32_t property_end;
|
||||
uint32_t property_size;
|
||||
uint32_t all_begin;
|
||||
uint32_t all_end;
|
||||
uint32_t all_size;
|
||||
};
|
||||
|
||||
// Function used to find the position where the property should be in the data
|
||||
@ -1063,10 +1063,10 @@ struct SpecificPropertyAndBufferInfo {
|
||||
//
|
||||
// @sa FindSpecificProperty
|
||||
SpecificPropertyAndBufferInfo FindSpecificPropertyAndBufferInfo(Reader *reader, PropertyId property) {
|
||||
uint64_t property_begin = reader->GetPosition();
|
||||
uint64_t property_end = reader->GetPosition();
|
||||
uint64_t all_begin = reader->GetPosition();
|
||||
uint64_t all_end = reader->GetPosition();
|
||||
uint32_t property_begin = reader->GetPosition();
|
||||
uint32_t property_end = reader->GetPosition();
|
||||
uint32_t all_begin = reader->GetPosition();
|
||||
uint32_t all_end = reader->GetPosition();
|
||||
while (true) {
|
||||
auto ret = HasExpectedProperty(reader, property);
|
||||
if (ret == ExpectedPropertyStatus::MISSING_DATA) {
|
||||
@ -1084,8 +1084,8 @@ SpecificPropertyAndBufferInfo FindSpecificPropertyAndBufferInfo(Reader *reader,
|
||||
}
|
||||
|
||||
// All data buffers will be allocated to a power of 8 size.
|
||||
uint64_t ToPowerOf8(uint64_t size) {
|
||||
uint64_t mod = size % 8;
|
||||
uint32_t ToPowerOf8(uint32_t size) {
|
||||
uint32_t mod = size % 8;
|
||||
if (mod == 0) return size;
|
||||
return size - mod + 8;
|
||||
}
|
||||
@ -1123,23 +1123,23 @@ const uint8_t kUseLocalBuffer = 0x01;
|
||||
// Helper functions used to retrieve/store `size` and `data` from/into the
|
||||
// `buffer_`.
|
||||
|
||||
std::pair<uint64_t, uint8_t *> GetSizeData(const uint8_t *buffer) {
|
||||
uint64_t size;
|
||||
std::pair<uint32_t, uint8_t *> GetSizeData(const uint8_t *buffer) {
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
memcpy(&size, buffer, sizeof(uint64_t));
|
||||
memcpy(&data, buffer + sizeof(uint64_t), sizeof(uint8_t *));
|
||||
memcpy(&size, buffer, sizeof(uint32_t));
|
||||
memcpy(&data, buffer + sizeof(uint32_t), sizeof(uint8_t *));
|
||||
return {size, data};
|
||||
}
|
||||
|
||||
struct BufferInfo {
|
||||
uint64_t size;
|
||||
uint32_t size;
|
||||
uint8_t *data{nullptr};
|
||||
bool in_local_buffer;
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
BufferInfo GetBufferInfo(const uint8_t (&buffer)[N]) {
|
||||
uint64_t size = 0;
|
||||
uint32_t size = 0;
|
||||
const uint8_t *data = nullptr;
|
||||
bool in_local_buffer = false;
|
||||
std::tie(size, data) = GetSizeData(buffer);
|
||||
@ -1156,9 +1156,9 @@ BufferInfo GetBufferInfo(const uint8_t (&buffer)[N]) {
|
||||
return {size, non_const_data, in_local_buffer};
|
||||
}
|
||||
|
||||
void SetSizeData(uint8_t *buffer, uint64_t size, uint8_t *data) {
|
||||
memcpy(buffer, &size, sizeof(uint64_t));
|
||||
memcpy(buffer + sizeof(uint64_t), &data, sizeof(uint8_t *));
|
||||
void SetSizeData(uint8_t *buffer, uint32_t size, uint8_t *data) {
|
||||
memcpy(buffer, &size, sizeof(uint32_t));
|
||||
memcpy(buffer + sizeof(uint32_t), &data, sizeof(uint8_t *));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -1171,7 +1171,7 @@ PropertyStore::PropertyStore(PropertyStore &&other) noexcept {
|
||||
}
|
||||
|
||||
PropertyStore &PropertyStore::operator=(PropertyStore &&other) noexcept {
|
||||
uint64_t size;
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
std::tie(size, data) = GetSizeData(buffer_);
|
||||
if (size % 8 == 0) {
|
||||
@ -1186,7 +1186,7 @@ PropertyStore &PropertyStore::operator=(PropertyStore &&other) noexcept {
|
||||
}
|
||||
|
||||
PropertyStore::~PropertyStore() {
|
||||
uint64_t size;
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
std::tie(size, data) = GetSizeData(buffer_);
|
||||
if (size % 8 == 0) {
|
||||
@ -1204,11 +1204,11 @@ PropertyValue PropertyStore::GetProperty(PropertyId property) const {
|
||||
return value;
|
||||
}
|
||||
|
||||
uint64_t PropertyStore::PropertySize(PropertyId property) const {
|
||||
uint32_t PropertyStore::PropertySize(PropertyId property) const {
|
||||
auto data_size_localbuffer = GetBufferInfo(buffer_);
|
||||
Reader reader(data_size_localbuffer.data, data_size_localbuffer.size);
|
||||
|
||||
uint64_t property_size = 0;
|
||||
uint32_t property_size = 0;
|
||||
if (FindSpecificPropertySize(&reader, property, property_size) != ExpectedPropertyStatus::EQUAL) return 0;
|
||||
return property_size;
|
||||
}
|
||||
@ -1276,7 +1276,7 @@ std::map<PropertyId, PropertyValue> PropertyStore::Properties() const {
|
||||
}
|
||||
|
||||
bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value) {
|
||||
uint64_t property_size = 0;
|
||||
uint32_t property_size = 0;
|
||||
if (!value.IsNull()) {
|
||||
Writer writer;
|
||||
EncodeProperty(&writer, property, value);
|
||||
@ -1284,7 +1284,7 @@ bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value)
|
||||
}
|
||||
|
||||
bool in_local_buffer = false;
|
||||
uint64_t size;
|
||||
uint32_t size;
|
||||
uint8_t *data;
|
||||
std::tie(size, data) = GetSizeData(buffer_);
|
||||
if (size % 8 != 0) {
|
||||
@ -1346,7 +1346,7 @@ bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value)
|
||||
// We need to enlarge/shrink the buffer.
|
||||
bool current_in_local_buffer = false;
|
||||
uint8_t *current_data = nullptr;
|
||||
uint64_t current_size = 0;
|
||||
uint32_t current_size = 0;
|
||||
if (new_size <= sizeof(buffer_) - 1) {
|
||||
// Use the local buffer.
|
||||
buffer_[0] = kUseLocalBuffer;
|
||||
@ -1400,14 +1400,14 @@ bool PropertyStore::SetProperty(PropertyId property, const PropertyValue &value)
|
||||
|
||||
template <typename TContainer>
|
||||
bool PropertyStore::DoInitProperties(const TContainer &properties) {
|
||||
uint64_t size = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t *data = nullptr;
|
||||
std::tie(size, data) = GetSizeData(buffer_);
|
||||
if (size != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t property_size = 0;
|
||||
uint32_t property_size = 0;
|
||||
{
|
||||
Writer writer;
|
||||
for (const auto &[property, value] : properties) {
|
||||
@ -1521,7 +1521,7 @@ void PropertyStore::SetBuffer(const std::string_view buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t size = 0;
|
||||
uint32_t size = 0;
|
||||
uint8_t *data = nullptr;
|
||||
size = buffer.size();
|
||||
if (buffer.size() == sizeof(buffer_) - 1) { // use local buffer
|
||||
|
@ -48,7 +48,7 @@ class PropertyStore {
|
||||
/// Returns the size of the encoded property in bytes.
|
||||
/// Returns 0 if the property does not exist.
|
||||
/// The time complexity of this function is O(n).
|
||||
uint64_t PropertySize(PropertyId property) const;
|
||||
uint32_t PropertySize(PropertyId property) const;
|
||||
|
||||
/// Checks whether the property `property` exists in the store. The time
|
||||
/// complexity of this function is O(n).
|
||||
@ -119,7 +119,8 @@ class PropertyStore {
|
||||
template <typename TContainer>
|
||||
bool DoInitProperties(const TContainer &properties);
|
||||
|
||||
uint8_t buffer_[sizeof(uint64_t) + sizeof(uint8_t *)];
|
||||
// uint8_t buffer_[sizeof(uint64_t) + sizeof(uint8_t *)];
|
||||
uint8_t buffer_[sizeof(uint32_t) + sizeof(uint8_t *)];
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
@ -293,7 +293,7 @@ Result<std::optional<std::unordered_set<Vertex *>>> Storage::Accessor::PrepareDe
|
||||
|
||||
if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_ptr->deleted) {
|
||||
if (vertex_ptr->deleted()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -328,8 +328,8 @@ EdgeInfoForDeletion Storage::Accessor::PrepareDeletableEdges(const std::unordere
|
||||
// add nodes which need to be detached on the other end of the edge
|
||||
if (detach) {
|
||||
for (auto *vertex_ptr : vertices) {
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
|
||||
{
|
||||
auto vertex_lock = std::shared_lock{vertex_ptr->lock};
|
||||
@ -348,7 +348,7 @@ EdgeInfoForDeletion Storage::Accessor::PrepareDeletableEdges(const std::unordere
|
||||
|
||||
// also add edges which we want to delete from the query
|
||||
for (const auto &edge_accessor : edges) {
|
||||
if (edge_accessor->from_vertex_->deleted || edge_accessor->to_vertex_->deleted) {
|
||||
if (edge_accessor->from_vertex_->deleted() || edge_accessor->to_vertex_->deleted()) {
|
||||
continue;
|
||||
}
|
||||
partial_src_vertices.insert(edge_accessor->from_vertex_);
|
||||
@ -390,7 +390,7 @@ Result<std::optional<std::vector<EdgeAccessor>>> Storage::Accessor::ClearEdgesOn
|
||||
}
|
||||
|
||||
if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!vertex_ptr->deleted(), "Invalid database state!");
|
||||
|
||||
// MarkEdgeAsDeleted allocates additional memory
|
||||
// and CreateAndLinkDelta needs memory
|
||||
@ -449,7 +449,7 @@ Result<std::optional<std::vector<EdgeAccessor>>> Storage::Accessor::DetachRemain
|
||||
auto vertex_lock = std::unique_lock{vertex_ptr->lock};
|
||||
|
||||
if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!vertex_ptr->deleted(), "Invalid database state!");
|
||||
|
||||
auto mid = std::partition(
|
||||
edges_attached_to_vertex->begin(), edges_attached_to_vertex->end(), [this, &set_for_erasure](auto &edge) {
|
||||
@ -520,14 +520,14 @@ Result<std::vector<VertexAccessor>> Storage::Accessor::TryDeleteVertices(const s
|
||||
|
||||
if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
|
||||
MG_ASSERT(!vertex_ptr->deleted(), "Invalid database state!");
|
||||
|
||||
if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) {
|
||||
return Error::VERTEX_HAS_EDGES;
|
||||
}
|
||||
|
||||
CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
|
||||
vertex_ptr->deleted = true;
|
||||
vertex_ptr->deleted(true);
|
||||
|
||||
deleted_vertices.emplace_back(vertex_ptr, storage_, &transaction_, true);
|
||||
}
|
||||
@ -536,9 +536,9 @@ Result<std::vector<VertexAccessor>> Storage::Accessor::TryDeleteVertices(const s
|
||||
}
|
||||
|
||||
void Storage::Accessor::MarkEdgeAsDeleted(Edge *edge) {
|
||||
if (!edge->deleted) {
|
||||
if (!edge->deleted()) {
|
||||
CreateAndLinkDelta(&transaction_, edge, Delta::RecreateObjectTag());
|
||||
edge->deleted = true;
|
||||
edge->deleted(true);
|
||||
storage_->edge_count_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
}
|
||||
}
|
||||
|
30
src/storage/v2/tco_delta.hpp
Normal file
30
src/storage/v2/tco_delta.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2024 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"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
struct TcoDelta {
|
||||
TcoDelta(Delta *in) : ptr_{in} { deleted(false); }
|
||||
|
||||
bool deleted() const { return (uint64_t)ptr_ & 0x01; }
|
||||
void deleted(bool in) { ptr_ = delta() + in; }
|
||||
Delta *delta() const { return (Delta *)((uint64_t)ptr_ & 0xFFFFFFFFFFFFFFFE); }
|
||||
void delta(Delta *in) { ptr_ = in + deleted(); }
|
||||
|
||||
private:
|
||||
Delta *ptr_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
288
src/storage/v2/tco_vector.hpp
Normal file
288
src/storage/v2/tco_vector.hpp
Normal file
@ -0,0 +1,288 @@
|
||||
// Copyright 2024 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 <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
template <typename T>
|
||||
class TcoVector {
|
||||
private:
|
||||
T *data_;
|
||||
uint32_t size_; // max 4 million
|
||||
uint32_t capacity_; // max 4 million
|
||||
|
||||
public:
|
||||
TcoVector() : data_(nullptr), size_(0), capacity_(0) {}
|
||||
|
||||
TcoVector(TcoVector &in) {
|
||||
reserve(in.size_);
|
||||
memcpy(data_, in.data_, in.size_ * sizeof(T));
|
||||
size_ = in.size_;
|
||||
}
|
||||
TcoVector(TcoVector &&in) : data_{in.data_}, size_{in.size_}, capacity_{in.capacity_} {
|
||||
if (&in != this) {
|
||||
in.data_ = nullptr;
|
||||
in.size_ = 0;
|
||||
in.capacity_ = 0;
|
||||
}
|
||||
}
|
||||
TcoVector<T> &operator=(TcoVector<T> &in) {
|
||||
reserve(in.size_);
|
||||
memcpy(data_, in.data_, in.size_ * sizeof(T));
|
||||
size_ = in.size_;
|
||||
return *this;
|
||||
}
|
||||
TcoVector<T> &operator=(TcoVector<T> &&in) {
|
||||
if (&in != this) {
|
||||
data_ = in.data_;
|
||||
size_ = in.size_;
|
||||
capacity_ = in.capacity_;
|
||||
in.data_ = nullptr;
|
||||
in.size_ = 0;
|
||||
in.capacity_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~TcoVector() { delete[] data_; }
|
||||
|
||||
TcoVector(const std::vector<T> &in) {
|
||||
reserve(in.size());
|
||||
memcpy(data_, in.data(), in.size() * sizeof(T));
|
||||
size_ = in.size();
|
||||
}
|
||||
|
||||
void push_back(const T &value) {
|
||||
if (size_ >= capacity_) {
|
||||
reserve(capacity_ == 0 ? 1 : 2 * capacity_);
|
||||
}
|
||||
data_[size_++] = value;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void emplace_back(Args &&...args) {
|
||||
if (size_ >= capacity_) {
|
||||
reserve(capacity_ == 0 ? 1 : 2 * capacity_);
|
||||
}
|
||||
new (&data_[size_++]) T{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
if (size_) {
|
||||
auto back = std::move(data_[--size_]);
|
||||
}
|
||||
}
|
||||
|
||||
T &operator[](unsigned int index) { return data_[index]; }
|
||||
|
||||
const T &operator[](unsigned int index) const { return data_[index]; }
|
||||
|
||||
T &at(unsigned int index) {
|
||||
if (index >= size_) {
|
||||
throw std::out_of_range("Index out of range");
|
||||
}
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
const T &at(unsigned int index) const {
|
||||
if (index >= size_) {
|
||||
throw std::out_of_range("Index out of range");
|
||||
}
|
||||
return data_[index];
|
||||
}
|
||||
|
||||
unsigned int size() const { return size_; }
|
||||
|
||||
unsigned int capacity() const { return capacity_; }
|
||||
|
||||
void reserve(unsigned int new_capacity) {
|
||||
// TODO Test if >= max uin32_t
|
||||
if (new_capacity <= capacity_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// T *new_data = new T[new_capacity];
|
||||
const auto size = ((sizeof(T) + 7) / 8) * 8;
|
||||
T *new_data = reinterpret_cast<T *>(new uint8_t[new_capacity * size]);
|
||||
memcpy(new_data, data_, size_ * sizeof(T));
|
||||
|
||||
delete[] data_;
|
||||
data_ = new_data;
|
||||
capacity_ = new_capacity;
|
||||
}
|
||||
|
||||
void resize(unsigned int new_size) {
|
||||
if (new_size > capacity_) {
|
||||
reserve(new_size);
|
||||
}
|
||||
if (new_size > size_) {
|
||||
for (unsigned int i = size_; i < new_size; ++i) {
|
||||
data_[i] = T();
|
||||
}
|
||||
}
|
||||
size_ = new_size;
|
||||
}
|
||||
|
||||
T &back() { return data_[size_ - 1]; }
|
||||
|
||||
const T &back() const { return data_[size_ - 1]; }
|
||||
|
||||
bool empty() const { return size_ == 0; }
|
||||
|
||||
class Iterator {
|
||||
private:
|
||||
T *ptr;
|
||||
friend class TcoVector<T>;
|
||||
|
||||
public:
|
||||
using iterator_category = std::contiguous_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T *;
|
||||
using reference = T &;
|
||||
|
||||
Iterator(T *p) : ptr(p) {}
|
||||
|
||||
Iterator &operator++() {
|
||||
++ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) {
|
||||
Iterator temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
Iterator &operator--() {
|
||||
--ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator--(int) {
|
||||
Iterator temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
T &operator*() const { return *ptr; }
|
||||
|
||||
T *operator->() const { return ptr; }
|
||||
|
||||
bool operator<(const Iterator &other) const { return ptr < other.ptr; }
|
||||
bool operator>(const Iterator &other) const { return ptr > other.ptr; }
|
||||
bool operator==(const Iterator &other) const { return ptr == other.ptr; }
|
||||
bool operator<=(const Iterator &other) const { return ptr <= other.ptr; }
|
||||
bool operator>=(const Iterator &other) const { return ptr >= other.ptr; }
|
||||
bool operator!=(const Iterator &other) const { return ptr != other.ptr; }
|
||||
|
||||
std::ptrdiff_t operator-(const Iterator &rv) const { return ptr - rv.ptr; }
|
||||
};
|
||||
|
||||
class ReverseIterator {
|
||||
private:
|
||||
T *ptr;
|
||||
friend class TcoVector<T>;
|
||||
|
||||
public:
|
||||
using iterator_category = std::contiguous_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = T *;
|
||||
using reference = T &;
|
||||
|
||||
ReverseIterator(T *p) : ptr(p) {}
|
||||
|
||||
ReverseIterator &operator++() {
|
||||
--ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReverseIterator operator++(int) {
|
||||
ReverseIterator temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
ReverseIterator &operator--() {
|
||||
++ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReverseIterator operator--(int) {
|
||||
ReverseIterator temp = *this;
|
||||
--(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
T &operator*() const { return *ptr; }
|
||||
|
||||
T *operator->() const { return ptr; }
|
||||
|
||||
bool operator<(const ReverseIterator &other) const { return ptr < other.ptr; }
|
||||
bool operator>(const ReverseIterator &other) const { return ptr > other.ptr; }
|
||||
bool operator==(const ReverseIterator &other) const { return ptr == other.ptr; }
|
||||
bool operator<=(const ReverseIterator &other) const { return ptr <= other.ptr; }
|
||||
bool operator>=(const ReverseIterator &other) const { return ptr >= other.ptr; }
|
||||
bool operator!=(const ReverseIterator &other) const { return ptr != other.ptr; }
|
||||
|
||||
std::ptrdiff_t operator-(const ReverseIterator &rv) const { return rv.ptr - ptr; }
|
||||
};
|
||||
|
||||
Iterator begin() { return Iterator(data_); }
|
||||
|
||||
Iterator end() { return Iterator(data_ + size_); }
|
||||
|
||||
const Iterator begin() const { return Iterator(data_); }
|
||||
|
||||
const Iterator end() const { return Iterator(data_ + size_); }
|
||||
|
||||
ReverseIterator rbegin() { return ReverseIterator(data_ + size_ - 1); }
|
||||
|
||||
ReverseIterator rend() { return ReverseIterator(data_ - 1); }
|
||||
|
||||
const ReverseIterator rbegin() const { return ReverseIterator(data_ + size_ - 1); }
|
||||
|
||||
const ReverseIterator rend() const { return ReverseIterator(data_ - 1); }
|
||||
|
||||
void erase(Iterator pos) {
|
||||
if (pos >= begin() && pos < end()) {
|
||||
Iterator next = pos;
|
||||
++next;
|
||||
while (next != end()) {
|
||||
*pos.ptr = std::move(*next);
|
||||
++pos;
|
||||
++next;
|
||||
}
|
||||
--size_;
|
||||
} else {
|
||||
throw std::out_of_range("Iterator out of range");
|
||||
}
|
||||
}
|
||||
|
||||
void erase(Iterator pos, Iterator end_itr) {
|
||||
if (pos < begin() || pos >= end() || end_itr < pos || end_itr > end()) {
|
||||
throw std::out_of_range("Iterator out of range");
|
||||
}
|
||||
for (auto itr = pos; itr < end_itr; ++itr, --end_itr) {
|
||||
erase(itr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(TcoVector<int>) == 16);
|
||||
|
||||
} // namespace memgraph::storage
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -11,6 +11,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <alloca.h>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
@ -19,12 +21,15 @@
|
||||
#include "storage/v2/edge_ref.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/property_store.hpp"
|
||||
#include "storage/v2/tco_delta.hpp"
|
||||
#include "storage/v2/tco_vector.hpp"
|
||||
#include "utils/rw_spin_lock.hpp"
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
struct Vertex {
|
||||
Vertex(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
|
||||
Vertex(Gid gid, Delta *delta) : gid(gid), tco_delta(delta) {
|
||||
tco_delta.deleted(false);
|
||||
MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT ||
|
||||
delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT,
|
||||
"Vertex must be created with an initial DELETE_OBJECT delta!");
|
||||
@ -33,20 +38,28 @@ struct Vertex {
|
||||
const Gid gid;
|
||||
|
||||
std::vector<LabelId> labels;
|
||||
PropertyStore properties;
|
||||
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
|
||||
mutable utils::RWSpinLock lock;
|
||||
bool deleted;
|
||||
// uint8_t PAD;
|
||||
// uint16_t PAD;
|
||||
PropertyStore properties;
|
||||
// bool deleted; 88 vs 112 == 24
|
||||
// // uint8_t PAD;
|
||||
// // uint16_t PAD;
|
||||
|
||||
Delta *delta;
|
||||
// Delta *delta;
|
||||
|
||||
TcoDelta tco_delta;
|
||||
|
||||
bool deleted() const { return tco_delta.deleted(); }
|
||||
void deleted(bool in) { tco_delta.deleted(in); }
|
||||
Delta *delta() const { return tco_delta.delta(); }
|
||||
void delta(Delta *in) { tco_delta.delta(in); }
|
||||
};
|
||||
|
||||
static_assert(alignof(Vertex) >= 8, "The Vertex should be aligned to at least 8!");
|
||||
static_assert(sizeof(Vertex) == 8 + 24 + 16 + 16 + 12 + 4 + 8);
|
||||
|
||||
inline bool operator==(const Vertex &first, const Vertex &second) { return first.gid == second.gid; }
|
||||
inline bool operator<(const Vertex &first, const Vertex &second) { return first.gid < second.gid; }
|
||||
|
@ -40,8 +40,8 @@ std::pair<bool, bool> IsVisible(Vertex const *vertex, Transaction const *transac
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex->lock};
|
||||
deleted = vertex->deleted;
|
||||
delta = vertex->delta;
|
||||
deleted = vertex->deleted();
|
||||
delta = vertex->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -105,7 +105,7 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
|
||||
auto guard = std::unique_lock{vertex_->lock};
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
if (vertex_->deleted()) return Error::DELETED_OBJECT;
|
||||
if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
|
||||
|
||||
utils::AtomicMemoryBlock atomic_memory_block{[transaction = transaction_, vertex = vertex_, &label]() {
|
||||
@ -135,7 +135,7 @@ Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
|
||||
auto guard = std::unique_lock{vertex_->lock};
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
if (vertex_->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
|
||||
if (it == vertex_->labels.end()) return false;
|
||||
@ -162,9 +162,9 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
has_label = std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end();
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -209,9 +209,9 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
labels = vertex_->labels;
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -259,7 +259,7 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
if (vertex_->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
auto current_value = vertex_->properties.GetProperty(property);
|
||||
// We could skip setting the value if the previous one is the same to the new
|
||||
@ -297,7 +297,7 @@ Result<bool> VertexAccessor::InitProperties(const std::map<storage::PropertyId,
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
if (vertex_->deleted()) return Error::DELETED_OBJECT;
|
||||
bool result{false};
|
||||
utils::AtomicMemoryBlock atomic_memory_block{
|
||||
[&result, &properties, storage = storage_, transaction = transaction_, vertex = vertex_]() {
|
||||
@ -333,7 +333,7 @@ Result<std::vector<std::tuple<PropertyId, PropertyValue, PropertyValue>>> Vertex
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
if (vertex_->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
using ReturnType = decltype(vertex_->properties.UpdateProperties(properties));
|
||||
std::optional<ReturnType> id_old_new_change;
|
||||
@ -367,7 +367,7 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
|
||||
|
||||
if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
|
||||
|
||||
if (vertex_->deleted) return Error::DELETED_OBJECT;
|
||||
if (vertex_->deleted()) return Error::DELETED_OBJECT;
|
||||
|
||||
using ReturnType = decltype(vertex_->properties.Properties());
|
||||
std::optional<ReturnType> properties;
|
||||
@ -397,9 +397,9 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
value = vertex_->properties.GetProperty(property);
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -441,7 +441,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
|
||||
Result<uint64_t> VertexAccessor::GetPropertySize(PropertyId property, View view) const {
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
Delta *delta = vertex_->delta;
|
||||
Delta *delta = vertex_->delta();
|
||||
if (!delta) {
|
||||
return vertex_->properties.PropertySize(property);
|
||||
}
|
||||
@ -465,9 +465,9 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
properties = vertex_->properties.Properties();
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -585,7 +585,7 @@ Result<EdgesVertexAccessorResult> VertexAccessor::InEdges(View view, const std::
|
||||
int64_t expanded_count = 0;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
expanded_count = static_cast<int64_t>(vertex_->in_edges.size());
|
||||
// TODO: a better filter copy
|
||||
if (edge_types.empty() && !destination) {
|
||||
@ -598,7 +598,7 @@ Result<EdgesVertexAccessorResult> VertexAccessor::InEdges(View view, const std::
|
||||
in_edges.emplace_back(edge_type, from_vertex, edge);
|
||||
}
|
||||
}
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -675,7 +675,7 @@ Result<EdgesVertexAccessorResult> VertexAccessor::OutEdges(View view, const std:
|
||||
int64_t expanded_count = 0;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
expanded_count = static_cast<int64_t>(vertex_->out_edges.size());
|
||||
if (edge_types.empty() && !destination) {
|
||||
out_edges = vertex_->out_edges;
|
||||
@ -687,7 +687,7 @@ Result<EdgesVertexAccessorResult> VertexAccessor::OutEdges(View view, const std:
|
||||
out_edges.emplace_back(edge_type, to_vertex, edge);
|
||||
}
|
||||
}
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -749,9 +749,9 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
degree = vertex_->in_edges.size();
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
@ -805,9 +805,9 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
|
||||
Delta *delta = nullptr;
|
||||
{
|
||||
auto guard = std::shared_lock{vertex_->lock};
|
||||
deleted = vertex_->deleted;
|
||||
deleted = vertex_->deleted();
|
||||
degree = vertex_->out_edges.size();
|
||||
delta = vertex_->delta;
|
||||
delta = vertex_->delta();
|
||||
}
|
||||
|
||||
// Checking cache has a cost, only do it if we have any deltas
|
||||
|
@ -27,7 +27,7 @@ class Storage;
|
||||
struct Constraints;
|
||||
struct Indices;
|
||||
struct EdgesVertexAccessorResult;
|
||||
using edge_store = std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>>;
|
||||
using edge_store = TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>>;
|
||||
|
||||
class VertexAccessor final {
|
||||
private:
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@ -13,6 +13,7 @@
|
||||
#include "storage/v2/edge_direction.hpp"
|
||||
#include "storage/v2/edge_ref.hpp"
|
||||
#include "storage/v2/id_types.hpp"
|
||||
#include "storage/v2/tco_vector.hpp"
|
||||
#include "storage/v2/view.hpp"
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
@ -86,7 +87,7 @@ struct VertexInfoCache final {
|
||||
|
||||
void Invalidate(Vertex const *vertex, PropertyId property_key);
|
||||
|
||||
using EdgeStore = std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>>;
|
||||
using EdgeStore = TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>>;
|
||||
|
||||
auto GetInEdges(View view, Vertex const *src_vertex, Vertex const *dst_vertex,
|
||||
const std::vector<EdgeTypeId> &edge_types) const -> detail::optref<const EdgeStore>;
|
||||
|
@ -144,8 +144,8 @@ inline auto Properties_ActionMethod(std::map<PropertyId, PropertyValue> &propert
|
||||
}
|
||||
|
||||
template <EdgeDirection dir>
|
||||
inline auto Edges_ActionMethod(std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> &edges,
|
||||
std::vector<EdgeTypeId> const &edge_types, Vertex const *destination) {
|
||||
inline auto Edges_ActionMethod(TcoVector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> &edges,
|
||||
TcoVector<EdgeTypeId> const &edge_types, Vertex const *destination) {
|
||||
auto const predicate = [&, destination](Delta const &delta) {
|
||||
if (destination && delta.vertex_edge.vertex != destination) return false;
|
||||
if (!edge_types.empty() &&
|
||||
|
Loading…
Reference in New Issue
Block a user