From 60e167d676d0bc73fe5d2be1b371f126d91157c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ante=20Pu=C5=A1i=C4=87?= Date: Wed, 23 Aug 2023 14:52:44 +0200 Subject: [PATCH] Optimize index and constraint updates (#1159) --- .../v2/inmemory/label_property_index.cpp | 28 +++++-- .../v2/inmemory/label_property_index.hpp | 1 + .../v2/inmemory/unique_constraints.cpp | 83 +++++++++++++------ .../v2/inmemory/unique_constraints.hpp | 1 + 4 files changed, 78 insertions(+), 35 deletions(-) diff --git a/src/storage/v2/inmemory/label_property_index.cpp b/src/storage/v2/inmemory/label_property_index.cpp index a62101ba0..9823cc580 100644 --- a/src/storage/v2/inmemory/label_property_index.cpp +++ b/src/storage/v2/inmemory/label_property_index.cpp @@ -67,6 +67,9 @@ bool InMemoryLabelPropertyIndex::CreateIndex(LabelId label, PropertyId property, auto [it, emplaced] = index_.emplace(std::piecewise_construct, std::forward_as_tuple(label, property), std::forward_as_tuple()); + + indices_by_property_[property].insert({label, &index_.at({label, property})}); + if (!emplaced) { // Index already exists. return false; @@ -75,6 +78,7 @@ bool InMemoryLabelPropertyIndex::CreateIndex(LabelId label, PropertyId property, if (parallel_exec_info) { return create_index_par(label, property, vertices, it, *parallel_exec_info); } + return create_index_seq(label, property, vertices, it); } @@ -97,18 +101,26 @@ void InMemoryLabelPropertyIndex::UpdateOnSetProperty(PropertyId property, const if (value.IsNull()) { return; } - for (auto &[label_prop, storage] : index_) { - if (label_prop.second != property) { - continue; - } - if (utils::Contains(vertex->labels, label_prop.first)) { - auto acc = storage.access(); - acc.insert(Entry{value, vertex, tx.start_timestamp}); - } + + if (!indices_by_property_.contains(property)) { + return; + } + + for (const auto &[_, storage] : indices_by_property_.at(property)) { + auto acc = storage->access(); + acc.insert(Entry{value, vertex, tx.start_timestamp}); } } bool InMemoryLabelPropertyIndex::DropIndex(LabelId label, PropertyId property) { + if (indices_by_property_.find(property) != indices_by_property_.end()) { + indices_by_property_.at(property).erase(label); + + if (indices_by_property_.at(property).empty()) { + indices_by_property_.erase(property); + } + } + return index_.erase({label, property}) > 0; } diff --git a/src/storage/v2/inmemory/label_property_index.hpp b/src/storage/v2/inmemory/label_property_index.hpp index 7557a379c..6129d4f7d 100644 --- a/src/storage/v2/inmemory/label_property_index.hpp +++ b/src/storage/v2/inmemory/label_property_index.hpp @@ -135,6 +135,7 @@ class InMemoryLabelPropertyIndex : public storage::LabelPropertyIndex { private: std::map, utils::SkipList> index_; + std::unordered_map *>> indices_by_property_; std::map, storage::LabelPropertyIndexStats> stats_; }; diff --git a/src/storage/v2/inmemory/unique_constraints.cpp b/src/storage/v2/inmemory/unique_constraints.cpp index 83507bc77..02e88f67c 100644 --- a/src/storage/v2/inmemory/unique_constraints.cpp +++ b/src/storage/v2/inmemory/unique_constraints.cpp @@ -241,13 +241,19 @@ bool InMemoryUniqueConstraints::Entry::operator<(const std::vector &rhs) const { return values == rhs; } void InMemoryUniqueConstraints::UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx) { - for (auto &[label_props, storage] : constraints_) { - if (!utils::Contains(vertex->labels, label_props.first)) { + for (const auto &label : vertex->labels) { + if (!constraints_by_label_.contains(label)) { continue; } - auto values = vertex->properties.ExtractPropertyValues(label_props.second); - if (values) { - auto acc = storage.access(); + + for (auto &[props, storage] : constraints_by_label_.at(label)) { + auto values = vertex->properties.ExtractPropertyValues(props); + + if (!values) { + continue; + } + + auto acc = storage->access(); acc.insert(Entry{std::move(*values), vertex, tx.start_timestamp}); } } @@ -303,6 +309,9 @@ InMemoryUniqueConstraints::CreateConstraint(LabelId label, const std::set 0) { + + auto erase_from_constraints_by_label_ = [this, label, &properties]() -> uint64_t { + if (!constraints_by_label_.contains(label)) { + return 1; // erase is successful if there’s nothing to erase + } + + const auto erase_entry_status = constraints_by_label_[label].erase(properties); + if (!constraints_by_label_[label].empty()) { + return erase_entry_status; + } + + return erase_entry_status > 0 && constraints_by_label_.erase(label) > 0; + }; + + if (constraints_.erase({label, properties}) > 0 && erase_from_constraints_by_label_() > 0) { return UniqueConstraints::DeletionStatus::SUCCESS; } return UniqueConstraints::DeletionStatus::NOT_FOUND; @@ -327,34 +350,37 @@ std::optional InMemoryUniqueConstraints::Validate(const Ver if (vertex.deleted) { return std::nullopt; } - for (const auto &[label_props, storage] : constraints_) { - const auto &label = label_props.first; - const auto &properties = label_props.second; - if (!utils::Contains(vertex.labels, label)) { + for (const auto &label : vertex.labels) { + if (!constraints_by_label_.contains(label)) { continue; } - auto value_array = vertex.properties.ExtractPropertyValues(properties); - if (!value_array) { - continue; - } - auto acc = storage.access(); - auto it = acc.find_equal_or_greater(*value_array); - for (; it != acc.end(); ++it) { - if (*value_array < it->values) { - break; + for (const auto &[properties, storage] : constraints_by_label_.at(label)) { + auto value_array = vertex.properties.ExtractPropertyValues(properties); + + if (!value_array) { + continue; } - // The `vertex` that is going to be committed violates a unique constraint - // if it's different than a vertex indexed in the list of constraints and - // has the same label and property value as the last committed version of - // the vertex from the list. - if (&vertex != it->vertex && - LastCommittedVersionHasLabelProperty(*it->vertex, label, properties, *value_array, tx, commit_timestamp)) { - return ConstraintViolation{ConstraintViolation::Type::UNIQUE, label, properties}; + auto acc = storage->access(); + auto it = acc.find_equal_or_greater(*value_array); + for (; it != acc.end(); ++it) { + if (*value_array < it->values) { + break; + } + + // The `vertex` that is going to be committed violates a unique constraint + // if it's different than a vertex indexed in the list of constraints and + // has the same label and property value as the last committed version of + // the vertex from the list. + if (&vertex != it->vertex && + LastCommittedVersionHasLabelProperty(*it->vertex, label, properties, *value_array, tx, commit_timestamp)) { + return ConstraintViolation{ConstraintViolation::Type::UNIQUE, label, properties}; + } } } } + return std::nullopt; } @@ -389,6 +415,9 @@ void InMemoryUniqueConstraints::RemoveObsoleteEntries(uint64_t oldest_active_sta } } -void InMemoryUniqueConstraints::Clear() { constraints_.clear(); } +void InMemoryUniqueConstraints::Clear() { + constraints_.clear(); + constraints_by_label_.clear(); +} } // namespace memgraph::storage diff --git a/src/storage/v2/inmemory/unique_constraints.hpp b/src/storage/v2/inmemory/unique_constraints.hpp index 401f1e036..64565056b 100644 --- a/src/storage/v2/inmemory/unique_constraints.hpp +++ b/src/storage/v2/inmemory/unique_constraints.hpp @@ -96,6 +96,7 @@ class InMemoryUniqueConstraints : public UniqueConstraints { private: std::map>, utils::SkipList> constraints_; + std::map, utils::SkipList *>> constraints_by_label_; }; } // namespace memgraph::storage