memgraph/tests/unit/database_label_property_index.cpp
florijan 1e0ac8ab8f Write-ahead log
Summary:
My dear fellow Memgraphians. It's friday afternoon, and I am as ready to pop as WAL is to get reviewed...

What's done:
- Vertices and Edges have global IDs, stored in `VersionList`. Main storage is now a concurrent map ID->vlist_ptr.
- WriteAheadLog class added. It's based around buffering WAL::Op objects (elementraly DB changes) and periodically serializing and flusing them to disk.
- Snapshot recovery refactored, WAL recovery added. Snapshot format changed again to include necessary info.
- Durability testing completely reworked.

What's not done (and should be when we decide how):
- Old WAL file purging.
- Config refactor (naming and organization). Will do when we discuss what we want.
- Changelog and new feature documentation (both depending on the point above).
- Better error handling and recovery feedback. Currently it's all returning bools, which is not fine-grained enough (neither for errors nor partial successes, also EOF is reported as a failure at the moment).
- Moving the implementation of WAL stuff to .cpp where possible.
- Not sure if there are transactions being created outside of `GraphDbAccessor` and it's `BuildIndex`. Need to look into.
- True write-ahead logic (flag controlled): not committing a DB transaction if the WAL has not flushed it's data. We can discuss the gain/effort ratio for this feature.

Reviewers: buda, mislav.bradac, teon.banek, dgleich

Reviewed By: dgleich

Subscribers: mtomic, pullbot

Differential Revision: https://phabricator.memgraph.io/D958
2017-11-13 09:51:39 +01:00

200 lines
5.8 KiB
C++

#include "gtest/gtest.h"
#include "database/graph_db.hpp"
#include "database/graph_db_accessor.hpp"
#include "database/graph_db_datatypes.hpp"
#include "database/indexes/label_property_index.hpp"
#include "mvcc_gc_common.hpp"
class LabelPropertyIndexComplexTest : public ::testing::Test {
protected:
virtual void SetUp() {
GraphDbAccessor accessor(db_);
label = accessor.Label("label");
property = accessor.Property("property");
label2 = accessor.Label("label2");
property2 = accessor.Property("property2");
key = new LabelPropertyIndex::Key(label, property);
EXPECT_EQ(index.CreateIndex(*key), true);
index.IndexFinishedBuilding(*key);
t = engine.Begin();
vlist = new mvcc::VersionList<Vertex>(*t, 0);
engine.Advance(t->id_);
vertex = vlist->find(*t);
ASSERT_NE(vertex, nullptr);
vertex->labels_.push_back(label);
vertex->properties_.set(property, 0);
EXPECT_EQ(index.Count(*key), 0);
}
virtual void TearDown() {
delete key;
delete vlist;
}
public:
GraphDb db_;
LabelPropertyIndex index;
LabelPropertyIndex::Key *key;
tx::Engine engine;
tx::Transaction *t{nullptr};
mvcc::VersionList<Vertex> *vlist;
Vertex *vertex;
GraphDbTypes::Label label;
GraphDbTypes::Property property;
GraphDbTypes::Label label2;
GraphDbTypes::Property property2;
};
TEST(LabelPropertyIndex, CreateIndex) {
GraphDb db;
GraphDbAccessor accessor(db);
LabelPropertyIndex::Key key(accessor.Label("test"),
accessor.Property("test2"));
LabelPropertyIndex index;
EXPECT_EQ(index.CreateIndex(key), true);
EXPECT_EQ(index.CreateIndex(key), false);
}
TEST(LabelPropertyIndex, IndexExistance) {
GraphDb db;
GraphDbAccessor accessor(db);
LabelPropertyIndex::Key key(accessor.Label("test"),
accessor.Property("test2"));
LabelPropertyIndex index;
EXPECT_EQ(index.CreateIndex(key), true);
// Index doesn't exist - and can't be used untill it's been notified as built.
EXPECT_EQ(index.IndexExists(key), false);
index.IndexFinishedBuilding(key);
EXPECT_EQ(index.IndexExists(key), true);
}
TEST(LabelPropertyIndex, Count) {
GraphDb db;
GraphDbAccessor accessor(db);
auto label = accessor.Label("label");
auto property = accessor.Property("property");
LabelPropertyIndex::Key key(label, property);
LabelPropertyIndex index;
::testing::FLAGS_gtest_death_test_style = "threadsafe";
EXPECT_DEATH(index.Count(key), "Index doesn't exist.");
EXPECT_EQ(index.CreateIndex(key), true);
EXPECT_DEATH(index.Count(key), "Index not yet ready.");
index.IndexFinishedBuilding(key);
EXPECT_EQ(index.Count(key), 0);
}
// Add on label+property to index.
TEST_F(LabelPropertyIndexComplexTest, UpdateOnLabelPropertyTrue) {
index.UpdateOnLabelProperty(vlist, vertex);
EXPECT_EQ(index.Count(*key), 1);
}
// Try adding on label+property but fail because labels are clear.
TEST_F(LabelPropertyIndexComplexTest, UpdateOnLabelPropertyFalse) {
vertex->labels_.clear();
index.UpdateOnLabelProperty(vlist, vertex);
EXPECT_EQ(index.Count(*key), 0);
}
// Add on label to index.
TEST_F(LabelPropertyIndexComplexTest, UpdateOnLabelTrue) {
index.UpdateOnLabel(label, vlist, vertex);
EXPECT_EQ(index.Count(*key), 1);
}
// Try adding on label but fail because label is wrong.
TEST_F(LabelPropertyIndexComplexTest, UpdateOnLabelFalse) {
index.UpdateOnLabel(label2, vlist, vertex);
EXPECT_EQ(index.Count(*key), 0);
}
// Add on property to index.
TEST_F(LabelPropertyIndexComplexTest, UpdateOnPropertyTrue) {
index.UpdateOnProperty(property, vlist, vertex);
EXPECT_EQ(index.Count(*key), 1);
}
// Try adding on property but fail because property is wrong.
TEST_F(LabelPropertyIndexComplexTest, UpdateOnPropertyFalse) {
index.UpdateOnProperty(property2, vlist, vertex);
EXPECT_EQ(index.Count(*key), 0);
}
// Test index does it insert everything uniquely
TEST_F(LabelPropertyIndexComplexTest, UniqueInsert) {
index.UpdateOnLabelProperty(vlist, vertex);
index.UpdateOnLabelProperty(vlist, vertex);
EXPECT_EQ(index.Count(*key), 1);
}
// Check if index filters duplicates.
TEST_F(LabelPropertyIndexComplexTest, UniqueFilter) {
index.UpdateOnLabelProperty(vlist, vertex);
t->Commit();
auto t2 = engine.Begin();
auto vertex2 = vlist->update(*t2);
t2->Commit();
index.UpdateOnLabelProperty(vlist, vertex2);
EXPECT_EQ(index.Count(*key), 2);
auto t3 = engine.Begin();
auto iter = index.GetVlists(*key, *t3, false);
EXPECT_EQ(std::distance(iter.begin(), iter.end()), 1);
t3->Commit();
}
// Remove label and check if index vertex is not returned now.
TEST_F(LabelPropertyIndexComplexTest, RemoveLabel) {
index.UpdateOnLabelProperty(vlist, vertex);
auto iter1 = index.GetVlists(*key, *t, false);
EXPECT_EQ(std::distance(iter1.begin(), iter1.end()), 1);
vertex->labels_.clear();
auto iter2 = index.GetVlists(*key, *t, false);
EXPECT_EQ(std::distance(iter2.begin(), iter2.end()), 0);
}
// Remove property and check if vertex is not returned now.
TEST_F(LabelPropertyIndexComplexTest, RemoveProperty) {
index.UpdateOnLabelProperty(vlist, vertex);
auto iter1 = index.GetVlists(*key, *t, false);
EXPECT_EQ(std::distance(iter1.begin(), iter1.end()), 1);
vertex->properties_.clear();
auto iter2 = index.GetVlists(*key, *t, false);
EXPECT_EQ(std::distance(iter2.begin(), iter2.end()), 0);
}
// Refresh with a vertex that looses its labels and properties.
TEST_F(LabelPropertyIndexComplexTest, Refresh) {
index.UpdateOnLabelProperty(vlist, vertex);
t->Commit();
EXPECT_EQ(index.Count(*key), 1);
vertex->labels_.clear();
vertex->properties_.clear();
index.Refresh(GcSnapshot(engine, nullptr), engine);
auto iter = index.GetVlists(*key, *t, false);
EXPECT_EQ(std::distance(iter.begin(), iter.end()), 0);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}