// Copyright 2024 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 #include #include "coordination/coordinator_instance.hpp" #include "dbms/constants.hpp" #include "replication_coordination_glue/common.hpp" #include "utils/functional.hpp" class CoordinationUtils : public ::testing::Test { protected: void SetUp() override {} void TearDown() override {} std::filesystem::path test_folder_{std::filesystem::temp_directory_path() / "MG_tests_unit_coordination"}; }; TEST_F(CoordinationUtils, MemgraphDbHistorySimple) { // Choose any if everything is same // X = dead // Main : A(24) B(36) C(48) D(50) E(51) X // replica 1: A(24) B(36) C(48) D(50) E(51) // replica 2: A(24) B(36) C(48) D(50) E(51) // replica 3: A(24) B(36) C(48) D(50) E(51) std::vector> instance_database_histories; std::vector> histories; histories.emplace_back(memgraph::utils::UUID{}, 24); histories.emplace_back(memgraph::utils::UUID{}, 36); histories.emplace_back(memgraph::utils::UUID{}, 48); histories.emplace_back(memgraph::utils::UUID{}, 50); histories.emplace_back(memgraph::utils::UUID{}, 51); memgraph::utils::UUID db_uuid; std::string default_name = std::string(memgraph::dbms::kDefaultDB); auto db_histories = memgraph::utils::fmap(histories, [](const std::pair &pair) { return std::make_pair(std::string(pair.first), pair.second); }); memgraph::replication_coordination_glue::DatabaseHistory history{ .db_uuid = db_uuid, .history = db_histories, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history}; instance_database_histories.emplace_back("instance_1", instance_1_db_histories_); memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history}; instance_database_histories.emplace_back("instance_2", instance_2_db_histories_); memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history}; instance_database_histories.emplace_back("instance_3", instance_3_db_histories_); memgraph::coordination::CoordinatorInstance instance; auto [instance_name, latest_epoch, latest_commit_timestamp] = instance.ChooseMostUpToDateInstance(instance_database_histories); ASSERT_TRUE(instance_name == "instance_1" || instance_name == "instance_2" || instance_name == "instance_3"); ASSERT_TRUE(latest_epoch == db_histories.back().first); ASSERT_TRUE(latest_commit_timestamp == db_histories.back().second); } TEST_F(CoordinationUtils, MemgraphDbHistoryLastEpochDifferent) { // Prioritize one with the biggest last commit timestamp on last epoch // X = dead // Main : A(24) B(36) C(48) D(50) E(59) X // replica 1: A(24) B(12) C(15) D(17) E(51) // replica 2: A(24) B(12) C(15) D(17) E(57) // replica 3: A(24) B(12) C(15) D(17) E(59) std::vector> instance_database_histories; std::vector> histories; histories.emplace_back(memgraph::utils::UUID{}, 24); histories.emplace_back(memgraph::utils::UUID{}, 36); histories.emplace_back(memgraph::utils::UUID{}, 48); histories.emplace_back(memgraph::utils::UUID{}, 50); histories.emplace_back(memgraph::utils::UUID{}, 59); memgraph::utils::UUID db_uuid; std::string default_name = std::string(memgraph::dbms::kDefaultDB); auto db_histories = memgraph::utils::fmap(histories, [](const std::pair &pair) { return std::make_pair(std::string(pair.first), pair.second); }); db_histories.back().second = 51; memgraph::replication_coordination_glue::DatabaseHistory history1{ .db_uuid = db_uuid, .history = db_histories, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history1}; instance_database_histories.emplace_back("instance_1", instance_1_db_histories_); db_histories.back().second = 57; memgraph::replication_coordination_glue::DatabaseHistory history2{ .db_uuid = db_uuid, .history = db_histories, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history2}; instance_database_histories.emplace_back("instance_2", instance_2_db_histories_); db_histories.back().second = 59; memgraph::replication_coordination_glue::DatabaseHistory history3{ .db_uuid = db_uuid, .history = db_histories, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history3}; instance_database_histories.emplace_back("instance_3", instance_3_db_histories_); memgraph::coordination::CoordinatorInstance instance; auto [instance_name, latest_epoch, latest_commit_timestamp] = instance.ChooseMostUpToDateInstance(instance_database_histories); ASSERT_TRUE(instance_name == "instance_3"); ASSERT_TRUE(latest_epoch == db_histories.back().first); ASSERT_TRUE(latest_commit_timestamp == db_histories.back().second); } TEST_F(CoordinationUtils, MemgraphDbHistoryOneInstanceAheadFewEpochs) { // Prioritize one biggest commit timestamp // X = dead // Main : A(24) B(36) C(48) D(50) E(51) X X X X // replica 1: A(24) B(36) C(48) D(50) E(51) F(60) G(65) X up // replica 2: A(24) B(36) C(48) D(50) E(51) X X X up // replica 3: A(24) B(36) C(48) D(50) E(51) X X X up std::vector> instance_database_histories; std::vector> histories; histories.emplace_back(memgraph::utils::UUID{}, 24); histories.emplace_back(memgraph::utils::UUID{}, 36); histories.emplace_back(memgraph::utils::UUID{}, 48); histories.emplace_back(memgraph::utils::UUID{}, 50); histories.emplace_back(memgraph::utils::UUID{}, 51); memgraph::utils::UUID db_uuid; std::string default_name = std::string(memgraph::dbms::kDefaultDB); auto db_histories = memgraph::utils::fmap(histories, [](const std::pair &pair) { return std::make_pair(std::string(pair.first), pair.second); }); memgraph::replication_coordination_glue::DatabaseHistory history{ .db_uuid = db_uuid, .history = db_histories, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history}; instance_database_histories.emplace_back("instance_1", instance_1_db_histories_); memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history}; instance_database_histories.emplace_back("instance_2", instance_2_db_histories_); histories.emplace_back(memgraph::utils::UUID{}, 60); histories.emplace_back(memgraph::utils::UUID{}, 65); auto db_histories_longest = memgraph::utils::fmap(histories, [](const std::pair &pair) { return std::make_pair(std::string(pair.first), pair.second); }); memgraph::replication_coordination_glue::DatabaseHistory history_longest{ .db_uuid = db_uuid, .history = db_histories_longest, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history_longest}; instance_database_histories.emplace_back("instance_3", instance_3_db_histories_); memgraph::coordination::CoordinatorInstance instance; auto [instance_name, latest_epoch, latest_commit_timestamp] = instance.ChooseMostUpToDateInstance(instance_database_histories); ASSERT_TRUE(instance_name == "instance_3"); ASSERT_TRUE(latest_epoch == db_histories_longest.back().first); ASSERT_TRUE(latest_commit_timestamp == db_histories_longest.back().second); } TEST_F(CoordinationUtils, MemgraphDbHistoryInstancesHistoryDiverged) { // When history diverged, also prioritize one with biggest last commit timestamp // Main : A(1) B(2) C(3) X // replica 1: A(1) B(2) C(3) X X up // replica 2: A(1) B(2) X D(5) X up // replica 3: A(1) B(2) X D(4) X up std::vector> instance_database_histories; std::vector> histories; histories.emplace_back(memgraph::utils::UUID{}, 1); histories.emplace_back(memgraph::utils::UUID{}, 2); histories.emplace_back(memgraph::utils::UUID{}, 3); memgraph::utils::UUID db_uuid; std::string default_name = std::string(memgraph::dbms::kDefaultDB); auto db_histories = memgraph::utils::fmap(histories, [](const std::pair &pair) { return std::make_pair(std::string(pair.first), pair.second); }); memgraph::replication_coordination_glue::DatabaseHistory history{ .db_uuid = db_uuid, .history = db_histories, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history}; instance_database_histories.emplace_back("instance_1", instance_1_db_histories_); db_histories.pop_back(); auto oldest_commit_timestamp{5}; auto newest_different_epoch = memgraph::utils::UUID{}; histories.emplace_back(newest_different_epoch, oldest_commit_timestamp); auto db_histories_different = memgraph::utils::fmap(histories, [](const std::pair &pair) { return std::make_pair(std::string(pair.first), pair.second); }); memgraph::replication_coordination_glue::DatabaseHistory history_3{ .db_uuid = db_uuid, .history = db_histories_different, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history_3}; instance_database_histories.emplace_back("instance_3", instance_3_db_histories_); db_histories_different.back().second = 4; memgraph::replication_coordination_glue::DatabaseHistory history_2{ .db_uuid = db_uuid, .history = db_histories_different, .name = default_name}; memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history_2}; instance_database_histories.emplace_back("instance_2", instance_2_db_histories_); memgraph::coordination::CoordinatorInstance instance; auto [instance_name, latest_epoch, latest_commit_timestamp] = instance.ChooseMostUpToDateInstance(instance_database_histories); ASSERT_TRUE(instance_name == "instance_3"); ASSERT_TRUE(latest_epoch == std::string(newest_different_epoch)); ASSERT_TRUE(latest_commit_timestamp == oldest_commit_timestamp); }