Storage fields refactoring
This commit is contained in:
@@ -230,22 +230,10 @@ DiskStorage::~DiskStorage() {
|
||||
}
|
||||
|
||||
DiskStorage::DiskAccessor::DiskAccessor(DiskStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode)
|
||||
: storage_(storage),
|
||||
// The lock must be acquired before creating the transaction object to
|
||||
// prevent freshly created transactions from dangling in an active state
|
||||
// during exclusive operations.
|
||||
storage_guard_(storage_->main_lock_),
|
||||
transaction_(storage->CreateTransaction(isolation_level, storage_mode)),
|
||||
is_transaction_active_(true),
|
||||
config_(storage->config_.items) {}
|
||||
: Accessor(storage, isolation_level, storage_mode), config_(storage->config_.items) {}
|
||||
|
||||
DiskStorage::DiskAccessor::DiskAccessor(DiskAccessor &&other) noexcept
|
||||
: storage_(other.storage_),
|
||||
storage_guard_(std::move(other.storage_guard_)),
|
||||
transaction_(std::move(other.transaction_)),
|
||||
commit_timestamp_(other.commit_timestamp_),
|
||||
is_transaction_active_(other.is_transaction_active_),
|
||||
config_(other.config_) {
|
||||
: Accessor(std::move(other)), config_(other.config_) {
|
||||
// Don't allow the other accessor to abort our transaction in destructor.
|
||||
other.is_transaction_active_ = false;
|
||||
other.commit_timestamp_.reset();
|
||||
@@ -322,23 +310,26 @@ std::optional<EdgeAccessor> DiskStorage::DiskAccessor::DeserializeEdge(const roc
|
||||
}
|
||||
|
||||
VerticesIterable DiskStorage::DiskAccessor::Vertices(View view) {
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
rocksdb::ReadOptions ro;
|
||||
rocksdb::Slice ts = utils::StringTimestamp(transaction_.start_timestamp);
|
||||
ro.timestamp = &ts;
|
||||
auto it =
|
||||
std::unique_ptr<rocksdb::Iterator>(storage_->kvstore_->db_->NewIterator(ro, storage_->kvstore_->vertex_chandle));
|
||||
auto it = std::unique_ptr<rocksdb::Iterator>(
|
||||
disk_storage->kvstore_->db_->NewIterator(ro, disk_storage->kvstore_->vertex_chandle));
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
// When deserializing vertex, key size is set to user key-size
|
||||
// To be able to extract timestamp, here a copy can be created
|
||||
// with size explicitly added with sizeof(uint64_t)
|
||||
DeserializeVertex(it->key(), it->value());
|
||||
}
|
||||
return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view, &storage_->indices_,
|
||||
&storage_->constraints_, storage_->config_.items));
|
||||
return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view,
|
||||
&disk_storage->indices_, &disk_storage->constraints_,
|
||||
disk_storage->config_.items));
|
||||
}
|
||||
|
||||
VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, View view) {
|
||||
return VerticesIterable(storage_->indices_.label_index.Vertices(label, view, &transaction_));
|
||||
return VerticesIterable(
|
||||
static_cast<DiskStorage *>(storage_)->indices_.label_index.Vertices(label, view, &transaction_));
|
||||
}
|
||||
|
||||
VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, PropertyId property, View view) {
|
||||
@@ -359,8 +350,10 @@ VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, PropertyId p
|
||||
|
||||
int64_t DiskStorage::DiskAccessor::ApproximateVertexCount() const {
|
||||
uint64_t estimate_num_keys = 0;
|
||||
storage_->kvstore_->db_->GetIntProperty(storage_->kvstore_->vertex_chandle, "rocksdb.estimate-num-keys",
|
||||
&estimate_num_keys);
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
// TODO: This method should probably be organized.
|
||||
disk_storage->kvstore_->db_->GetIntProperty(disk_storage->kvstore_->vertex_chandle, "rocksdb.estimate-num-keys",
|
||||
&estimate_num_keys);
|
||||
return static_cast<int64_t>(estimate_num_keys);
|
||||
}
|
||||
|
||||
@@ -377,8 +370,9 @@ VertexAccessor DiskStorage::DiskAccessor::CreateVertex() {
|
||||
if (delta) {
|
||||
delta->prev.Set(&*it);
|
||||
}
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
|
||||
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
|
||||
return {&*it, &transaction_, &disk_storage->indices_, &disk_storage->constraints_, config_};
|
||||
}
|
||||
|
||||
VertexAccessor DiskStorage::DiskAccessor::CreateVertex(storage::Gid gid) {
|
||||
@@ -402,7 +396,8 @@ VertexAccessor DiskStorage::DiskAccessor::CreateVertex(storage::Gid gid) {
|
||||
if (delta) {
|
||||
delta->prev.Set(&*it);
|
||||
}
|
||||
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return {&*it, &transaction_, &disk_storage->indices_, &disk_storage->constraints_, config_};
|
||||
}
|
||||
|
||||
/// TODO(andi): This method is the duplicate of CreateVertex(storage::Gid gid), the only thing that is different is
|
||||
@@ -431,11 +426,13 @@ VertexAccessor DiskStorage::DiskAccessor::CreateVertex(storage::Gid gid, uint64_
|
||||
if (delta) {
|
||||
delta->prev.Set(&*it);
|
||||
}
|
||||
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return {&*it, &transaction_, &disk_storage->indices_, &disk_storage->constraints_, config_};
|
||||
}
|
||||
|
||||
std::optional<VertexAccessor> DiskStorage::DiskAccessor::FindVertex(storage::Gid gid, View view) {
|
||||
/// Check if the vertex is in the cache.
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
auto acc = storage_->vertices_.access();
|
||||
auto vertex_it = acc.find(gid);
|
||||
if (vertex_it != acc.end()) {
|
||||
@@ -445,15 +442,15 @@ std::optional<VertexAccessor> DiskStorage::DiskAccessor::FindVertex(storage::Gid
|
||||
} else {
|
||||
spdlog::debug("Vertex with gid {} found in the cache! Delta is null.", gid.AsUint());
|
||||
}
|
||||
return VertexAccessor::Create(&*vertex_it, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
|
||||
view);
|
||||
return VertexAccessor::Create(&*vertex_it, &transaction_, &disk_storage->indices_, &disk_storage->constraints_,
|
||||
config_, view);
|
||||
}
|
||||
/// If not in the memory, check whether it exists in RocksDB.
|
||||
rocksdb::ReadOptions read_opts;
|
||||
rocksdb::Slice ts = utils::StringTimestamp(transaction_.start_timestamp);
|
||||
read_opts.timestamp = &ts;
|
||||
auto it = std::unique_ptr<rocksdb::Iterator>(
|
||||
storage_->kvstore_->db_->NewIterator(read_opts, storage_->kvstore_->vertex_chandle));
|
||||
disk_storage->kvstore_->db_->NewIterator(read_opts, disk_storage->kvstore_->vertex_chandle));
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
const auto &key = it->key();
|
||||
// TODO(andi): If we change format of vertex serialization, change vertex_parts[1] to vertex_parts[0].
|
||||
@@ -484,8 +481,9 @@ Result<std::optional<VertexAccessor>> DiskStorage::DiskAccessor::DeleteVertex(Ve
|
||||
vertex_ptr->deleted = true;
|
||||
vertices_to_delete_.emplace_back(utils::SerializeVertex(*vertex_ptr));
|
||||
|
||||
return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
|
||||
config_, true);
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &disk_storage->indices_,
|
||||
&disk_storage->constraints_, config_, true);
|
||||
}
|
||||
|
||||
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
|
||||
@@ -495,6 +493,7 @@ DiskStorage::DiskAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
"VertexAccessor must be from the same transaction as the storage "
|
||||
"accessor when deleting a vertex!");
|
||||
auto *vertex_ptr = vertex->vertex_;
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
|
||||
std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
|
||||
@@ -513,8 +512,8 @@ DiskStorage::DiskAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
std::vector<EdgeAccessor> deleted_edges;
|
||||
for (const auto &item : in_edges) {
|
||||
auto [edge_type, from_vertex, edge] = item;
|
||||
EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &disk_storage->indices_,
|
||||
&disk_storage->constraints_, config_);
|
||||
auto ret = DeleteEdge(&e);
|
||||
if (ret.HasError()) {
|
||||
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
|
||||
@@ -527,8 +526,8 @@ DiskStorage::DiskAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
}
|
||||
for (const auto &item : out_edges) {
|
||||
auto [edge_type, to_vertex, edge] = item;
|
||||
EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
|
||||
config_);
|
||||
EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &disk_storage->indices_,
|
||||
&disk_storage->constraints_, config_);
|
||||
auto ret = DeleteEdge(&e);
|
||||
if (ret.HasError()) {
|
||||
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
|
||||
@@ -555,7 +554,7 @@ DiskStorage::DiskAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
vertices_to_delete_.emplace_back(utils::SerializeVertex(*vertex_ptr));
|
||||
|
||||
return std::make_optional<ReturnType>(
|
||||
VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
|
||||
VertexAccessor{vertex_ptr, &transaction_, &disk_storage->indices_, &disk_storage->constraints_, config_, true},
|
||||
std::move(deleted_edges));
|
||||
}
|
||||
|
||||
@@ -563,8 +562,9 @@ void DiskStorage::DiskAccessor::PrefetchEdges(const auto &prefetch_edge_filter)
|
||||
rocksdb::ReadOptions read_opts;
|
||||
rocksdb::Slice ts = utils::StringTimestamp(transaction_.start_timestamp);
|
||||
read_opts.timestamp = &ts;
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
auto it = std::unique_ptr<rocksdb::Iterator>(
|
||||
storage_->kvstore_->db_->NewIterator(read_opts, storage_->kvstore_->edge_chandle));
|
||||
disk_storage->kvstore_->db_->NewIterator(read_opts, disk_storage->kvstore_->edge_chandle));
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
const rocksdb::Slice &key = it->key();
|
||||
const auto edge_parts = utils::Split(key.ToStringView(), "|");
|
||||
@@ -652,8 +652,9 @@ Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from,
|
||||
// Increment edge count.
|
||||
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &disk_storage->indices_,
|
||||
&disk_storage->constraints_, config_);
|
||||
}
|
||||
|
||||
Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to,
|
||||
@@ -720,8 +721,9 @@ Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from,
|
||||
// Increment edge count.
|
||||
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &disk_storage->indices_,
|
||||
&disk_storage->constraints_, config_);
|
||||
}
|
||||
|
||||
Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to,
|
||||
@@ -779,8 +781,9 @@ Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from,
|
||||
// Increment edge count.
|
||||
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &disk_storage->indices_,
|
||||
&disk_storage->constraints_, config_);
|
||||
}
|
||||
|
||||
Result<std::optional<EdgeAccessor>> DiskStorage::DiskAccessor::DeleteEdge(EdgeAccessor *edge) {
|
||||
@@ -868,8 +871,9 @@ Result<std::optional<EdgeAccessor>> DiskStorage::DiskAccessor::DeleteEdge(EdgeAc
|
||||
// Decrement edge count.
|
||||
storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
|
||||
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
|
||||
&storage_->indices_, &storage_->constraints_, config_, true);
|
||||
&disk_storage->indices_, &disk_storage->constraints_, config_, true);
|
||||
}
|
||||
|
||||
// this should be handled on an above level of abstraction
|
||||
@@ -908,22 +912,23 @@ void DiskStorage::DiskAccessor::FlushCache() {
|
||||
rocksdb::WriteOptions write_options;
|
||||
rocksdb::Slice ts = utils::StringTimestamp(*commit_timestamp_);
|
||||
write_options.timestamp = &ts;
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
for (Vertex &vertex : vertex_acc) {
|
||||
logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put(write_options, storage_->kvstore_->vertex_chandle,
|
||||
utils::SerializeVertex(vertex),
|
||||
utils::SerializeProperties(vertex.properties)));
|
||||
logging::AssertRocksDBStatus(disk_storage->kvstore_->db_->Put(write_options, disk_storage->kvstore_->vertex_chandle,
|
||||
utils::SerializeVertex(vertex),
|
||||
utils::SerializeProperties(vertex.properties)));
|
||||
spdlog::debug("rocksdb: Saved vertex with key {} and ts {}", utils::SerializeVertex(vertex), *commit_timestamp_);
|
||||
spdlog::debug("Vertex {} has {} out edges", vertex.gid.AsUint(), vertex.out_edges.size());
|
||||
for (auto &edge_entry : vertex.out_edges) {
|
||||
Edge *edge_ptr = std::get<2>(edge_entry).ptr;
|
||||
auto [src_dest_key, dest_src_key] =
|
||||
utils::SerializeEdge(vertex.gid, std::get<1>(edge_entry)->gid, std::get<0>(edge_entry), edge_ptr);
|
||||
logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put(write_options, storage_->kvstore_->edge_chandle,
|
||||
src_dest_key,
|
||||
utils::SerializeProperties(edge_ptr->properties)));
|
||||
logging::AssertRocksDBStatus(storage_->kvstore_->db_->Put(write_options, storage_->kvstore_->edge_chandle,
|
||||
dest_src_key,
|
||||
utils::SerializeProperties(edge_ptr->properties)));
|
||||
logging::AssertRocksDBStatus(disk_storage->kvstore_->db_->Put(write_options, disk_storage->kvstore_->edge_chandle,
|
||||
src_dest_key,
|
||||
utils::SerializeProperties(edge_ptr->properties)));
|
||||
logging::AssertRocksDBStatus(disk_storage->kvstore_->db_->Put(write_options, disk_storage->kvstore_->edge_chandle,
|
||||
dest_src_key,
|
||||
utils::SerializeProperties(edge_ptr->properties)));
|
||||
spdlog::debug("rocksdb: Saved edge with key {} and ts {}", src_dest_key, *commit_timestamp_);
|
||||
spdlog::debug("rocksdb: Saved edge with key {} and ts {}", dest_src_key, *commit_timestamp_);
|
||||
num_ser_edges++;
|
||||
@@ -933,14 +938,14 @@ void DiskStorage::DiskAccessor::FlushCache() {
|
||||
for (const auto &vertex_to_delete : vertices_to_delete_) {
|
||||
spdlog::debug("rocksdb: Deleted vertex with key {}", vertex_to_delete);
|
||||
logging::AssertRocksDBStatus(
|
||||
storage_->kvstore_->db_->Delete(write_options, storage_->kvstore_->vertex_chandle, vertex_to_delete));
|
||||
disk_storage->kvstore_->db_->Delete(write_options, disk_storage->kvstore_->vertex_chandle, vertex_to_delete));
|
||||
}
|
||||
|
||||
// Delete edges that were deleted in the current transaction.
|
||||
for (const auto &edge_to_delete : edges_to_delete_) {
|
||||
spdlog::debug("rocksdb: Deleted edge with key {}", edge_to_delete);
|
||||
logging::AssertRocksDBStatus(
|
||||
storage_->kvstore_->db_->Delete(write_options, storage_->kvstore_->edge_chandle, edge_to_delete));
|
||||
disk_storage->kvstore_->db_->Delete(write_options, disk_storage->kvstore_->edge_chandle, edge_to_delete));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,6 +955,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
|
||||
MG_ASSERT(!transaction_.must_abort, "The transaction can't be committed!");
|
||||
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
auto could_replicate_all_sync_replicas = true;
|
||||
|
||||
if (transaction_.deltas.empty() ||
|
||||
@@ -958,7 +964,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
// We don't have to update the commit timestamp here because no one reads
|
||||
// it.
|
||||
// If there are no deltas, then we don't have to serialize anything on the disk.
|
||||
storage_->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
disk_storage->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
} else {
|
||||
// Validate that existence constraints are satisfied for all modified
|
||||
// vertices.
|
||||
@@ -970,7 +976,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
}
|
||||
// No need to take any locks here because we modified this vertex and no
|
||||
// one else can touch it until we commit.
|
||||
auto validation_result = ValidateExistenceConstraints(*prev.vertex, storage_->constraints_);
|
||||
auto validation_result = ValidateExistenceConstraints(*prev.vertex, disk_storage->constraints_);
|
||||
if (validation_result) {
|
||||
Abort();
|
||||
return StorageDataManipulationError{*validation_result};
|
||||
@@ -987,7 +993,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
|
||||
{
|
||||
std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
|
||||
commit_timestamp_.emplace(storage_->CommitTimestamp(desired_commit_timestamp));
|
||||
commit_timestamp_.emplace(disk_storage->CommitTimestamp(desired_commit_timestamp));
|
||||
|
||||
// Before committing and validating vertices against unique constraints,
|
||||
// we have to update unique constraints with the vertices that are going
|
||||
@@ -998,7 +1004,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
if (prev.type != PreviousPtr::Type::VERTEX) {
|
||||
continue;
|
||||
}
|
||||
storage_->constraints_.unique_constraints.UpdateBeforeCommit(prev.vertex, transaction_);
|
||||
disk_storage->constraints_.unique_constraints.UpdateBeforeCommit(prev.vertex, transaction_);
|
||||
}
|
||||
|
||||
// Validate that unique constraints are satisfied for all modified
|
||||
@@ -1013,7 +1019,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
// No need to take any locks here because we modified this vertex and no
|
||||
// one else can touch it until we commit.
|
||||
unique_constraint_violation =
|
||||
storage_->constraints_.unique_constraints.Validate(*prev.vertex, transaction_, *commit_timestamp_);
|
||||
disk_storage->constraints_.unique_constraints.Validate(*prev.vertex, transaction_, *commit_timestamp_);
|
||||
if (unique_constraint_violation) {
|
||||
break;
|
||||
}
|
||||
@@ -1031,13 +1037,14 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
// so the Wal files are consistent
|
||||
// if (storage_->replication_role_ == ReplicationRole::MAIN || desired_commit_timestamp.has_value()) {
|
||||
if (desired_commit_timestamp.has_value()) {
|
||||
could_replicate_all_sync_replicas = storage_->AppendToWalDataManipulation(transaction_, *commit_timestamp_);
|
||||
could_replicate_all_sync_replicas =
|
||||
disk_storage->AppendToWalDataManipulation(transaction_, *commit_timestamp_);
|
||||
}
|
||||
|
||||
// Take committed_transactions lock while holding the engine lock to
|
||||
// make sure that committed transactions are sorted by the commit
|
||||
// timestamp in the list.
|
||||
storage_->committed_transactions_.WithLock([&](auto &committed_transactions) {
|
||||
disk_storage->committed_transactions_.WithLock([&](auto &committed_transactions) {
|
||||
// TODO: release lock, and update all deltas to have a local copy
|
||||
// of the commit timestamp
|
||||
MG_ASSERT(transaction_.commit_timestamp != nullptr, "Invalid database state!");
|
||||
@@ -1054,7 +1061,7 @@ utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor
|
||||
engine_guard.unlock();
|
||||
});
|
||||
|
||||
storage_->commit_log_->MarkFinished(start_timestamp);
|
||||
disk_storage->commit_log_->MarkFinished(start_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1221,32 +1228,34 @@ void DiskStorage::DiskAccessor::Abort() {
|
||||
}
|
||||
}
|
||||
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
{
|
||||
std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
|
||||
uint64_t mark_timestamp = storage_->timestamp_;
|
||||
// Take garbage_undo_buffers lock while holding the engine lock to make
|
||||
// sure that entries are sorted by mark timestamp in the list.
|
||||
storage_->garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
|
||||
disk_storage->garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
|
||||
// Release engine lock because we don't have to hold it anymore and
|
||||
// emplace back could take a long time.
|
||||
engine_guard.unlock();
|
||||
garbage_undo_buffers.emplace_back(mark_timestamp, std::move(transaction_.deltas));
|
||||
});
|
||||
storage_->deleted_vertices_.WithLock(
|
||||
disk_storage->deleted_vertices_.WithLock(
|
||||
[&](auto &deleted_vertices) { deleted_vertices.splice(deleted_vertices.begin(), my_deleted_vertices); });
|
||||
storage_->deleted_edges_.WithLock(
|
||||
disk_storage->deleted_edges_.WithLock(
|
||||
[&](auto &deleted_edges) { deleted_edges.splice(deleted_edges.begin(), my_deleted_edges); });
|
||||
}
|
||||
|
||||
storage_->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
disk_storage->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
is_transaction_active_ = false;
|
||||
}
|
||||
|
||||
// maybe will need some usages here
|
||||
void DiskStorage::DiskAccessor::FinalizeTransaction() {
|
||||
auto *disk_storage = static_cast<DiskStorage *>(storage_);
|
||||
if (commit_timestamp_) {
|
||||
storage_->commit_log_->MarkFinished(*commit_timestamp_);
|
||||
storage_->committed_transactions_.WithLock(
|
||||
disk_storage->commit_log_->MarkFinished(*commit_timestamp_);
|
||||
disk_storage->committed_transactions_.WithLock(
|
||||
[&](auto &committed_transactions) { committed_transactions.emplace_back(std::move(transaction_)); });
|
||||
commit_timestamp_.reset();
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ class DiskStorage final : public Storage {
|
||||
int64_t ApproximateVertexCount() const override;
|
||||
|
||||
int64_t ApproximateVertexCount(LabelId label) const override {
|
||||
return storage_->indices_.label_index.ApproximateVertexCount(label);
|
||||
throw utils::NotYetImplemented("ApproximateVertexCount(label) is not implemented for DiskStorage.");
|
||||
}
|
||||
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const override {
|
||||
@@ -181,14 +181,14 @@ class DiskStorage final : public Storage {
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
EdgeTypeId NameToEdgeType(std::string_view name) override;
|
||||
|
||||
bool LabelIndexExists(LabelId label) const override { return storage_->indices_.label_index.IndexExists(label); }
|
||||
bool LabelIndexExists(LabelId label) const override { throw utils::NotYetImplemented("LabelIndexExists()"); }
|
||||
|
||||
bool LabelPropertyIndexExists(LabelId label, PropertyId property) const override {
|
||||
return storage_->indices_.label_property_index.IndexExists(label, property);
|
||||
throw utils::NotYetImplemented("LabelPropertyIndexExists() is not implemented for DiskStorage.");
|
||||
}
|
||||
|
||||
IndicesInfo ListAllIndices() const override {
|
||||
return {storage_->indices_.label_index.ListIndices(), storage_->indices_.label_property_index.ListIndices()};
|
||||
throw utils::NotYetImplemented("ListAllIndices() is not implemented for DiskStorage.");
|
||||
}
|
||||
|
||||
ConstraintsInfo ListAllConstraints() const override {
|
||||
@@ -244,15 +244,9 @@ class DiskStorage final : public Storage {
|
||||
/// After this method, the vertex and edge caches are cleared.
|
||||
void FlushCache();
|
||||
|
||||
DiskStorage *storage_;
|
||||
|
||||
std::shared_lock<utils::RWLock> storage_guard_;
|
||||
Transaction transaction_;
|
||||
Config::Items config_;
|
||||
std::vector<std::string> edges_to_delete_;
|
||||
std::vector<std::string> vertices_to_delete_;
|
||||
std::optional<uint64_t> commit_timestamp_;
|
||||
bool is_transaction_active_;
|
||||
Config::Items config_;
|
||||
};
|
||||
|
||||
std::unique_ptr<Storage::Accessor> Access(std::optional<IsolationLevel> override_isolation_level) override {
|
||||
@@ -386,9 +380,9 @@ class DiskStorage final : public Storage {
|
||||
|
||||
utils::BasicResult<CreateSnapshotError> CreateSnapshot(std::optional<bool> is_periodic) override;
|
||||
|
||||
private:
|
||||
Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode);
|
||||
Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) override;
|
||||
|
||||
private:
|
||||
/// The force parameter determines the behaviour of the garbage collector.
|
||||
/// If it's set to true, it will behave as a global operation, i.e. it can't
|
||||
/// be part of a transaction, and no other transaction can be active at the same time.
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "storage/v2/durability/snapshot.hpp"
|
||||
#include "storage/v2/durability/wal.hpp"
|
||||
#include "storage/v2/edge_accessor.hpp"
|
||||
#include "storage/v2/inmemory/storage.hpp"
|
||||
#include "storage/v2/mvcc.hpp"
|
||||
#include "storage/v2/replication/config.hpp"
|
||||
#include "storage/v2/replication/enums.hpp"
|
||||
@@ -223,26 +224,9 @@ InMemoryStorage::~InMemoryStorage() {
|
||||
|
||||
InMemoryStorage::InMemoryAccessor::InMemoryAccessor(InMemoryStorage *storage, IsolationLevel isolation_level,
|
||||
StorageMode storage_mode)
|
||||
: storage_(storage),
|
||||
// The lock must be acquired before creating the transaction object to
|
||||
// prevent freshly created transactions from dangling in an active state
|
||||
// during exclusive operations.
|
||||
storage_guard_(storage_->main_lock_),
|
||||
transaction_(storage->CreateTransaction(isolation_level, storage_mode)),
|
||||
is_transaction_active_(true),
|
||||
config_(storage->config_.items) {}
|
||||
|
||||
: Accessor(storage, isolation_level, storage_mode), config_(storage->config_.items) {}
|
||||
InMemoryStorage::InMemoryAccessor::InMemoryAccessor(InMemoryAccessor &&other) noexcept
|
||||
: storage_(other.storage_),
|
||||
storage_guard_(std::move(other.storage_guard_)),
|
||||
transaction_(std::move(other.transaction_)),
|
||||
commit_timestamp_(other.commit_timestamp_),
|
||||
is_transaction_active_(other.is_transaction_active_),
|
||||
config_(other.config_) {
|
||||
// Don't allow the other accessor to abort our transaction in destructor.
|
||||
other.is_transaction_active_ = false;
|
||||
other.commit_timestamp_.reset();
|
||||
}
|
||||
: Accessor(std::move(other)), config_(other.config_) {}
|
||||
|
||||
InMemoryStorage::InMemoryAccessor::~InMemoryAccessor() {
|
||||
if (is_transaction_active_) {
|
||||
@@ -266,7 +250,9 @@ VertexAccessor InMemoryStorage::InMemoryAccessor::CreateVertex() {
|
||||
delta->prev.Set(&*it);
|
||||
}
|
||||
|
||||
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
|
||||
return {&*it, &transaction_, &mem_storage->indices_, &mem_storage->constraints_, config_};
|
||||
}
|
||||
|
||||
VertexAccessor InMemoryStorage::InMemoryAccessor::CreateVertex(storage::Gid gid) {
|
||||
@@ -288,14 +274,16 @@ VertexAccessor InMemoryStorage::InMemoryAccessor::CreateVertex(storage::Gid gid)
|
||||
if (delta) {
|
||||
delta->prev.Set(&*it);
|
||||
}
|
||||
return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return {&*it, &transaction_, &mem_storage->indices_, &mem_storage->constraints_, config_};
|
||||
}
|
||||
|
||||
std::optional<VertexAccessor> InMemoryStorage::InMemoryAccessor::FindVertex(Gid gid, View view) {
|
||||
auto acc = storage_->vertices_.access();
|
||||
auto it = acc.find(gid);
|
||||
if (it == acc.end()) return std::nullopt;
|
||||
return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, view);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return VertexAccessor::Create(&*it, &transaction_, &mem_storage->indices_, &mem_storage->constraints_, config_, view);
|
||||
}
|
||||
|
||||
Result<std::optional<VertexAccessor>> InMemoryStorage::InMemoryAccessor::DeleteVertex(VertexAccessor *vertex) {
|
||||
@@ -317,8 +305,9 @@ Result<std::optional<VertexAccessor>> InMemoryStorage::InMemoryAccessor::DeleteV
|
||||
CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
|
||||
vertex_ptr->deleted = true;
|
||||
|
||||
return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
|
||||
config_, true);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &mem_storage->indices_,
|
||||
&mem_storage->constraints_, config_, true);
|
||||
}
|
||||
|
||||
Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
|
||||
@@ -345,10 +334,11 @@ InMemoryStorage::InMemoryAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> deleted_edges;
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
for (const auto &item : in_edges) {
|
||||
auto [edge_type, from_vertex, edge] = item;
|
||||
EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &mem_storage->indices_,
|
||||
&mem_storage->constraints_, config_);
|
||||
auto ret = DeleteEdge(&e);
|
||||
if (ret.HasError()) {
|
||||
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
|
||||
@@ -361,8 +351,8 @@ InMemoryStorage::InMemoryAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
}
|
||||
for (const auto &item : out_edges) {
|
||||
auto [edge_type, to_vertex, edge] = item;
|
||||
EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
|
||||
config_);
|
||||
EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &mem_storage->indices_,
|
||||
&mem_storage->constraints_, config_);
|
||||
auto ret = DeleteEdge(&e);
|
||||
if (ret.HasError()) {
|
||||
MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
|
||||
@@ -388,7 +378,7 @@ InMemoryStorage::InMemoryAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
|
||||
vertex_ptr->deleted = true;
|
||||
|
||||
return std::make_optional<ReturnType>(
|
||||
VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
|
||||
VertexAccessor{vertex_ptr, &transaction_, &mem_storage->indices_, &mem_storage->constraints_, config_, true},
|
||||
std::move(deleted_edges));
|
||||
}
|
||||
|
||||
@@ -451,8 +441,9 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
|
||||
// Increment edge count.
|
||||
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &mem_storage->indices_,
|
||||
&mem_storage->constraints_, config_);
|
||||
}
|
||||
|
||||
Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to,
|
||||
@@ -522,8 +513,9 @@ Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccesso
|
||||
// Increment edge count.
|
||||
storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
|
||||
&storage_->constraints_, config_);
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &mem_storage->indices_,
|
||||
&mem_storage->constraints_, config_);
|
||||
}
|
||||
|
||||
Result<std::optional<EdgeAccessor>> InMemoryStorage::InMemoryAccessor::DeleteEdge(EdgeAccessor *edge) {
|
||||
@@ -606,8 +598,9 @@ Result<std::optional<EdgeAccessor>> InMemoryStorage::InMemoryAccessor::DeleteEdg
|
||||
// Decrement edge count.
|
||||
storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
|
||||
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
|
||||
&storage_->indices_, &storage_->constraints_, config_, true);
|
||||
&mem_storage->indices_, &mem_storage->constraints_, config_, true);
|
||||
}
|
||||
|
||||
const std::string &InMemoryStorage::InMemoryAccessor::LabelToName(LabelId label) const {
|
||||
@@ -643,10 +636,12 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
|
||||
auto could_replicate_all_sync_replicas = true;
|
||||
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
|
||||
if (transaction_.deltas.empty()) {
|
||||
// We don't have to update the commit timestamp here because no one reads
|
||||
// it.
|
||||
storage_->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
mem_storage->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
} else {
|
||||
// Validate that existence constraints are satisfied for all modified
|
||||
// vertices.
|
||||
@@ -658,7 +653,7 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
}
|
||||
// No need to take any locks here because we modified this vertex and no
|
||||
// one else can touch it until we commit.
|
||||
auto validation_result = ValidateExistenceConstraints(*prev.vertex, storage_->constraints_);
|
||||
auto validation_result = ValidateExistenceConstraints(*prev.vertex, mem_storage->constraints_);
|
||||
if (validation_result) {
|
||||
Abort();
|
||||
return StorageDataManipulationError{*validation_result};
|
||||
@@ -675,7 +670,7 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
|
||||
{
|
||||
std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
|
||||
commit_timestamp_.emplace(storage_->CommitTimestamp(desired_commit_timestamp));
|
||||
commit_timestamp_.emplace(mem_storage->CommitTimestamp(desired_commit_timestamp));
|
||||
|
||||
// Before committing and validating vertices against unique constraints,
|
||||
// we have to update unique constraints with the vertices that are going
|
||||
@@ -686,7 +681,7 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
if (prev.type != PreviousPtr::Type::VERTEX) {
|
||||
continue;
|
||||
}
|
||||
storage_->constraints_.unique_constraints.UpdateBeforeCommit(prev.vertex, transaction_);
|
||||
mem_storage->constraints_.unique_constraints.UpdateBeforeCommit(prev.vertex, transaction_);
|
||||
}
|
||||
|
||||
// Validate that unique constraints are satisfied for all modified
|
||||
@@ -701,7 +696,7 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
// No need to take any locks here because we modified this vertex and no
|
||||
// one else can touch it until we commit.
|
||||
unique_constraint_violation =
|
||||
storage_->constraints_.unique_constraints.Validate(*prev.vertex, transaction_, *commit_timestamp_);
|
||||
mem_storage->constraints_.unique_constraints.Validate(*prev.vertex, transaction_, *commit_timestamp_);
|
||||
if (unique_constraint_violation) {
|
||||
break;
|
||||
}
|
||||
@@ -717,21 +712,22 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
// modifications before they are written to disk.
|
||||
// Replica can log only the write transaction received from Main
|
||||
// so the Wal files are consistent
|
||||
if (storage_->replication_role_ == ReplicationRole::MAIN || desired_commit_timestamp.has_value()) {
|
||||
could_replicate_all_sync_replicas = storage_->AppendToWalDataManipulation(transaction_, *commit_timestamp_);
|
||||
if (mem_storage->replication_role_ == ReplicationRole::MAIN || desired_commit_timestamp.has_value()) {
|
||||
could_replicate_all_sync_replicas =
|
||||
mem_storage->AppendToWalDataManipulation(transaction_, *commit_timestamp_);
|
||||
}
|
||||
|
||||
// Take committed_transactions lock while holding the engine lock to
|
||||
// make sure that committed transactions are sorted by the commit
|
||||
// timestamp in the list.
|
||||
storage_->committed_transactions_.WithLock([&](auto & /*committed_transactions*/) {
|
||||
mem_storage->committed_transactions_.WithLock([&](auto & /*committed_transactions*/) {
|
||||
// TODO: release lock, and update all deltas to have a local copy
|
||||
// of the commit timestamp
|
||||
MG_ASSERT(transaction_.commit_timestamp != nullptr, "Invalid database state!");
|
||||
transaction_.commit_timestamp->store(*commit_timestamp_, std::memory_order_release);
|
||||
// Replica can only update the last commit timestamp with
|
||||
// the commits received from main.
|
||||
if (storage_->replication_role_ == ReplicationRole::MAIN || desired_commit_timestamp.has_value()) {
|
||||
if (mem_storage->replication_role_ == ReplicationRole::MAIN || desired_commit_timestamp.has_value()) {
|
||||
// Update the last commit timestamp
|
||||
storage_->last_commit_timestamp_.store(*commit_timestamp_);
|
||||
}
|
||||
@@ -740,7 +736,7 @@ utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemory
|
||||
engine_guard.unlock();
|
||||
});
|
||||
|
||||
storage_->commit_log_->MarkFinished(start_timestamp);
|
||||
mem_storage->commit_log_->MarkFinished(start_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,31 +901,33 @@ void InMemoryStorage::InMemoryAccessor::Abort() {
|
||||
}
|
||||
}
|
||||
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
{
|
||||
std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
|
||||
uint64_t mark_timestamp = storage_->timestamp_;
|
||||
// Take garbage_undo_buffers lock while holding the engine lock to make
|
||||
// sure that entries are sorted by mark timestamp in the list.
|
||||
storage_->garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
|
||||
mem_storage->garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
|
||||
// Release engine lock because we don't have to hold it anymore and
|
||||
// emplace back could take a long time.
|
||||
engine_guard.unlock();
|
||||
garbage_undo_buffers.emplace_back(mark_timestamp, std::move(transaction_.deltas));
|
||||
});
|
||||
storage_->deleted_vertices_.WithLock(
|
||||
mem_storage->deleted_vertices_.WithLock(
|
||||
[&](auto &deleted_vertices) { deleted_vertices.splice(deleted_vertices.begin(), my_deleted_vertices); });
|
||||
storage_->deleted_edges_.WithLock(
|
||||
mem_storage->deleted_edges_.WithLock(
|
||||
[&](auto &deleted_edges) { deleted_edges.splice(deleted_edges.begin(), my_deleted_edges); });
|
||||
}
|
||||
|
||||
storage_->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
mem_storage->commit_log_->MarkFinished(transaction_.start_timestamp);
|
||||
is_transaction_active_ = false;
|
||||
}
|
||||
|
||||
void InMemoryStorage::InMemoryAccessor::FinalizeTransaction() {
|
||||
if (commit_timestamp_) {
|
||||
storage_->commit_log_->MarkFinished(*commit_timestamp_);
|
||||
storage_->committed_transactions_.WithLock(
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
mem_storage->commit_log_->MarkFinished(*commit_timestamp_);
|
||||
mem_storage->committed_transactions_.WithLock(
|
||||
[&](auto &committed_transactions) { committed_transactions.emplace_back(std::move(transaction_)); });
|
||||
commit_timestamp_.reset();
|
||||
}
|
||||
@@ -1155,25 +1153,26 @@ StorageInfo InMemoryStorage::GetInfo() const {
|
||||
}
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, View view) {
|
||||
return VerticesIterable(storage_->indices_.label_index.Vertices(label, view, &transaction_));
|
||||
return VerticesIterable(
|
||||
static_cast<InMemoryStorage *>(storage_)->indices_.label_index.Vertices(label, view, &transaction_));
|
||||
}
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property, View view) {
|
||||
return VerticesIterable(storage_->indices_.label_property_index.Vertices(label, property, std::nullopt, std::nullopt,
|
||||
view, &transaction_));
|
||||
return VerticesIterable(static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.Vertices(
|
||||
label, property, std::nullopt, std::nullopt, view, &transaction_));
|
||||
}
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property,
|
||||
const PropertyValue &value, View view) {
|
||||
return VerticesIterable(storage_->indices_.label_property_index.Vertices(
|
||||
return VerticesIterable(static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.Vertices(
|
||||
label, property, utils::MakeBoundInclusive(value), utils::MakeBoundInclusive(value), view, &transaction_));
|
||||
}
|
||||
|
||||
VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
|
||||
LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
|
||||
return VerticesIterable(
|
||||
storage_->indices_.label_property_index.Vertices(label, property, lower_bound, upper_bound, view, &transaction_));
|
||||
return VerticesIterable(static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.Vertices(
|
||||
label, property, lower_bound, upper_bound, view, &transaction_));
|
||||
}
|
||||
|
||||
Transaction InMemoryStorage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
|
||||
|
||||
@@ -91,9 +91,10 @@ class InMemoryStorage final : public Storage {
|
||||
std::optional<VertexAccessor> FindVertex(Gid gid, View view) override;
|
||||
|
||||
VerticesIterable Vertices(View view) override {
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view,
|
||||
&storage_->indices_, &storage_->constraints_,
|
||||
storage_->config_.items));
|
||||
&mem_storage->indices_, &mem_storage->constraints_,
|
||||
mem_storage->config_.items));
|
||||
}
|
||||
|
||||
VerticesIterable Vertices(LabelId label, View view) override;
|
||||
@@ -113,20 +114,22 @@ class InMemoryStorage final : public Storage {
|
||||
/// Return approximate number of vertices with the given label.
|
||||
/// Note that this is always an over-estimate and never an under-estimate.
|
||||
int64_t ApproximateVertexCount(LabelId label) const override {
|
||||
return storage_->indices_.label_index.ApproximateVertexCount(label);
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_index.ApproximateVertexCount(label);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and property.
|
||||
/// Note that this is always an over-estimate and never an under-estimate.
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property) const override {
|
||||
return storage_->indices_.label_property_index.ApproximateVertexCount(label, property);
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.ApproximateVertexCount(label,
|
||||
property);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and the given
|
||||
/// value for the given property. Note that this is always an over-estimate
|
||||
/// and never an under-estimate.
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override {
|
||||
return storage_->indices_.label_property_index.ApproximateVertexCount(label, property, value);
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.ApproximateVertexCount(
|
||||
label, property, value);
|
||||
}
|
||||
|
||||
/// Return approximate number of vertices with the given label and value for
|
||||
@@ -135,16 +138,17 @@ class InMemoryStorage final : public Storage {
|
||||
int64_t ApproximateVertexCount(LabelId label, PropertyId property,
|
||||
const std::optional<utils::Bound<PropertyValue>> &lower,
|
||||
const std::optional<utils::Bound<PropertyValue>> &upper) const override {
|
||||
return storage_->indices_.label_property_index.ApproximateVertexCount(label, property, lower, upper);
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.ApproximateVertexCount(
|
||||
label, property, lower, upper);
|
||||
}
|
||||
|
||||
std::optional<storage::IndexStats> GetIndexStats(const storage::LabelId &label,
|
||||
const storage::PropertyId &property) const override {
|
||||
return storage_->indices_.label_property_index.GetIndexStats(label, property);
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.GetIndexStats(label, property);
|
||||
}
|
||||
|
||||
std::vector<std::pair<LabelId, PropertyId>> ClearIndexStats() override {
|
||||
return storage_->indices_.label_property_index.ClearIndexStats();
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.ClearIndexStats();
|
||||
}
|
||||
|
||||
std::vector<std::pair<LabelId, PropertyId>> DeleteIndexStatsForLabels(
|
||||
@@ -152,7 +156,8 @@ class InMemoryStorage final : public Storage {
|
||||
std::vector<std::pair<LabelId, PropertyId>> deleted_indexes;
|
||||
std::for_each(labels.begin(), labels.end(), [this, &deleted_indexes](const auto &label_str) {
|
||||
std::vector<std::pair<LabelId, PropertyId>> loc_results =
|
||||
storage_->indices_.label_property_index.DeleteIndexStatsForLabel(NameToLabel(label_str));
|
||||
static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.DeleteIndexStatsForLabel(
|
||||
NameToLabel(label_str));
|
||||
deleted_indexes.insert(deleted_indexes.end(), std::make_move_iterator(loc_results.begin()),
|
||||
std::make_move_iterator(loc_results.end()));
|
||||
});
|
||||
@@ -161,7 +166,7 @@ class InMemoryStorage final : public Storage {
|
||||
|
||||
void SetIndexStats(const storage::LabelId &label, const storage::PropertyId &property,
|
||||
const IndexStats &stats) override {
|
||||
storage_->indices_.label_property_index.SetIndexStats(label, property, stats);
|
||||
static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.SetIndexStats(label, property, stats);
|
||||
}
|
||||
|
||||
/// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise
|
||||
@@ -197,19 +202,24 @@ class InMemoryStorage final : public Storage {
|
||||
/// @throw std::bad_alloc if unable to insert a new mapping
|
||||
EdgeTypeId NameToEdgeType(std::string_view name) override;
|
||||
|
||||
bool LabelIndexExists(LabelId label) const override { return storage_->indices_.label_index.IndexExists(label); }
|
||||
bool LabelIndexExists(LabelId label) const override {
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_index.IndexExists(label);
|
||||
}
|
||||
|
||||
bool LabelPropertyIndexExists(LabelId label, PropertyId property) const override {
|
||||
return storage_->indices_.label_property_index.IndexExists(label, property);
|
||||
return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index.IndexExists(label, property);
|
||||
}
|
||||
|
||||
IndicesInfo ListAllIndices() const override {
|
||||
return {storage_->indices_.label_index.ListIndices(), storage_->indices_.label_property_index.ListIndices()};
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return {mem_storage->indices_.label_index.ListIndices(),
|
||||
mem_storage->indices_.label_property_index.ListIndices()};
|
||||
}
|
||||
|
||||
ConstraintsInfo ListAllConstraints() const override {
|
||||
return {ListExistenceConstraints(storage_->constraints_),
|
||||
storage_->constraints_.unique_constraints.ListConstraints()};
|
||||
auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
|
||||
return {ListExistenceConstraints(mem_storage->constraints_),
|
||||
mem_storage->constraints_.unique_constraints.ListConstraints()};
|
||||
}
|
||||
|
||||
void AdvanceCommand() override;
|
||||
@@ -237,11 +247,6 @@ class InMemoryStorage final : public Storage {
|
||||
/// @throw std::bad_alloc
|
||||
Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, storage::Gid gid);
|
||||
|
||||
InMemoryStorage *storage_;
|
||||
std::shared_lock<utils::RWLock> storage_guard_;
|
||||
Transaction transaction_;
|
||||
std::optional<uint64_t> commit_timestamp_;
|
||||
bool is_transaction_active_;
|
||||
Config::Items config_;
|
||||
};
|
||||
|
||||
@@ -375,9 +380,9 @@ class InMemoryStorage final : public Storage {
|
||||
|
||||
utils::BasicResult<CreateSnapshotError> CreateSnapshot(std::optional<bool> is_periodic) override;
|
||||
|
||||
private:
|
||||
Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode);
|
||||
Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) override;
|
||||
|
||||
private:
|
||||
/// The force parameter determines the behaviour of the garbage collector.
|
||||
/// If it's set to true, it will behave as a global operation, i.e. it can't
|
||||
/// be part of a transaction, and no other transaction can be active at the same time.
|
||||
@@ -408,10 +413,10 @@ class InMemoryStorage final : public Storage {
|
||||
bool ShouldStoreAndRestoreReplicas() const;
|
||||
|
||||
// Specific per storage engine
|
||||
Constraints constraints_;
|
||||
Indices indices_;
|
||||
IsolationLevel isolation_level_;
|
||||
StorageMode storage_mode_;
|
||||
Constraints constraints_;
|
||||
Indices indices_;
|
||||
Config config_;
|
||||
|
||||
// TODO: This isn't really a commit log, it doesn't even care if a
|
||||
|
||||
@@ -259,4 +259,24 @@ bool VerticesIterable::Iterator::operator==(const Iterator &other) const {
|
||||
}
|
||||
}
|
||||
|
||||
Storage::Accessor::Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode)
|
||||
: storage_(storage),
|
||||
// The lock must be acquired before creating the transaction object to
|
||||
// prevent freshly created transactions from dangling in an active state
|
||||
// during exclusive operations.
|
||||
storage_guard_(storage_->main_lock_),
|
||||
transaction_(storage->CreateTransaction(isolation_level, storage_mode)),
|
||||
is_transaction_active_(true) {}
|
||||
|
||||
Storage::Accessor::Accessor(Accessor &&other) noexcept
|
||||
: storage_(other.storage_),
|
||||
storage_guard_(std::move(other.storage_guard_)),
|
||||
transaction_(std::move(other.transaction_)),
|
||||
commit_timestamp_(other.commit_timestamp_),
|
||||
is_transaction_active_(other.is_transaction_active_) {
|
||||
// Don't allow the other accessor to abort our transaction in destructor.
|
||||
other.is_transaction_active_ = false;
|
||||
other.commit_timestamp_.reset();
|
||||
}
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
||||
@@ -189,7 +189,7 @@ class Storage {
|
||||
virtual ~Storage() {}
|
||||
class Accessor {
|
||||
public:
|
||||
Accessor() {}
|
||||
Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
|
||||
Accessor(const Accessor &) = delete;
|
||||
Accessor &operator=(const Accessor &) = delete;
|
||||
Accessor &operator=(Accessor &&other) = delete;
|
||||
@@ -310,6 +310,13 @@ class Storage {
|
||||
virtual void FinalizeTransaction() = 0;
|
||||
|
||||
virtual std::optional<uint64_t> GetTransactionId() const = 0;
|
||||
|
||||
protected:
|
||||
Storage *storage_;
|
||||
std::shared_lock<utils::RWLock> storage_guard_;
|
||||
Transaction transaction_;
|
||||
std::optional<uint64_t> commit_timestamp_;
|
||||
bool is_transaction_active_;
|
||||
};
|
||||
|
||||
virtual std::unique_ptr<Accessor> Access(std::optional<IsolationLevel> override_isolation_level) = 0;
|
||||
@@ -518,7 +525,8 @@ class Storage {
|
||||
|
||||
virtual utils::BasicResult<CreateSnapshotError> CreateSnapshot(std::optional<bool> is_periodic) = 0;
|
||||
|
||||
protected:
|
||||
virtual Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) = 0;
|
||||
|
||||
// Main storage lock.
|
||||
// Accessors take a shared lock when starting, so it is possible to block
|
||||
// creation of new accessors by taking a unique lock. This is used when doing
|
||||
@@ -543,6 +551,7 @@ class Storage {
|
||||
utils::SpinLock engine_lock_;
|
||||
uint64_t timestamp_{kTimestampInitialId};
|
||||
uint64_t transaction_id_{kTransactionInitialId};
|
||||
|
||||
// Durability
|
||||
std::filesystem::path snapshot_directory_;
|
||||
std::filesystem::path wal_directory_;
|
||||
|
||||
Reference in New Issue
Block a user