diff --git a/src/storage/v2/inmemory/storage.cpp b/src/storage/v2/inmemory/storage.cpp index 381a67d3f..78e5f059b 100644 --- a/src/storage/v2/inmemory/storage.cpp +++ b/src/storage/v2/inmemory/storage.cpp @@ -176,9 +176,9 @@ InMemoryStorage::~InMemoryStorage() { committed_transactions_.WithLock([](auto &transactions) { transactions.clear(); }); } -InMemoryStorage::InMemoryAccessor::InMemoryAccessor(auto tag, InMemoryStorage *storage, IsolationLevel isolation_level, - StorageMode storage_mode, - memgraph::replication_coordination_glue::ReplicationRole replication_role) +InMemoryStorage::InMemoryAccessor::InMemoryAccessor( + auto tag, InMemoryStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode, + memgraph::replication_coordination_glue::ReplicationRole replication_role) : Accessor(tag, storage, isolation_level, storage_mode, replication_role), config_(storage->config_.salient.items) {} InMemoryStorage::InMemoryAccessor::InMemoryAccessor(InMemoryAccessor &&other) noexcept @@ -882,6 +882,9 @@ void InMemoryStorage::InMemoryAccessor::Abort() { std::list<Gid> my_deleted_vertices; std::list<Gid> my_deleted_edges; + std::unordered_map<Gid, uint64_t> added_in_edges; + std::unordered_map<Gid, uint64_t> added_out_edges; + std::map<LabelId, std::vector<Vertex *>> label_cleanup; std::map<LabelId, std::vector<std::pair<PropertyValue, Vertex *>>> label_property_cleanup; std::map<PropertyId, std::vector<std::pair<PropertyValue, Vertex *>>> property_cleanup; @@ -904,6 +907,10 @@ void InMemoryStorage::InMemoryAccessor::Abort() { auto *vertex = prev.vertex; auto guard = std::unique_lock{vertex->lock}; Delta *current = vertex->delta; + + added_in_edges.clear(); + added_out_edges.clear(); + while (current != nullptr && current->timestamp->load(std::memory_order_acquire) == transaction_.transaction_id) { switch (current->action) { @@ -955,19 +962,43 @@ void InMemoryStorage::InMemoryAccessor::Abort() { break; } case Delta::Action::ADD_IN_EDGE: { + if (added_in_edges.empty()) { + uint64_t idx = 0; + for (auto &[type, opposing_vertex, ref] : vertex->in_edges) { + auto gid = config_.properties_on_edges ? ref.ptr->gid : ref.gid; + added_in_edges.emplace(gid, idx); + idx++; + } + } + + auto wanted_gid = + config_.properties_on_edges ? current->vertex_edge.edge.ptr->gid : current->vertex_edge.edge.gid; + MG_ASSERT(!added_in_edges.contains(wanted_gid), "Invalid database state!"); + std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type, current->vertex_edge.vertex, current->vertex_edge.edge}; - auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link); - MG_ASSERT(it == vertex->in_edges.end(), "Invalid database state!"); vertex->in_edges.push_back(link); + added_in_edges.emplace(wanted_gid, vertex->in_edges.size() - 1); break; } case Delta::Action::ADD_OUT_EDGE: { + if (added_out_edges.empty()) { + uint64_t idx = 0; + for (auto &[type, opposing_vertex, ref] : vertex->out_edges) { + auto gid = config_.properties_on_edges ? ref.ptr->gid : ref.gid; + added_out_edges.emplace(gid, idx); + idx++; + } + } + + auto wanted_gid = + config_.properties_on_edges ? current->vertex_edge.edge.ptr->gid : current->vertex_edge.edge.gid; + MG_ASSERT(!added_out_edges.contains(wanted_gid), "Invalid database state!"); + std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type, current->vertex_edge.vertex, current->vertex_edge.edge}; - auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link); - MG_ASSERT(it == vertex->out_edges.end(), "Invalid database state!"); vertex->out_edges.push_back(link); + added_out_edges.emplace(wanted_gid, vertex->out_edges.size() - 1); // Increment edge count. We only increment the count here because // the information in `ADD_IN_EDGE` and `Edge/RECREATE_OBJECT` is // redundant. Also, `Edge/RECREATE_OBJECT` isn't available when @@ -976,21 +1007,55 @@ void InMemoryStorage::InMemoryAccessor::Abort() { break; } case Delta::Action::REMOVE_IN_EDGE: { - std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type, - current->vertex_edge.vertex, current->vertex_edge.edge}; - auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link); - MG_ASSERT(it != vertex->in_edges.end(), "Invalid database state!"); - std::swap(*it, *vertex->in_edges.rbegin()); + if (added_in_edges.empty()) { + uint64_t idx = 0; + for (auto &[type, opposing_vertex, ref] : vertex->in_edges) { + auto gid = config_.properties_on_edges ? ref.ptr->gid : ref.gid; + added_in_edges.emplace(gid, idx); + idx++; + } + } + + auto wanted_gid = + config_.properties_on_edges ? current->vertex_edge.edge.ptr->gid : current->vertex_edge.edge.gid; + + MG_ASSERT(added_in_edges.count(wanted_gid), "Invalid database state!"); + + auto pop_index = added_in_edges[wanted_gid]; + + std::swap(vertex->in_edges[pop_index], *vertex->in_edges.rbegin()); vertex->in_edges.pop_back(); + added_in_edges.erase(wanted_gid); + + auto switched_vertex = std::get<2>(vertex->in_edges[pop_index]); + auto switch_gid = config_.properties_on_edges ? switched_vertex.ptr->gid : switched_vertex.gid; + added_in_edges[switch_gid] = pop_index; break; } case Delta::Action::REMOVE_OUT_EDGE: { - std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type, - current->vertex_edge.vertex, current->vertex_edge.edge}; - auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link); - MG_ASSERT(it != vertex->out_edges.end(), "Invalid database state!"); - std::swap(*it, *vertex->out_edges.rbegin()); + if (added_out_edges.empty()) { + uint64_t idx = 0; + for (auto &[type, opposing_vertex, ref] : vertex->out_edges) { + auto gid = config_.properties_on_edges ? ref.ptr->gid : ref.gid; + added_out_edges.emplace(gid, idx); + idx++; + } + } + + auto wanted_gid = + config_.properties_on_edges ? current->vertex_edge.edge.ptr->gid : current->vertex_edge.edge.gid; + + MG_ASSERT(added_out_edges.count(wanted_gid), "Invalid database state!"); + + auto pop_index = added_out_edges[wanted_gid]; + + std::swap(vertex->out_edges[pop_index], *vertex->out_edges.rbegin()); vertex->out_edges.pop_back(); + added_out_edges.erase(wanted_gid); + + auto switched_vertex = std::get<2>(vertex->out_edges[pop_index]); + auto switch_gid = config_.properties_on_edges ? switched_vertex.ptr->gid : switched_vertex.gid; + added_out_edges[switch_gid] = pop_index; // Decrement edge count. We only decrement the count here because // the information in `REMOVE_IN_EDGE` and `Edge/DELETE_OBJECT` is // redundant. Also, `Edge/DELETE_OBJECT` isn't available when edge