memgraph/tests/e2e/replication/indices.cpp
Gareth Andrew Lloyd 3cc2bc2791
Refactor interpreter to support multiple distributed clocks (Part 1) (#1281)
* Interpreter transaction ID decoupled from storage transaction ID
* Transactional scope for indices, statistics and constraints
* Storage::Accessor now has 2 modes (unique and shared)
* Introduced ResourceLock to fix pthread mutex problems
* Split InfoQuery in two: non-transactional SystemInfoQuery and transactional DatabaseInfoQuery
* Replicable and durable statistics
* Bumped WAL/Snapshot versions
* Initial implementation of the Lamport clock

---------

Co-authored-by: Andreja Tonev <andreja.tonev@memgraph.io>
2023-10-05 16:58:39 +02:00

175 lines
6.5 KiB
C++

// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include <chrono>
#include <random>
#include <thread>
#include <unordered_set>
#include <fmt/format.h>
#include <gflags/gflags.h>
#include "common.hpp"
#include "utils/logging.hpp"
#include "utils/thread.hpp"
#include "utils/timer.hpp"
int main(int argc, char **argv) {
google::SetUsageMessage("Memgraph E2E Replication Indices Test");
gflags::ParseCommandLineFlags(&argc, &argv, true);
memgraph::logging::RedirectToStderr();
auto database_endpoints = mg::e2e::replication::ParseDatabaseEndpoints(FLAGS_database_endpoints);
mg::Client::Init();
auto check_index = [](const auto &data, size_t i, const auto &label, const std::string &property = "") {
const auto label_name = (*data)[i][1].ValueString();
const auto p_type = (*data)[i][2].type();
const auto property_name = p_type == mg::Value::Type::String ? (*data)[i][2].ValueString() : "";
if (label_name != label || property_name != property) {
LOG_FATAL("Invalid index.");
}
};
auto check_delete_stats = [](const auto &data, size_t i, const auto &label, const std::string &property = "") {
const auto label_name = (*data)[i][0].ValueString();
const auto p_type = (*data)[i][1].type();
const auto property_name = p_type == mg::Value::Type::String ? (*data)[i][1].ValueString() : "";
if (label_name != label || property_name != property) {
LOG_FATAL("Invalid stats.");
}
};
{
auto client = mg::e2e::replication::Connect(database_endpoints[0]);
client->Execute("MATCH (n) DETACH DELETE n;");
client->DiscardAll();
client->Execute("CREATE INDEX ON :Node;");
client->DiscardAll();
client->Execute("CREATE INDEX ON :Node(id);");
client->DiscardAll();
client->Execute("CREATE INDEX ON :Node(id2);");
client->DiscardAll();
client->Execute("CREATE INDEX ON :Node2;");
client->DiscardAll();
// Sleep a bit so the constraints get replicated.
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (const auto &database_endpoint : database_endpoints) {
auto client = mg::e2e::replication::Connect(database_endpoint);
client->Execute("SHOW INDEX INFO;");
if (const auto data = client->FetchAll()) {
if (data->size() != 4) {
LOG_FATAL("Missing indices!");
}
check_index(data, 0, "Node");
check_index(data, 1, "Node2");
check_index(data, 2, "Node", "id");
check_index(data, 3, "Node", "id2");
} else {
LOG_FATAL("Unable to get INDEX INFO from {}", database_endpoint);
}
}
spdlog::info("All indices are in-place.");
for (int i = 0; i < FLAGS_nodes; ++i) {
client->Execute("CREATE (:Node {id:" + std::to_string(i) + "});");
client->DiscardAll();
}
mg::e2e::replication::IntGenerator edge_generator("EdgeCreateGenerator", 0, FLAGS_nodes - 1);
for (int i = 0; i < FLAGS_edges; ++i) {
client->Execute("MATCH (n {id:" + std::to_string(edge_generator.Next()) +
"}), (m {id:" + std::to_string(edge_generator.Next()) + "}) CREATE (n)-[:Edge]->(m);");
client->DiscardAll();
}
client->Execute("ANALYZE GRAPH;");
client->DiscardAll();
}
{
for (int i = 1; i < database_endpoints.size(); ++i) {
auto &database_endpoint = database_endpoints[i];
auto client = mg::e2e::replication::Connect(database_endpoint);
try {
// Hacky way to determine if the statistics were replicated
client->Execute("ANALYZE GRAPH DELETE STATISTICS;");
const auto data = client->FetchAll();
if (data->size() != 4) {
LOG_FATAL("Not all statistics were replicated! Failed endpoint {}:{}", database_endpoint.address,
database_endpoint.port);
}
check_delete_stats(data, 0, "Node");
check_delete_stats(data, 1, "Node2");
check_delete_stats(data, 2, "Node", "id");
check_delete_stats(data, 3, "Node", "id2");
} catch (const std::exception &e) {
LOG_FATAL(e.what());
break;
}
}
}
{
auto client = mg::e2e::replication::Connect(database_endpoints[0]);
client->Execute("ANALYZE GRAPH;"); // Resend stats
client->DiscardAll();
client->Execute("ANALYZE GRAPH DELETE STATISTICS;");
client->DiscardAll();
// Sleep a bit so the drop constraints get replicated.
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (int i = 1; i < database_endpoints.size(); ++i) {
auto &database_endpoint = database_endpoints[i];
auto client = mg::e2e::replication::Connect(database_endpoints[i]);
client->Execute("ANALYZE GRAPH DELETE STATISTICS;");
if (const auto data = client->FetchAll()) {
// Hacky way to determine if the statistics were replicated
if (data->size() != 0) {
LOG_FATAL("Not all statistics were replicated! Failed endpoint {}:{}", database_endpoint.address,
database_endpoint.port);
}
} else {
LOG_FATAL("Unable to delete statistics from {}", database_endpoints[i]);
}
}
}
{
auto client = mg::e2e::replication::Connect(database_endpoints[0]);
client->Execute("DROP INDEX ON :Node;");
client->DiscardAll();
client->Execute("DROP INDEX ON :Node(id);");
client->DiscardAll();
client->Execute("DROP INDEX ON :Node(id2);");
client->DiscardAll();
client->Execute("DROP INDEX ON :Node2;");
client->DiscardAll();
// Sleep a bit so the constraints get replicated.
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (const auto &database_endpoint : database_endpoints) {
auto client = mg::e2e::replication::Connect(database_endpoint);
client->Execute("SHOW INDEX INFO;");
if (const auto data = client->FetchAll()) {
if (!data->empty()) {
LOG_FATAL("Undeleted indices!");
}
} else {
LOG_FATAL("Unable to get INDEX INFO from {}", database_endpoint);
}
}
spdlog::info("All indices have been deleted.");
}
return 0;
}