mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
feat: Block clio if migration is blocking (#1834)
Add: - Block server if migration is blocking - Initialise the migration related table when server starts against empty DB Add MigrationInspectorInterface. server uses inspector to check the migrators status.
This commit is contained in:
@@ -60,6 +60,8 @@ target_sources(
|
||||
migration/cassandra/SpecTests.cpp
|
||||
migration/MigratorRegisterTests.cpp
|
||||
migration/MigratorStatusTests.cpp
|
||||
migration/MigrationInspectorFactoryTests.cpp
|
||||
migration/MigrationInspectorBaseTests.cpp
|
||||
migration/MigrationManagerBaseTests.cpp
|
||||
migration/MigrationManagerFactoryTests.cpp
|
||||
migration/SpecTests.cpp
|
||||
|
||||
130
tests/unit/migration/MigrationInspectorBaseTests.cpp
Normal file
130
tests/unit/migration/MigrationInspectorBaseTests.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "migration/MigratiorStatus.hpp"
|
||||
#include "migration/TestMigrators.hpp"
|
||||
#include "migration/impl/MigrationManagerBase.hpp"
|
||||
#include "migration/impl/MigratorsRegister.hpp"
|
||||
#include "util/MockBackendTestFixture.hpp"
|
||||
#include "util/MockPrometheus.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
using TestMigratorRegister =
|
||||
migration::impl::MigratorsRegister<data::BackendInterface, SimpleTestMigrator, SimpleTestMigrator2>;
|
||||
|
||||
using TestCassandramigrationInspector = migration::impl::MigrationInspectorBase<TestMigratorRegister>;
|
||||
|
||||
struct MigrationInspectorBaseTest : public util::prometheus::WithMockPrometheus, public MockBackendTest {
|
||||
MigrationInspectorBaseTest()
|
||||
{
|
||||
migrationInspector_ = std::make_shared<TestCassandramigrationInspector>(backend_);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<TestCassandramigrationInspector> migrationInspector_;
|
||||
};
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, AllStatus)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator", testing::_)).WillOnce(testing::Return("Migrated"));
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator2", testing::_))
|
||||
.WillOnce(testing::Return("NotMigrated"));
|
||||
auto const status = migrationInspector_->allMigratorsStatusPairs();
|
||||
EXPECT_EQ(status.size(), 2);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator", migration::MigratorStatus::Migrated)) !=
|
||||
status.end()
|
||||
);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator2", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, AllNames)
|
||||
{
|
||||
auto const names = migrationInspector_->allMigratorsNames();
|
||||
EXPECT_EQ(names.size(), 2);
|
||||
EXPECT_EQ(names[0], "SimpleTestMigrator");
|
||||
EXPECT_EQ(names[1], "SimpleTestMigrator2");
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, Description)
|
||||
{
|
||||
EXPECT_EQ(migrationInspector_->getMigratorDescriptionByName("unknown"), "No Description");
|
||||
EXPECT_EQ(
|
||||
migrationInspector_->getMigratorDescriptionByName("SimpleTestMigrator"), "The migrator for version 0 -> 1"
|
||||
);
|
||||
EXPECT_EQ(
|
||||
migrationInspector_->getMigratorDescriptionByName("SimpleTestMigrator2"), "The migrator for version 1 -> 2"
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, getMigratorStatusByName)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator", testing::_)).WillOnce(testing::Return("Migrated"));
|
||||
EXPECT_EQ(migrationInspector_->getMigratorStatusByName("SimpleTestMigrator"), migration::MigratorStatus::Migrated);
|
||||
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator2", testing::_))
|
||||
.WillOnce(testing::Return("NotMigrated"));
|
||||
EXPECT_EQ(
|
||||
migrationInspector_->getMigratorStatusByName("SimpleTestMigrator2"), migration::MigratorStatus::NotMigrated
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, oneMigratorBlockingClio)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator", testing::_))
|
||||
.WillOnce(testing::Return("NotMigrated"));
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator2", testing::_)).Times(0);
|
||||
|
||||
EXPECT_TRUE(migrationInspector_->isBlockingClio());
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, oneMigratorBlockingClioGetMigrated)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator", testing::_)).WillOnce(testing::Return("Migrated"));
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator2", testing::_)).Times(0);
|
||||
EXPECT_FALSE(migrationInspector_->isBlockingClio());
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, noMigratorBlockingClio)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus).Times(0);
|
||||
|
||||
auto const migrations = migration::impl::MigrationInspectorBase<
|
||||
migration::impl::MigratorsRegister<data::BackendInterface, SimpleTestMigrator2, SimpleTestMigrator3>>(backend_);
|
||||
EXPECT_FALSE(migrations.isBlockingClio());
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorBaseTest, isBlockingClioWhenNoMigrator)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus).Times(0);
|
||||
|
||||
auto const migrations =
|
||||
migration::impl::MigrationInspectorBase<migration::impl::MigratorsRegister<data::BackendInterface>>(backend_);
|
||||
EXPECT_FALSE(migrations.isBlockingClio());
|
||||
}
|
||||
75
tests/unit/migration/MigrationInspectorFactoryTests.cpp
Normal file
75
tests/unit/migration/MigrationInspectorFactoryTests.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "data/Types.hpp"
|
||||
#include "migration/MigrationInspectorFactory.hpp"
|
||||
#include "util/MockBackendTestFixture.hpp"
|
||||
#include "util/MockPrometheus.hpp"
|
||||
#include "util/newconfig/ConfigDefinition.hpp"
|
||||
#include "util/newconfig/ConfigValue.hpp"
|
||||
#include "util/newconfig/Types.hpp"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
struct MigrationInspectorFactoryTests : public util::prometheus::WithPrometheus, public MockBackendTest {
|
||||
protected:
|
||||
util::config::ClioConfigDefinition const readerConfig_ = util::config::ClioConfigDefinition{
|
||||
{"read_only", util::config::ConfigValue{util::config::ConfigType::Boolean}.defaultValue(true)}
|
||||
};
|
||||
};
|
||||
|
||||
struct MigrationInspectorFactoryTestsDeathTest : public MigrationInspectorFactoryTests {};
|
||||
|
||||
TEST_F(MigrationInspectorFactoryTestsDeathTest, NullBackend)
|
||||
{
|
||||
EXPECT_DEATH(migration::makeMigrationInspector(readerConfig_, nullptr), ".*");
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorFactoryTests, NotInitMigrationTableIfReader)
|
||||
{
|
||||
EXPECT_CALL(*backend_, hardFetchLedgerRange).Times(0);
|
||||
|
||||
EXPECT_NE(migration::makeMigrationInspector(readerConfig_, backend_), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorFactoryTests, BackendIsWriterAndDBEmpty)
|
||||
{
|
||||
EXPECT_CALL(*backend_, hardFetchLedgerRange).WillOnce(Return(std::nullopt));
|
||||
|
||||
util::config::ClioConfigDefinition const writerConfig = util::config::ClioConfigDefinition{
|
||||
{"read_only", util::config::ConfigValue{util::config::ConfigType::Boolean}.defaultValue(false)}
|
||||
};
|
||||
EXPECT_NE(migration::makeMigrationInspector(writerConfig, backend_), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(MigrationInspectorFactoryTests, BackendIsWriterAndDBNotEmpty)
|
||||
{
|
||||
LedgerRange const range{.minSequence = 1, .maxSequence = 5};
|
||||
EXPECT_CALL(*backend_, hardFetchLedgerRange).WillOnce(Return(range));
|
||||
|
||||
util::config::ClioConfigDefinition const writerConfig = util::config::ClioConfigDefinition{
|
||||
{"read_only", util::config::ConfigValue{util::config::ConfigType::Boolean}.defaultValue(false)}
|
||||
};
|
||||
EXPECT_NE(migration::makeMigrationInspector(writerConfig, backend_), nullptr);
|
||||
}
|
||||
@@ -54,7 +54,6 @@ struct MigrationManagerBaseTest : public util::prometheus::WithMockPrometheus, p
|
||||
MigrationManagerBaseTest()
|
||||
{
|
||||
auto mockBackendPtr = backend_.operator std::shared_ptr<MockMigrationBackend>();
|
||||
TestMigratorRegister const migratorRegister(mockBackendPtr);
|
||||
migrationManager = std::make_shared<TestCassandraMigrationManager>(mockBackendPtr, cfg.getObject("migration"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,8 +67,8 @@ TEST_F(MigratorRegisterTests, EmptyMigratorRegister)
|
||||
EXPECT_EQ(migratorRegister.getMigratorDescription("unknown"), "No Description");
|
||||
}
|
||||
|
||||
using MultipleMigratorRegister =
|
||||
migration::impl::MigratorsRegister<MockMigrationBackend, SimpleTestMigrator, SimpleTestMigrator2>;
|
||||
using MultipleMigratorRegister = migration::impl::
|
||||
MigratorsRegister<MockMigrationBackend, SimpleTestMigrator, SimpleTestMigrator2, SimpleTestMigrator3>;
|
||||
|
||||
struct MultipleMigratorRegisterTests : public util::prometheus::WithMockPrometheus, public MockMigrationBackendTest {
|
||||
std::optional<MultipleMigratorRegister> migratorRegister;
|
||||
@@ -82,11 +82,11 @@ struct MultipleMigratorRegisterTests : public util::prometheus::WithMockPromethe
|
||||
TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenError)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus(testing::_, testing::_))
|
||||
.Times(2)
|
||||
.Times(3)
|
||||
.WillRepeatedly(testing::Return(std::nullopt));
|
||||
|
||||
auto const status = migratorRegister->getMigratorsStatus();
|
||||
EXPECT_EQ(status.size(), 2);
|
||||
EXPECT_EQ(status.size(), 3);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
@@ -95,16 +95,20 @@ TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenError)
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator2", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator3", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenReturnInvalidStatus)
|
||||
{
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus(testing::_, testing::_))
|
||||
.Times(2)
|
||||
.Times(3)
|
||||
.WillRepeatedly(testing::Return("Invalid"));
|
||||
|
||||
auto const status = migratorRegister->getMigratorsStatus();
|
||||
EXPECT_EQ(status.size(), 2);
|
||||
EXPECT_EQ(status.size(), 3);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
@@ -113,6 +117,10 @@ TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenReturnInvalidStatus)
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator2", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator3", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenOneMigrated)
|
||||
@@ -120,9 +128,11 @@ TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenOneMigrated)
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator", testing::_)).WillOnce(testing::Return("Migrated"));
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator2", testing::_))
|
||||
.WillOnce(testing::Return("NotMigrated"));
|
||||
EXPECT_CALL(*backend_, fetchMigratorStatus("SimpleTestMigrator3", testing::_))
|
||||
.WillOnce(testing::Return("NotMigrated"));
|
||||
|
||||
auto const status = migratorRegister->getMigratorsStatus();
|
||||
EXPECT_EQ(status.size(), 2);
|
||||
EXPECT_EQ(status.size(), 3);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator", migration::MigratorStatus::Migrated)) !=
|
||||
status.end()
|
||||
@@ -131,6 +141,10 @@ TEST_F(MultipleMigratorRegisterTests, GetMigratorsStatusWhenOneMigrated)
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator2", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
EXPECT_TRUE(
|
||||
std::ranges::find(status, std::make_tuple("SimpleTestMigrator3", migration::MigratorStatus::NotMigrated)) !=
|
||||
status.end()
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(MultipleMigratorRegisterTests, GetMigratorStatus)
|
||||
@@ -158,9 +172,10 @@ TEST_F(MultipleMigratorRegisterTests, GetMigratorStatusWhenError)
|
||||
TEST_F(MultipleMigratorRegisterTests, Names)
|
||||
{
|
||||
auto names = migratorRegister->getMigratorNames();
|
||||
EXPECT_EQ(names.size(), 2);
|
||||
EXPECT_EQ(names.size(), 3);
|
||||
EXPECT_TRUE(std::ranges::find(names, "SimpleTestMigrator") != names.end());
|
||||
EXPECT_TRUE(std::ranges::find(names, "SimpleTestMigrator2") != names.end());
|
||||
EXPECT_TRUE(std::ranges::find(names, "SimpleTestMigrator3") != names.end());
|
||||
}
|
||||
|
||||
TEST_F(MultipleMigratorRegisterTests, Description)
|
||||
@@ -181,3 +196,21 @@ TEST_F(MultipleMigratorRegisterTests, MigrateNormalMigrator)
|
||||
EXPECT_CALL(*backend_, writeMigratorStatus("SimpleTestMigrator", "Migrated")).Times(1);
|
||||
EXPECT_NO_THROW(migratorRegister->runMigrator("SimpleTestMigrator", gCfg.getObject("migration")));
|
||||
}
|
||||
|
||||
TEST_F(MultipleMigratorRegisterTests, canBlock)
|
||||
{
|
||||
auto canBlock = migratorRegister->canMigratorBlockClio("SimpleTestMigrator");
|
||||
EXPECT_TRUE(canBlock);
|
||||
EXPECT_TRUE(*canBlock);
|
||||
|
||||
canBlock = migratorRegister->canMigratorBlockClio("SimpleTestMigrator2");
|
||||
EXPECT_TRUE(canBlock);
|
||||
EXPECT_FALSE(*canBlock);
|
||||
|
||||
canBlock = migratorRegister->canMigratorBlockClio("SimpleTestMigrator3");
|
||||
EXPECT_TRUE(canBlock);
|
||||
EXPECT_FALSE(*canBlock);
|
||||
|
||||
canBlock = migratorRegister->canMigratorBlockClio("NotAMigrator");
|
||||
EXPECT_FALSE(canBlock);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user