75950664a7
Summary: This diff splits single node and distributed storage from each other. Currently all of the storage code is copied into two directories (one single node, one distributed). The logic used in the storage implementation isn't touched, it will be refactored in following diffs. To clean the working directory after this diff you should execute: ``` rm database/state_delta.capnp rm database/state_delta.hpp rm storage/concurrent_id_mapper_rpc_messages.capnp rm storage/concurrent_id_mapper_rpc_messages.hpp ``` Reviewers: teon.banek, buda, msantl Reviewed By: teon.banek, msantl Subscribers: teon.banek, pullbot Differential Revision: https://phabricator.memgraph.io/D1625
399 lines
12 KiB
C++
399 lines
12 KiB
C++
#include <experimental/optional>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "database/graph_db.hpp"
|
|
#include "database/graph_db_accessor.hpp"
|
|
#include "storage/common/types.hpp"
|
|
#include "storage/edge_accessor.hpp"
|
|
#include "storage/vertex_accessor.hpp"
|
|
|
|
using namespace database;
|
|
using namespace storage;
|
|
|
|
template <typename TIterable>
|
|
auto Count(TIterable iterable) {
|
|
return std::distance(iterable.begin(), iterable.end());
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, InsertVertex) {
|
|
SingleNode db;
|
|
auto accessor = db.Access();
|
|
gid::Generator generator(0);
|
|
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
|
|
|
EXPECT_EQ(accessor->InsertVertex().gid(), generator.Next());
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
|
EXPECT_EQ(Count(accessor->Vertices(true)), 1);
|
|
accessor->AdvanceCommand();
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 1);
|
|
|
|
EXPECT_EQ(accessor->InsertVertex().gid(), generator.Next());
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 1);
|
|
EXPECT_EQ(Count(accessor->Vertices(true)), 2);
|
|
accessor->AdvanceCommand();
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 2);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, UniqueVertexId) {
|
|
SingleNode db;
|
|
SkipList<int64_t> ids;
|
|
|
|
std::vector<std::thread> threads;
|
|
for (int i = 0; i < 50; i++) {
|
|
threads.emplace_back([&db, &ids]() {
|
|
auto dba = db.Access();
|
|
auto access = ids.access();
|
|
for (int i = 0; i < 200; i++) access.insert(dba->InsertVertex().gid());
|
|
});
|
|
}
|
|
|
|
for (auto &thread : threads) thread.join();
|
|
EXPECT_EQ(ids.access().size(), 50 * 200);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, RemoveVertexSameTransaction) {
|
|
SingleNode db;
|
|
auto accessor = db.Access();
|
|
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
|
|
|
auto va1 = accessor->InsertVertex();
|
|
accessor->AdvanceCommand();
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 1);
|
|
|
|
EXPECT_TRUE(accessor->RemoveVertex(va1));
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 1);
|
|
EXPECT_EQ(Count(accessor->Vertices(true)), 0);
|
|
accessor->AdvanceCommand();
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
|
EXPECT_EQ(Count(accessor->Vertices(true)), 0);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, RemoveVertexDifferentTransaction) {
|
|
SingleNode db;
|
|
// first transaction creates a vertex
|
|
{
|
|
auto accessor = db.Access();
|
|
accessor->InsertVertex();
|
|
accessor->Commit();
|
|
}
|
|
// second transaction checks that it sees it, and deletes it
|
|
{
|
|
auto accessor = db.Access();
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 1);
|
|
EXPECT_EQ(Count(accessor->Vertices(true)), 1);
|
|
for (auto vertex_accessor : accessor->Vertices(false))
|
|
accessor->RemoveVertex(vertex_accessor);
|
|
accessor->Commit();
|
|
}
|
|
// third transaction checks that it does not see the vertex
|
|
{
|
|
auto accessor = db.Access();
|
|
EXPECT_EQ(Count(accessor->Vertices(false)), 0);
|
|
EXPECT_EQ(Count(accessor->Vertices(true)), 0);
|
|
}
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, InsertEdge) {
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
auto va1 = dba->InsertVertex();
|
|
auto va2 = dba->InsertVertex();
|
|
dba->AdvanceCommand();
|
|
EXPECT_EQ(va1.in_degree(), 0);
|
|
EXPECT_EQ(va1.out_degree(), 0);
|
|
EXPECT_EQ(va2.in_degree(), 0);
|
|
EXPECT_EQ(va2.out_degree(), 0);
|
|
|
|
// setup (v1) - [:likes] -> (v2)
|
|
dba->InsertEdge(va1, va2, dba->EdgeType("likes"));
|
|
EXPECT_EQ(Count(dba->Edges(false)), 0);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 1);
|
|
dba->AdvanceCommand();
|
|
EXPECT_EQ(Count(dba->Edges(false)), 1);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 1);
|
|
EXPECT_EQ(va1.out().begin()->to(), va2);
|
|
EXPECT_EQ(va2.in().begin()->from(), va1);
|
|
EXPECT_EQ(va1.in_degree(), 0);
|
|
EXPECT_EQ(va1.out_degree(), 1);
|
|
EXPECT_EQ(va2.in_degree(), 1);
|
|
EXPECT_EQ(va2.out_degree(), 0);
|
|
|
|
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
|
|
auto va3 = dba->InsertVertex();
|
|
dba->InsertEdge(va3, va2, dba->EdgeType("hates"));
|
|
EXPECT_EQ(Count(dba->Edges(false)), 1);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 2);
|
|
dba->AdvanceCommand();
|
|
EXPECT_EQ(Count(dba->Edges(false)), 2);
|
|
EXPECT_EQ(va3.out().begin()->to(), va2);
|
|
EXPECT_EQ(va1.in_degree(), 0);
|
|
EXPECT_EQ(va1.out_degree(), 1);
|
|
EXPECT_EQ(va2.in_degree(), 2);
|
|
EXPECT_EQ(va2.out_degree(), 0);
|
|
EXPECT_EQ(va3.in_degree(), 0);
|
|
EXPECT_EQ(va3.out_degree(), 1);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, UniqueEdgeId) {
|
|
SingleNode db;
|
|
SkipList<int64_t> ids;
|
|
|
|
std::vector<std::thread> threads;
|
|
for (int i = 0; i < 50; i++) {
|
|
threads.emplace_back([&db, &ids]() {
|
|
auto dba = db.Access();
|
|
auto v1 = dba->InsertVertex();
|
|
auto v2 = dba->InsertVertex();
|
|
auto edge_type = dba->EdgeType("edge_type");
|
|
auto access = ids.access();
|
|
for (int i = 0; i < 200; i++)
|
|
access.insert(dba->InsertEdge(v1, v2, edge_type).gid());
|
|
});
|
|
}
|
|
|
|
for (auto &thread : threads) thread.join();
|
|
EXPECT_EQ(ids.access().size(), 50 * 200);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, RemoveEdge) {
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
|
|
auto va1 = dba->InsertVertex();
|
|
auto va2 = dba->InsertVertex();
|
|
auto va3 = dba->InsertVertex();
|
|
dba->InsertEdge(va1, va2, dba->EdgeType("likes"));
|
|
dba->InsertEdge(va3, va2, dba->EdgeType("hates"));
|
|
dba->AdvanceCommand();
|
|
EXPECT_EQ(Count(dba->Edges(false)), 2);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 2);
|
|
|
|
// remove all [:hates] edges
|
|
for (auto edge : dba->Edges(false))
|
|
if (edge.EdgeType() == dba->EdgeType("hates")) dba->RemoveEdge(edge);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 2);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 1);
|
|
|
|
// current state: (v1) - [:likes] -> (v2), (v3)
|
|
dba->AdvanceCommand();
|
|
EXPECT_EQ(Count(dba->Edges(false)), 1);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 1);
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 3);
|
|
EXPECT_EQ(Count(dba->Vertices(true)), 3);
|
|
for (auto edge : dba->Edges(false)) {
|
|
EXPECT_EQ(edge.EdgeType(), dba->EdgeType("likes"));
|
|
auto v1 = edge.from();
|
|
auto v2 = edge.to();
|
|
|
|
// ensure correct connectivity for all the vertices
|
|
for (auto vertex : dba->Vertices(false)) {
|
|
if (vertex == v1) {
|
|
EXPECT_EQ(vertex.in_degree(), 0);
|
|
EXPECT_EQ(vertex.out_degree(), 1);
|
|
} else if (vertex == v2) {
|
|
EXPECT_EQ(vertex.in_degree(), 1);
|
|
EXPECT_EQ(vertex.out_degree(), 0);
|
|
} else {
|
|
EXPECT_EQ(vertex.in_degree(), 0);
|
|
EXPECT_EQ(vertex.out_degree(), 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, DetachRemoveVertex) {
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
// setup (v0)- []->(v1)<-[]-(v2)<-[]-(v3)
|
|
std::vector<VertexAccessor> vertices;
|
|
for (int i = 0; i < 4; ++i) vertices.emplace_back(dba->InsertVertex());
|
|
|
|
auto edge_type = dba->EdgeType("type");
|
|
dba->InsertEdge(vertices[0], vertices[1], edge_type);
|
|
dba->InsertEdge(vertices[2], vertices[1], edge_type);
|
|
dba->InsertEdge(vertices[3], vertices[2], edge_type);
|
|
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
|
|
// ensure that plain remove does NOT work
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 4);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 3);
|
|
EXPECT_FALSE(dba->RemoveVertex(vertices[0]));
|
|
EXPECT_FALSE(dba->RemoveVertex(vertices[1]));
|
|
EXPECT_FALSE(dba->RemoveVertex(vertices[2]));
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 4);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 3);
|
|
|
|
dba->DetachRemoveVertex(vertices[2]);
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 4);
|
|
EXPECT_EQ(Count(dba->Vertices(true)), 3);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 3);
|
|
EXPECT_EQ(Count(dba->Edges(true)), 1);
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 3);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 1);
|
|
EXPECT_TRUE(dba->RemoveVertex(vertices[3]));
|
|
EXPECT_EQ(Count(dba->Vertices(true)), 2);
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 3);
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 2);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 1);
|
|
for (auto va : dba->Vertices(false)) EXPECT_FALSE(dba->RemoveVertex(va));
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 2);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 1);
|
|
for (auto va : dba->Vertices(false)) {
|
|
EXPECT_FALSE(dba->RemoveVertex(va));
|
|
dba->DetachRemoveVertex(va);
|
|
break;
|
|
}
|
|
EXPECT_EQ(Count(dba->Vertices(true)), 1);
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 2);
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 1);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 0);
|
|
|
|
// remove the last vertex, it has no connections
|
|
// so that should work
|
|
for (auto va : dba->Vertices(false)) EXPECT_TRUE(dba->RemoveVertex(va));
|
|
dba->AdvanceCommand();
|
|
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 0);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 0);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, DetachRemoveVertexMultiple) {
|
|
// This test checks that we can detach remove the
|
|
// same vertex / edge multiple times
|
|
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
// setup: make a fully connected N graph
|
|
// with cycles too!
|
|
int N = 7;
|
|
std::vector<VertexAccessor> vertices;
|
|
auto edge_type = dba->EdgeType("edge");
|
|
for (int i = 0; i < N; ++i) vertices.emplace_back(dba->InsertVertex());
|
|
for (int j = 0; j < N; ++j)
|
|
for (int k = 0; k < N; ++k)
|
|
dba->InsertEdge(vertices[j], vertices[k], edge_type);
|
|
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
|
|
EXPECT_EQ(Count(dba->Vertices(false)), N);
|
|
EXPECT_EQ(Count(dba->Edges(false)), N * N);
|
|
|
|
// detach delete one edge
|
|
dba->DetachRemoveVertex(vertices[0]);
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
EXPECT_EQ(Count(dba->Vertices(false)), N - 1);
|
|
EXPECT_EQ(Count(dba->Edges(false)), (N - 1) * (N - 1));
|
|
|
|
// detach delete two neighboring edges
|
|
dba->DetachRemoveVertex(vertices[1]);
|
|
dba->DetachRemoveVertex(vertices[2]);
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
EXPECT_EQ(Count(dba->Vertices(false)), N - 3);
|
|
EXPECT_EQ(Count(dba->Edges(false)), (N - 3) * (N - 3));
|
|
|
|
// detach delete everything, buwahahahaha
|
|
for (int l = 3; l < N; ++l) dba->DetachRemoveVertex(vertices[l]);
|
|
dba->AdvanceCommand();
|
|
for (auto &vertex : vertices) vertex.Reconstruct();
|
|
EXPECT_EQ(Count(dba->Vertices(false)), 0);
|
|
EXPECT_EQ(Count(dba->Edges(false)), 0);
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, Labels) {
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
Label label_friend = dba->Label("friend");
|
|
EXPECT_EQ(label_friend, dba->Label("friend"));
|
|
EXPECT_NE(label_friend, dba->Label("friend2"));
|
|
EXPECT_EQ(dba->LabelName(label_friend), "friend");
|
|
|
|
// test that getting labels through a different accessor works
|
|
EXPECT_EQ(label_friend, db.Access()->Label("friend"));
|
|
EXPECT_NE(label_friend, db.Access()->Label("friend2"));
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, EdgeTypes) {
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
EdgeType edge_type = dba->EdgeType("likes");
|
|
EXPECT_EQ(edge_type, dba->EdgeType("likes"));
|
|
EXPECT_NE(edge_type, dba->EdgeType("hates"));
|
|
EXPECT_EQ(dba->EdgeTypeName(edge_type), "likes");
|
|
|
|
// test that getting labels through a different accessor works
|
|
EXPECT_EQ(edge_type, db.Access()->EdgeType("likes"));
|
|
EXPECT_NE(edge_type, db.Access()->EdgeType("hates"));
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, Properties) {
|
|
SingleNode db;
|
|
auto dba = db.Access();
|
|
|
|
Property prop = dba->Property("name");
|
|
EXPECT_EQ(prop, dba->Property("name"));
|
|
EXPECT_NE(prop, dba->Property("surname"));
|
|
EXPECT_EQ(dba->PropertyName(prop), "name");
|
|
|
|
// test that getting labels through a different accessor works
|
|
EXPECT_EQ(prop, db.Access()->Property("name"));
|
|
EXPECT_NE(prop, db.Access()->Property("surname"));
|
|
}
|
|
|
|
TEST(GraphDbAccessorTest, Transfer) {
|
|
SingleNode db;
|
|
|
|
auto dba1 = db.Access();
|
|
auto prop = dba1->Property("property");
|
|
VertexAccessor v1 = dba1->InsertVertex();
|
|
v1.PropsSet(prop, 1);
|
|
VertexAccessor v2 = dba1->InsertVertex();
|
|
v2.PropsSet(prop, 2);
|
|
EdgeAccessor e12 = dba1->InsertEdge(v1, v2, dba1->EdgeType("et"));
|
|
e12.PropsSet(prop, 12);
|
|
|
|
// make dba2 that has dba1 in it's snapshot, so data isn't visible
|
|
auto dba2 = db.Access();
|
|
EXPECT_EQ(dba2->Transfer(v1), std::experimental::nullopt);
|
|
EXPECT_EQ(dba2->Transfer(e12), std::experimental::nullopt);
|
|
|
|
// make dba3 that does not have dba1 in it's snapshot
|
|
dba1->Commit();
|
|
auto dba3 = db.Access();
|
|
// we can transfer accessors even though the GraphDbAccessor they
|
|
// belong to is not alive anymore
|
|
EXPECT_EQ(dba3->Transfer(v1)->PropsAt(prop).Value<int64_t>(), 1);
|
|
EXPECT_EQ(dba3->Transfer(e12)->PropsAt(prop).Value<int64_t>(), 12);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
// ::testing::GTEST_FLAG(filter) = "*.DetachRemoveVertex";
|
|
return RUN_ALL_TESTS();
|
|
}
|