From 7f1eb3a6fbdfdace0eab0166541c13c54aaf3a08 Mon Sep 17 00:00:00 2001 From: ya-ksgamora Date: Thu, 19 Dec 2024 23:31:29 +0000 Subject: [PATCH] NBS-5637: Asynchronous disks allocation --- .../disk_registry/disk_registry_actor.h | 15 +++ .../disk_registry_actor_allocate.cpp | 71 ++++++++++++++- .../disk_registry_actor_secure_erase.cpp | 8 +- .../disk_registry/disk_registry_state.cpp | 39 +++++--- .../disk_registry/disk_registry_state.h | 18 +++- .../storage/disk_registry/disk_registry_tx.h | 2 + .../disk_registry/model/device_list.cpp | 50 +++++++--- .../storage/disk_registry/model/device_list.h | 6 ++ .../disk_registry/model/pending_cleanup.cpp | 91 +++++++++++++------ .../disk_registry/model/pending_cleanup.h | 21 ++++- 10 files changed, 257 insertions(+), 64 deletions(-) diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor.h b/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor.h index aa372f426e0..6542e883990 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor.h +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor.h @@ -83,6 +83,7 @@ class TDiskRegistryActor final // Pending requests TDeque PendingRequests; + THashMap> PendingDiskAllocationRequests; THashMap> PendingDiskDeallocationRequests; bool BrokenDisksDestructionInProgress = false; @@ -227,6 +228,20 @@ class TDiskRegistryActor final TDiskRegistryDatabase& db, TDiskRegistryStateSnapshot& args); + void AddPendingAllocation( + const NActors::TActorContext& ctx, + const TString& diskId, + TRequestInfoPtr requestInfoPtr); + + void ReplyToPendingAllocations( + const NActors::TActorContext& ctx, + const TString& diskId); + + void ReplyToPendingAllocations( + const NActors::TActorContext& ctx, + TVector& requestInfos, + NProto::TError error); + void AddPendingDeallocation( const NActors::TActorContext& ctx, const TString& diskId, diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_allocate.cpp b/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_allocate.cpp index 06ad674e2a9..c5855cc2738 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_allocate.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_allocate.cpp @@ -240,12 +240,79 @@ void TDiskRegistryActor::CompleteAddDisk( response->Record.SetMuteIOErrors(args.MuteIOErrors); } - NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); + TVector dirtyDevices; + for (const auto& device: args.Devices) { + const bool dirty = State->IsDirtyDevice(device.GetDeviceUUID()); + if (dirty) { + dirtyDevices.push_back(device.GetDeviceUUID()); + } + } + + if (HasError(args.Error) || args.Error.GetCode() == S_ALREADY || dirtyDevices.empty()) { + NCloud::Reply(ctx, *args.RequestInfo, std::move(response)); + } else { + AddPendingAllocation(ctx, args.DiskId, args.RequestInfo); + } DestroyBrokenDisks(ctx); + SecureErase(ctx); // I think we need this only if allocating dirty devices NotifyUsers(ctx); } +void TDiskRegistryActor::AddPendingAllocation( + const NActors::TActorContext& ctx, + const TString& diskId, + TRequestInfoPtr requestInfo) +{ + auto& requestInfos = PendingDiskAllocationRequests[diskId]; + + // TODO: GetMaxNonReplicatedDiskAllocationRequests + if (requestInfos.size() > Config->GetMaxNonReplicatedDiskDeallocationRequests()) { + LOG_WARN(ctx, TBlockStoreComponents::DISK_REGISTRY, + "Too many pending allocation requests (%lu) for disk %s. " + "Reject all requests.", + requestInfos.size(), + diskId.Quote().c_str()); + + ReplyToPendingAllocations(ctx, requestInfos, MakeError(E_REJECTED)); + } + + requestInfos.emplace_back(std::move(requestInfo)); +} + +void TDiskRegistryActor::ReplyToPendingAllocations( + const NActors::TActorContext& ctx, + TVector& requestInfos, + NProto::TError error) +{ + for (auto& requestInfo: requestInfos) { + NCloud::Reply( + ctx, + *requestInfo, + std::make_unique(error)); + } + requestInfos.clear(); +} + +void TDiskRegistryActor::ReplyToPendingAllocations( + const NActors::TActorContext& ctx, + const TString& diskId) +{ + auto it = PendingDiskAllocationRequests.find(diskId); + if (it == PendingDiskAllocationRequests.end()) { + return; + } + + LOG_INFO(ctx, TBlockStoreComponents::DISK_REGISTRY, + "Reply to pending allocation requests. DiskId=%s PendingRequests=%d", + diskId.Quote().c_str(), + static_cast(it->second.size())); + + ReplyToPendingAllocations(ctx, it->second, MakeError(S_OK)); + + PendingDiskAllocationRequests.erase(it); +} + //////////////////////////////////////////////////////////////////////////////// void TDiskRegistryActor::HandleDeallocateDisk( @@ -379,7 +446,7 @@ void TDiskRegistryActor::ReplyToPendingDeallocations( } LOG_INFO(ctx, TBlockStoreComponents::DISK_REGISTRY, - "Reply to pending deallocation requests. DiskId=%s PendingRquests=%d", + "Reply to pending deallocation requests. DiskId=%s PendingRequests=%d", diskId.Quote().c_str(), static_cast(it->second.size())); diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_secure_erase.cpp b/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_secure_erase.cpp index e65ac54d5d7..34995de5c35 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_secure_erase.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_actor_secure_erase.cpp @@ -279,8 +279,8 @@ void TDiskRegistryActor::ExecuteCleanupDevices( TTxDiskRegistry::TCleanupDevices& args) { TDiskRegistryDatabase db(tx.DB); - args.SyncDeallocatedDisks = - State->MarkDevicesAsClean(ctx.Now(), db, args.Devices); + std::tie(args.SyncAllocatedDisks, args.SyncDeallocatedDisks) = + std::move(State->MarkDevicesAsClean(ctx.Now(), db, args.Devices)); } void TDiskRegistryActor::CompleteCleanupDevices( @@ -293,6 +293,10 @@ void TDiskRegistryActor::CompleteCleanupDevices( for (const auto& diskId: args.SyncDeallocatedDisks) { ReplyToPendingDeallocations(ctx, diskId); } + + for (const auto& diskId: args.SyncAllocatedDisks) { + ReplyToPendingAllocations(ctx, diskId); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.cpp b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.cpp index 0ee39952938..42ec757908e 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.cpp @@ -1299,16 +1299,17 @@ NProto::TError TDiskRegistryState::ReplaceDeviceWithoutDiskStateUpdate( } if (!manual && !deviceReplacementId.empty()) { - auto cleaningDiskId = + auto [allocating, deallocating] = PendingCleanup.FindDiskId(deviceReplacementId); - if (!cleaningDiskId.empty() && cleaningDiskId != diskId) { + Y_UNUSED(allocating); // TODO: probably should react to this + if (deallocating && *deallocating != diskId) { return MakeError( E_ARGUMENT, TStringBuilder() << "can't allocate specific device " << deviceReplacementId.Quote() << " for disk " << diskId << " since it is in pending cleanup for disk " - << cleaningDiskId); + << *deallocating); } } @@ -3855,16 +3856,20 @@ bool TDiskRegistryState::MarkDeviceAsDirty( return true; } -TDiskRegistryState::TDiskId TDiskRegistryState::MarkDeviceAsClean( +TDiskRegistryState::TOpt2Disk TDiskRegistryState::MarkDeviceAsClean( TInstant now, TDiskRegistryDatabase& db, const TDeviceId& uuid) { - auto ret = MarkDevicesAsClean(now, db, TVector{uuid}); - return ret.empty() ? "" : ret[0]; + auto [alloc, dealloc] = MarkDevicesAsClean(now, db, TVector{uuid}); + return { + alloc.empty() ? std::nullopt : std::make_optional(std::move(alloc[0])), + dealloc.empty() ? std::nullopt + : std::make_optional(std::move(dealloc[0]))}; } -TVector TDiskRegistryState::MarkDevicesAsClean( +std::pair +TDiskRegistryState::MarkDevicesAsClean( TInstant now, TDiskRegistryDatabase& db, const TVector& uuids) @@ -3878,14 +3883,19 @@ TVector TDiskRegistryState::MarkDevicesAsClean( } } - TVector ret; + TAllocatedDisksList allocatedDisks; + TDellocatedDisksList dellocatedDisks; for (const auto& uuid: TryUpdateDevices(now, db, uuids)) { - if (auto diskId = PendingCleanup.EraseDevice(uuid); !diskId.empty()) { - ret.push_back(std::move(diskId)); + auto [allocatedDisk, deallocatedDisk] = PendingCleanup.EraseDevice(uuid); + if (allocatedDisk) { + allocatedDisks.push_back(std::move(*allocatedDisk)); + } + if (deallocatedDisk) { + dellocatedDisks.push_back(std::move(*deallocatedDisk)); } } - return ret; + return {std::move(allocatedDisks), std::move(dellocatedDisks)}; } bool TDiskRegistryState::TryUpdateDevice( @@ -5072,8 +5082,9 @@ bool TDiskRegistryState::HasDependentSsdDisks( continue; } + auto [allocating, deallocating] = PendingCleanup.FindDiskId(d.GetDeviceUUID()); if (d.GetPoolKind() == NProto::DEVICE_POOL_KIND_LOCAL && - PendingCleanup.FindDiskId(d.GetDeviceUUID())) + (allocating || deallocating)) { return true; } @@ -6354,7 +6365,9 @@ NProto::TDiskRegistryStateBackup TDiskRegistryState::BackupState() const transform(GetDirtyDevices(), backup.MutableDirtyDevices(), [this] (auto& x) { NProto::TDiskRegistryStateBackup::TDirtyDevice dd; dd.SetId(x.GetDeviceUUID()); - dd.SetDiskId(PendingCleanup.FindDiskId(x.GetDeviceUUID())); + auto [allocating, deallocating] = PendingCleanup.FindDiskId(x.GetDeviceUUID()); // TODO: need to backup + Y_UNUSED(allocating); + dd.SetDiskId(*deallocating); return dd; }); diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.h b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.h index 3d806eb2189..d655d0f691c 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.h +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_state.h @@ -281,6 +281,15 @@ class TDiskRegistryState using TCheckpoints = THashMap; using TPlacementGroups = THashMap; + using TAllocatingDiskId = TDiskRegistryState::TDiskId; + using TDeallocatingDiskId = TDiskRegistryState::TDiskId; + using TAllocatedDisksList = TVector; + using TDellocatedDisksList = TVector; + + template + using TOpt2 = std::pair, std::optional>; + using TOpt2Disk = TOpt2; + private: TLog Log; @@ -503,16 +512,17 @@ class TDiskRegistryState /// Mark selected device as clean and remove it /// from lists of suspended/dirty/pending cleanup devices - /// @return disk id where selected device was allocated - TDiskId MarkDeviceAsClean( + /// @return allocated/deallocated disk id of where selected device was allocated/deallocated + TOpt2Disk MarkDeviceAsClean( TInstant now, TDiskRegistryDatabase& db, const TDeviceId& uuid); /// Mark selected devices as clean and remove them /// from lists of suspended/dirty/pending cleanup devices - /// @return vector of disk ids where selected devices were allocated - TVector MarkDevicesAsClean( + /// @return vector of allocated/deallocated disk ids where selected devices were allocated/deallocated + std::pair + MarkDevicesAsClean( TInstant now, TDiskRegistryDatabase& db, const TVector& uuids); diff --git a/cloud/blockstore/libs/storage/disk_registry/disk_registry_tx.h b/cloud/blockstore/libs/storage/disk_registry/disk_registry_tx.h index 43619484ef4..c29c1d96ad8 100644 --- a/cloud/blockstore/libs/storage/disk_registry/disk_registry_tx.h +++ b/cloud/blockstore/libs/storage/disk_registry/disk_registry_tx.h @@ -305,6 +305,7 @@ struct TTxDiskRegistry const TRequestInfoPtr RequestInfo; const TVector Devices; + TVector SyncAllocatedDisks; TVector SyncDeallocatedDisks; explicit TCleanupDevices( @@ -316,6 +317,7 @@ struct TTxDiskRegistry void Clear() { + SyncAllocatedDisks.clear(); SyncDeallocatedDisks.clear(); } }; diff --git a/cloud/blockstore/libs/storage/disk_registry/model/device_list.cpp b/cloud/blockstore/libs/storage/disk_registry/model/device_list.cpp index 4cbe6b32257..661c60569d8 100644 --- a/cloud/blockstore/libs/storage/disk_registry/model/device_list.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/model/device_list.cpp @@ -130,6 +130,7 @@ void TDeviceList::UpdateDevices( auto& nodeDevices = NodeDevices[agent.GetNodeId()]; nodeDevices.FreeDevices.clear(); + nodeDevices.FreeDevicesIncludingDirty.clear(); nodeDevices.Rack.clear(); nodeDevices.TotalSize = 0; @@ -187,6 +188,15 @@ void TDeviceList::UpdateDevices( nodeDevices.FreeDevices.push_back(device); } + const bool isFreeIncludingDirty = + DevicesAllocationAllowed(device.GetPoolKind(), agent.GetState()) && + device.GetState() == NProto::DEVICE_STATE_ONLINE && + !AllocatedDevices.contains(uuid) && + !SuspendedDevices.contains(uuid); + if (isFreeIncludingDirty) { + nodeDevices.FreeDevicesIncludingDirty.push_back(device); + } + auto& poolNames = PoolKind2PoolNames[device.GetPoolKind()]; auto it = Find(poolNames, device.GetPoolName()); if (it == poolNames.end()) { @@ -197,6 +207,7 @@ void TDeviceList::UpdateDevices( } SortBy(nodeDevices.FreeDevices, TBySortQueryKey()); + SortBy(nodeDevices.FreeDevicesIncludingDirty, TBySortQueryKey()); } void TDeviceList::RemoveDevices(const NProto::TAgentConfig& agent) @@ -396,19 +407,20 @@ bool TDeviceList::ValidateAllocationQuery( return false; } - const TNodeDevices& nodeDevices = nodeItr->second; + TNodeDevices& nodeDevices = nodeItr->second; if (query.ForbiddenRacks.contains(nodeDevices.Rack)) { return false; } + const auto& availableDevices = GetAvailableDevices(&nodeDevices, query); const auto freeDeviceItr = FindIf( - nodeDevices.FreeDevices, + availableDevices, [&targetDeviceId] (const NProto::TDeviceConfig& device) { return device.GetDeviceUUID() == targetDeviceId; }); - if (freeDeviceItr == nodeDevices.FreeDevices.end()) { + if (freeDeviceItr == availableDevices.end()) { return false; } @@ -526,6 +538,15 @@ auto TDeviceList::SelectRacks( return result; } +TVector& TDeviceList::GetAvailableDevices( + TNodeDevices* nodeDevices, + const TAllocationQuery& query) const { + const bool allowDirtyDevices = + query.PoolKind == NProto::DEVICE_POOL_KIND_LOCAL && + true; // TODO: query.allowDirtyLocalDevices; + return allowDirtyDevices ? nodeDevices->FreeDevicesIncludingDirty : nodeDevices->FreeDevices; +} + TVector TDeviceList::CollectDevices( const TAllocationQuery& query, const TString& poolName) @@ -539,13 +560,13 @@ TVector TDeviceList::CollectDevices( for (const auto& rack: SelectRacks(query, poolName)) { for (const auto& node: rack.Nodes) { - const auto* nodeDevices = NodeDevices.FindPtr(node.NodeId); + auto* nodeDevices = NodeDevices.FindPtr(node.NodeId); Y_ABORT_UNLESS(nodeDevices); // finding free devices belonging to this node that match our // query auto [begin, end] = - FindDeviceRange(query, poolName, nodeDevices->FreeDevices); + FindDeviceRange(query, poolName, GetAvailableDevices(nodeDevices, query)); using TDeviceIter = decltype(begin); struct TDeviceInfo @@ -670,9 +691,9 @@ TVector TDeviceList::AllocateDevices( }); auto& nodeDevices = NodeDevices[nodeId]; - + auto& availableDevices = GetAvailableDevices(&nodeDevices, query); for (const auto& arange: aranges) { - nodeDevices.FreeDevices.erase(arange.first, arange.second); + availableDevices.erase(arange.first, arange.second); } } @@ -716,15 +737,20 @@ void TDeviceList::MarkDeviceAsDirty(const TDeviceId& id) void TDeviceList::RemoveDeviceFromFreeList(const TDeviceId& id) { auto nodeId = FindNodeId(id); - + const auto& predicate = [&](const auto& x) + { + return x.GetDeviceUUID() == id; + }; if (nodeId) { auto& devices = NodeDevices[nodeId].FreeDevices; + auto& devicesIncludingDirty = + NodeDevices[nodeId].FreeDevicesIncludingDirty; - auto it = FindIf(devices, [&] (const auto& x) { - return x.GetDeviceUUID() == id; - }); + if (auto* it = FindIf(devices, predicate); it != devices.end()) { + devices.erase(it); + } - if (it != devices.end()) { + if (auto* it = FindIf(devicesIncludingDirty, predicate); it != devices.end()) { devices.erase(it); } } diff --git a/cloud/blockstore/libs/storage/disk_registry/model/device_list.h b/cloud/blockstore/libs/storage/disk_registry/model/device_list.h index a3c4bbdb444..a19b7685ac4 100644 --- a/cloud/blockstore/libs/storage/disk_registry/model/device_list.h +++ b/cloud/blockstore/libs/storage/disk_registry/model/device_list.h @@ -47,6 +47,8 @@ class TDeviceList // sorted by {PoolKind, BlockSize} TVector FreeDevices; + // sorted by {PoolKind, BlockSize} + TVector FreeDevicesIncludingDirty; ui64 TotalSize = 0; }; @@ -182,6 +184,10 @@ class TDeviceList const TDeviceId& id, const NProto::TDeviceConfig& device); void RemoveFromAllDevices(const TDeviceId& id); + // TODO: this maybe class method of nodeDevices + TVector& GetAvailableDevices( + TNodeDevices* nodeDevices, + const TAllocationQuery& query) const; }; //////////////////////////////////////////////////////////////////////////////// diff --git a/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.cpp b/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.cpp index d96bdf1211f..44fee796b33 100644 --- a/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.cpp +++ b/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.cpp @@ -10,9 +10,10 @@ namespace NCloud::NBlockStore::NStorage { NProto::TError TPendingCleanup::Insert( const TString& diskId, - TVector uuids) + TVector uuids, + bool allocation) { - auto error = ValidateInsertion(diskId, uuids); + auto error = ValidateInsertion(diskId, uuids, allocation); if (HasError(error)) { return error; } @@ -21,8 +22,8 @@ NProto::TError TPendingCleanup::Insert( for (auto& uuid: uuids) { Y_DEBUG_ABORT_UNLESS(!uuid.empty()); - auto [_, success] = DeviceToDisk.emplace(std::move(uuid), diskId); - Y_DEBUG_ABORT_UNLESS(success); + auto& [allocatingDiskId, deallocatingDiskId] = DeviceToDisk[uuid]; + (allocation ? allocatingDiskId : deallocatingDiskId) = diskId; } return {}; @@ -35,7 +36,8 @@ NProto::TError TPendingCleanup::Insert(const TString& diskId, TString uuid) [[nodiscard]] NProto::TError TPendingCleanup::ValidateInsertion( const TString& diskId, - const TVector& uuids) const + const TVector& uuids, + bool allocation) const { if (diskId.empty() || uuids.empty()) { return MakeError( @@ -55,42 +57,57 @@ NProto::TError TPendingCleanup::Insert(const TString& diskId, TString uuid) << JoinStrings(uuids, ", ") << "]"); } - const auto* foundDiskId = DeviceToDisk.FindPtr(uuid); - if (foundDiskId) { - return MakeError( - E_ARGUMENT, - TStringBuilder() << "Could not insert device: " << uuid - << "; diskId: " << diskId - << " to PendingCleanup. It was already " - "inserted with the diskId: " - << *foundDiskId); + const auto* foundDisks = DeviceToDisk.FindPtr(uuid); + if (foundDisks) { + const auto& foundDiskId = allocation ? foundDisks->first : foundDisks->second; + if (foundDiskId) { + return MakeError( + E_ARGUMENT, + TStringBuilder() << "Could not insert device: " << uuid + << "; diskId: " << diskId + << " to PendingCleanup. It was already " + "inserted with the diskId: " + << *foundDiskId); + } } } return {}; } -TString TPendingCleanup::EraseDevice(const TString& uuid) +TPendingCleanup::TOpt2Disk TPendingCleanup::EraseDevice(const TString& uuid) { auto it = DeviceToDisk.find(uuid); if (it == DeviceToDisk.end()) { return {}; } - auto diskId = std::move(it->second); - DeviceToDisk.erase(it); + auto& [allocatingDisk, deallocatingDisk] = it->second; + TOpt2Disk ret; - Y_DEBUG_ABORT_UNLESS(DiskToDeviceCount.contains(diskId)); - if (--DiskToDeviceCount[diskId] > 0) { - return {}; + Y_DEBUG_ABORT_UNLESS(allocatingDisk || deallocatingDisk); + Y_DEBUG_ABORT_UNLESS(!allocatingDisk || DiskToDeviceCount.contains(*allocatingDisk)); + Y_DEBUG_ABORT_UNLESS(!deallocatingDisk || DiskToDeviceCount.contains(*deallocatingDisk)); + if (allocatingDisk && --DiskToDeviceCount[*allocatingDisk] <= 0) { + DiskToDeviceCount.erase(*allocatingDisk); + ret.first = std::move(allocatingDisk); + allocatingDisk.reset(); } - DiskToDeviceCount.erase(diskId); + if (deallocatingDisk && --DiskToDeviceCount[*deallocatingDisk] <= 0) { + DiskToDeviceCount.erase(*deallocatingDisk); + ret.second = std::move(deallocatingDisk); + deallocatingDisk.reset(); + } - return diskId; + if (!allocatingDisk && !deallocatingDisk) { + DeviceToDisk.erase(it); + } + + return ret; } -TString TPendingCleanup::FindDiskId(const TString& uuid) const +TPendingCleanup::TOpt2Disk TPendingCleanup::FindDiskId(const TString& uuid) const { auto it = DeviceToDisk.find(uuid); if (it == DeviceToDisk.end()) { @@ -106,9 +123,31 @@ bool TPendingCleanup::EraseDisk(const TString& diskId) return false; } - EraseNodesIf(DeviceToDisk, [&] (auto& x) { - return x.second == diskId; - }); + bool isAllocatingDisk = false; + bool isDellocatingDisk = false; + auto it = std::find_if( + DeviceToDisk.begin(), + DeviceToDisk.end(), + [&](const auto& elem) + { + auto& [allocating, deallocating] = elem.second; + isAllocatingDisk = allocating && *allocating == diskId; + isDellocatingDisk = deallocating && *deallocating == diskId; + return isAllocatingDisk || isDellocatingDisk; + }); + + Y_ABORT_IF(isAllocatingDisk && isDellocatingDisk); + + auto& [allocating, deallocating] = it->second; + if (isAllocatingDisk) { + allocating.reset(); + } else { + deallocating.reset(); + } + + if (!allocating && !deallocating) { + DeviceToDisk.erase(it); + } return true; } diff --git a/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.h b/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.h index 841184ce555..3430a0897ac 100644 --- a/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.h +++ b/cloud/blockstore/libs/storage/disk_registry/model/pending_cleanup.h @@ -15,27 +15,38 @@ namespace NCloud::NBlockStore::NStorage { class TPendingCleanup { +public: + using TAllocatingDiskId = TString; + using TDeallocatingDiskId = TString; + + template + using TOpt2 = std::pair, std::optional>; + using TOpt2Disk = TOpt2; private: THashMap DiskToDeviceCount; - THashMap DeviceToDisk; + THashMap DeviceToDisk; public: [[nodiscard]] NProto::TError Insert( const TString& diskId, - TVector uuids); + TVector uuids, + bool allocation = false); [[nodiscard]] NProto::TError Insert(const TString& diskId, TString uuid); - TString EraseDevice(const TString& uuid); + /// Removes the device from deallocating disk (for pending deallocation disks) + /// @return diskId of allocated/deallocated disk if the device with the given UUID was the last to complete its allocation/deallocation + TOpt2Disk EraseDevice(const TString& uuid); bool EraseDisk(const TString& diskId); - [[nodiscard]] TString FindDiskId(const TString& uuid) const; + [[nodiscard]] TOpt2Disk FindDiskId(const TString& uuid) const; [[nodiscard]] bool IsEmpty() const; [[nodiscard]] bool Contains(const TString& diskId) const; private: [[nodiscard]] NProto::TError ValidateInsertion( const TString& diskId, - const TVector& uuids) const; + const TVector& uuids, + bool allocation) const; }; } // namespace NCloud::NBlockStore::NStorage