From e76f785bbddba8f90d3afee74f0b422f78b98c2a Mon Sep 17 00:00:00 2001 From: Stepanyuk Vladislav Date: Tue, 21 Jan 2025 18:07:04 +0700 Subject: [PATCH] issue-2404: correct issues --- .../disk_registry/disk_registry_database.h | 7 +- .../disk_registry_state_ut_cms.cpp | 546 ------------------ .../disk_registry_state_ut_migration.cpp | 543 +++++++++++++++++ 3 files changed, 547 insertions(+), 549 deletions(-) diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_database.h b/cloud/blockstore/libs/storage/disk_registry/disk_registry_database.h index a71877a589..7720bff258 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_database.h +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_database.h @@ -30,9 +30,10 @@ struct TFinishedMigration { TString DeviceId; ui64 SeqNo = 0; - bool IsCanceled = - true; // by default, this value is set to true, because - // we may not start migration if this field is set to false + bool IsCanceled = // by default, this value is set to true, because + // we may not start migration if this field is set to + // false + true; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_cms.cpp b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_cms.cpp index 2cbb31e7f3..845b8b0347 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_cms.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_cms.cpp @@ -20,65 +20,6 @@ namespace NCloud::NBlockStore::NStorage { using namespace NDiskRegistryStateTest; //////////////////////////////////////////////////////////////////////////////// - - -namespace { - -//////////////////////////////////////////////////////////////////////////////// - -TResultOrError AllocateDisk( - TDiskRegistryDatabase& db, - TDiskRegistryState& state, - TString diskId) -{ - TDiskRegistryState::TAllocateDiskResult result{}; - - auto error = state.AllocateDisk( - Now(), - db, - TDiskRegistryState::TAllocateDiskParams{ - .DiskId = std::move(diskId), - .BlockSize = NDiskRegistryStateTest::DefaultLogicalBlockSize, - .BlocksCount = 20_GB / DefaultLogicalBlockSize}, - &result); - if (HasError(error)) { - return error; - } - - return result; -} - -TVector GetSeveralAgents() -{ - return { - AgentConfig( - 1, - { - Device("dev-1", "uuid-1.1", "rack-1"), - Device("dev-2", "uuid-1.2", "rack-1"), - }), - AgentConfig( - 2, - { - Device("dev-1", "uuid-2.1", "rack-2"), - Device("dev-2", "uuid-2.2", "rack-2"), - })}; -} - -TDiskRegistryState GetTestState(const TVector& agents) -{ - return TDiskRegistryStateBuilder() - .WithKnownAgents(agents) - .WithDisks({ - Disk("disk-1", {"uuid-1.1", "uuid-1.2"}), - }) - .Build(); -} - -} // namespace - - - Y_UNIT_TEST_SUITE(TDiskRegistryStateCMSTest) { Y_UNIT_TEST(ShouldAddNewDevice) @@ -1185,493 +1126,6 @@ Y_UNIT_TEST_SUITE(TDiskRegistryStateCMSTest) }); } - Y_UNIT_TEST(ShouldNotStartAlreadyFinishedMigrationAgent) - { - TTestExecutor executor; - executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); - - const TVector agents = GetSeveralAgents(); - - TDiskRegistryState state = GetTestState(agents); - - UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); - UNIT_ASSERT(state.IsMigrationListEmpty()); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1"); - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - TVector affectedDisks; - TDuration timeout; - auto error = state.UpdateCmsHostState( - db, - agents[0].agentid(), - NProto::AGENT_STATE_WARNING, - Now(), - false, // dryRun - affectedDisks, - timeout); - - UNIT_ASSERT_VALUES_EQUAL(error.code(), E_TRY_AGAIN); - UNIT_ASSERT_VALUES_EQUAL(1, affectedDisks.size()); - UNIT_ASSERT(!state.IsMigrationListEmpty()); - }); - - const auto migrations = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(2, migrations.size()); - - TVector targets; - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - for (const auto& [diskId, uuid]: migrations) { - auto [config, error] = - state.StartDeviceMigration(Now(), db, diskId, uuid); - UNIT_ASSERT_SUCCESS(error); - targets.push_back(config.GetDeviceUUID()); - } - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1");; - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(2, result.Migrations.size()); - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); - } - - UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); - - // finish migrations - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - for (size_t i = 0; i < migrations.size(); ++i) { - const auto& diskId = migrations[i].DiskId; - const auto& uuid = migrations[i].SourceDeviceId; - const auto& target = targets[i]; - - bool updated = false; - auto error = state.FinishDeviceMigration( - db, - diskId, - uuid, - target, - TInstant::Now(), - &updated); - - UNIT_ASSERT_VALUES_EQUAL(S_OK, error.GetCode()); - } - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.FinishedMigrations.size()); - } - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - TVector affectedDisks; - TDuration timeout; - auto error = state.UpdateCmsHostState( - db, - agents[0].agentid(), - NProto::AGENT_STATE_WARNING, - Now(), - false, // dryRun - affectedDisks, - timeout); - - UNIT_ASSERT_VALUES_EQUAL(error.code(), E_TRY_AGAIN); - UNIT_ASSERT(state.IsMigrationListEmpty()); - }); - - auto migrationsAfterSecondRequest = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(0, migrationsAfterSecondRequest.size()); - } - - Y_UNIT_TEST(ShouldStartCanceledMigrationAgent) - { - TTestExecutor executor; - executor.WriteTx([&] (TDiskRegistryDatabase db) { - db.InitSchema(); - }); - - const TVector agents = GetSeveralAgents(); - - TDiskRegistryState state = GetTestState(agents); - - UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); - UNIT_ASSERT(state.IsMigrationListEmpty()); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1"); - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto affectedDisks = UpdateAgentState( - state, - db, - agents[0], - NProto::AGENT_STATE_WARNING); - UNIT_ASSERT_VALUES_EQUAL(1, affectedDisks.size()); - UNIT_ASSERT(!state.IsMigrationListEmpty()); - }); - - const auto migrations = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(2, migrations.size()); - - TVector targets; - executor.WriteTx([&] (TDiskRegistryDatabase db) mutable { - for (const auto& [diskId, uuid]: migrations) { - auto [config, error] = state.StartDeviceMigration(Now(), db, diskId, uuid); - UNIT_ASSERT_SUCCESS(error); - targets.push_back(config.GetDeviceUUID()); - } - }); - Sort(targets); - - executor.WriteTx([&] (TDiskRegistryDatabase db) { - auto [result, error] = AllocateDisk(db, state, "disk-1");; - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(2, result.Migrations.size()); - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); - } - - UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); - - // cancel migrations - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - TVector affectedDisks; - TDuration timeout; - auto error = state.UpdateCmsHostState( - db, - agents[0].agentid(), - NProto::AGENT_STATE_ONLINE, - Now(), - false, // dryRun - affectedDisks, - timeout); - UNIT_ASSERT_VALUES_EQUAL(1, affectedDisks.size()); - UNIT_ASSERT(state.IsMigrationListEmpty()); - }); - - UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.FinishedMigrations.size()); - } - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - TVector affectedDisks; - TDuration timeout; - auto error = state.UpdateCmsHostState( - db, - agents[0].agentid(), - NProto::AGENT_STATE_WARNING, - Now(), - false, // dryRun - affectedDisks, - timeout); - - UNIT_ASSERT_VALUES_EQUAL(error.code(), E_TRY_AGAIN); - UNIT_ASSERT(!state.IsMigrationListEmpty()); - }); - - auto migrationsAfterSecondRequest = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(2, migrationsAfterSecondRequest.size()); - } - - Y_UNIT_TEST(ShouldNotStartAlreadyFinishedMigrationDevice) - { - TTestExecutor executor; - executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); - - const TVector agents = GetSeveralAgents(); - - TDiskRegistryState state = GetTestState(agents); - - UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); - UNIT_ASSERT(state.IsMigrationListEmpty()); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1"); - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto result = state.UpdateCmsDeviceState( - db, - agents[0].agentid(), - agents[0].GetDevices()[0].GetDeviceName(), - NProto::DEVICE_STATE_WARNING, - Now(), - false, // shouldResumeDevice - false); // dryRun - - UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); - UNIT_ASSERT_VALUES_EQUAL(1, result.AffectedDisks.size()); - UNIT_ASSERT(!state.IsMigrationListEmpty()); - }); - - const auto migrations = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(1, migrations.size()); - const auto& migration = migrations[0]; - - TString target; - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto [config, error] = state.StartDeviceMigration( - Now(), - db, - migration.DiskId, - migration.SourceDeviceId); - UNIT_ASSERT_SUCCESS(error); - target = config.GetDeviceUUID(); - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1");; - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(1, result.Migrations.size()); - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); - } - - UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); - - // finish migration - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - const auto& diskId = migration.DiskId; - const auto& uuid = migration.SourceDeviceId; - - bool updated = false; - auto error = state.FinishDeviceMigration( - db, - diskId, - uuid, - target, - TInstant::Now(), - &updated); - - UNIT_ASSERT_VALUES_EQUAL(S_OK, error.GetCode()); - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.FinishedMigrations.size()); - } - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto result = state.UpdateCmsDeviceState( - db, - agents[0].agentid(), - agents[0].GetDevices()[0].GetDeviceName(), - NProto::DEVICE_STATE_WARNING, - Now(), - false, // shouldResumeDevice - false); // dryRun - - UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); - UNIT_ASSERT(state.IsMigrationListEmpty()); - }); - - auto migrationsAfterSecondRequest = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(0, migrationsAfterSecondRequest.size()); - } - - Y_UNIT_TEST(ShouldStartCanceledMigrationDevice) - { - TTestExecutor executor; - executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); - - const TVector agents = GetSeveralAgents(); - - TDiskRegistryState state = GetTestState(agents); - - UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); - UNIT_ASSERT(state.IsMigrationListEmpty()); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1"); - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto result = state.UpdateCmsDeviceState( - db, - agents[0].agentid(), - agents[0].GetDevices()[0].GetDeviceName(), - NProto::DEVICE_STATE_WARNING, - Now(), - false, // shouldResumeDevice - false); // dryRun - - UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); - UNIT_ASSERT_VALUES_EQUAL(1, result.AffectedDisks.size()); - UNIT_ASSERT(!state.IsMigrationListEmpty()); - }); - - const auto migrations = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(1, migrations.size()); - const auto& migration = migrations[0]; - - TString target; - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto [config, error] = state.StartDeviceMigration( - Now(), - db, - migration.DiskId, - migration.SourceDeviceId); - UNIT_ASSERT_SUCCESS(error); - target = config.GetDeviceUUID(); - }); - - executor.WriteTx( - [&](TDiskRegistryDatabase db) - { - auto [result, error] = AllocateDisk(db, state, "disk-1"); - ; - UNIT_ASSERT_SUCCESS(error); - - UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(1, result.Migrations.size()); - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); - } - - UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); - - // cancel migration - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto result = state.UpdateCmsDeviceState( - db, - agents[0].agentid(), - agents[0].GetDevices()[0].GetDeviceName(), - NProto::DEVICE_STATE_ONLINE, - Now(), - false, // shouldResumeDevice - false); // dryRun - UNIT_ASSERT(state.IsMigrationListEmpty()); - }); - - { - TDiskInfo diskInfo; - UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); - UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); - UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); - UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.FinishedMigrations.size()); - } - - executor.WriteTx( - [&](TDiskRegistryDatabase db) mutable - { - auto result = state.UpdateCmsDeviceState( - db, - agents[0].agentid(), - agents[0].GetDevices()[0].GetDeviceName(), - NProto::DEVICE_STATE_WARNING, - Now(), - false, // shouldResumeDevice - false); // dryRun - - UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); - UNIT_ASSERT(!state.IsMigrationListEmpty()); - }); - - auto migrationsAfterSecondRequest = state.BuildMigrationList(); - UNIT_ASSERT_VALUES_EQUAL(1, migrationsAfterSecondRequest.size()); - } } } // namespace NCloud::NBlockStore::NStorage diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_migration.cpp b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_migration.cpp index 619b180e29..ee4a05fd94 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_migration.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state_ut_migration.cpp @@ -39,6 +39,55 @@ auto ChangeAgentState( return affectedDisks; }; +TResultOrError AllocateDisk( + TDiskRegistryDatabase& db, + TDiskRegistryState& state, + TString diskId) +{ + TDiskRegistryState::TAllocateDiskResult result{}; + + auto error = state.AllocateDisk( + Now(), + db, + TDiskRegistryState::TAllocateDiskParams{ + .DiskId = std::move(diskId), + .BlockSize = DefaultLogicalBlockSize, + .BlocksCount = 20_GB / DefaultLogicalBlockSize}, + &result); + if (HasError(error)) { + return error; + } + + return result; +} + +TVector GetSeveralAgents() +{ + return { + AgentConfig( + 1, + { + Device("dev-1", "uuid-1.1", "rack-1"), + Device("dev-2", "uuid-1.2", "rack-1"), + }), + AgentConfig( + 2, + { + Device("dev-1", "uuid-2.1", "rack-2"), + Device("dev-2", "uuid-2.2", "rack-2"), + })}; +} + +TDiskRegistryState GetTestState(const TVector& agents) +{ + return TDiskRegistryStateBuilder() + .WithKnownAgents(agents) + .WithDisks({ + Disk("disk-1", {"uuid-1.1", "uuid-1.2"}), + }) + .Build(); +} + } //namespace //////////////////////////////////////////////////////////////////////////////// @@ -1115,6 +1164,500 @@ Y_UNIT_TEST_SUITE(TDiskRegistryStateMigrationTest) config.SetMaxNonReplicatedDeviceMigrationPercentageInProgress(34); DoTestShouldNotMigrateMoreThanNDevicesAtTheSameTime(std::move(config)); } + + Y_UNIT_TEST(ShouldNotStartAlreadyFinishedMigrationAgent) + { + TTestExecutor executor; + executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); + + const TVector agents = GetSeveralAgents(); + + TDiskRegistryState state = GetTestState(agents); + + UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); + UNIT_ASSERT(state.IsMigrationListEmpty()); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + TVector affectedDisks; + TDuration timeout; + auto error = state.UpdateCmsHostState( + db, + agents[0].agentid(), + NProto::AGENT_STATE_WARNING, + Now(), + false, // dryRun + affectedDisks, + timeout); + + UNIT_ASSERT_VALUES_EQUAL(error.code(), E_TRY_AGAIN); + UNIT_ASSERT_VALUES_EQUAL(1, affectedDisks.size()); + UNIT_ASSERT(!state.IsMigrationListEmpty()); + }); + + const auto migrations = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(2, migrations.size()); + + TVector targets; + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + for (const auto& [diskId, uuid]: migrations) { + auto [config, error] = + state.StartDeviceMigration(Now(), db, diskId, uuid); + UNIT_ASSERT_SUCCESS(error); + targets.push_back(config.GetDeviceUUID()); + } + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + ; + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(2, result.Migrations.size()); + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); + } + + UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); + + // finish migrations + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + for (size_t i = 0; i < migrations.size(); ++i) { + const auto& diskId = migrations[i].DiskId; + const auto& uuid = migrations[i].SourceDeviceId; + const auto& target = targets[i]; + + bool updated = false; + auto error = state.FinishDeviceMigration( + db, + diskId, + uuid, + target, + TInstant::Now(), + &updated); + + UNIT_ASSERT_VALUES_EQUAL(S_OK, error.GetCode()); + } + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.FinishedMigrations.size()); + } + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + TVector affectedDisks; + TDuration timeout; + auto error = state.UpdateCmsHostState( + db, + agents[0].agentid(), + NProto::AGENT_STATE_WARNING, + Now(), + false, // dryRun + affectedDisks, + timeout); + + UNIT_ASSERT_VALUES_EQUAL(error.code(), E_TRY_AGAIN); + UNIT_ASSERT(state.IsMigrationListEmpty()); + }); + + auto migrationsAfterSecondRequest = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(0, migrationsAfterSecondRequest.size()); + } + + Y_UNIT_TEST(ShouldStartCanceledMigrationAgent) + { + TTestExecutor executor; + executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); + + const TVector agents = GetSeveralAgents(); + + TDiskRegistryState state = GetTestState(agents); + + UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); + UNIT_ASSERT(state.IsMigrationListEmpty()); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto affectedDisks = UpdateAgentState( + state, + db, + agents[0], + NProto::AGENT_STATE_WARNING); + UNIT_ASSERT_VALUES_EQUAL(1, affectedDisks.size()); + UNIT_ASSERT(!state.IsMigrationListEmpty()); + }); + + const auto migrations = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(2, migrations.size()); + + TVector targets; + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + for (const auto& [diskId, uuid]: migrations) { + auto [config, error] = + state.StartDeviceMigration(Now(), db, diskId, uuid); + UNIT_ASSERT_SUCCESS(error); + targets.push_back(config.GetDeviceUUID()); + } + }); + Sort(targets); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + ; + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(2, result.Migrations.size()); + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); + } + + UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); + + // cancel migrations + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + TVector affectedDisks; + TDuration timeout; + auto error = state.UpdateCmsHostState( + db, + agents[0].agentid(), + NProto::AGENT_STATE_ONLINE, + Now(), + false, // dryRun + affectedDisks, + timeout); + UNIT_ASSERT_VALUES_EQUAL(1, affectedDisks.size()); + UNIT_ASSERT(state.IsMigrationListEmpty()); + }); + + UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.FinishedMigrations.size()); + } + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + TVector affectedDisks; + TDuration timeout; + auto error = state.UpdateCmsHostState( + db, + agents[0].agentid(), + NProto::AGENT_STATE_WARNING, + Now(), + false, // dryRun + affectedDisks, + timeout); + + UNIT_ASSERT_VALUES_EQUAL(error.code(), E_TRY_AGAIN); + UNIT_ASSERT(!state.IsMigrationListEmpty()); + }); + + auto migrationsAfterSecondRequest = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(2, migrationsAfterSecondRequest.size()); + } + + Y_UNIT_TEST(ShouldNotStartAlreadyFinishedMigrationDevice) + { + TTestExecutor executor; + executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); + + const TVector agents = GetSeveralAgents(); + + TDiskRegistryState state = GetTestState(agents); + + UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); + UNIT_ASSERT(state.IsMigrationListEmpty()); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto result = state.UpdateCmsDeviceState( + db, + agents[0].agentid(), + agents[0].GetDevices()[0].GetDeviceName(), + NProto::DEVICE_STATE_WARNING, + Now(), + false, // shouldResumeDevice + false); // dryRun + + UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); + UNIT_ASSERT_VALUES_EQUAL(1, result.AffectedDisks.size()); + UNIT_ASSERT(!state.IsMigrationListEmpty()); + }); + + const auto migrations = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(1, migrations.size()); + const auto& migration = migrations[0]; + + TString target; + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto [config, error] = state.StartDeviceMigration( + Now(), + db, + migration.DiskId, + migration.SourceDeviceId); + UNIT_ASSERT_SUCCESS(error); + target = config.GetDeviceUUID(); + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + ; + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(1, result.Migrations.size()); + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); + } + + UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); + + // finish migration + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + const auto& diskId = migration.DiskId; + const auto& uuid = migration.SourceDeviceId; + + bool updated = false; + auto error = state.FinishDeviceMigration( + db, + diskId, + uuid, + target, + TInstant::Now(), + &updated); + + UNIT_ASSERT_VALUES_EQUAL(S_OK, error.GetCode()); + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.FinishedMigrations.size()); + } + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto result = state.UpdateCmsDeviceState( + db, + agents[0].agentid(), + agents[0].GetDevices()[0].GetDeviceName(), + NProto::DEVICE_STATE_WARNING, + Now(), + false, // shouldResumeDevice + false); // dryRun + + UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); + UNIT_ASSERT(state.IsMigrationListEmpty()); + }); + + auto migrationsAfterSecondRequest = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(0, migrationsAfterSecondRequest.size()); + } + + Y_UNIT_TEST(ShouldStartCanceledMigrationDevice) + { + TTestExecutor executor; + executor.WriteTx([&](TDiskRegistryDatabase db) { db.InitSchema(); }); + + const TVector agents = GetSeveralAgents(); + + TDiskRegistryState state = GetTestState(agents); + + UNIT_ASSERT_VALUES_EQUAL(0, state.BuildMigrationList().size()); + UNIT_ASSERT(state.IsMigrationListEmpty()); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, result.Migrations.size()); + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto result = state.UpdateCmsDeviceState( + db, + agents[0].agentid(), + agents[0].GetDevices()[0].GetDeviceName(), + NProto::DEVICE_STATE_WARNING, + Now(), + false, // shouldResumeDevice + false); // dryRun + + UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); + UNIT_ASSERT_VALUES_EQUAL(1, result.AffectedDisks.size()); + UNIT_ASSERT(!state.IsMigrationListEmpty()); + }); + + const auto migrations = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(1, migrations.size()); + const auto& migration = migrations[0]; + + TString target; + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto [config, error] = state.StartDeviceMigration( + Now(), + db, + migration.DiskId, + migration.SourceDeviceId); + UNIT_ASSERT_SUCCESS(error); + target = config.GetDeviceUUID(); + }); + + executor.WriteTx( + [&](TDiskRegistryDatabase db) + { + auto [result, error] = AllocateDisk(db, state, "disk-1"); + ; + UNIT_ASSERT_SUCCESS(error); + + UNIT_ASSERT_VALUES_EQUAL(2, result.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(1, result.Migrations.size()); + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.FinishedMigrations.size()); + } + + UNIT_ASSERT_VALUES_EQUAL(0, state.GetDirtyDevices().size()); + + // cancel migration + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto result = state.UpdateCmsDeviceState( + db, + agents[0].agentid(), + agents[0].GetDevices()[0].GetDeviceName(), + NProto::DEVICE_STATE_ONLINE, + Now(), + false, // shouldResumeDevice + false); // dryRun + UNIT_ASSERT(state.IsMigrationListEmpty()); + }); + + { + TDiskInfo diskInfo; + UNIT_ASSERT_SUCCESS(state.GetDiskInfo("disk-1", diskInfo)); + UNIT_ASSERT_VALUES_EQUAL(2, diskInfo.Devices.size()); + UNIT_ASSERT_VALUES_EQUAL(0, diskInfo.Migrations.size()); + UNIT_ASSERT_VALUES_EQUAL(1, diskInfo.FinishedMigrations.size()); + } + + executor.WriteTx( + [&](TDiskRegistryDatabase db) mutable + { + auto result = state.UpdateCmsDeviceState( + db, + agents[0].agentid(), + agents[0].GetDevices()[0].GetDeviceName(), + NProto::DEVICE_STATE_WARNING, + Now(), + false, // shouldResumeDevice + false); // dryRun + + UNIT_ASSERT_VALUES_EQUAL(result.Error.code(), E_TRY_AGAIN); + UNIT_ASSERT(!state.IsMigrationListEmpty()); + }); + + auto migrationsAfterSecondRequest = state.BuildMigrationList(); + UNIT_ASSERT_VALUES_EQUAL(1, migrationsAfterSecondRequest.size()); + } } } // namespace NCloud::NBlockStore::NStorage