Fix unique constraint recovery

Summary:
See https://app.asana.com/0/743890251333732/888297761596047/f for more details.

# BEFORE
```
Note: Google Test filter = *UniqueConstraintRecovery*
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Durability
[ RUN       ] Durability.UniqueConstraintRecovery
unknown file: Failure
C++ exception with description "Index couldn't be created due to constraint
violation!" thrown in the test body.
[  FAILED   ] Durability.UniqueConstraintRecovery (3 ms)
[----------] 1 test from Durability (3 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (3 ms total)
[  PASSED   ] 0 tests.
[  FAILED   ] 1 test, listed below:
[  FAILED   ] Durability.UniqueConstraintRecovery

 1 FAILED TEST
 ```

# AFTER

 ```
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Durability
[ RUN       ] Durability.UniqueConstraintRecovery
[       OK  ] Durability.UniqueConstraintRecovery (4 ms)
[----------] 1 test from Durability (4 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (4 ms total)
[  PASSED   ] 1 test.
```

Reviewers: ipaljak, vkasljevic

Reviewed By: ipaljak

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1714
This commit is contained in:
Matija Santl 2018-10-31 14:03:33 +01:00
parent 0493470f98
commit 4f0a7df4bb
3 changed files with 155 additions and 18 deletions

View File

@ -402,17 +402,47 @@ void RecoverWal(const fs::path &durability_dir, database::GraphDb *db,
case database::StateDelta::Type::TRANSACTION_COMMIT:
transactions->Commit(delta.transaction_id);
break;
case database::StateDelta::Type::BUILD_INDEX:
case database::StateDelta::Type::BUILD_INDEX: {
// TODO index building might still be problematic in HA
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ true, delta.unique});
auto drop_it = std::find_if(
recovery_data->indexes.begin(), recovery_data->indexes.end(),
[label = delta.label_name, property = delta.property_name](
const IndexRecoveryData &other) {
return other.label == label && other.property == property &&
other.create == false;
});
// If we already have a drop index in the recovery data, just erase
// the drop index action. Otherwise add the build index action.
if (drop_it != recovery_data->indexes.end()) {
recovery_data->indexes.erase(drop_it);
} else {
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ true, delta.unique});
}
break;
case database::StateDelta::Type::DROP_INDEX:
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ false});
}
case database::StateDelta::Type::DROP_INDEX: {
auto build_it = std::find_if(
recovery_data->indexes.begin(), recovery_data->indexes.end(),
[label = delta.label_name, property = delta.property_name](
const IndexRecoveryData &other) {
return other.label == label && other.property == property &&
other.create == true;
});
// If we already have a build index in the recovery data, just erase
// the build index action. Otherwise add the drop index action.
if (build_it != recovery_data->indexes.end()) {
recovery_data->indexes.erase(build_it);
} else {
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ false});
}
break;
}
default:
transactions->Apply(delta);
}

View File

@ -402,17 +402,47 @@ void RecoverWal(const fs::path &durability_dir, database::GraphDb *db,
case database::StateDelta::Type::TRANSACTION_COMMIT:
transactions->Commit(delta.transaction_id);
break;
case database::StateDelta::Type::BUILD_INDEX:
case database::StateDelta::Type::BUILD_INDEX: {
// TODO index building might still be problematic in HA
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ true, delta.unique});
auto drop_it = std::find_if(
recovery_data->indexes.begin(), recovery_data->indexes.end(),
[label = delta.label_name, property = delta.property_name](
const IndexRecoveryData &other) {
return other.label == label && other.property == property &&
other.create == false;
});
// If we already have a drop index in the recovery data, just erase
// the drop index action. Otherwise add the build index action.
if (drop_it != recovery_data->indexes.end()) {
recovery_data->indexes.erase(drop_it);
} else {
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ true, delta.unique});
}
break;
case database::StateDelta::Type::DROP_INDEX:
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ false});
}
case database::StateDelta::Type::DROP_INDEX: {
auto build_it = std::find_if(
recovery_data->indexes.begin(), recovery_data->indexes.end(),
[label = delta.label_name, property = delta.property_name](
const IndexRecoveryData &other) {
return other.label == label && other.property == property &&
other.create == true;
});
// If we already have a build index in the recovery data, just erase
// the build index action. Otherwise add the drop index action.
if (build_it != recovery_data->indexes.end()) {
recovery_data->indexes.erase(build_it);
} else {
recovery_data->indexes.emplace_back(
IndexRecoveryData{delta.label_name, delta.property_name,
/*create = */ false});
}
break;
}
default:
transactions->Apply(delta);
}

View File

@ -811,8 +811,9 @@ TEST_F(Durability, SequentialRecovery) {
return threads;
};
auto make_updates = [&run_updates, this](
database::GraphDb &db, bool snapshot_during, bool snapshot_after) {
auto make_updates = [&run_updates, this](database::GraphDb &db,
bool snapshot_during,
bool snapshot_after) {
std::atomic<bool> keep_running{true};
auto update_theads = run_updates(db, keep_running);
std::this_thread::sleep_for(25ms);
@ -907,6 +908,82 @@ TEST_F(Durability, MoveToBackupWal) {
ASSERT_TRUE(durability::ContainsDurabilityFiles(backup_dir_));
}
TEST_F(Durability, UniqueConstraintRecoverySnapshotAndWal) {
auto config = DbConfig();
config.durability_enabled = true;
database::GraphDb db{config};
{
auto dba = db.Access();
auto label = dba->Label("A");
auto property = dba->Property("x");
dba->BuildIndex(label, property, true);
auto v0 = dba->InsertVertex();
v0.add_label(label);
v0.PropsSet(property, 5);
dba->Commit();
}
// create snapshot with build index and vertex
MakeSnapshot(db);
{
auto dba = db.Access();
auto label = dba->Label("A");
auto property = dba->Property("x");
dba->DeleteIndex(label, property);
auto v0 = dba->InsertVertex();
v0.add_label(label);
v0.PropsSet(property, 5);
dba->Commit();
}
// create wal with drop index and vertex
db.wal().Flush();
{
auto recovered_config = DbConfig();
recovered_config.db_recover_on_startup = true;
database::GraphDb recovered{recovered_config};
CompareDbs(db, recovered);
}
}
TEST_F(Durability, UniqueConstraintRecoveryWal) {
auto config = DbConfig();
config.durability_enabled = true;
database::GraphDb db{config};
{
auto dba = db.Access();
auto label = dba->Label("A");
auto property = dba->Property("x");
dba->BuildIndex(label, property, true);
auto v0 = dba->InsertVertex();
v0.add_label(label);
v0.PropsSet(property, 5);
dba->DeleteIndex(label, property);
auto v1 = dba->InsertVertex();
v1.add_label(label);
v1.PropsSet(property, 5);
dba->Commit();
}
db.wal().Flush();
{
auto recovered_config = DbConfig();
recovered_config.db_recover_on_startup = true;
database::GraphDb recovered{recovered_config};
CompareDbs(db, recovered);
}
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
google::InitGoogleLogging(argv[0]);