Remove existence constraint

Summary: Removing existence constraint

Reviewers: ipaljak, mferencevic, vkasljevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2043
This commit is contained in:
Matija Santl 2019-05-13 13:47:22 +02:00
parent d37460105a
commit d70792f1ce
22 changed files with 38 additions and 827 deletions

View File

@ -69,7 +69,6 @@ set(mg_single_node_sources
storage/common/types/property_value_store.cpp
storage/single_node/record_accessor.cpp
storage/single_node/vertex_accessor.cpp
storage/single_node/constraints/existence_constraints.cpp
storage/single_node/constraints/record.cpp
storage/single_node/constraints/unique_label_properties_constraint.cpp
storage/single_node/constraints/unique_label_property_constraint.cpp

View File

@ -40,8 +40,6 @@ GraphDb::GraphDb(Config config) : config_(config) {
durability::RecoverWal(config_.durability_directory, this, &recovery_data,
&recovery_transactions);
durability::RecoverIndexes(this, recovery_data.indexes);
durability::RecoverExistenceConstraints(
this, recovery_data.existence_constraints);
durability::RecoverUniqueConstraints(
this, recovery_data.unique_constraints);
}

View File

@ -291,11 +291,6 @@ void GraphDbAccessor::UpdateOnAddLabel(storage::Label label,
"Node couldn't be updated due to index constraint violation!");
}
if (!db_->storage().existence_constraints_.CheckOnAddLabel(vertex, label)) {
throw IndexConstraintViolationException(
"Node couldn't be updated due to existence constraint violation!");
}
db_->storage().labels_index_.Update(label, vlist_ptr, vertex);
}
@ -325,71 +320,6 @@ void GraphDbAccessor::UpdateOnRemoveProperty(
const Vertex *vertex) {
db_->storage().unique_label_property_constraints_.UpdateOnRemoveProperty(
property, accessor, transaction());
if (!db_->storage().existence_constraints_.CheckOnRemoveProperty(vertex,
property)) {
throw IndexConstraintViolationException(
"Node couldn't be updated due to existence constraint violation!");
}
}
void GraphDbAccessor::BuildExistenceConstraint(
storage::Label label, const std::vector<storage::Property> &properties) {
auto dba = db_->AccessBlocking(std::make_optional(transaction().id_));
storage::constraints::ExistenceRule rule{label, properties};
for (auto v : dba.Vertices(false)) {
if (!CheckIfSatisfiesExistenceRule(v.GetOld(), rule)) {
throw IndexConstraintViolationException(
"Existence constraint couldn't be built because existing data is "
"violating it!");
}
}
if (!db_->storage().existence_constraints_.AddConstraint(rule)) {
// Already exists
return;
}
std::vector<std::string> property_names(properties.size());
std::transform(properties.begin(), properties.end(), property_names.begin(),
[&dba](auto p) { return dba.PropertyName(p); });
dba.wal().Emplace(database::StateDelta::BuildExistenceConstraint(
dba.transaction().id_, label, dba.LabelName(label), properties,
property_names));
dba.Commit();
}
void GraphDbAccessor::DeleteExistenceConstraint(
storage::Label label, const std::vector<storage::Property> &properties) {
auto dba = db_->AccessBlocking(std::make_optional(transaction().id_));
storage::constraints::ExistenceRule rule{label, properties};
if (!db_->storage().existence_constraints_.RemoveConstraint(rule)) {
// Nothing was deleted
return;
}
std::vector<std::string> property_names(properties.size());
std::transform(properties.begin(), properties.end(), property_names.begin(),
[&dba](auto p) { return dba.PropertyName(p); });
dba.wal().Emplace(database::StateDelta::DropExistenceConstraint(
dba.transaction().id_, label, dba.LabelName(label), properties,
property_names));
dba.Commit();
}
bool GraphDbAccessor::ExistenceConstraintExists(
storage::Label label,
const std::vector<storage::Property> &properties) const {
storage::constraints::ExistenceRule rule{label, properties};
return db_->storage().existence_constraints_.Exists(rule);
}
std::vector<storage::constraints::ExistenceRule>
GraphDbAccessor::ListExistenceConstraints() const {
return db_->storage().existence_constraints_.ListConstraints();
}
int64_t GraphDbAccessor::VerticesCount() const {

View File

@ -472,31 +472,6 @@ class GraphDbAccessor {
return db_->storage().label_property_index_.Keys();
}
/**
* Creates new existence constraint.
*/
void BuildExistenceConstraint(
storage::Label label, const std::vector<storage::Property> &properties);
/**
* Deletes existing existence constraint.
*/
void DeleteExistenceConstraint(
storage::Label label, const std::vector<storage::Property> &properties);
/**
* Checks whether constraint exists.
*/
bool ExistenceConstraintExists(
storage::Label label,
const std::vector<storage::Property> &properties) const;
/**
* Returns the list of existence constraints currently active.
*/
std::vector<storage::constraints::ExistenceRule> ListExistenceConstraints()
const;
/**
* Return approximate number of all vertices in the database.
* Note that this is always an over-estimate and never an under-estimate.

View File

@ -150,31 +150,6 @@ bool RecoverSnapshot(const fs::path &snapshot_file, database::GraphDb *db,
/*create = */ true, unique.ValueBool()});
}
// Read a list of existence constraints
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::List));
auto existence_constraints = dv.ValueList();
for (auto it = existence_constraints.begin();
it != existence_constraints.end();) {
RETURN_IF_NOT(it->IsString());
auto label = it->ValueString();
++it;
RETURN_IF_NOT(it != existence_constraints.end());
RETURN_IF_NOT(it->IsInt());
auto prop_size = it->ValueInt();
++it;
std::vector<std::string> properties;
properties.reserve(prop_size);
for (size_t i = 0; i < prop_size; ++i) {
RETURN_IF_NOT(it != existence_constraints.end());
RETURN_IF_NOT(it->IsString());
properties.emplace_back(it->ValueString());
++it;
}
recovery_data->existence_constraints.emplace_back(
ExistenceConstraintRecoveryData{label, std::move(properties), true});
}
// Read a list of unique constraints
RETURN_IF_NOT(decoder.ReadValue(&dv, Value::Type::List));
auto unique_constraints = dv.ValueList();
@ -499,48 +474,6 @@ void RecoverWal(const fs::path &durability_dir, database::GraphDb *db,
}
break;
}
case database::StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT: {
auto drop_it = std::find_if(
recovery_data->existence_constraints.begin(),
recovery_data->existence_constraints.end(),
[&delta](const ExistenceConstraintRecoveryData &data) {
return data.label == delta.label_name &&
std::is_permutation(data.properties.begin(),
data.properties.end(),
delta.property_names.begin()) &&
data.create == false;
});
if (drop_it != recovery_data->existence_constraints.end()) {
recovery_data->existence_constraints.erase(drop_it);
} else {
recovery_data->existence_constraints.emplace_back(
ExistenceConstraintRecoveryData{delta.label_name,
delta.property_names, true});
}
break;
}
case database::StateDelta::Type::DROP_EXISTENCE_CONSTRAINT: {
auto build_it = std::find_if(
recovery_data->existence_constraints.begin(),
recovery_data->existence_constraints.end(),
[&delta](const ExistenceConstraintRecoveryData &data) {
return data.label == delta.label_name &&
std::is_permutation(data.properties.begin(),
data.properties.end(),
delta.property_names.begin()) &&
data.create == true;
});
if (build_it != recovery_data->existence_constraints.end()) {
recovery_data->existence_constraints.erase(build_it);
} else {
recovery_data->existence_constraints.emplace_back(
ExistenceConstraintRecoveryData{delta.label_name,
delta.property_names, false});
}
break;
}
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT: {
auto drop_it = std::find_if(
recovery_data->unique_constraints.begin(),
@ -612,26 +545,6 @@ void RecoverIndexes(database::GraphDb *db,
dba.Commit();
}
void RecoverExistenceConstraints(
database::GraphDb *db,
const std::vector<ExistenceConstraintRecoveryData> &constraints) {
auto dba = db->Access();
for (auto &constraint : constraints) {
auto label = dba.Label(constraint.label);
std::vector<storage::Property> properties;
properties.reserve(constraint.properties.size());
for (auto &prop : constraint.properties) {
properties.push_back(dba.Property(prop));
}
if (constraint.create) {
dba.BuildExistenceConstraint(label, properties);
} else {
dba.DeleteExistenceConstraint(label, properties);
}
}
}
void RecoverUniqueConstraints(
database::GraphDb *db,
const std::vector<UniqueConstraintRecoveryData> &constraints) {

View File

@ -43,13 +43,6 @@ struct IndexRecoveryData {
bool unique; // used only when creating an index
};
struct ExistenceConstraintRecoveryData {
std::string label;
std::vector<std::string> properties;
// distinguish between creating and dropping existence constraint
bool create;
};
struct UniqueConstraintRecoveryData {
std::string label;
std::vector<std::string> properties;
@ -67,14 +60,12 @@ struct RecoveryData {
// can be rebuilt at the end of the recovery transaction.
std::vector<IndexRecoveryData> indexes;
std::vector<ExistenceConstraintRecoveryData> existence_constraints;
std::vector<UniqueConstraintRecoveryData> unique_constraints;
void Clear() {
snapshooter_tx_id = 0;
snapshooter_tx_snapshot.clear();
indexes.clear();
existence_constraints.clear();
unique_constraints.clear();
}
};
@ -165,10 +156,6 @@ void RecoverWal(const std::filesystem::path &durability_dir,
void RecoverIndexes(database::GraphDb *db,
const std::vector<IndexRecoveryData> &indexes);
void RecoverExistenceConstraints(
database::GraphDb *db,
const std::vector<ExistenceConstraintRecoveryData> &constraints);
void RecoverUniqueConstraints(
database::GraphDb *db,
const std::vector<UniqueConstraintRecoveryData> &constraints);

View File

@ -17,7 +17,7 @@ namespace fs = std::filesystem;
namespace durability {
// Snapshot layout is described in durability/version.hpp
static_assert(durability::kVersion == 9,
static_assert(durability::kVersion == 10,
"Wrong snapshot version, please update!");
namespace {
@ -55,20 +55,6 @@ bool Encode(const fs::path &snapshot_file, database::GraphDb &db,
encoder.WriteList(index_vec);
}
// Write existence constraints to snapshoot
{
std::vector<communication::bolt::Value> existence_constraints;
for (const auto &rule : dba.ListExistenceConstraints()) {
existence_constraints.emplace_back(dba.LabelName(rule.label));
existence_constraints.emplace_back(
static_cast<int64_t>(rule.properties.size()));
for (auto &prop : rule.properties) {
existence_constraints.emplace_back(dba.PropertyName(prop));
}
}
encoder.WriteList(existence_constraints);
}
// Write unique constraints to snapshoot
{
std::vector<communication::bolt::Value> unique_constraints;

View File

@ -126,32 +126,6 @@ StateDelta StateDelta::DropIndex(tx::TransactionId tx_id, storage::Label label,
return op;
}
StateDelta StateDelta::BuildExistenceConstraint(
tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
const std::vector<storage::Property> &properties,
const std::vector<std::string> &property_names) {
StateDelta op(StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT, tx_id);
op.label = label;
op.label_name = label_name;
op.properties = properties;
op.property_names = property_names;
return op;
}
StateDelta StateDelta::DropExistenceConstraint(
tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
const std::vector<storage::Property> &properties,
const std::vector<std::string> &property_names) {
StateDelta op(StateDelta::Type::DROP_EXISTENCE_CONSTRAINT, tx_id);
op.label = label;
op.label_name = label_name;
op.properties = properties;
op.property_names = property_names;
return op;
}
StateDelta StateDelta::BuildUniqueConstraint(
tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
@ -236,7 +210,6 @@ void StateDelta::Encode(
encoder.WriteInt(property.Id());
encoder.WriteString(property_name);
break;
case Type::BUILD_EXISTENCE_CONSTRAINT:
case Type::BUILD_UNIQUE_CONSTRAINT:
encoder.WriteInt(label.Id());
encoder.WriteString(label_name);
@ -248,7 +221,6 @@ void StateDelta::Encode(
encoder.WriteString(name);
}
break;
case Type::DROP_EXISTENCE_CONSTRAINT:
case Type::DROP_UNIQUE_CONSTRAINT:
encoder.WriteInt(label.Id());
encoder.WriteString(label_name);
@ -341,7 +313,6 @@ std::optional<StateDelta> StateDelta::Decode(
DECODE_MEMBER_CAST(property, ValueInt, storage::Property)
DECODE_MEMBER(property_name, ValueString)
break;
case Type::BUILD_EXISTENCE_CONSTRAINT:
case Type::BUILD_UNIQUE_CONSTRAINT: {
DECODE_MEMBER_CAST(label, ValueInt, storage::Label)
DECODE_MEMBER(label_name, ValueString)
@ -358,7 +329,6 @@ std::optional<StateDelta> StateDelta::Decode(
}
break;
}
case Type::DROP_EXISTENCE_CONSTRAINT:
case Type::DROP_UNIQUE_CONSTRAINT: {
DECODE_MEMBER_CAST(label, ValueInt, storage::Label)
DECODE_MEMBER(label_name, ValueString)
@ -444,24 +414,6 @@ void StateDelta::Apply(GraphDbAccessor &dba) const {
LOG(FATAL) << "Index handling not handled in Apply";
break;
}
case Type::BUILD_EXISTENCE_CONSTRAINT: {
std::vector<storage::Property> properties;
properties.reserve(property_names.size());
for (auto &p : property_names) {
properties.push_back(dba.Property(p));
}
dba.BuildExistenceConstraint(dba.Label(label_name), properties);
} break;
case Type::DROP_EXISTENCE_CONSTRAINT: {
std::vector<storage::Property> properties;
properties.reserve(property_names.size());
for (auto &p : property_names) {
properties.push_back(dba.Property(p));
}
dba.DeleteExistenceConstraint(dba.Label(label_name), properties);
} break;
case Type::BUILD_UNIQUE_CONSTRAINT: {
std::vector<storage::Property> properties;
properties.reserve(property_names.size());

View File

@ -73,8 +73,6 @@ in StateDeltas.")
remove-edge ;; edge_id
build-index ;; label, label_name, property, property_name, unique
drop-index ;; label, label_name, property, property_name
build-existence-constraint ;; label, label_name, properties, property_names
drop-existence-constraint ;; label, label_name, properties, property_names
build-unique_constraint ;; label, label_name, properties, property_names
drop-unique_constraint ;; label, label_name, properties, property_names
)
@ -135,16 +133,6 @@ omitted in the comment."))
const std::string &label_name,
storage::Property property,
const std::string &property_name);
static StateDelta BuildExistenceConstraint(
tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
const std::vector<storage::Property> &properties,
const std::vector<std::string> &property_names);
static StateDelta DropExistenceConstraint(
tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,
const std::vector<storage::Property> &property,
const std::vector<std::string> &property_names);
static StateDelta BuildUniqueConstraint(
tx::TransactionId tx_id, storage::Label label,
const std::string &label_name,

View File

@ -15,9 +15,9 @@ constexpr std::array<uint8_t, 4> kSnapshotMagic{{'M', 'G', 's', 'n'}};
constexpr std::array<uint8_t, 4> kWalMagic{{'M', 'G', 'w', 'l'}};
// The current default version of snapshot and WAL encoding / decoding.
constexpr int64_t kVersion{9};
constexpr int64_t kVersion{10};
// Snapshot format (version 8):
// Snapshot format (version 10):
// 1) Magic number + snapshot version
//
// The following two entries are required when recovering from snapshot combined
@ -27,17 +27,15 @@ constexpr int64_t kVersion{9};
//
// 4) A list of label+property indices.
//
// 5) A list of existence constraints
// 5) A list of unique constraints
//
// 6) A list of unique constraints
//
// 7) Bolt encoded nodes. Each node is written in the following format:
// 6) Bolt encoded nodes. Each node is written in the following format:
// * gid, labels, properties
// 8) Bolt encoded edges. Each edge is written in the following format:
// 7) Bolt encoded edges. Each edge is written in the following format:
// * gid
// * from, to
// * edge_type
// * properties
//
// 9) Snapshot summary (number of nodes, number of edges, hash)
// 8) Snapshot summary (number of nodes, number of edges, hash)
} // namespace durability

View File

@ -144,8 +144,6 @@ bool WriteAheadLog::IsStateDeltaTransactionEnd(
case database::StateDelta::Type::REMOVE_EDGE:
case database::StateDelta::Type::BUILD_INDEX:
case database::StateDelta::Type::DROP_INDEX:
case database::StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT:
case database::StateDelta::Type::DROP_EXISTENCE_CONSTRAINT:
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT:
case database::StateDelta::Type::DROP_UNIQUE_CONSTRAINT:
return false;

View File

@ -654,36 +654,36 @@ Callback HandleInfoQuery(InfoQuery *info_query,
Callback HandleConstraintQuery(ConstraintQuery *constraint_query,
database::GraphDbAccessor *db_accessor) {
#ifdef MG_SINGLE_NODE
Callback callback;
std::vector<std::string> property_names;
property_names.reserve(constraint_query->constraint_.properties.size());
for (const auto &prop_ix : constraint_query->constraint_.properties) {
property_names.push_back(prop_ix.name);
}
std::string label_name = constraint_query->constraint_.label.name;
std::string type;
switch (constraint_query->constraint_.type) {
case Constraint::Type::EXISTS:
type = "exists";
break;
case Constraint::Type::UNIQUE:
type = "unique";
break;
case Constraint::Type::NODE_KEY:
type = "node key";
break;
}
switch (constraint_query->action_type_) {
case ConstraintQuery::ActionType::CREATE:
throw utils::NotYetImplemented("create constraint :{}({}) {}", label_name,
utils::Join(property_names, ", "), type);
case ConstraintQuery::ActionType::CREATE: {
switch (constraint_query->constraint_.type) {
case Constraint::Type::NODE_KEY:
throw utils::NotYetImplemented("Node key constraints");
case Constraint::Type::EXISTS:
throw utils::NotYetImplemented("Existence constraints");
case Constraint::Type::UNIQUE:
throw utils::NotYetImplemented("Unique constraints");
}
break;
case ConstraintQuery::ActionType::DROP:
throw utils::NotYetImplemented("drop constraint :{}({}) {}", label_name,
utils::Join(property_names, ", "), type);
}
case ConstraintQuery::ActionType::DROP: {
switch (constraint_query->constraint_.type) {
case Constraint::Type::NODE_KEY:
throw utils::NotYetImplemented("Node key constraints");
case Constraint::Type::EXISTS:
throw utils::NotYetImplemented("Existence constraints");
case Constraint::Type::UNIQUE:
throw utils::NotYetImplemented("Unique constraints");
}
break;
}
}
return callback;
#else
throw utils::NotYetImplemented("Constraints");
#endif
}
Interpreter::Interpreter() : is_tsc_available_(utils::CheckAvailableTSC()) {}

View File

@ -1,87 +0,0 @@
/// @file
#pragma once
#include <list>
#include <unordered_map>
#include <vector>
#include "storage/common/types/property_value.hpp"
#include "storage/common/types/types.hpp"
#include "storage/single_node/vertex.hpp"
#include "transactions/type.hpp"
namespace storage::constraints {
/// Existence rule defines label -> set of properties rule. This means that
/// every vertex with that label has to have every property in the set of
/// properties. This rule doesn't care about the PropertyValues for those
/// properties.
struct ExistenceRule {
storage::Label label;
std::vector<storage::Property> properties;
bool operator==(const ExistenceRule &rule) const {
return label == rule.label &&
std::is_permutation(properties.begin(), properties.end(),
rule.properties.begin());
}
bool operator!=(const ExistenceRule &rule) const { return !(*this == rule); }
};
bool CheckIfSatisfiesExistenceRule(const Vertex *vertex,
const ExistenceRule &rule);
/// ExistenceConstraints contains all active constrains. Existence constraints
/// are restriction on the vertices and are defined by ExistenceRule.
/// To create and delete constraint, the caller must ensure that there are no
/// other transactions running in parallel.
/// Additionally, for adding constraint caller must check existing vertices for
/// constraint violations before adding that constraint. You may use
/// CheckIfSatisfiesExistenceRule function for that.
/// This is needed to ensure logical correctness of transactions.
/// Once created, the client uses method CheckIfSatisfies to check that updated
/// vertex doesn't violate any of the existing constraints. If it does, that
/// update must be reverted.
class ExistenceConstraints {
public:
ExistenceConstraints() = default;
ExistenceConstraints(const ExistenceConstraints &) = delete;
ExistenceConstraints(ExistenceConstraints &&) = delete;
ExistenceConstraints &operator=(const ExistenceConstraints &) = delete;
ExistenceConstraints &operator=(ExistenceConstraints &&) = delete;
/// Adds new constraint, if the constraint already exists this method does
/// nothing. This method doesn't check if any of the existing vertices breaks
/// this constraint. Caller must do that instead. Caller must also ensure
/// that no other transaction is running in parallel.
///
/// @returns true if constraint was added, false otherwise
bool AddConstraint(const ExistenceRule &rule);
/// Removes existing constraint, if the constraint doesn't exist this method
/// does nothing. Caller must ensure that no other transaction is running in
/// parallel.
///
/// @returns true if constraint was removed, false otherwise
bool RemoveConstraint(const ExistenceRule &rule);
/// Checks whether given constraint is visible.
bool Exists(const ExistenceRule &rule) const;
/// Check if add label update satisfies all visible constraints.
/// @return true if all constraints are satisfied, false otherwise
bool CheckOnAddLabel(const Vertex *vertex, storage::Label label) const;
/// Check if remove property update satisfies all visible constraints.
/// @return true if all constraints are satisfied, false otherwise
bool CheckOnRemoveProperty(const Vertex *vertex,
storage::Property property) const;
/// Returns list of all constraints.
const std::vector<ExistenceRule> &ListConstraints() const;
private:
std::vector<ExistenceRule> constraints_;
};
}; // namespace storage::constraints

View File

@ -6,7 +6,6 @@
#include "data_structures/concurrent/concurrent_map.hpp"
#include "storage/common/kvstore/kvstore.hpp"
#include "storage/common/types/types.hpp"
#include "storage/single_node/constraints/existence_constraints.hpp"
#include "storage/single_node/constraints/unique_label_property_constraint.hpp"
#include "storage/single_node/edge.hpp"
#include "storage/single_node/indexes/key_index.hpp"
@ -77,9 +76,6 @@ class Storage {
KeyIndex<storage::Label, Vertex> labels_index_;
LabelPropertyIndex label_property_index_;
// existence constraints
storage::constraints::ExistenceConstraints existence_constraints_;
// unique constraints
storage::constraints::UniqueLabelPropertyConstraint
unique_label_property_constraints_;

View File

@ -18,9 +18,6 @@ function(add_benchmark test_cpp)
add_dependencies(memgraph__benchmark ${target_name})
endfunction(add_benchmark)
add_benchmark(existence_constraint.cpp)
target_link_libraries(${test_prefix}existence_constraint mg-single-node kvstore_dummy_lib)
add_benchmark(data_structures/concurrent/map_mix_concurrent.cpp)
target_link_libraries(${test_prefix}map_mix_concurrent mg-single-node kvstore_dummy_lib)

View File

@ -1,133 +0,0 @@
#include <benchmark/benchmark.h>
#include <benchmark/benchmark_api.h>
#include <glog/logging.h>
#include <random>
#include "database/single_node/graph_db.hpp"
#include "database/single_node/graph_db_accessor.hpp"
const constexpr size_t kNumOfConstraints = 10;
const constexpr size_t kNumOfPropsInConstraint = 5;
const constexpr size_t kNumOfProps = 100;
const constexpr size_t kNumOfLabels = 10;
const constexpr size_t kNumOfVertices = 10000;
struct TestSet {
public:
TestSet() {
constraint_labels.reserve(kNumOfConstraints);
for (size_t i = 0; i < kNumOfConstraints; ++i) {
constraint_labels.emplace_back("clabel_" + std::to_string(i));
}
constraint_props.reserve(kNumOfConstraints * kNumOfPropsInConstraint);
for (size_t i = 0; i < kNumOfConstraints * kNumOfPropsInConstraint; ++i) {
constraint_props.emplace_back("cprop_" + std::to_string(i));
}
labels.reserve(kNumOfLabels);
for (size_t i = 0; i < kNumOfLabels; ++i) {
labels.emplace_back("label_" + std::to_string(i));
}
props.reserve(kNumOfProps);
for (size_t i = 0; i < kNumOfProps; ++i) {
props.emplace_back("prop_" + std::to_string(i));
}
}
std::vector<std::string> constraint_labels;
std::vector<std::string> constraint_props;
std::vector<std::string> labels;
std::vector<std::string> props;
long short_prop_value{0};
std::string long_prop_value{20, ' '};
};
void Run(benchmark::State &state, bool enable_constraint) {
while (state.KeepRunning()) {
database::GraphDb db;
TestSet test_set;
// CreateExistence constraints
if (enable_constraint) {
state.PauseTiming();
for (size_t i = 0; i < kNumOfConstraints; ++i) {
auto dba = db.Access();
auto label = dba.Label(test_set.constraint_labels.at(i));
std::vector<storage::Property> props;
props.reserve(kNumOfPropsInConstraint);
for (size_t j = 0; j < kNumOfPropsInConstraint; ++j) {
props.push_back(dba.Property(
test_set.constraint_props.at(kNumOfPropsInConstraint * i + j)));
}
dba.BuildExistenceConstraint(label, props);
dba.Commit();
}
state.ResumeTiming();
}
// Add all verices and also add labels and properties
std::vector<gid::Gid> vertices;
vertices.reserve(kNumOfVertices);
for (size_t k = 0; k < kNumOfVertices; ++k) {
auto dba = db.Access();
auto v = dba.InsertVertex();
vertices.push_back(v.gid());
// Labels and properties that define constraints
for (size_t i = 0; i < kNumOfConstraints; ++i) {
for (size_t j = 0; j < kNumOfPropsInConstraint; ++j) {
v.PropsSet(dba.Property(test_set.constraint_props.at(
kNumOfPropsInConstraint * i + j)),
test_set.short_prop_value);
}
auto label = dba.Label(test_set.constraint_labels.at(i));
v.add_label(label);
}
// Add other labels
for (auto label : test_set.labels) {
v.add_label(dba.Label(label));
}
// Add other properties
for (auto prop : test_set.props) {
v.PropsSet(dba.Property(prop), test_set.short_prop_value);
}
dba.Commit();
}
// Delete all properties and labels
for (auto gid : vertices) {
auto dba = db.Access();
auto v = dba.FindVertex(gid, false);
std::vector<storage::Label> labels_to_del(v.labels());
for (auto &label : labels_to_del) {
v.remove_label(label);
}
v.PropsClear();
}
// Delete all vertices
for (auto gid : vertices) {
auto dba = db.Access();
auto v = dba.FindVertex(gid, false);
dba.RemoveVertex(v);
dba.Commit();
}
}
}
BENCHMARK_CAPTURE(Run, WithoutConstraint, false)->Unit(benchmark::kMillisecond);
BENCHMARK_CAPTURE(Run, WithConstraint, true)->Unit(benchmark::kMillisecond);
BENCHMARK_MAIN();

View File

@ -20,7 +20,7 @@ int main(int argc, char *argv[]) {
// This makes sure we update the explorer when we bump the snapshot version.
// Snapshot layout is described in durability/version.hpp
static_assert(durability::kVersion == 9,
static_assert(durability::kVersion == 10,
"Wrong snapshot version, please update!");
fs::path snapshot_path(FLAGS_snapshot_file);
@ -76,32 +76,6 @@ int main(int argc, char *argv[]) {
<< property.ValueString();
}
decoder.ReadValue(&dv, Value::Type::List);
auto existence_constraint = dv.ValueList();
for (auto it = existence_constraint.begin(); it != existence_constraint.end();) {
std::string log("Adding existence constraint: ");
CHECK(it->IsString()) << "Label is not a string!";
log.append(it->ValueString());
log.append(" -> [");
++it;
CHECK(it->IsInt()) << "Number of properties is not an int!";
int64_t prop_size = it->ValueInt();
++it;
for (size_t i = 0; i < prop_size; ++i) {
CHECK(it->IsString()) << "Property is not a string!";
log.append(it->ValueString());
if (i != prop_size -1) {
log.append(", ");
} else {
log.append("]");
}
++it;
}
LOG(INFO) << log;
}
decoder.ReadValue(&dv, Value::Type::List);
auto unique_constraint = dv.ValueList();
for (auto it = unique_constraint.begin(); it != unique_constraint.end();) {

View File

@ -45,10 +45,6 @@ std::string StateDeltaTypeToString(database::StateDelta::Type type) {
return "BUILD_INDEX";
case database::StateDelta::Type::DROP_INDEX:
return "DROP_INDEX";
case database::StateDelta::Type::BUILD_EXISTENCE_CONSTRAINT:
return "BUILD_EXISTENCE_CONSTRAINT";
case database::StateDelta::Type::DROP_EXISTENCE_CONSTRAINT:
return "DROP_EXISTENCE_CONSTRAINT";
case database::StateDelta::Type::BUILD_UNIQUE_CONSTRAINT:
return "BUILD_UNIQUE_CONSTRAINT";
case database::StateDelta::Type::DROP_UNIQUE_CONSTRAINT:

View File

@ -133,9 +133,6 @@ target_link_libraries(${test_prefix}edges_distributed mg-distributed kvstore_dum
add_unit_test(edges_single_node.cpp)
target_link_libraries(${test_prefix}edges_single_node mg-single-node kvstore_dummy_lib)
add_unit_test(existence_constraints.cpp)
target_link_libraries(${test_prefix}existence_constraints mg-single-node kvstore_dummy_lib)
add_unit_test(gid.cpp)
target_link_libraries(${test_prefix}gid mg-distributed kvstore_dummy_lib)

View File

@ -166,36 +166,6 @@ class DbGenerator {
}
};
bool CompareExistenceConstraints(const database::GraphDbAccessor &dba1,
const database::GraphDbAccessor &dba2) {
auto c1 = dba1.ListExistenceConstraints();
auto c2 = dba2.ListExistenceConstraints();
auto compare_prop = [](auto &dba1, auto &p1, auto &dba2, auto &p2) {
std::vector<std::string> p1_names;
p1_names.reserve(p1.size());
std::vector<std::string> p2_names;
p2_names.reserve(p2.size());
std::transform(p1.begin(), p1.end(), std::back_inserter(p1_names),
[&dba1](auto p) { return dba1.PropertyName(p); });
std::transform(p2.begin(), p2.end(), std::back_inserter(p2_names),
[&dba2](auto p) { return dba2.PropertyName(p); });
return p1_names.size() == p2_names.size() &&
std::is_permutation(p1_names.begin(), p1_names.end(),
p2_names.begin());
};
return c1.size() == c2.size() &&
std::is_permutation(
c1.begin(), c1.end(), c2.begin(),
[&dba1, &dba2, &compare_prop](auto &r1, auto &r2) {
return dba1.LabelName(r1.label) == dba2.LabelName(r2.label) &&
compare_prop(dba1, r1.properties, dba2, r2.properties);
});
}
bool CompareUniqueConstraints(const database::GraphDbAccessor &dba1,
const database::GraphDbAccessor &dba2) {
auto c1 = dba1.ListUniqueLabelPropertyConstraints();
@ -226,7 +196,6 @@ void CompareDbs(database::GraphDb &a, database::GraphDb &b) {
<< "Indexes not equal [" << utils::Join(index_a, ", ") << "] != ["
<< utils::Join(index_b, ", ");
}
EXPECT_TRUE(CompareExistenceConstraints(dba_a, dba_b));
EXPECT_TRUE(CompareUniqueConstraints(dba_a, dba_b));
auto is_permutation_props = [&dba_a, &dba_b](const auto &p1_id,
@ -532,11 +501,6 @@ TEST_F(Durability, SnapshotEncoding) {
EXPECT_EQ(dv.ValueList()[1].ValueString(), "p1");
EXPECT_EQ(dv.ValueList()[2].ValueBool(), false);
// Existence constraints
decoder.ReadValue(&dv);
ASSERT_TRUE(dv.IsList());
ASSERT_EQ(dv.ValueList().size(), 0);
// Unique constraints
decoder.ReadValue(&dv);
ASSERT_TRUE(dv.IsList());
@ -1041,81 +1005,6 @@ TEST_F(Durability, UniqueIndexRecoveryWal) {
}
}
TEST_F(Durability, ExistenceConstraintRecoveryWal) {
auto config = DbConfig(true, false);
database::GraphDb db{config};
{
// Fill database with some data
auto dba = db.Access();
DbGenerator gen(dba);
dba.InsertVertex();
gen.InsertVertex();
gen.InsertVertex();
auto l1 = dba.Label("l1");
std::vector<storage::Property> p1{dba.Property("p1"), dba.Property("p2")};
dba.BuildExistenceConstraint(l1, p1);
gen.InsertEdge();
auto l2 = dba.Label("l2");
std::vector<storage::Property> p2{dba.Property("p3"), dba.Property("p4")};
dba.BuildExistenceConstraint(l2, p2);
gen.InsertVertex();
gen.InsertEdge();
dba.DeleteExistenceConstraint(l1, p1);
dba.Commit();
}
{
// Recover and compare
db.wal().Flush();
auto recovered_config = DbConfig(false, true);
database::GraphDb recovered_db{recovered_config};
CompareDbs(db, recovered_db);
}
}
TEST_F(Durability, ExistenceConstraintRecoverySnapshotAndWal) {
auto config = DbConfig(true, false);
database::GraphDb db{config};
{
// Fill database with some data
auto dba = db.Access();
DbGenerator gen(dba);
dba.InsertVertex();
gen.InsertVertex();
gen.InsertVertex();
auto l1 = dba.Label("l1");
std::vector<storage::Property> p1{dba.Property("p1"), dba.Property("p2")};
dba.BuildExistenceConstraint(l1, p1);
gen.InsertEdge();
auto l2 = dba.Label("l2");
std::vector<storage::Property> p2{dba.Property("p3"), dba.Property("p4")};
dba.BuildExistenceConstraint(l2, p2);
gen.InsertVertex();
gen.InsertEdge();
dba.DeleteExistenceConstraint(l1, p1);
dba.Commit();
}
// create snapshot with build existence constraint
MakeSnapshot(db);
{
auto dba = db.Access();
DbGenerator gen(dba);
gen.InsertVertex();
gen.InsertVertex();
auto l3 = dba.Label("l3");
std::vector<storage::Property> p3{dba.Property("p5"), dba.Property("p6")};
dba.BuildExistenceConstraint(l3, p3);
dba.Commit();
}
{
// Recover and compare
db.wal().Flush();
auto recovered_config = DbConfig(false, true);
database::GraphDb recovered_db{recovered_config};
CompareDbs(db, recovered_db);
}
}
TEST_F(Durability, UniqueConstraintRecoveryWal) {
auto config = DbConfig(true, false);
database::GraphDb db{config};

View File

@ -1,141 +0,0 @@
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <gtest/gtest.h>
#include "database/single_node/graph_db.hpp"
#include "database/single_node/graph_db_accessor.hpp"
class ExistenceConstraintsTest : public ::testing::Test {
public:
void SetUp() override {
auto dba = db_.Access();
label_ = dba.Label("label");
property_ = dba.Property("property");
properties_ = {property_};
rule_ = {label_, properties_};
dba.Commit();
}
storage::constraints::ExistenceConstraints constraints_;
database::GraphDb db_;
storage::Label label_;
storage::Property property_;
std::vector<storage::Property> properties_;
storage::constraints::ExistenceRule rule_;
};
TEST_F(ExistenceConstraintsTest, BuildDrop) {
{
auto dba = db_.Access();
EXPECT_FALSE(dba.ExistenceConstraintExists(label_, properties_));
dba.Commit();
}
{
auto dba = db_.Access();
dba.BuildExistenceConstraint(label_, properties_);
EXPECT_TRUE(dba.ExistenceConstraintExists(label_, properties_));
dba.Commit();
}
{
auto dba = db_.Access();
dba.DeleteExistenceConstraint(label_, properties_);
EXPECT_FALSE(dba.ExistenceConstraintExists(label_, properties_));
dba.Commit();
}
}
TEST_F(ExistenceConstraintsTest, BuildWithViolation) {
{
auto dba = db_.Access();
auto v = dba.InsertVertex();
v.add_label(label_);
dba.Commit();
}
{
auto dba = db_.Access();
EXPECT_THROW(dba.BuildExistenceConstraint(label_, properties_),
database::IndexConstraintViolationException);
}
}
TEST_F(ExistenceConstraintsTest, InsertFail) {
{
auto dba = db_.Access();
dba.BuildExistenceConstraint(label_, properties_);
dba.Commit();
}
{
auto dba = db_.Access();
auto v = dba.InsertVertex();
EXPECT_THROW(v.add_label(label_),
database::IndexConstraintViolationException);
}
}
TEST_F(ExistenceConstraintsTest, InsertPass) {
{
auto dba = db_.Access();
dba.BuildExistenceConstraint(label_, properties_);
dba.Commit();
}
{
auto dba = db_.Access();
auto v = dba.InsertVertex();
v.PropsSet(property_, PropertyValue("Something"));
v.add_label(label_);
dba.Commit();
}
}
TEST_F(ExistenceConstraintsTest, RemoveFail) {
{
auto dba = db_.Access();
dba.BuildExistenceConstraint(label_, properties_);
dba.Commit();
}
gid::Gid gid;
{
auto dba = db_.Access();
auto v = dba.InsertVertex();
v.PropsSet(property_, PropertyValue("Something"));
v.add_label(label_);
gid = v.gid();
dba.Commit();
}
{
auto dba = db_.Access();
auto v = dba.FindVertex(gid, false);
EXPECT_THROW(v.PropsErase(property_),
database::IndexConstraintViolationException);
}
}
TEST_F(ExistenceConstraintsTest, RemovePass) {
{
auto dba = db_.Access();
dba.BuildExistenceConstraint(label_, properties_);
dba.Commit();
}
gid::Gid gid;
{
auto dba = db_.Access();
auto v = dba.InsertVertex();
v.PropsSet(property_, PropertyValue("Something"));
v.add_label(label_);
gid = v.gid();
dba.Commit();
}
{
auto dba = db_.Access();
auto v = dba.FindVertex(gid, false);
v.remove_label(label_);
v.PropsErase(property_);
dba.Commit();
}
}
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -20,7 +20,7 @@
#include "utils/timer.hpp"
// Snapshot layout is described in durability/version.hpp
static_assert(durability::kVersion == 9,
static_assert(durability::kVersion == 10,
"Wrong snapshot version, please update!");
bool ValidateNotEmpty(const char *flagname, const std::string &value) {
@ -424,7 +424,6 @@ void Convert(const std::vector<std::string> &nodes,
encoder.WriteInt(0); // Id of transaction that is snapshooting.
encoder.WriteList({}); // Transactional snapshot.
encoder.WriteList({}); // Label + property indexes.
encoder.WriteList({}); // Existence constraints
encoder.WriteList({}); // Unique constraints
// PassNodes streams vertices to the encoder.
for (const auto &nodes_file : nodes) {