memgraph/src/storage/v2/delta.hpp
2022-09-18 09:05:29 +02:00

244 lines
7.6 KiB
C++

// Copyright 2022 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <atomic>
#include "storage/v2/edge_ref.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "utils/logging.hpp"
namespace memgraph::storage {
// Forward declarations because we only store pointers here.
struct Vertex;
struct Edge;
struct Delta;
// This class stores one of three pointers (`Delta`, `Vertex` and `Edge`)
// without using additional memory for storing the type. The type is stored in
// the pointer itself in the lower bits. All of those structures contain large
// items in themselves (e.g. `uint64_t`) that require the pointer to be aligned
// to their size (for `uint64_t` it is 8). That means that the pointer will
// always be a multiple of 8 which implies that the lower 3 bits of the pointer
// will always be 0. We can use those 3 bits to store information about the type
// of the pointer stored (2 bits).
class PreviousPtr {
private:
static constexpr uintptr_t kDelta = 0b01UL;
static constexpr uintptr_t kVertex = 0b10UL;
static constexpr uintptr_t kEdge = 0b11UL;
static constexpr uintptr_t kMask = 0b11UL;
public:
enum class Type {
NULLPTR,
DELTA,
VERTEX,
EDGE,
};
struct Pointer {
Pointer() = default;
explicit Pointer(Delta *delta) : type(Type::DELTA), delta(delta) {}
explicit Pointer(Vertex *vertex) : type(Type::VERTEX), vertex(vertex) {}
explicit Pointer(Edge *edge) : type(Type::EDGE), edge(edge) {}
Type type{Type::NULLPTR};
Delta *delta{nullptr};
Vertex *vertex{nullptr};
Edge *edge{nullptr};
};
PreviousPtr() : storage_(0) {}
PreviousPtr(const PreviousPtr &other) noexcept : storage_(other.storage_.load(std::memory_order_acquire)) {}
Pointer Get() const {
uintptr_t value = storage_.load(std::memory_order_acquire);
if (value == 0) {
return {};
}
uintptr_t type = value & kMask;
if (type == kDelta) {
return Pointer{reinterpret_cast<Delta *>(value & ~kMask)};
} else if (type == kVertex) {
return Pointer{reinterpret_cast<Vertex *>(value & ~kMask)};
} else if (type == kEdge) {
return Pointer{reinterpret_cast<Edge *>(value & ~kMask)};
} else {
LOG_FATAL("Invalid pointer type!");
}
}
void Set(Delta *delta) {
uintptr_t value = reinterpret_cast<uintptr_t>(delta);
MG_ASSERT((value & kMask) == 0, "Invalid pointer!");
storage_.store(value | kDelta, std::memory_order_release);
}
void Set(Vertex *vertex) {
uintptr_t value = reinterpret_cast<uintptr_t>(vertex);
MG_ASSERT((value & kMask) == 0, "Invalid pointer!");
storage_.store(value | kVertex, std::memory_order_release);
}
void Set(Edge *edge) {
uintptr_t value = reinterpret_cast<uintptr_t>(edge);
MG_ASSERT((value & kMask) == 0, "Invalid pointer!");
storage_.store(value | kEdge, std::memory_order_release);
}
private:
std::atomic<uintptr_t> storage_;
};
inline bool operator==(const PreviousPtr::Pointer &a, const PreviousPtr::Pointer &b) {
if (a.type != b.type) return false;
switch (a.type) {
case PreviousPtr::Type::VERTEX:
return a.vertex == b.vertex;
case PreviousPtr::Type::EDGE:
return a.edge == b.edge;
case PreviousPtr::Type::DELTA:
return a.delta == b.delta;
case PreviousPtr::Type::NULLPTR:
return b.type == PreviousPtr::Type::NULLPTR;
}
throw 1;
}
inline bool operator!=(const PreviousPtr::Pointer &a, const PreviousPtr::Pointer &b) { return !(a == b); }
struct Delta {
enum class Action {
// Used for both Vertex and Edge
DELETE_OBJECT,
RECREATE_OBJECT,
SET_PROPERTY,
// Used only for Vertex
ADD_LABEL,
REMOVE_LABEL,
ADD_IN_EDGE,
ADD_OUT_EDGE,
REMOVE_IN_EDGE,
REMOVE_OUT_EDGE,
};
// Used for both Vertex and Edge
struct DeleteObjectTag {};
struct RecreateObjectTag {};
struct SetPropertyTag {};
// Used only for Vertex
struct AddLabelTag {};
struct RemoveLabelTag {};
struct AddInEdgeTag {};
struct AddOutEdgeTag {};
struct RemoveInEdgeTag {};
struct RemoveOutEdgeTag {};
Delta(DeleteObjectTag, std::atomic<uint64_t> *timestamp, uint64_t command_id)
: action(Action::DELETE_OBJECT), timestamp(timestamp), command_id(command_id) {}
Delta(RecreateObjectTag, std::atomic<uint64_t> *timestamp, uint64_t command_id)
: action(Action::RECREATE_OBJECT), timestamp(timestamp), command_id(command_id) {}
Delta(AddLabelTag, LabelId label, std::atomic<uint64_t> *timestamp, uint64_t command_id)
: action(Action::ADD_LABEL), timestamp(timestamp), command_id(command_id), label(label) {}
Delta(RemoveLabelTag, LabelId label, std::atomic<uint64_t> *timestamp, uint64_t command_id)
: action(Action::REMOVE_LABEL), timestamp(timestamp), command_id(command_id), label(label) {}
Delta(SetPropertyTag, PropertyId key, const PropertyValue &value, std::atomic<uint64_t> *timestamp,
uint64_t command_id)
: action(Action::SET_PROPERTY), timestamp(timestamp), command_id(command_id), property({key, value}) {}
Delta(AddInEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
uint64_t command_id)
: action(Action::ADD_IN_EDGE),
timestamp(timestamp),
command_id(command_id),
vertex_edge({edge_type, vertex, edge}) {}
Delta(AddOutEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
uint64_t command_id)
: action(Action::ADD_OUT_EDGE),
timestamp(timestamp),
command_id(command_id),
vertex_edge({edge_type, vertex, edge}) {}
Delta(RemoveInEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
uint64_t command_id)
: action(Action::REMOVE_IN_EDGE),
timestamp(timestamp),
command_id(command_id),
vertex_edge({edge_type, vertex, edge}) {}
Delta(RemoveOutEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
uint64_t command_id)
: action(Action::REMOVE_OUT_EDGE),
timestamp(timestamp),
command_id(command_id),
vertex_edge({edge_type, vertex, edge}) {}
Delta(const Delta &) = delete;
Delta(Delta &&) = delete;
Delta &operator=(const Delta &) = delete;
Delta &operator=(Delta &&) = delete;
~Delta() {
switch (action) {
case Action::DELETE_OBJECT:
case Action::RECREATE_OBJECT:
case Action::ADD_LABEL:
case Action::REMOVE_LABEL:
case Action::ADD_IN_EDGE:
case Action::ADD_OUT_EDGE:
case Action::REMOVE_IN_EDGE:
case Action::REMOVE_OUT_EDGE:
break;
case Action::SET_PROPERTY:
property.value.~PropertyValue();
break;
}
}
Action action;
// TODO: optimize with in-place copy
std::atomic<uint64_t> *timestamp;
uint64_t command_id;
PreviousPtr prev;
std::atomic<Delta *> next{nullptr};
union {
LabelId label;
struct {
PropertyId key;
storage::PropertyValue value;
} property;
struct {
EdgeTypeId edge_type;
Vertex *vertex;
EdgeRef edge;
} vertex_edge;
};
};
static_assert(alignof(Delta) >= 8, "The Delta should be aligned to at least 8!");
} // namespace memgraph::storage