Optimize index and constraint updates (#1159)
This commit is contained in:
parent
3f8befde79
commit
60e167d676
@ -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;
|
||||
}
|
||||
|
||||
|
@ -135,6 +135,7 @@ class InMemoryLabelPropertyIndex : public storage::LabelPropertyIndex {
|
||||
|
||||
private:
|
||||
std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>> index_;
|
||||
std::unordered_map<PropertyId, std::unordered_map<LabelId, utils::SkipList<Entry> *>> indices_by_property_;
|
||||
std::map<std::pair<LabelId, PropertyId>, storage::LabelPropertyIndexStats> stats_;
|
||||
};
|
||||
|
||||
|
@ -241,13 +241,19 @@ bool InMemoryUniqueConstraints::Entry::operator<(const std::vector<PropertyValue
|
||||
bool InMemoryUniqueConstraints::Entry::operator==(const std::vector<PropertyValue> &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<Proper
|
||||
constraints_.erase(constraint);
|
||||
return ConstraintViolation{ConstraintViolation::Type::UNIQUE, label, properties};
|
||||
}
|
||||
|
||||
// Add the new constraint to the optimized structure only if there are no violations.
|
||||
constraints_by_label_[label].insert({properties, &constraints_.at({label, properties})});
|
||||
return CreationStatus::SUCCESS;
|
||||
}
|
||||
|
||||
@ -312,7 +321,21 @@ InMemoryUniqueConstraints::DeletionStatus InMemoryUniqueConstraints::DropConstra
|
||||
drop_properties_check_result != UniqueConstraints::DeletionStatus::SUCCESS) {
|
||||
return drop_properties_check_result;
|
||||
}
|
||||
if (constraints_.erase({label, properties}) > 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<ConstraintViolation> 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
|
||||
|
@ -96,6 +96,7 @@ class InMemoryUniqueConstraints : public UniqueConstraints {
|
||||
|
||||
private:
|
||||
std::map<std::pair<LabelId, std::set<PropertyId>>, utils::SkipList<Entry>> constraints_;
|
||||
std::map<LabelId, std::map<std::set<PropertyId>, utils::SkipList<Entry> *>> constraints_by_label_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::storage
|
||||
|
Loading…
Reference in New Issue
Block a user