diff --git a/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp b/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp index ad9b486e6740..5fabd73846a1 100644 --- a/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp +++ b/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp @@ -137,7 +137,6 @@ class THugeModuleRecoveryActor : public TActorBootstrappedPDiskCtx->Dsk->ChunkSize, HmCtx->PDiskCtx->Dsk->AppendBlockSize, HmCtx->PDiskCtx->Dsk->AppendBlockSize, - oldMinHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, HmCtx->Config->HugeBlobOverhead, @@ -169,7 +167,6 @@ class THugeModuleRecoveryActor : public TActorBootstrappedPDiskCtx->Dsk->ChunkSize, HmCtx->PDiskCtx->Dsk->AppendBlockSize, HmCtx->PDiskCtx->Dsk->AppendBlockSize, - oldMinHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, HmCtx->Config->HugeBlobOverhead, diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp b/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp index 540edfaac511..edfcd7c452b8 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp +++ b/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp @@ -136,9 +136,7 @@ namespace NKikimr { MinHugeBlobInBytes = 512u << 10u; break; } - OldMinHugeBlobInBytes = MinHugeBlobInBytes; // preserved to migrate entry point state correctly MilestoneHugeBlobInBytes = 512u << 10u; // for compatibility reasons it must be 512KB - } void TVDiskConfig::Merge(const NKikimrBlobStorage::TVDiskConfig &update) { diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_config.h b/ydb/core/blobstorage/vdisk/common/vdisk_config.h index dd80ff86cef0..70bfdd1ee1c0 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_config.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_config.h @@ -120,7 +120,6 @@ namespace NKikimr { ui32 HullSstSizeInChunksLevel; ui32 HugeBlobsFreeChunkReservation; ui32 MinHugeBlobInBytes; - ui32 OldMinHugeBlobInBytes; ui32 MilestoneHugeBlobInBytes; ui32 HugeBlobOverhead; ui32 HullCompLevel0MaxSstsAtOnce; diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.cpp b/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.cpp index 55b168cb331d..d2f1ad450e58 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.cpp +++ b/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.cpp @@ -3,23 +3,23 @@ namespace NKikimr { - THugeSlotsMap::THugeSlotsMap(ui32 appendBlockSize, TAllSlotsInfo &&slotsInfo, TSearchTable &&searchTable) + THugeSlotsMap::THugeSlotsMap(ui32 appendBlockSize, ui32 minHugeBlobInBlocks, TAllSlotsInfo &&slotsInfo, + TSearchTable &&searchTable) : AppendBlockSize(appendBlockSize) + , MinHugeBlobInBlocks(minHugeBlobInBlocks) , AllSlotsInfo(std::move(slotsInfo)) , SearchTable(std::move(searchTable)) {} const THugeSlotsMap::TSlotInfo *THugeSlotsMap::GetSlotInfo(ui32 size) const { - ui32 sizeInBlocks = size / AppendBlockSize; - sizeInBlocks += !(sizeInBlocks * AppendBlockSize == size); - const ui64 idx = SearchTable.at(sizeInBlocks); + const ui32 sizeInBlocks = (size + AppendBlockSize - 1) / AppendBlockSize; + Y_ABORT_UNLESS(MinHugeBlobInBlocks <= sizeInBlocks); + const ui64 idx = SearchTable.at(sizeInBlocks - MinHugeBlobInBlocks); return &AllSlotsInfo.at(idx); } ui32 THugeSlotsMap::AlignByBlockSize(ui32 size) const { - ui32 sizeInBlocks = size / AppendBlockSize; - Y_ABORT_UNLESS(sizeInBlocks, "Blob size to align is smaller than a single block. BlobSize# %" PRIu32, size); - return sizeInBlocks * AppendBlockSize; + return Max(MinHugeBlobInBlocks * AppendBlockSize, size - size % AppendBlockSize); } void THugeSlotsMap::Output(IOutputStream &str) const { @@ -31,11 +31,7 @@ namespace NKikimr { str << "]}\n"; str << "{SearchTable# ["; for (const auto &idx : SearchTable) { - if (idx != NoOpIdx) { - AllSlotsInfo.at(idx).Output(str); - } else { - str << "null"; - } + AllSlotsInfo.at(idx).Output(str); str << "\n"; } str << "]}"; diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h b/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h index b6d4f8527b07..1d7e61f04e16 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h @@ -38,16 +38,13 @@ namespace NKikimr { }; // All slot types - using TAllSlotsInfo = TVector; + using TAllSlotsInfo = std::vector; // Type to address TAllSlotsInfo using TIndex = ui16; // Size in AppendBlockSize -> index in TAllSlotsInfo - using TSearchTable = TVector; - // Idx that indicates there is no record for it in TAllSlotsInfo - static constexpr TIndex NoOpIdx = Max(); + using TSearchTable = std::vector; - - THugeSlotsMap(ui32 appendBlockSize, TAllSlotsInfo &&slotsInfo, TSearchTable &&searchTable); + THugeSlotsMap(ui32 appendBlockSize, ui32 minHugeBlobInBlocks, TAllSlotsInfo &&slotsInfo, TSearchTable &&searchTable); const TSlotInfo *GetSlotInfo(ui32 size) const; ui32 AlignByBlockSize(ui32 size) const; void Output(IOutputStream &str) const; @@ -55,6 +52,7 @@ namespace NKikimr { private: const ui32 AppendBlockSize; + const ui32 MinHugeBlobInBlocks; TAllSlotsInfo AllSlotsInfo; TSearchTable SearchTable; }; diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhuge_ut.cpp b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhuge_ut.cpp index 2aa98ea92cc0..699f5ca6ef79 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhuge_ut.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhuge_ut.cpp @@ -17,7 +17,6 @@ namespace NKikimr { Y_UNIT_TEST(SerializeParse) { ui32 chunkSize = 134274560u; ui32 appendBlockSize = 56896u; - ui32 minHugeBlobInBytes = 512u << 10u; ui32 milestoneHugeBlobInBytes = 512u << 10u; ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; @@ -29,8 +28,8 @@ namespace NKikimr { auto vctx = MakeIntrusive(TActorId(), info->PickTopology(), counters, TVDiskID(0, 1, 0, 0, 0), nullptr, NPDisk::DEVICE_TYPE_UNKNOWN); std::unique_ptr state( - new THullHugeKeeperPersState(vctx, chunkSize, appendBlockSize, appendBlockSize, - minHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, + new THullHugeKeeperPersState(vctx, chunkSize, appendBlockSize, + appendBlockSize, milestoneHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation, logf)); state->LogPos = THullHugeRecoveryLogPos(0, 0, 100500, 50000, 70000, 56789, 39482); diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.cpp b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.cpp index be27874e3f7e..a6ff46e10788 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.cpp @@ -2,6 +2,8 @@ #include +#include + namespace NKikimr { namespace NHuge { @@ -24,7 +26,7 @@ namespace NKikimr { } void TChainLayoutBuilder::Output(IOutputStream &str, ui32 appendBlockSize) const { - str << "CHAIN TABLE (MilesoneId=" << MilesoneId << " rows=" << Layout.size() << "):\n"; + str << "CHAIN TABLE (MilestoneId=" << MilestoneId << " rows=" << Layout.size() << "):\n"; for (const auto &x : Layout) { str << "Blocks# (" << x.Left << ", " << x.Right << "]"; if (appendBlockSize) { @@ -64,7 +66,7 @@ namespace NKikimr { } void TChainLayoutBuilder::BuildUpward(ui32 left, ui32 right, ui32 overhead) { - MilesoneId = Layout.size(); + MilestoneId = Layout.size(); ui32 valBlocks = left; ui32 shiftBlocks = 0; @@ -83,6 +85,21 @@ namespace NKikimr { //////////////////////////////////////////////////////////////////////////// // TChain //////////////////////////////////////////////////////////////////////////// + THugeSlot TChain::Convert(const NPrivate::TChunkSlot& id) const { + return THugeSlot(id.GetChunkId(), id.GetSlotId() * SlotSize, SlotSize); + } + + NPrivate::TChunkSlot TChain::Convert(const TDiskPart& addr) const { + ui32 slotId = addr.Offset / SlotSize; + Y_VERIFY_S(slotId * SlotSize == addr.Offset, VDiskLogPrefix << "slotId# " << slotId + << " addr# " << addr.ToString() << " State# " << ToString()); + return NPrivate::TChunkSlot(addr.ChunkIdx, slotId); + } + + NPrivate::TChunkSlot TChain::Convert(const THugeSlot& slot) const { + return Convert(slot.GetDiskPart()); + } + TMask TChain::BuildConstMask(const TString &prefix, ui32 slotsInChunk) { Y_VERIFY_S(1 < slotsInChunk && slotsInChunk <= MaxNumberOfSlots, prefix << "It's not a good idea to have so many slots in chunk;" @@ -250,73 +267,114 @@ namespace NKikimr { void TChain::Save(IOutputStream *s) const { ::Save(s, SlotsInChunk); ::Save(s, AllocatedSlots); - if (LockedChunks) { - TFreeSpace temp(FreeSpace); - temp.insert(LockedChunks.begin(), LockedChunks.end()); - ::Save(s, temp); - } else { - ::Save(s, FreeSpace); - } + ::SaveSize(s, FreeSpace.size() + LockedChunks.size()); + ForEachFreeSpaceChunk(std::bind(&::Save, s, std::placeholders::_1)); } - void TChain::Load(IInputStream *s) { - FreeSpace.clear(); - ui32 slotsInChunk = 0; + TChain TChain::Load(IInputStream *s, TString vdiskLogPrefix, ui32 appendBlockSize, ui32 blocksInChunk, + std::span chains, bool *compatible) { + ui32 slotsInChunk; ::Load(s, slotsInChunk); - Y_VERIFY_S(slotsInChunk == SlotsInChunk, VDiskLogPrefix - << "slotsInChunk# " << slotsInChunk << " SlotsInChunk# " << SlotsInChunk); - ::Load(s, AllocatedSlots); - ::Load(s, FreeSpace); - FreeSlotsInFreeSpace = 0; - for (const auto &[chunkId, mask] : FreeSpace) { - // all 1 in mask -- free slots - // 0 - slot is in use - FreeSlotsInFreeSpace += mask.Count(); + + const ui32 slotSizeInBlocks = blocksInChunk / slotsInChunk; + Y_ABORT_UNLESS(slotSizeInBlocks); + ui32 slotSize = slotSizeInBlocks * appendBlockSize; // assume optimal slot size for specific slots per chunk + + // check if this goes with compatible chain + for (const TChain& chainFromBuilder : chains) { + if (chainFromBuilder.SlotsInChunk < slotsInChunk) { // no such chain from builder + *compatible = false; + } else if (chainFromBuilder.SlotsInChunk == slotsInChunk) { + slotSize = chainFromBuilder.SlotSize; + } else { + continue; + } + break; + } + + TChain res{ + std::move(vdiskLogPrefix), + slotsInChunk, + slotSize, // in bytes + }; + + ::Load(s, res.AllocatedSlots); + ::Load(s, res.FreeSpace); + for (const auto& [chunkId, mask] : res.FreeSpace) { + res.FreeSlotsInFreeSpace += mask.Count(); } + + return res; } bool TChain::HaveBeenUsed() const { - return AllocatedSlots != 0 || !FreeSpace.empty(); + return AllocatedSlots; // chain is considered to be used if it contains any allocated slots } TString TChain::ToString() const { TStringStream str; - auto output = [&str] (const TFreeSpace &c) { - for (const auto &x : c) { - for (size_t i = 0; i < x.second.Size(); i++) { - if (x.second.Test(i)) - str << " [" << x.first << " " << i << "]"; + + str << "{" << SlotSize << '/' << SlotsInChunk << " AllocatedSlots# " << AllocatedSlots << " Free#"; + + bool any = false; + ForEachFreeSpaceChunk([&](const auto& value) { + const auto& [chunk, bitmap] = value; + str << " {" << chunk; + ui32 begin; + ui32 prev = Max(); + Y_FOR_EACH_BIT(i, bitmap) { + if (prev == Max()) { + begin = i; + } else if (i != prev + 1) { + str << ' ' << begin; + if (begin != prev) { + str << '-' << prev; + } + begin = prev; } + prev = i; } - }; + str << ' ' << begin; + if (begin != prev) { + str << '-' << prev; + } + str << '}'; + + any = true; + }); + + if (!any) { + str << " none"; + } - str << "{AllocatedSlots# " << AllocatedSlots << " [ChunkId FreeSlot]:"; - output(FreeSpace); - output(LockedChunks); str << "}"; return str.Str(); } void TChain::RenderHtml(IOutputStream &str) const { - auto output = [&str] (const TFreeSpace &c) { - for (const auto &x : c) { - size_t freeSlots = 0; - for (size_t i = 0; i < x.second.Size(); i++) { - if (x.second.Test(i)) - ++freeSlots; + HTML(str) { + TABLER() { + TABLED() { + str << SlotSize << "/" << SlotsInChunk; } - if (freeSlots) { - str << " [" << x.first << " " << freeSlots << "]"; + TABLED() { + ForEachFreeSpaceChunk([&](const auto& value) { + const auto& [chunk, bitmap] = value; + str << " [" << chunk << " " << bitmap.Count() << "]"; + }); } } - }; - - output(FreeSpace); - output(LockedChunks); + } } - ui32 TChain::GetAllocatedSlots() const { - return AllocatedSlots; + void TChain::RenderHtmlForUsage(IOutputStream &str) const { + HTML(str) { + TABLER() { + TABLED() { str << SlotSize; } + TABLED() { str << SlotsInChunk; } + TABLED() { str << AllocatedSlots; } + } + } } void TChain::GetOwnedChunks(TSet& chunks) const { @@ -329,87 +387,6 @@ namespace NKikimr { traverse(LockedChunks); } - //////////////////////////////////////////////////////////////////////////// - // TChainDelegator - //////////////////////////////////////////////////////////////////////////// - TChainDelegator::TChainDelegator(const TString &vdiskLogPrefix, ui32 valBlocks, ui32 shiftBlocks, - ui32 chunkSize, ui32 appendBlockSize) - : VDiskLogPrefix(vdiskLogPrefix) - , Blocks(valBlocks) - , ShiftInBlocks(shiftBlocks) - , SlotsInChunk(0) - , SlotSize(0) - { - ui32 slotSizeInBlocks = Blocks + ShiftInBlocks; - ui32 blocksInChunk = chunkSize / appendBlockSize; - Y_VERIFY_S(appendBlockSize * blocksInChunk == chunkSize, VDiskLogPrefix - << "Blocks# " << Blocks << " ShiftInBlocks# " << ShiftInBlocks - << " chunkSize# " << chunkSize << " appendBlockSize# " << appendBlockSize); - - SlotsInChunk = blocksInChunk / slotSizeInBlocks; - SlotSize = slotSizeInBlocks * appendBlockSize; - - ChainPtr = MakeIntrusive(vdiskLogPrefix, SlotsInChunk); - } - - THugeSlot TChainDelegator::Convert(const NPrivate::TChunkSlot &id) const { - return THugeSlot(id.GetChunkId(), id.GetSlotId() * SlotSize, SlotSize); - } - - NPrivate::TChunkSlot TChainDelegator::Convert(const TDiskPart &addr) const { - ui32 slotId = addr.Offset / SlotSize; - Y_VERIFY_S(slotId * SlotSize == addr.Offset, VDiskLogPrefix - << "slotId# " << slotId << " addr# " << addr.ToString() << " State# " << ToString()); - return NPrivate::TChunkSlot(addr.ChunkIdx, slotId); - } - - NPrivate::TChunkSlot TChainDelegator::Convert(const THugeSlot &slot) const { - return Convert(slot.GetDiskPart()); - } - - void TChainDelegator::Save(IOutputStream *s) const { - ::Save(s, *ChainPtr); - } - - void TChainDelegator::Load(IInputStream *s) { - ::Load(s, *ChainPtr); - } - - bool TChainDelegator::HaveBeenUsed() const { - return ChainPtr->HaveBeenUsed(); - } - - TString TChainDelegator::ToString() const { - TStringStream str; - str << "{[SlotSize, SlotsInChunk]: [" << SlotSize << ", " << SlotsInChunk << "] " - << ChainPtr->ToString() << "}"; - return str.Str(); - } - - void TChainDelegator::GetOwnedChunks(TSet& chunks) const { - ChainPtr->GetOwnedChunks(chunks); - } - - void TChainDelegator::RenderHtml(IOutputStream &str) const { - HTML(str) { - TABLER() { - TABLED() {str << SlotSize << " / " << SlotsInChunk;} - TABLED() {ChainPtr->RenderHtml(str);} - } - } - } - - void TChainDelegator::RenderHtmlForUsage(IOutputStream &str) const { - HTML(str) { - TABLER() { - TABLED() {str << SlotSize;} - TABLED() {str << SlotsInChunk;} - TABLED() {str << ChainPtr->GetAllocatedSlots();} - } - } - } - - //////////////////////////////////////////////////////////////////////////// // TAllChains //////////////////////////////////////////////////////////////////////////// @@ -418,7 +395,6 @@ namespace NKikimr { ui32 chunkSize, ui32 appendBlockSize, ui32 minHugeBlobInBytes, - ui32 oldMinHugeBlobSizeInBytes, ui32 milestoneBlobInBytes, ui32 maxBlobInBytes, ui32 overhead) @@ -426,143 +402,137 @@ namespace NKikimr { , ChunkSize(chunkSize) , AppendBlockSize(appendBlockSize) , MinHugeBlobInBytes(minHugeBlobInBytes) - , OldMinHugeBlobSizeInBytes(oldMinHugeBlobSizeInBytes) , MilestoneBlobInBytes(milestoneBlobInBytes) , MaxBlobInBytes(maxBlobInBytes) , Overhead(overhead) + , MinHugeBlobInBlocks(MinHugeBlobInBytes / AppendBlockSize) { - Y_VERIFY_S(MinHugeBlobInBytes != 0 && - MinHugeBlobInBytes >= AppendBlockSize && + Y_VERIFY_S(MinHugeBlobInBytes && MinHugeBlobInBytes <= MilestoneBlobInBytes && - MinHugeBlobInBytes <= OldMinHugeBlobSizeInBytes && MilestoneBlobInBytes < MaxBlobInBytes, "INVALID CONFIGURATION! (SETTINGS ARE:" - << " MaxBlobInBytes# " << MaxBlobInBytes << " MinHugeBlobInBytes# " << MinHugeBlobInBytes << " OldMinHugeBlobSizeInBytes# " << OldMinHugeBlobSizeInBytes + << " MaxBlobInBytes# " << MaxBlobInBytes << " MinHugeBlobInBytes# " << MinHugeBlobInBytes << " MilestoneBlobInBytes# " << MilestoneBlobInBytes << " ChunkSize# " << ChunkSize << " AppendBlockSize# " << AppendBlockSize << ")"); - BuildLayout(); - } - - TChainDelegator *TAllChains::GetChain(ui32 size) { - return SearchTable.at(SizeToBlocks(size)); - } - const TChainDelegator *TAllChains::GetChain(ui32 size) const { - return SearchTable.at(SizeToBlocks(size)); + BuildChains(); } - THeapStat TAllChains::GetStat() const { - THeapStat stat; - for (const auto &x : ChainDelegators) { - stat += x.ChainPtr->GetStat(); + TChain *TAllChains::GetChain(ui32 size) { + if (size < MinHugeBlobInBytes || GetEndBlocks() * AppendBlockSize < size) { + return nullptr; } - return stat; + const size_t index = SizeToBlocks(size) - MinHugeBlobInBlocks; + Y_DEBUG_ABORT_UNLESS(index < SearchTable.size()); + const size_t chainIndex = SearchTable[index]; + Y_DEBUG_ABORT_UNLESS(chainIndex < Chains.size()); + return &Chains[chainIndex]; } - void TAllChains::PrintOutChains(IOutputStream &str) const { - str << "CHAIN TABLE (rows=" << ChainDelegators.size() << "):\n"; - for (const auto &x : ChainDelegators) { - str << "Blocks# (" << x.Blocks << ", " << (x.Blocks + x.ShiftInBlocks) << "] " - << "Bytes# (" << (x.Blocks * AppendBlockSize) << ", " - << ((x.Blocks + x.ShiftInBlocks) * AppendBlockSize) << "] " - << " SlotSize# " << x.SlotSize << " SlotsInChunk# " << x.SlotsInChunk << "\n"; + const TChain *TAllChains::GetChain(ui32 size) const { + if (size < MinHugeBlobInBytes || GetEndBlocks() * AppendBlockSize < size) { + return nullptr; } + Y_ABORT_UNLESS(MinHugeBlobInBytes <= size); + const size_t index = SizeToBlocks(size) - MinHugeBlobInBlocks; + Y_DEBUG_ABORT_UNLESS(index < SearchTable.size()); + const size_t chainIndex = SearchTable[index]; + Y_DEBUG_ABORT_UNLESS(chainIndex < Chains.size()); + return &Chains[chainIndex]; } - void TAllChains::PrintOutSearchTable(IOutputStream &str) const { - str << "SEARCH TABLE:\n"; - for (const auto &x : SearchTable) { - str << (&x - &SearchTable[0]) << " idx: "; - if (x) { - str << x - &ChainDelegators[0]; - } else { - str << "null"; - } - str << "\n"; + THeapStat TAllChains::GetStat() const { + THeapStat stat; + for (const auto& chain : Chains) { + stat += chain.GetStat(); } + return stat; } void TAllChains::Save(IOutputStream *s) const { - if (StartMode == EStartMode::Loaded) { - ui32 size = ChainDelegators.size(); - ::Save(s, size); - for (auto& d : ChainDelegators) { - ::Save(s, d); - } - } else { - std::vector delegators; - for (auto &x : ChainDelegators) { - if (!x.HaveBeenUsed() && x.SlotSize < FirstLoadedSlotSize) { - continue; // preserving backward compatibility until no allocations + // check if we can write compatible entrypoint (with exactly the same set of chains) + bool writeCompatible = true; + ui32 numChains = 0; + if (DeserializedSlotSizes.empty()) { // if this was initially empty heap, write it fully + writeCompatible = false; + } else { + for (auto& chain : Chains) { + if (DeserializedSlotSizes.contains(chain.SlotSize)) { + ++numChains; + } else if (chain.HaveBeenUsed()) { + writeCompatible = false; + break; } - delegators.emplace_back(&x); } + } - ui32 size = delegators.size(); - ::Save(s, size); - for (auto x : delegators) { - ::Save(s, *x); - } + if (!writeCompatible) { // we can't, so we serialize all our chains anyway + numChains = Chains.size(); } + ::Save(s, numChains); + + // serialize selected chains + auto chains = Chains | std::views::filter([&](const auto& chain) { + return !writeCompatible || DeserializedSlotSizes.contains(chain.SlotSize); + }); + ::SaveRange(s, chains.begin(), chains.end()); } void TAllChains::Load(IInputStream *s) { - ui32 size = 0; - // load array size - ::Load(s, size); - if (size == ChainDelegators.size()) { - StartMode = EStartMode::Loaded; - // load map and current map are of the same size, just load it - for (auto &x : ChainDelegators) { - ::Load(s, x); - } - } else if (size < ChainDelegators.size()) { - // map size has been changed, run migration - StartMode = EStartMode::Migrated; - TAllChainDelegators chainDelegators = BuildChains(OldMinHugeBlobSizeInBytes); - Y_VERIFY_S(size > 0 && size == chainDelegators.size(), "size# " << size - << " chainDelegators.size()# " << chainDelegators.size()); - - // load into temporary delegators - for (auto &x : chainDelegators) { - ::Load(s, x); - } + std::vector newChains; + newChains.reserve(Chains.size()); + + auto chainsIt = Chains.begin(); + const auto chainsEnd = Chains.end(); + + Y_DEBUG_ABORT_UNLESS(ChunkSize % AppendBlockSize == 0); + Y_DEBUG_ABORT_UNLESS(AppendBlockSize <= ChunkSize); + const ui32 blocksInChunk = ChunkSize / AppendBlockSize; + + bool compatible = true; // if the entrypoint is compatible with chain layout builder + ui32 prevSlotSize = 0; + + ui32 size; + for (::Load(s, size); size; --size) { + auto chain = TChain::Load(s, VDiskLogPrefix, AppendBlockSize, blocksInChunk, {chainsIt, chainsEnd}, + &compatible); + DeserializedSlotSizes.insert(chain.SlotSize); - // migrate - using TIt = TAllChainDelegators::iterator; - TIt loadedIt = chainDelegators.begin(); - TIt loadedEnd = chainDelegators.end(); - FirstLoadedSlotSize = loadedIt->SlotSize; - for (TIt it = ChainDelegators.begin(); it != ChainDelegators.end(); ++it) { - Y_ABORT_UNLESS(loadedIt != loadedEnd); - if (loadedIt->SlotSize == it->SlotSize) { - *it = std::move(*loadedIt); - ++loadedIt; + Y_ABORT_UNLESS(chain.SlotSize > std::exchange(prevSlotSize, chain.SlotSize)); + + for (; chainsIt != chainsEnd && chainsIt->SlotSize <= chain.SlotSize; ++chainsIt) { + if (chainsIt->SlotSize == chain.SlotSize) { + Y_ABORT_UNLESS(chain.SlotsInChunk == chainsIt->SlotsInChunk); + Y_ABORT_UNLESS(!chainsIt->HaveBeenUsed()); + } else { + newChains.push_back(std::move(*chainsIt)); } } - Y_ABORT_UNLESS(loadedIt == loadedEnd); - } else { - // entry point size rollback case - Y_ABORT_UNLESS(size > ChainDelegators.size()); - ui32 curChainDelegatorsSize = ChainDelegators.size(); - Y_FAIL_S("Impossible case; MinHugeBlobInBytes# " << MinHugeBlobInBytes - << " MilestoneBlobInBytes# " << MilestoneBlobInBytes - << " loadedSize# " << size - << " curChainDelegatorsSize# " << curChainDelegatorsSize); + + newChains.push_back(std::move(chain)); + } + + std::ranges::move(chainsIt, chainsEnd, std::back_inserter(newChains)); + + Chains = std::move(newChains); + if (!compatible) { // deserialized slot sizes can't be stored in compatible way + DeserializedSlotSizes.clear(); } } void TAllChains::GetOwnedChunks(TSet& chunks) const { - for (const TChainDelegator& delegator : ChainDelegators) { - delegator.GetOwnedChunks(chunks); + for (const TChain& chain : Chains) { + chain.GetOwnedChunks(chunks); } } TString TAllChains::ToString() const { TStringStream str; - str << "{ChunkSize# " << ChunkSize << " AppendBlockSize# " << AppendBlockSize - << " MinHugeBlobInBytes# " << MinHugeBlobInBytes << " MaxBlobInBytes# " << MaxBlobInBytes; - for (const auto & x : ChainDelegators) { - str << " {CHAIN " << x.ToString() << "}"; + str << "{ChunkSize# " << ChunkSize + << " AppendBlockSize# " << AppendBlockSize + << " MinHugeBlobInBytes# " << MinHugeBlobInBytes + << " MaxBlobInBytes# " << MaxBlobInBytes; + for (const auto& chain : Chains) { + str << " {CHAIN " << chain.ToString() << "}"; } str << "}"; return str.Str(); @@ -578,8 +548,9 @@ namespace NKikimr { } } TABLEBODY() { - for (const auto & x : ChainDelegators) - x.RenderHtml(str); + for (const auto& chain : Chains) { + chain.RenderHtml(str); + } } } } @@ -596,8 +567,9 @@ namespace NKikimr { } } TABLEBODY() { - for (const auto & x : ChainDelegators) - x.RenderHtmlForUsage(str); + for (const auto& chain : Chains) { + chain.RenderHtmlForUsage(str); + } } } } @@ -605,91 +577,87 @@ namespace NKikimr { TVector TAllChains::GetLayout() const { TVector res; - res.reserve(ChainDelegators.size()); - for (const auto &x : ChainDelegators) { - res.push_back(NPrivate::TChainLayoutBuilder::TSeg {x.Blocks, x.Blocks + x.ShiftInBlocks} ); + res.reserve(Chains.size()); + ui32 prevSlotSizeInBlocks = MinHugeBlobInBlocks; + for (const auto& chain : Chains) { + const ui32 slotSizeInBlocks = chain.SlotSize / AppendBlockSize; + res.push_back({ + prevSlotSizeInBlocks, + slotSizeInBlocks - prevSlotSizeInBlocks, + }); + prevSlotSizeInBlocks = slotSizeInBlocks; } return res; } std::shared_ptr TAllChains::BuildHugeSlotsMap() const { - THugeSlotsMap::TAllSlotsInfo targetAllSlotsInfo; - THugeSlotsMap::TSearchTable targetSearchTable; - - for (const auto &x : SearchTable) { - if (!x) { - // first records in SearchTable are equal to nullptr - targetSearchTable.push_back(THugeSlotsMap::NoOpIdx); - continue; - } - - if (targetAllSlotsInfo.empty() || targetAllSlotsInfo.back().SlotSize != x->SlotSize) { - targetAllSlotsInfo.emplace_back(x->SlotSize, x->SlotsInChunk); - } - targetSearchTable.push_back(THugeSlotsMap::TIndex(targetAllSlotsInfo.size() - 1)); + THugeSlotsMap::TAllSlotsInfo allSlotsInfo; + for (const auto& chain : Chains) { + allSlotsInfo.emplace_back(chain.SlotSize, chain.SlotsInChunk); } - - return std::make_shared(AppendBlockSize, std::move(targetAllSlotsInfo), - std::move(targetSearchTable)); + return std::make_shared(AppendBlockSize, MinHugeBlobInBlocks, std::move(allSlotsInfo), + THugeSlotsMap::TSearchTable(SearchTable)); } //////////////////////////////////////////////////////////////////////////// // TAllChains: Private //////////////////////////////////////////////////////////////////////////// - TAllChains::TAllChainDelegators TAllChains::BuildChains(ui32 minHugeBlobInBytes) const { - // minHugeBlobInBytes -- is the only variable parameter, used for migration - const ui32 startBlocks = minHugeBlobInBytes / AppendBlockSize; - const ui32 mileStoneBlocks = MilestoneBlobInBytes / AppendBlockSize; + void TAllChains::BuildChains() { + const ui32 startBlocks = MinHugeBlobInBlocks; + const ui32 milestoneBlocks = MilestoneBlobInBytes / AppendBlockSize; const ui32 endBlocks = GetEndBlocks(); - NPrivate::TChainLayoutBuilder builder(startBlocks, mileStoneBlocks, endBlocks, Overhead); - Y_ABORT_UNLESS(!builder.GetLayout().empty()); + NPrivate::TChainLayoutBuilder builder(startBlocks, milestoneBlocks, endBlocks, Overhead); + const ui32 blocksInChunk = ChunkSize / AppendBlockSize; - TAllChainDelegators result; for (auto x : builder.GetLayout()) { - result.emplace_back(VDiskLogPrefix, x.Left, x.Right - x.Left, - ChunkSize, AppendBlockSize); + const ui32 slotSizeInBlocks = x.Right; + const ui32 slotSize = slotSizeInBlocks * AppendBlockSize; + const ui32 slotsInChunk = blocksInChunk / slotSizeInBlocks; + Chains.emplace_back(VDiskLogPrefix, slotsInChunk, slotSize); } - return result; + + Y_ABORT_UNLESS(!Chains.empty()); } void TAllChains::BuildSearchTable() { - const ui32 endBlocks = GetEndBlocks(); - Y_DEBUG_ABORT_UNLESS(!ChainDelegators.empty()); - TAllChainDelegators::iterator it = ChainDelegators.begin(); - TChainDelegator *ptr = nullptr; - ui32 blocks = it->Blocks; - for (ui32 i = 0; i <= endBlocks; i++) { - if (i <= blocks) { - } else { - ptr = &(*it); + Y_ABORT_UNLESS(SearchTable.empty()); + Y_ABORT_UNLESS(!Chains.empty()); + + const ui32 startBlocks = MinHugeBlobInBlocks; + const ui32 minSize = startBlocks * AppendBlockSize; + const ui32 endBlocks = GetEndBlocks(); // maximum possible number of blocks per huge blob + auto it = Chains.begin(); + ui16 index = 0; + + SearchTable.reserve(endBlocks - startBlocks + 1); + for (ui32 i = startBlocks, size = minSize; i <= endBlocks; ++i, size += AppendBlockSize) { + if (it->SlotSize < size) { // size doesn't fit in current chain, but it must fit into next one ++it; - if (it == ChainDelegators.end()) - blocks = ui32(-1); - else - blocks = it->Blocks; + Y_ABORT_UNLESS(it != Chains.end()); + Y_ABORT_UNLESS(size <= it->SlotSize); + Y_ABORT_UNLESS(index != Max()); + ++index; } - SearchTable.push_back(ptr); + SearchTable.push_back(index); } } - void TAllChains::BuildLayout() - { - ChainDelegators = BuildChains(MinHugeBlobInBytes); - Y_ABORT_UNLESS(!ChainDelegators.empty()); - BuildSearchTable(); + ui32 TAllChains::SizeToBlocks(ui32 size) const { + return (size + AppendBlockSize - 1) / AppendBlockSize; } - inline ui32 TAllChains::SizeToBlocks(ui32 size) const { - ui32 sizeInBlocks = size / AppendBlockSize; - sizeInBlocks += !(sizeInBlocks * AppendBlockSize == size); - return sizeInBlocks; + ui32 TAllChains::GetEndBlocks() const { + return SizeToBlocks(MaxBlobInBytes); } - inline ui32 TAllChains::GetEndBlocks() const { - ui32 endBlocks = MaxBlobInBytes / AppendBlockSize; - endBlocks += !(endBlocks * AppendBlockSize == MaxBlobInBytes); - return endBlocks; + void TAllChains::FinishRecovery() { + ui32 prevSlotSize = 0; + for (const TChain& chain : Chains) { + Y_ABORT_UNLESS(prevSlotSize < chain.SlotSize); + prevSlotSize = chain.SlotSize; + } + BuildSearchTable(); } //////////////////////////////////////////////////////////////////////////// @@ -701,7 +669,6 @@ namespace NKikimr { ui32 chunkSize, ui32 appendBlockSize, ui32 minHugeBlobInBytes, - ui32 oldMinHugeBlobSizeInBytes, ui32 mileStoneBlobInBytes, ui32 maxBlobInBytes, ui32 overhead, @@ -709,42 +676,41 @@ namespace NKikimr { : VDiskLogPrefix(vdiskLogPrefix) , FreeChunksReservation(freeChunksReservation) , FreeChunks() - , Chains(vdiskLogPrefix, chunkSize, appendBlockSize, minHugeBlobInBytes, oldMinHugeBlobSizeInBytes, - mileStoneBlobInBytes, maxBlobInBytes, overhead) + , Chains(vdiskLogPrefix, chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + maxBlobInBytes, overhead) {} ////////////////////////////////////////////////////////////////////////////////////////// // THeap: main functions ////////////////////////////////////////////////////////////////////////////////////////// - THugeSlot THeap::ConvertDiskPartToHugeSlot(const TDiskPart &addr) const { - const TChainDelegator *chainD = Chains.GetChain(addr.Size); - Y_VERIFY_S(chainD && (addr.Offset / chainD->SlotSize * chainD->SlotSize == addr.Offset), VDiskLogPrefix - << "chainD# " << (chainD ? chainD->ToString() : "nullptr") << " addr# " << addr.ToString()); - return THugeSlot(addr.ChunkIdx, addr.Offset, chainD->SlotSize); + THugeSlot THeap::ConvertDiskPartToHugeSlot(const TDiskPart& addr) const { + const TChain *chain = Chains.GetChain(addr.Size); + Y_ABORT_UNLESS(chain); + return chain->Convert(chain->Convert(addr)); } bool THeap::Allocate(ui32 size, THugeSlot *hugeSlot, ui32 *slotSize) { - TChainDelegator *chainD = Chains.GetChain(size); - Y_VERIFY_S(chainD, VDiskLogPrefix << "size# " << size << " Heap# " << ToString()); - *slotSize = chainD->SlotSize; + TChain *chain = Chains.GetChain(size); + Y_VERIFY_S(chain, VDiskLogPrefix << "size# " << size << " Heap# " << ToString()); + *slotSize = chain->SlotSize; NPrivate::TChunkSlot id; - if (!chainD->ChainPtr->Allocate(&id)) { // no available slot in free space of the chain + if (!chain->Allocate(&id)) { // no available slot in free space of the chain if (FreeChunks.empty()) { // no free chunks left for reuse -- request a new chunk return false; } - chainD->ChainPtr->Allocate(&id, GetChunkIdFromFreeChunks()); // reuse free chunk for this chain + chain->Allocate(&id, GetChunkIdFromFreeChunks()); // reuse free chunk for this chain } - *hugeSlot = chainD->Convert(id); + *hugeSlot = chain->Convert(id); return true; } TFreeRes THeap::Free(const TDiskPart &addr) { ui32 size = addr.Size; - TChainDelegator *chainD = Chains.GetChain(size); - Y_ABORT_UNLESS(chainD); + TChain *chain = Chains.GetChain(size); + Y_ABORT_UNLESS(chain); - TFreeRes res = chainD->ChainPtr->Free(chainD->Convert(addr)); + TFreeRes res = chain->Free(chain->Convert(addr)); if (res.ChunkId) { PutChunkIdToFreeChunks(res.ChunkId); } @@ -766,13 +732,19 @@ namespace NKikimr { } bool THeap::LockChunkForAllocation(ui32 chunkId, ui32 slotSize) { - TChainDelegator *cd = Chains.GetChain(slotSize); - return cd->ChainPtr->LockChunkForAllocation(chunkId); + TChain *chain = Chains.GetChain(slotSize); + Y_ABORT_UNLESS(chain); + return chain->LockChunkForAllocation(chunkId); } void THeap::UnlockChunk(ui32 chunkId, ui32 slotSize) { - TChainDelegator *cd = Chains.GetChain(slotSize); - cd->ChainPtr->UnlockChunk(chunkId); + TChain *chain = Chains.GetChain(slotSize); + Y_ABORT_UNLESS(chain); + chain->UnlockChunk(chunkId); + } + + void THeap::FinishRecovery() { + Chains.FinishRecovery(); } THeapStat THeap::GetStat() const { @@ -788,11 +760,11 @@ namespace NKikimr { void THeap::RecoveryModeAllocate(const TDiskPart &addr) { ui32 size = addr.Size; - TChainDelegator *chainD = Chains.GetChain(size); - Y_VERIFY_S(chainD, VDiskLogPrefix << "State# " << ToString()); + TChain *chain = Chains.GetChain(size); + Y_VERIFY_S(chain, VDiskLogPrefix << "State# " << ToString()); - NPrivate::TChunkSlot id(chainD->Convert(addr)); - bool allocated = chainD->ChainPtr->RecoveryModeAllocate(id); + NPrivate::TChunkSlot id(chain->Convert(addr)); + bool allocated = chain->RecoveryModeAllocate(id); if (allocated) { return; } else { @@ -800,7 +772,7 @@ namespace NKikimr { TFreeChunks::iterator it = FreeChunks.find(chunkId); Y_VERIFY_S(it != FreeChunks.end(), VDiskLogPrefix << "addr# " << addr.ToString() << " State# " << ToString()); FreeChunks.erase(it); - chainD->ChainPtr->RecoveryModeAllocate(id, chunkId, false); + chain->RecoveryModeAllocate(id, chunkId, false); } } @@ -817,9 +789,9 @@ namespace NKikimr { } bool THeap::ReleaseSlot(THugeSlot slot) { - TChainDelegator* const chain = Chains.GetChain(slot.GetSize()); + TChain* const chain = Chains.GetChain(slot.GetSize()); Y_VERIFY_S(chain, VDiskLogPrefix << "State# " << ToString() << " slot# " << slot.ToString()); - if (TFreeRes res = chain->ChainPtr->Free(chain->Convert(slot)); res.ChunkId) { + if (TFreeRes res = chain->Free(chain->Convert(slot)); res.ChunkId) { PutChunkIdToFreeChunks(res.ChunkId); return res.InLockedChunks; } @@ -827,12 +799,12 @@ namespace NKikimr { } void THeap::OccupySlot(THugeSlot slot, bool inLockedChunks) { - TChainDelegator* const chain = Chains.GetChain(slot.GetSize()); + TChain* const chain = Chains.GetChain(slot.GetSize()); Y_VERIFY_S(chain, VDiskLogPrefix << "State# " << ToString() << " slot# " << slot.ToString()); - if (!chain->ChainPtr->RecoveryModeAllocate(chain->Convert(slot))) { + if (!chain->RecoveryModeAllocate(chain->Convert(slot))) { const size_t numErased = FreeChunks.erase(slot.GetChunkId()); Y_VERIFY_S(numErased, VDiskLogPrefix << "State# " << ToString() << " slot# " << slot.ToString()); - chain->ChainPtr->RecoveryModeAllocate(chain->Convert(slot), slot.GetChunkId(), inLockedChunks); + chain->RecoveryModeAllocate(chain->Convert(slot), slot.GetChunkId(), inLockedChunks); } } @@ -899,9 +871,8 @@ namespace NKikimr { TString THeap::ToString() const { TStringStream str; - str << "FreeChunks: "; - str << FormatList(FreeChunks); - str << " CHAINS: " << Chains.ToString(); + str << "FreeChunks# " << FormatList(FreeChunks) + << " Chains# {" << Chains.ToString() << '}'; return str.Str(); } diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.h b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.h index 6fbae987e4ba..afdfe26959db 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.h +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.h @@ -70,7 +70,7 @@ namespace NKikimr { TChainLayoutBuilder(ui32 left, ui32 milestone, ui32 right, ui32 overhead); const TVector &GetLayout() const { return Layout; } - const TSeg &GetMilestoneSegment() const { return Layout.at(MilesoneId); } + const TSeg &GetMilestoneSegment() const { return Layout.at(MilestoneId); } TString ToString(ui32 appendBlockSize = 0) const; void Output(IOutputStream &str, ui32 appendBlockSize = 0) const; @@ -81,7 +81,7 @@ namespace NKikimr { TVector Layout; // An index in Layout vector, where milestone segment starts - size_t MilesoneId = Max(); + size_t MilestoneId = Max(); }; } // NPrivate @@ -90,29 +90,43 @@ namespace NKikimr { // TChain // It manages all slots of some fixed size. //////////////////////////////////////////////////////////////////////////// - class TChain : public TThrRefBase { + class TChain { using TChunkID = ui32; using TFreeSpace = TMap; static constexpr ui32 MaxNumberOfSlots = 32768; // it's not a good idea to have more slots than this - const TString VDiskLogPrefix; - const ui32 SlotsInChunk; - const TMask ConstMask; // mask of 'all slots are free' + /*const*/ TString VDiskLogPrefix; + /*const*/ TMask ConstMask; // mask of 'all slots are free' TFreeSpace FreeSpace; TFreeSpace LockedChunks; ui32 AllocatedSlots = 0; ui32 FreeSlotsInFreeSpace = 0; + public: + /*const*/ ui32 SlotsInChunk; + /*const*/ ui32 SlotSize; + public: static TMask BuildConstMask(const TString &prefix, ui32 slotsInChunk); public: - TChain(const TString &vdiskLogPrefix, const ui32 slotsInChunk) - : VDiskLogPrefix(vdiskLogPrefix) - , SlotsInChunk(slotsInChunk) + TChain(TString vdiskLogPrefix, ui32 slotsInChunk, ui32 slotSize) + : VDiskLogPrefix(std::move(vdiskLogPrefix)) , ConstMask(BuildConstMask(vdiskLogPrefix, slotsInChunk)) + , SlotsInChunk(slotsInChunk) + , SlotSize(slotSize) {} + TChain(TChain&&) = default; + TChain(const TChain&) = delete; + + TChain& operator=(TChain&&) = default; + TChain& operator=(const TChain&) = delete; + + THugeSlot Convert(const NPrivate::TChunkSlot& id) const; + NPrivate::TChunkSlot Convert(const TDiskPart& addr) const; + NPrivate::TChunkSlot Convert(const THugeSlot& slot) const; + // returns true if allocated, false -- if no free slots bool Allocate(NPrivate::TChunkSlot *id); // allocate id, but we know that this chain doesn't have free slots, so add a chunk to it @@ -126,71 +140,49 @@ namespace NKikimr { bool RecoveryModeAllocate(const NPrivate::TChunkSlot &id); void RecoveryModeAllocate(const NPrivate::TChunkSlot &id, TChunkID chunkId, bool inLockedChunks); void Save(IOutputStream *s) const; - void Load(IInputStream *s); bool HaveBeenUsed() const; TString ToString() const; void RenderHtml(IOutputStream &str) const; - ui32 GetAllocatedSlots() const; + void RenderHtmlForUsage(IOutputStream &str) const; void GetOwnedChunks(TSet& chunks) const; - }; - using TChainPtr = TIntrusivePtr; - - //////////////////////////////////////////////////////////////////////////// - // TChainDelegator - //////////////////////////////////////////////////////////////////////////// - struct TChainDelegator { - TString VDiskLogPrefix; - ui32 Blocks; - ui32 ShiftInBlocks; - ui32 SlotsInChunk; - ui32 SlotSize; - TChainPtr ChainPtr; - - TChainDelegator(const TString &vdiskLogPrefix, - ui32 valBlocks, - ui32 shiftBlocks, - ui32 chunkSize, - ui32 appendBlockSize); - TChainDelegator(TChainDelegator &&) = default; - TChainDelegator &operator =(TChainDelegator &&) = default; - TChainDelegator(const TChainDelegator &) = delete; - TChainDelegator &operator =(const TChainDelegator &) = delete; - THugeSlot Convert(const NPrivate::TChunkSlot &id) const; - NPrivate::TChunkSlot Convert(const TDiskPart &addr) const; - NPrivate::TChunkSlot Convert(const THugeSlot &slot) const; - void Save(IOutputStream *s) const; - void Load(IInputStream *s); - bool HaveBeenUsed() const; - TString ToString() const; - void GetOwnedChunks(TSet& chunks) const; - void RenderHtml(IOutputStream &str) const; - void RenderHtmlForUsage(IOutputStream &str) const; + static TChain Load(IInputStream *s, TString vdiskLogPrefix, ui32 appendBlockSize, ui32 blocksInChunk, + std::span chains, bool *compatible); + + template + void ForEachFreeSpaceChunk(T&& callback) const { + auto freeIt = FreeSpace.begin(); + const auto freeEnd = FreeSpace.end(); + auto lockedIt = LockedChunks.begin(); + const auto lockedEnd = LockedChunks.end(); + while (freeIt != freeEnd || lockedIt != lockedEnd) { + if (lockedIt == lockedEnd || freeIt->first < lockedIt->first) { + std::invoke(callback, *freeIt++); + } else if (freeIt == freeEnd || lockedIt->first < freeIt->first) { + std::invoke(callback, *lockedIt++); + } else { + Y_ABORT("intersecting sets of keys for FreeSpace and LockedChunks"); + } + } + } }; - //////////////////////////////////////////////////////////////////////////// // TAllChains //////////////////////////////////////////////////////////////////////////// class TAllChains { public: - using TAllChainDelegators = TVector; - using TSearchTable = TVector; - TAllChains(const TString &vdiskLogPrefix, ui32 chunkSize, ui32 appendBlockSize, ui32 minHugeBlobInBytes, - ui32 oldMinHugeBlobSizeInBytes, ui32 milestoneBlobInBytes, ui32 maxBlobInBytes, ui32 overhead); // return a pointer to corresponding chain delegator by object byte size - TChainDelegator *GetChain(ui32 size); - const TChainDelegator *GetChain(ui32 size) const; + TChain *GetChain(ui32 size); + const TChain *GetChain(ui32 size) const; THeapStat GetStat() const; - void PrintOutChains(IOutputStream &str) const; - void PrintOutSearchTable(IOutputStream &str) const; void Save(IOutputStream *s) const; void Load(IInputStream *s); void GetOwnedChunks(TSet& chunks) const; @@ -199,39 +191,28 @@ namespace NKikimr { void RenderHtmlForUsage(IOutputStream &str) const; // for testing purposes TVector GetLayout() const; - // returns (ChainsSize, SearchTableSize) - std::pair GetTablesSize() const { - return std::pair(ChainDelegators.size(), SearchTable.size()); - } // Builds a map of BlobSize -> THugeSlotsMap::TSlotInfo for THugeBlobCtx std::shared_ptr BuildHugeSlotsMap() const; - private: + void FinishRecovery(); - TAllChainDelegators BuildChains(ui32 minHugeBlobInBytes) const; + private: + void BuildChains(); void BuildSearchTable(); - void BuildLayout(); inline ui32 SizeToBlocks(ui32 size) const; inline ui32 GetEndBlocks() const; - enum class EStartMode { - Empty = 1, - Loaded = 2, - Migrated = 3, - }; - const TString VDiskLogPrefix; const ui32 ChunkSize; const ui32 AppendBlockSize; const ui32 MinHugeBlobInBytes; - const ui32 OldMinHugeBlobSizeInBytes; const ui32 MilestoneBlobInBytes; const ui32 MaxBlobInBytes; const ui32 Overhead; - EStartMode StartMode = EStartMode::Empty; - ui32 FirstLoadedSlotSize = 0; - TAllChainDelegators ChainDelegators; - TSearchTable SearchTable; + const ui32 MinHugeBlobInBlocks; + THashSet DeserializedSlotSizes; // a set of SlotSize values of recovered stream + std::vector Chains; + std::vector SearchTable; // (NumFullBlocks - 1) -> Chain index }; @@ -255,7 +236,6 @@ namespace NKikimr { ui32 appendBlockSize, // min size of the huge blob ui32 minHugeBlobInBytes, - ui32 oldMinHugeBlobSizeInBytes, // fixed point to calculate layout (for backward compatibility) ui32 mileStoneBlobInBytes, // max size of the blob @@ -266,13 +246,13 @@ namespace NKikimr { ui32 SlotNumberOfThisSize(ui32 size) const { - const TChainDelegator *chainD = Chains.GetChain(size); - return chainD ? chainD->SlotsInChunk : 0; + const TChain *chain = Chains.GetChain(size); + return chain ? chain->SlotsInChunk : 0; } ui32 SlotSizeOfThisSize(ui32 size) const { - const TChainDelegator *chainD = Chains.GetChain(size); - return chainD ? chainD->SlotSize : 0; + const TChain *chain = Chains.GetChain(size); + return chain ? chain->SlotSize : 0; } // Builds a map of BlobSize -> THugeSlotsMap::TSlotInfo for THugeBlobCtx @@ -302,6 +282,7 @@ namespace NKikimr { void RecoveryModeRemoveChunks(const TVector &chunkIds); bool ReleaseSlot(THugeSlot slot); void OccupySlot(THugeSlot slot, bool inLockedChunks); + void FinishRecovery(); ////////////////////////////////////////////////////////////////////////////////////////// // Serialize/Parse/Check @@ -317,10 +298,6 @@ namespace NKikimr { void RenderHtml(IOutputStream &str) const; TString ToString() const; - void PrintOutSearchTable(IOutputStream &str) { - Chains.PrintOutSearchTable(str); - } - private: inline ui32 GetChunkIdFromFreeChunks(); inline void PutChunkIdToFreeChunks(ui32 chunkId); diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ctx_ut.cpp b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ctx_ut.cpp index df27d118e986..865bf385130b 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ctx_ut.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ctx_ut.cpp @@ -26,7 +26,6 @@ namespace NKikimr { Contexts.GetVCtx(), ChunkSize, AppendBlockSize, - AppendBlockSize, cfg.MinHugeBlobInBytes, cfg.MilestoneHugeBlobInBytes, cfg.MaxLogoBlobDataSize, diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ut.cpp b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ut.cpp index 967a1e2e0dcd..7a4fcd1aae77 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ut.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ut.cpp @@ -79,7 +79,7 @@ namespace NKikimr { } void AllocFreeOneChunk(ui32 slotsInChunk) { - TChain chain("vdisk", slotsInChunk); + TChain chain("vdisk", slotsInChunk, 1); TVector arr; AllocateScenaryOneChunk(chain, arr, slotsInChunk); FreeScenaryOneChunk(chain, arr, slotsInChunk); @@ -156,7 +156,7 @@ namespace NKikimr { } void AllocFreeAlloc(ui32 slotsInChunk) { - TChain chain("vdisk", slotsInChunk); + TChain chain("vdisk", slotsInChunk, 1); TVector arr; TVector chunks; @@ -171,17 +171,14 @@ namespace NKikimr { TStringStream serialized; - { - TChain chain("vdisk", slotsInChunk); - PreliminaryAllocate(24, chain, arr); - FreeChunksScenary(chain, arr, chunks); - chain.Save(&serialized); - } - { - TChain chain("vdisk", slotsInChunk); - TStringInput str(serialized.Str()); - chain.Load(&str); - } + TChain chain("vdisk", slotsInChunk, 1); + PreliminaryAllocate(24, chain, arr); + FreeChunksScenary(chain, arr, chunks); + chain.Save(&serialized); + + TStringInput str(serialized.Str()); + bool comp = true; + TChain chain2 = TChain::Load(&str, "vdisk", 1 /*appendBlockSize*/, slotsInChunk, {&chain, &chain + 1}, &comp); } Y_UNIT_TEST(AllocFreeAllocTest) { @@ -248,38 +245,6 @@ namespace NKikimr { } } - Y_UNIT_TEST_SUITE(TBlobStorageHullHugeLayout) { - - Y_UNIT_TEST(TestOldAppendBlockSize) { - TAllChains all("vdisk", 134274560, 56896, 512 << 10, 512 << 10, 512 << 10, 10 << 20, 8); - all.PrintOutChains(STR); - all.PrintOutSearchTable(STR); - std::pair p = all.GetTablesSize(); - TVector canonical = { - {9, 10}, {10, 11}, {11, 12}, {12, 13}, {13, 14}, {14, 15}, {15, 16}, {16, 18}, {18, 20}, - {20, 22}, {22, 24}, {24, 27}, {27, 30}, {30, 33}, {33, 37}, {37, 41}, {41, 46}, {46, 51}, - {51, 57}, {57, 64}, {64, 72}, {72, 81}, {81, 91}, {91, 102}, {102, 114}, {114, 128}, - {128, 144}, {144, 162}, {162, 182}, {182, 204} - }; - UNIT_ASSERT_EQUAL(all.GetLayout(), canonical); - UNIT_ASSERT_EQUAL(p, (std::pair(30, 186))); - } - - Y_UNIT_TEST(TestNewAppendBlockSize) { - TAllChains all("vdisk", 134274560, 4064, 512 << 10, 512 << 10, 512 << 10, 10 << 20, 8); - all.PrintOutChains(STR); - all.PrintOutSearchTable(STR); - TVector canonical = { - {129, 145}, {145, 163}, {163, 183}, {183, 205}, {205, 230}, {230, 258}, {258, 290}, - {290, 326}, {326, 366}, {366, 411}, {411, 462}, {462, 519}, {519, 583}, {583, 655}, - {655, 736}, {736, 828}, {828, 931}, {931, 1047}, {1047, 1177}, {1177, 1324}, - {1324, 1489}, {1489, 1675}, {1675, 1884}, {1884, 2119}, {2119, 2383}, {2383, 2680} - }; - UNIT_ASSERT_EQUAL(all.GetLayout(), canonical); - } - } - - Y_UNIT_TEST_SUITE(TBlobStorageHullHugeHeap) { Y_UNIT_TEST(AllocateAllFromOneChunk) { @@ -290,8 +255,9 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); + heap.FinishRecovery(); ui32 hugeBlobSize = 6u << 20u; heap.AddChunk(5); @@ -314,9 +280,10 @@ namespace NKikimr { // just serialize/deserialize TString serialized = heap.Serialize(); - THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, mileStoneBlobInBytes, + THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); newHeap.ParseFromString(serialized); + newHeap.FinishRecovery(); } void AllocateScenary(THeap &heap, ui32 hugeBlobSize, TVector &arr) { @@ -357,8 +324,9 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); + heap.FinishRecovery(); TVector arr; AllocateScenary(heap, 6u << 20u, arr); @@ -372,16 +340,21 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); + heap.FinishRecovery(); TVector arr; AllocateScenary(heap, 6u << 20u, arr); + TString heap1 = heap.ToString(); TString serialized = heap.Serialize(); UNIT_ASSERT(THeap::CheckEntryPoint(serialized)); - THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, + THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); newHeap.ParseFromString(serialized); + newHeap.FinishRecovery(); + TString heap2 = newHeap.ToString(); + UNIT_ASSERT_VALUES_EQUAL(heap1, heap2); FreeScenary(newHeap, arr); } @@ -392,8 +365,9 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); + heap.FinishRecovery(); heap.RecoveryModeAddChunk(2); heap.RecoveryModeAddChunk(34); @@ -421,8 +395,9 @@ namespace NKikimr { ui32 maxBlobInBytes = MaxVDiskBlobSize; ui32 overhead = 8u; ui32 freeChunksReservation = 1; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); + heap.FinishRecovery(); THugeSlot hugeSlot; ui32 slotSize; @@ -438,52 +413,34 @@ namespace NKikimr { RollbackFrom_New_To_Old, }; - void Write_SaveEntryPoint_Restart(EWrite_SaveEntryPoint_Restart mode) { + Y_UNIT_TEST(WriteRestore) { ui32 chunkSize = 134274560u; ui32 appendBlockSize = 4064u; ui32 minHugeBlobInBytes = appendBlockSize; - ui32 oldMinHugeBlobInBytes = 64u << 10u; ui32 mileStoneBlobInBytes = 512u << 10u; ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - ui32 fromMin = 0; - ui32 toMin = 0; - switch (mode) { - case EWrite_SaveEntryPoint_Restart::MigrateFrom_Old_To_New: - fromMin = oldMinHugeBlobInBytes; - toMin = minHugeBlobInBytes; - break; - case EWrite_SaveEntryPoint_Restart::RollbackFrom_New_To_Old: - fromMin = minHugeBlobInBytes; - toMin = oldMinHugeBlobInBytes; - break; - } - THeap oldHeap("vdisk", chunkSize, appendBlockSize, oldMinHugeBlobInBytes, oldMinHugeBlobInBytes, mileStoneBlobInBytes, + THeap oldHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); + oldHeap.FinishRecovery(); - THeap fromHeap("vdisk", chunkSize, appendBlockSize, fromMin, oldMinHugeBlobInBytes, mileStoneBlobInBytes, + THeap fromHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); fromHeap.ParseFromString(oldHeap.Serialize()); + fromHeap.FinishRecovery(); TVector arr; AllocateScenary(fromHeap, 6u << 20u, arr); TString serialized = fromHeap.Serialize(); UNIT_ASSERT(THeap::CheckEntryPoint(serialized)); - THeap toHeap("vdisk", chunkSize, appendBlockSize, toMin, oldMinHugeBlobInBytes, mileStoneBlobInBytes, + THeap toHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); toHeap.ParseFromString(serialized); + toHeap.FinishRecovery(); FreeScenary(toHeap, arr); } - - Y_UNIT_TEST(MigrateFrom_Old_To_New) { - Write_SaveEntryPoint_Restart(EWrite_SaveEntryPoint_Restart::MigrateFrom_Old_To_New); - } - - Y_UNIT_TEST(RollbackFrom_New_To_Old) { - Write_SaveEntryPoint_Restart(EWrite_SaveEntryPoint_Restart::RollbackFrom_New_To_Old); - } } } // NKikimr diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.cpp b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.cpp index 898271ef6d41..f5aa9d561f48 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.cpp @@ -64,7 +64,6 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, - const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -73,10 +72,11 @@ namespace NKikimr { : VCtx(std::move(vctx)) , LogPos(THullHugeRecoveryLogPos::Default()) , Heap(new NHuge::THeap(VCtx->VDiskLogPrefix, chunkSize, appendBlockSize, - minHugeBlobInBytes, oldMinHugeBlobInBytes, milestoneHugeBlobInBytes, + minHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation)) , Guid(TAppData::RandomProvider->GenRand64()) { + Heap->FinishRecovery(); logFunc(VDISKP(VCtx->VDiskLogPrefix, "Recovery started (guid# %" PRIu64 " entryLsn# null): State# %s", Guid, ToString().data())); @@ -86,7 +86,6 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, - const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -97,12 +96,13 @@ namespace NKikimr { : VCtx(std::move(vctx)) , LogPos(THullHugeRecoveryLogPos::Default()) , Heap(new NHuge::THeap(VCtx->VDiskLogPrefix, chunkSize, appendBlockSize, - minHugeBlobInBytes, oldMinHugeBlobInBytes, milestoneHugeBlobInBytes, + minHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation)) , Guid(TAppData::RandomProvider->GenRand64()) , PersistentLsn(entryPointLsn) { ParseFromString(entryPointData); + Heap->FinishRecovery(); Y_ABORT_UNLESS(entryPointLsn == LogPos.EntryPointLsn); logFunc(VDISKP(VCtx->VDiskLogPrefix, "Recovery started (guid# %" PRIu64 " entryLsn# %" PRIu64 "): State# %s", @@ -113,7 +113,6 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, - const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -124,12 +123,13 @@ namespace NKikimr { : VCtx(std::move(vctx)) , LogPos(THullHugeRecoveryLogPos::Default()) , Heap(new NHuge::THeap(VCtx->VDiskLogPrefix, chunkSize, appendBlockSize, - minHugeBlobInBytes, oldMinHugeBlobInBytes, milestoneHugeBlobInBytes, + minHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation)) , Guid(TAppData::RandomProvider->GenRand64()) , PersistentLsn(entryPointLsn) { ParseFromArray(entryPointData.GetData(), entryPointData.GetSize()); + Heap->FinishRecovery(); Y_ABORT_UNLESS(entryPointLsn == LogPos.EntryPointLsn); logFunc(VDISKP(VCtx->VDiskLogPrefix, "Recovery started (guid# %" PRIu64 " entryLsn# %" PRIu64 "): State# %s", diff --git a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.h b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.h index 548a4997c270..cea53fbe3136 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.h +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.h @@ -92,7 +92,6 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, - const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -102,7 +101,6 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, - const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -114,7 +112,6 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, - const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, diff --git a/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp b/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp index cda349b52197..c71d6c35244c 100644 --- a/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp +++ b/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp @@ -444,7 +444,6 @@ namespace NKikimr { LocRecCtx->PDiskCtx->Dsk->ChunkSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, - Config->OldMinHugeBlobInBytes, Config->MilestoneHugeBlobInBytes, Config->MaxLogoBlobDataSize, Config->HugeBlobOverhead, @@ -466,7 +465,6 @@ namespace NKikimr { LocRecCtx->PDiskCtx->Dsk->ChunkSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, - Config->OldMinHugeBlobInBytes, Config->MilestoneHugeBlobInBytes, Config->MaxLogoBlobDataSize, Config->HugeBlobOverhead,