diff options
author | Alexander Rutkovsky <alexvru@ydb.tech> | 2024-11-25 21:25:12 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-25 21:25:12 +0300 |
commit | a328b3002af3e74977283814bdeeb064c3b83645 (patch) | |
tree | 1315195d228c1bbbe82a56b2eaa1513764a317c9 | |
parent | ec75229918d0b7357a38e7aa1e92556429ea58a7 (diff) | |
download | ydb-a328b3002af3e74977283814bdeeb064c3b83645.tar.gz |
Revert "Support arbitrary chain set in huge blob keeper heap" (#11961)
13 files changed, 529 insertions, 390 deletions
diff --git a/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp b/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp index 5fabd73846..ad9b486e67 100644 --- a/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp +++ b/ydb/core/blobstorage/ut_vdisk/lib/test_huge.cpp @@ -137,6 +137,7 @@ class THugeModuleRecoveryActor : public TActorBootstrapped<THugeModuleRecoveryAc bool InitHugeBlobKeeper(const TActorContext &ctx, const TStartingPoints &startingPoints) { Y_UNUSED(ctx); + const ui32 oldMinHugeBlobInBytes = 64 << 10; const ui32 milestoneHugeBlobInBytes = 64 << 10; const ui32 maxBlobInBytes = 128 << 10; auto logFunc = [] (const TString) { /* empty */ }; @@ -149,6 +150,7 @@ class THugeModuleRecoveryActor : public TActorBootstrapped<THugeModuleRecoveryAc HmCtx->PDiskCtx->Dsk->ChunkSize, HmCtx->PDiskCtx->Dsk->AppendBlockSize, HmCtx->PDiskCtx->Dsk->AppendBlockSize, + oldMinHugeBlobInBytes, milestoneHugeBlobInBytes, maxBlobInBytes, HmCtx->Config->HugeBlobOverhead, @@ -167,6 +169,7 @@ class THugeModuleRecoveryActor : public TActorBootstrapped<THugeModuleRecoveryAc HmCtx->PDiskCtx->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 edfcd7c452..540edfaac5 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp +++ b/ydb/core/blobstorage/vdisk/common/vdisk_config.cpp @@ -136,7 +136,9 @@ 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 70bfdd1ee1..dd80ff86ce 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_config.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_config.h @@ -120,6 +120,7 @@ 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 d2f1ad450e..55b168cb33 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, ui32 minHugeBlobInBlocks, TAllSlotsInfo &&slotsInfo, - TSearchTable &&searchTable) + THugeSlotsMap::THugeSlotsMap(ui32 appendBlockSize, TAllSlotsInfo &&slotsInfo, TSearchTable &&searchTable) : AppendBlockSize(appendBlockSize) - , MinHugeBlobInBlocks(minHugeBlobInBlocks) , AllSlotsInfo(std::move(slotsInfo)) , SearchTable(std::move(searchTable)) {} const THugeSlotsMap::TSlotInfo *THugeSlotsMap::GetSlotInfo(ui32 size) const { - const ui32 sizeInBlocks = (size + AppendBlockSize - 1) / AppendBlockSize; - Y_ABORT_UNLESS(MinHugeBlobInBlocks <= sizeInBlocks); - const ui64 idx = SearchTable.at(sizeInBlocks - MinHugeBlobInBlocks); + ui32 sizeInBlocks = size / AppendBlockSize; + sizeInBlocks += !(sizeInBlocks * AppendBlockSize == size); + const ui64 idx = SearchTable.at(sizeInBlocks); return &AllSlotsInfo.at(idx); } ui32 THugeSlotsMap::AlignByBlockSize(ui32 size) const { - return Max(MinHugeBlobInBlocks * AppendBlockSize, size - size % AppendBlockSize); + ui32 sizeInBlocks = size / AppendBlockSize; + Y_ABORT_UNLESS(sizeInBlocks, "Blob size to align is smaller than a single block. BlobSize# %" PRIu32, size); + return sizeInBlocks * AppendBlockSize; } void THugeSlotsMap::Output(IOutputStream &str) const { @@ -31,7 +31,11 @@ namespace NKikimr { str << "]}\n"; str << "{SearchTable# ["; for (const auto &idx : SearchTable) { - AllSlotsInfo.at(idx).Output(str); + if (idx != NoOpIdx) { + AllSlotsInfo.at(idx).Output(str); + } else { + str << "null"; + } str << "\n"; } str << "]}"; diff --git a/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h b/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h index 1d7e61f04e..b6d4f8527b 100644 --- a/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h +++ b/ydb/core/blobstorage/vdisk/common/vdisk_hugeblobctx.h @@ -38,13 +38,16 @@ namespace NKikimr { }; // All slot types - using TAllSlotsInfo = std::vector<TSlotInfo>; + using TAllSlotsInfo = TVector<TSlotInfo>; // Type to address TAllSlotsInfo using TIndex = ui16; // Size in AppendBlockSize -> index in TAllSlotsInfo - using TSearchTable = std::vector<TIndex>; + using TSearchTable = TVector<TIndex>; + // Idx that indicates there is no record for it in TAllSlotsInfo + static constexpr TIndex NoOpIdx = Max<TIndex>(); - THugeSlotsMap(ui32 appendBlockSize, ui32 minHugeBlobInBlocks, TAllSlotsInfo &&slotsInfo, TSearchTable &&searchTable); + + THugeSlotsMap(ui32 appendBlockSize, TAllSlotsInfo &&slotsInfo, TSearchTable &&searchTable); const TSlotInfo *GetSlotInfo(ui32 size) const; ui32 AlignByBlockSize(ui32 size) const; void Output(IOutputStream &str) const; @@ -52,7 +55,6 @@ 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 699f5ca6ef..2aa98ea92c 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhuge_ut.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhuge_ut.cpp @@ -17,6 +17,7 @@ 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; @@ -28,8 +29,8 @@ namespace NKikimr { auto vctx = MakeIntrusive<TVDiskContext>(TActorId(), info->PickTopology(), counters, TVDiskID(0, 1, 0, 0, 0), nullptr, NPDisk::DEVICE_TYPE_UNKNOWN); std::unique_ptr<THullHugeKeeperPersState> state( - new THullHugeKeeperPersState(vctx, chunkSize, appendBlockSize, - appendBlockSize, milestoneHugeBlobInBytes, maxBlobInBytes, + new THullHugeKeeperPersState(vctx, chunkSize, appendBlockSize, appendBlockSize, + minHugeBlobInBytes, 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 d7973d1d2c..be27874e3f 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap.cpp @@ -2,8 +2,6 @@ #include <library/cpp/monlib/service/pages/templates.h> -#include <ranges> - namespace NKikimr { namespace NHuge { @@ -26,7 +24,7 @@ namespace NKikimr { } void TChainLayoutBuilder::Output(IOutputStream &str, ui32 appendBlockSize) const { - str << "CHAIN TABLE (MilestoneId=" << MilestoneId << " rows=" << Layout.size() << "):\n"; + str << "CHAIN TABLE (MilesoneId=" << MilesoneId << " rows=" << Layout.size() << "):\n"; for (const auto &x : Layout) { str << "Blocks# (" << x.Left << ", " << x.Right << "]"; if (appendBlockSize) { @@ -66,7 +64,7 @@ namespace NKikimr { } void TChainLayoutBuilder::BuildUpward(ui32 left, ui32 right, ui32 overhead) { - MilestoneId = Layout.size(); + MilesoneId = Layout.size(); ui32 valBlocks = left; ui32 shiftBlocks = 0; @@ -85,21 +83,6 @@ 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;" @@ -267,103 +250,73 @@ namespace NKikimr { void TChain::Save(IOutputStream *s) const { ::Save(s, SlotsInChunk); ::Save(s, AllocatedSlots); - ::SaveSize(s, FreeSpace.size() + LockedChunks.size()); - ForEachFreeSpaceChunk(std::bind(&::Save<TFreeSpace::value_type>, s, std::placeholders::_1)); + if (LockedChunks) { + TFreeSpace temp(FreeSpace); + temp.insert(LockedChunks.begin(), LockedChunks.end()); + ::Save(s, temp); + } else { + ::Save(s, FreeSpace); + } } - TChain TChain::Load(IInputStream *s, TString vdiskLogPrefix, ui32 appendBlockSize, ui32 blocksInChunk) { - ui32 slotsInChunk; + void TChain::Load(IInputStream *s) { + FreeSpace.clear(); + ui32 slotsInChunk = 0; ::Load(s, slotsInChunk); - - // calculate optimal slot size for this number of slots per chunk; it may differ from builder's one, - // this will be fixed in caller function - const ui32 slotSizeInBlocks = blocksInChunk / slotsInChunk; - Y_ABORT_UNLESS(slotSizeInBlocks); - ui32 slotSize = slotSizeInBlocks * appendBlockSize; - - 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(); + 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(); } - - return res; } bool TChain::HaveBeenUsed() const { - return AllocatedSlots; // chain is considered to be used if it contains any allocated slots + return AllocatedSlots != 0 || !FreeSpace.empty(); } TString TChain::ToString() const { TStringStream str; - - 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<ui32>(); - Y_FOR_EACH_BIT(i, bitmap) { - if (prev == Max<ui32>()) { - begin = i; - } else if (i != prev + 1) { - str << ' ' << begin; - if (begin != prev) { - str << '-' << prev; - } - begin = prev; + 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 << "]"; } - 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 { - HTML(str) { - TABLER() { - TABLED() { - str << SlotSize << "/" << SlotsInChunk; + 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; } - TABLED() { - ForEachFreeSpaceChunk([&](const auto& value) { - const auto& [chunk, bitmap] = value; - str << " [" << chunk << " " << bitmap.Count() << "]"; - }); + if (freeSlots) { + str << " [" << x.first << " " << freeSlots << "]"; } } - } + }; + + output(FreeSpace); + output(LockedChunks); } - void TChain::RenderHtmlForUsage(IOutputStream &str) const { - HTML(str) { - TABLER() { - TABLED() { str << SlotSize; } - TABLED() { str << SlotsInChunk; } - TABLED() { str << AllocatedSlots; } - } - } + ui32 TChain::GetAllocatedSlots() const { + return AllocatedSlots; } void TChain::GetOwnedChunks(TSet<TChunkIdx>& chunks) const { @@ -377,6 +330,87 @@ namespace NKikimr { } //////////////////////////////////////////////////////////////////////////// + // 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<TChain>(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<TChunkIdx>& 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 //////////////////////////////////////////////////////////////////////////// TAllChains::TAllChains( @@ -384,6 +418,7 @@ namespace NKikimr { ui32 chunkSize, ui32 appendBlockSize, ui32 minHugeBlobInBytes, + ui32 oldMinHugeBlobSizeInBytes, ui32 milestoneBlobInBytes, ui32 maxBlobInBytes, ui32 overhead) @@ -391,136 +426,143 @@ namespace NKikimr { , ChunkSize(chunkSize) , AppendBlockSize(appendBlockSize) , MinHugeBlobInBytes(minHugeBlobInBytes) + , OldMinHugeBlobSizeInBytes(oldMinHugeBlobSizeInBytes) , MilestoneBlobInBytes(milestoneBlobInBytes) + , MaxBlobInBytes(maxBlobInBytes) , Overhead(overhead) - , MinHugeBlobInBlocks(MinHugeBlobInBytes / AppendBlockSize) - , MaxHugeBlobInBlocks(SizeToBlocks(maxBlobInBytes)) { - Y_VERIFY_S(MinHugeBlobInBytes && + Y_VERIFY_S(MinHugeBlobInBytes != 0 && + MinHugeBlobInBytes >= AppendBlockSize && MinHugeBlobInBytes <= MilestoneBlobInBytes && - MilestoneBlobInBytes < maxBlobInBytes, "INVALID CONFIGURATION! (SETTINGS ARE:" - << " MaxBlobInBytes# " << maxBlobInBytes << " MinHugeBlobInBytes# " << MinHugeBlobInBytes + MinHugeBlobInBytes <= OldMinHugeBlobSizeInBytes && + MilestoneBlobInBytes < MaxBlobInBytes, "INVALID CONFIGURATION! (SETTINGS ARE:" + << " MaxBlobInBytes# " << MaxBlobInBytes << " MinHugeBlobInBytes# " << MinHugeBlobInBytes << " OldMinHugeBlobSizeInBytes# " << OldMinHugeBlobSizeInBytes << " MilestoneBlobInBytes# " << MilestoneBlobInBytes << " ChunkSize# " << ChunkSize << " AppendBlockSize# " << AppendBlockSize << ")"); - - BuildChains(); + BuildLayout(); } - TChain *TAllChains::GetChain(ui32 size) { - if (size < MinHugeBlobInBytes || MaxHugeBlobInBlocks * AppendBlockSize < size) { - return nullptr; - } - 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]; + TChainDelegator *TAllChains::GetChain(ui32 size) { + return SearchTable.at(SizeToBlocks(size)); } - const TChain *TAllChains::GetChain(ui32 size) const { - if (size < MinHugeBlobInBytes || MaxHugeBlobInBlocks * 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]; + const TChainDelegator *TAllChains::GetChain(ui32 size) const { + return SearchTable.at(SizeToBlocks(size)); } THeapStat TAllChains::GetStat() const { THeapStat stat; - for (const auto& chain : Chains) { - stat += chain.GetStat(); + for (const auto &x : ChainDelegators) { + stat += x.ChainPtr->GetStat(); } return stat; } - void TAllChains::Save(IOutputStream *s) const { - // check if we can write compatible entrypoint (with exactly the same set of chains) - bool writeCompatible = true; - ui32 numChains = 0; - if (DeserializedChains.Empty()) { // if this was initially empty heap, write it fully - writeCompatible = false; - } else { - for (size_t i = 0; i < Chains.size(); ++i) { - if (DeserializedChains[i]) { - ++numChains; - } else if (Chains[i].HaveBeenUsed()) { - writeCompatible = false; - break; - } - } + 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"; } + } - if (!writeCompatible) { // we can't, so we serialize all our chains anyway - numChains = Chains.size(); + 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"; } - ::Save(s, numChains); + } - // serialize selected chains - for (size_t i = 0; i < Chains.size(); ++i) { - if (!writeCompatible || DeserializedChains[i]) { - ::Save(s, Chains[i]); + 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<const TChainDelegator*> delegators; + for (auto &x : ChainDelegators) { + if (!x.HaveBeenUsed() && x.SlotSize < FirstLoadedSlotSize) { + continue; // preserving backward compatibility until no allocations + } + delegators.emplace_back(&x); + } + + ui32 size = delegators.size(); + ::Save(s, size); + for (auto x : delegators) { + ::Save(s, *x); } } } void TAllChains::Load(IInputStream *s) { - std::vector<TChain> newChains; - newChains.reserve(Chains.size()); - - Y_DEBUG_ABORT_UNLESS(ChunkSize % AppendBlockSize == 0); - Y_DEBUG_ABORT_UNLESS(AppendBlockSize <= ChunkSize); - const ui32 blocksInChunk = ChunkSize / AppendBlockSize; - - auto chainsIt = Chains.begin(); - const auto chainsEnd = Chains.end(); - - ui32 prevSlotSize = 0; - ui32 numChains; - for (::Load(s, numChains); numChains; --numChains) { - auto chain = TChain::Load(s, VDiskLogPrefix, AppendBlockSize, blocksInChunk); - - // merge new item with originating ones from TChainLayoutBuilder -- we may have not every one of them - // serialized - for (; chainsIt != chainsEnd && chain.SlotsInChunk < chainsIt->SlotsInChunk; ++chainsIt) { - newChains.push_back(std::move(*chainsIt)); + 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); } - if (chainsIt != chainsEnd && chainsIt->SlotsInChunk == chain.SlotsInChunk) { - // reuse slot size from the builder's one to retain compatibility - chain.SlotSize = chainsIt->SlotSize; - ++chainsIt; + } 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); } - // assert SlotSize-s are coming in strictly increasing order - Y_ABORT_UNLESS(std::exchange(prevSlotSize, chain.SlotSize) < chain.SlotSize); - - DeserializedChains.Set(newChains.size()); - newChains.push_back(std::move(chain)); + // 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(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); } - - std::ranges::move(chainsIt, chainsEnd, std::back_inserter(newChains)); - - Chains = std::move(newChains); } void TAllChains::GetOwnedChunks(TSet<TChunkIdx>& chunks) const { - for (const TChain& chain : Chains) { - chain.GetOwnedChunks(chunks); + for (const TChainDelegator& delegator : ChainDelegators) { + delegator.GetOwnedChunks(chunks); } } TString TAllChains::ToString() const { TStringStream str; - str << "{ChunkSize# " << ChunkSize - << " AppendBlockSize# " << AppendBlockSize - << " MinHugeBlobInBytes# " << MinHugeBlobInBytes - << " MinHugeBlobInBlocks# " << MinHugeBlobInBlocks - << " MaxHugeBlobInBlocks# " << MaxHugeBlobInBlocks; - for (const auto& chain : Chains) { - str << " {CHAIN " << chain.ToString() << "}"; + str << "{ChunkSize# " << ChunkSize << " AppendBlockSize# " << AppendBlockSize + << " MinHugeBlobInBytes# " << MinHugeBlobInBytes << " MaxBlobInBytes# " << MaxBlobInBytes; + for (const auto & x : ChainDelegators) { + str << " {CHAIN " << x.ToString() << "}"; } str << "}"; return str.Str(); @@ -536,9 +578,8 @@ namespace NKikimr { } } TABLEBODY() { - for (const auto& chain : Chains) { - chain.RenderHtml(str); - } + for (const auto & x : ChainDelegators) + x.RenderHtml(str); } } } @@ -555,9 +596,8 @@ namespace NKikimr { } } TABLEBODY() { - for (const auto& chain : Chains) { - chain.RenderHtmlForUsage(str); - } + for (const auto & x : ChainDelegators) + x.RenderHtmlForUsage(str); } } } @@ -565,83 +605,91 @@ namespace NKikimr { TVector<NPrivate::TChainLayoutBuilder::TSeg> TAllChains::GetLayout() const { TVector<NPrivate::TChainLayoutBuilder::TSeg> res; - 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; + res.reserve(ChainDelegators.size()); + for (const auto &x : ChainDelegators) { + res.push_back(NPrivate::TChainLayoutBuilder::TSeg {x.Blocks, x.Blocks + x.ShiftInBlocks} ); } return res; } std::shared_ptr<THugeSlotsMap> TAllChains::BuildHugeSlotsMap() const { - THugeSlotsMap::TAllSlotsInfo allSlotsInfo; - for (const auto& chain : Chains) { - allSlotsInfo.emplace_back(chain.SlotSize, chain.SlotsInChunk); + 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)); } - return std::make_shared<THugeSlotsMap>(AppendBlockSize, MinHugeBlobInBlocks, std::move(allSlotsInfo), - THugeSlotsMap::TSearchTable(SearchTable)); + + return std::make_shared<THugeSlotsMap>(AppendBlockSize, std::move(targetAllSlotsInfo), + std::move(targetSearchTable)); } //////////////////////////////////////////////////////////////////////////// // TAllChains: Private //////////////////////////////////////////////////////////////////////////// - void TAllChains::BuildChains() { - const ui32 startBlocks = MinHugeBlobInBlocks; - const ui32 milestoneBlocks = MilestoneBlobInBytes / AppendBlockSize; - const ui32 endBlocks = MaxHugeBlobInBlocks; + 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; + const ui32 endBlocks = GetEndBlocks(); - NPrivate::TChainLayoutBuilder builder(startBlocks, milestoneBlocks, endBlocks, Overhead); - const ui32 blocksInChunk = ChunkSize / AppendBlockSize; + NPrivate::TChainLayoutBuilder builder(startBlocks, mileStoneBlocks, endBlocks, Overhead); + Y_ABORT_UNLESS(!builder.GetLayout().empty()); + TAllChainDelegators result; for (auto x : builder.GetLayout()) { - const ui32 slotSizeInBlocks = x.Right; - const ui32 slotSize = slotSizeInBlocks * AppendBlockSize; - const ui32 slotsInChunk = blocksInChunk / slotSizeInBlocks; - Chains.emplace_back(VDiskLogPrefix, slotsInChunk, slotSize); + result.emplace_back(VDiskLogPrefix, x.Left, x.Right - x.Left, + ChunkSize, AppendBlockSize); } - - Y_ABORT_UNLESS(!Chains.empty()); + return result; } void TAllChains::BuildSearchTable() { - Y_ABORT_UNLESS(SearchTable.empty()); - Y_ABORT_UNLESS(!Chains.empty()); - - const ui32 startBlocks = MinHugeBlobInBlocks; - const ui32 minSize = startBlocks * AppendBlockSize; - const ui32 endBlocks = MaxHugeBlobInBlocks; // 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 + 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); ++it; - Y_ABORT_UNLESS(it != Chains.end()); - Y_ABORT_UNLESS(size <= it->SlotSize); - Y_ABORT_UNLESS(index != Max<ui16>()); - ++index; + if (it == ChainDelegators.end()) + blocks = ui32(-1); + else + blocks = it->Blocks; } - SearchTable.push_back(index); + SearchTable.push_back(ptr); } } - ui32 TAllChains::SizeToBlocks(ui32 size) const { - return (size + AppendBlockSize - 1) / AppendBlockSize; + void TAllChains::BuildLayout() + { + ChainDelegators = BuildChains(MinHugeBlobInBytes); + Y_ABORT_UNLESS(!ChainDelegators.empty()); + BuildSearchTable(); + } + + inline ui32 TAllChains::SizeToBlocks(ui32 size) const { + ui32 sizeInBlocks = size / AppendBlockSize; + sizeInBlocks += !(sizeInBlocks * AppendBlockSize == size); + return sizeInBlocks; } - void TAllChains::FinishRecovery() { - ui32 prevSlotSize = 0; - for (const TChain& chain : Chains) { - Y_ABORT_UNLESS(prevSlotSize < chain.SlotSize); - prevSlotSize = chain.SlotSize; - } - BuildSearchTable(); + inline ui32 TAllChains::GetEndBlocks() const { + ui32 endBlocks = MaxBlobInBytes / AppendBlockSize; + endBlocks += !(endBlocks * AppendBlockSize == MaxBlobInBytes); + return endBlocks; } //////////////////////////////////////////////////////////////////////////// @@ -653,6 +701,7 @@ namespace NKikimr { ui32 chunkSize, ui32 appendBlockSize, ui32 minHugeBlobInBytes, + ui32 oldMinHugeBlobSizeInBytes, ui32 mileStoneBlobInBytes, ui32 maxBlobInBytes, ui32 overhead, @@ -660,41 +709,42 @@ namespace NKikimr { : VDiskLogPrefix(vdiskLogPrefix) , FreeChunksReservation(freeChunksReservation) , FreeChunks() - , Chains(vdiskLogPrefix, chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, - maxBlobInBytes, overhead) + , Chains(vdiskLogPrefix, chunkSize, appendBlockSize, minHugeBlobInBytes, oldMinHugeBlobSizeInBytes, + mileStoneBlobInBytes, maxBlobInBytes, overhead) {} ////////////////////////////////////////////////////////////////////////////////////////// // THeap: main functions ////////////////////////////////////////////////////////////////////////////////////////// - THugeSlot THeap::ConvertDiskPartToHugeSlot(const TDiskPart& addr) const { - const TChain *chain = Chains.GetChain(addr.Size); - Y_ABORT_UNLESS(chain); - return chain->Convert(chain->Convert(addr)); + 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); } bool THeap::Allocate(ui32 size, THugeSlot *hugeSlot, ui32 *slotSize) { - TChain *chain = Chains.GetChain(size); - Y_VERIFY_S(chain, VDiskLogPrefix << "size# " << size << " Heap# " << ToString()); - *slotSize = chain->SlotSize; + TChainDelegator *chainD = Chains.GetChain(size); + Y_VERIFY_S(chainD, VDiskLogPrefix << "size# " << size << " Heap# " << ToString()); + *slotSize = chainD->SlotSize; NPrivate::TChunkSlot id; - if (!chain->Allocate(&id)) { // no available slot in free space of the chain + if (!chainD->ChainPtr->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; } - chain->Allocate(&id, GetChunkIdFromFreeChunks()); // reuse free chunk for this chain + chainD->ChainPtr->Allocate(&id, GetChunkIdFromFreeChunks()); // reuse free chunk for this chain } - *hugeSlot = chain->Convert(id); + *hugeSlot = chainD->Convert(id); return true; } TFreeRes THeap::Free(const TDiskPart &addr) { ui32 size = addr.Size; - TChain *chain = Chains.GetChain(size); - Y_ABORT_UNLESS(chain); + TChainDelegator *chainD = Chains.GetChain(size); + Y_ABORT_UNLESS(chainD); - TFreeRes res = chain->Free(chain->Convert(addr)); + TFreeRes res = chainD->ChainPtr->Free(chainD->Convert(addr)); if (res.ChunkId) { PutChunkIdToFreeChunks(res.ChunkId); } @@ -716,19 +766,13 @@ namespace NKikimr { } bool THeap::LockChunkForAllocation(ui32 chunkId, ui32 slotSize) { - TChain *chain = Chains.GetChain(slotSize); - Y_ABORT_UNLESS(chain); - return chain->LockChunkForAllocation(chunkId); + TChainDelegator *cd = Chains.GetChain(slotSize); + return cd->ChainPtr->LockChunkForAllocation(chunkId); } void THeap::UnlockChunk(ui32 chunkId, ui32 slotSize) { - TChain *chain = Chains.GetChain(slotSize); - Y_ABORT_UNLESS(chain); - chain->UnlockChunk(chunkId); - } - - void THeap::FinishRecovery() { - Chains.FinishRecovery(); + TChainDelegator *cd = Chains.GetChain(slotSize); + cd->ChainPtr->UnlockChunk(chunkId); } THeapStat THeap::GetStat() const { @@ -744,11 +788,11 @@ namespace NKikimr { void THeap::RecoveryModeAllocate(const TDiskPart &addr) { ui32 size = addr.Size; - TChain *chain = Chains.GetChain(size); - Y_VERIFY_S(chain, VDiskLogPrefix << "State# " << ToString()); + TChainDelegator *chainD = Chains.GetChain(size); + Y_VERIFY_S(chainD, VDiskLogPrefix << "State# " << ToString()); - NPrivate::TChunkSlot id(chain->Convert(addr)); - bool allocated = chain->RecoveryModeAllocate(id); + NPrivate::TChunkSlot id(chainD->Convert(addr)); + bool allocated = chainD->ChainPtr->RecoveryModeAllocate(id); if (allocated) { return; } else { @@ -756,7 +800,7 @@ namespace NKikimr { TFreeChunks::iterator it = FreeChunks.find(chunkId); Y_VERIFY_S(it != FreeChunks.end(), VDiskLogPrefix << "addr# " << addr.ToString() << " State# " << ToString()); FreeChunks.erase(it); - chain->RecoveryModeAllocate(id, chunkId, false); + chainD->ChainPtr->RecoveryModeAllocate(id, chunkId, false); } } @@ -773,9 +817,9 @@ namespace NKikimr { } bool THeap::ReleaseSlot(THugeSlot slot) { - TChain* const chain = Chains.GetChain(slot.GetSize()); + TChainDelegator* const chain = Chains.GetChain(slot.GetSize()); Y_VERIFY_S(chain, VDiskLogPrefix << "State# " << ToString() << " slot# " << slot.ToString()); - if (TFreeRes res = chain->Free(chain->Convert(slot)); res.ChunkId) { + if (TFreeRes res = chain->ChainPtr->Free(chain->Convert(slot)); res.ChunkId) { PutChunkIdToFreeChunks(res.ChunkId); return res.InLockedChunks; } @@ -783,12 +827,12 @@ namespace NKikimr { } void THeap::OccupySlot(THugeSlot slot, bool inLockedChunks) { - TChain* const chain = Chains.GetChain(slot.GetSize()); + TChainDelegator* const chain = Chains.GetChain(slot.GetSize()); Y_VERIFY_S(chain, VDiskLogPrefix << "State# " << ToString() << " slot# " << slot.ToString()); - if (!chain->RecoveryModeAllocate(chain->Convert(slot))) { + if (!chain->ChainPtr->RecoveryModeAllocate(chain->Convert(slot))) { const size_t numErased = FreeChunks.erase(slot.GetChunkId()); Y_VERIFY_S(numErased, VDiskLogPrefix << "State# " << ToString() << " slot# " << slot.ToString()); - chain->RecoveryModeAllocate(chain->Convert(slot), slot.GetChunkId(), inLockedChunks); + chain->ChainPtr->RecoveryModeAllocate(chain->Convert(slot), slot.GetChunkId(), inLockedChunks); } } @@ -855,8 +899,9 @@ namespace NKikimr { TString THeap::ToString() const { TStringStream str; - str << "FreeChunks# " << FormatList(FreeChunks) - << " Chains# {" << Chains.ToString() << '}'; + str << "FreeChunks: "; + str << FormatList(FreeChunks); + str << " 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 c7f987b5bb..6fbae987e4 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<TSeg> &GetLayout() const { return Layout; } - const TSeg &GetMilestoneSegment() const { return Layout.at(MilestoneId); } + const TSeg &GetMilestoneSegment() const { return Layout.at(MilesoneId); } TString ToString(ui32 appendBlockSize = 0) const; void Output(IOutputStream &str, ui32 appendBlockSize = 0) const; @@ -81,7 +81,7 @@ namespace NKikimr { TVector<TSeg> Layout; // An index in Layout vector, where milestone segment starts - size_t MilestoneId = Max<size_t>(); + size_t MilesoneId = Max<size_t>(); }; } // NPrivate @@ -90,43 +90,29 @@ namespace NKikimr { // TChain // It manages all slots of some fixed size. //////////////////////////////////////////////////////////////////////////// - class TChain { + class TChain : public TThrRefBase { using TChunkID = ui32; using TFreeSpace = TMap<TChunkID, TMask>; static constexpr ui32 MaxNumberOfSlots = 32768; // it's not a good idea to have more slots than this - TString VDiskLogPrefix; - TMask ConstMask; // mask of 'all slots are free' + const TString VDiskLogPrefix; + const ui32 SlotsInChunk; + const TMask ConstMask; // mask of 'all slots are free' TFreeSpace FreeSpace; TFreeSpace LockedChunks; ui32 AllocatedSlots = 0; ui32 FreeSlotsInFreeSpace = 0; public: - ui32 SlotsInChunk; - ui32 SlotSize; // may be adjusted during deserialization - - public: static TMask BuildConstMask(const TString &prefix, ui32 slotsInChunk); public: - TChain(TString vdiskLogPrefix, ui32 slotsInChunk, ui32 slotSize) - : VDiskLogPrefix(std::move(vdiskLogPrefix)) - , ConstMask(BuildConstMask(vdiskLogPrefix, slotsInChunk)) + TChain(const TString &vdiskLogPrefix, const ui32 slotsInChunk) + : VDiskLogPrefix(vdiskLogPrefix) , SlotsInChunk(slotsInChunk) - , SlotSize(slotSize) + , ConstMask(BuildConstMask(vdiskLogPrefix, slotsInChunk)) {} - 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 @@ -140,48 +126,71 @@ 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; - void RenderHtmlForUsage(IOutputStream &str) const; + ui32 GetAllocatedSlots() const; void GetOwnedChunks(TSet<TChunkIdx>& chunks) const; + }; - static TChain Load(IInputStream *s, TString vdiskLogPrefix, ui32 appendBlockSize, ui32 blocksInChunk); - - template<typename T> - 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"); - } - } - } + using TChainPtr = TIntrusivePtr<TChain>; + + //////////////////////////////////////////////////////////////////////////// + // 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<TChunkIdx>& chunks) const; + void RenderHtml(IOutputStream &str) const; + void RenderHtmlForUsage(IOutputStream &str) const; }; + //////////////////////////////////////////////////////////////////////////// // TAllChains //////////////////////////////////////////////////////////////////////////// class TAllChains { public: + using TAllChainDelegators = TVector<TChainDelegator>; + using TSearchTable = TVector<TChainDelegator*>; + 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 - TChain *GetChain(ui32 size); - const TChain *GetChain(ui32 size) const; + TChainDelegator *GetChain(ui32 size); + const TChainDelegator *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<TChunkIdx>& chunks) const; @@ -190,27 +199,39 @@ namespace NKikimr { void RenderHtmlForUsage(IOutputStream &str) const; // for testing purposes TVector<NPrivate::TChainLayoutBuilder::TSeg> GetLayout() const; + // returns (ChainsSize, SearchTableSize) + std::pair<ui32, ui32> GetTablesSize() const { + return std::pair<ui32, ui32>(ChainDelegators.size(), SearchTable.size()); + } // Builds a map of BlobSize -> THugeSlotsMap::TSlotInfo for THugeBlobCtx std::shared_ptr<THugeSlotsMap> BuildHugeSlotsMap() const; - void FinishRecovery(); - private: - void BuildChains(); + + TAllChainDelegators BuildChains(ui32 minHugeBlobInBytes) const; 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; - const ui32 MinHugeBlobInBlocks; - const ui32 MaxHugeBlobInBlocks; - TDynBitMap DeserializedChains; // a bit mask of chains that were deserialized from the origin stream - std::vector<TChain> Chains; - std::vector<ui16> SearchTable; // (NumFullBlocks - 1) -> Chain index + EStartMode StartMode = EStartMode::Empty; + ui32 FirstLoadedSlotSize = 0; + TAllChainDelegators ChainDelegators; + TSearchTable SearchTable; }; @@ -234,6 +255,7 @@ 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 @@ -244,13 +266,13 @@ namespace NKikimr { ui32 SlotNumberOfThisSize(ui32 size) const { - const TChain *chain = Chains.GetChain(size); - return chain ? chain->SlotsInChunk : 0; + const TChainDelegator *chainD = Chains.GetChain(size); + return chainD ? chainD->SlotsInChunk : 0; } ui32 SlotSizeOfThisSize(ui32 size) const { - const TChain *chain = Chains.GetChain(size); - return chain ? chain->SlotSize : 0; + const TChainDelegator *chainD = Chains.GetChain(size); + return chainD ? chainD->SlotSize : 0; } // Builds a map of BlobSize -> THugeSlotsMap::TSlotInfo for THugeBlobCtx @@ -280,7 +302,6 @@ namespace NKikimr { void RecoveryModeRemoveChunks(const TVector<ui32> &chunkIds); bool ReleaseSlot(THugeSlot slot); void OccupySlot(THugeSlot slot, bool inLockedChunks); - void FinishRecovery(); ////////////////////////////////////////////////////////////////////////////////////////// // Serialize/Parse/Check @@ -296,6 +317,10 @@ 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 865bf38513..df27d118e9 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ctx_ut.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugeheap_ctx_ut.cpp @@ -26,6 +26,7 @@ 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 2a07af1013..967a1e2e0d 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, 1); + TChain chain("vdisk", slotsInChunk); TVector<NPrivate::TChunkSlot> arr; AllocateScenaryOneChunk(chain, arr, slotsInChunk); FreeScenaryOneChunk(chain, arr, slotsInChunk); @@ -156,7 +156,7 @@ namespace NKikimr { } void AllocFreeAlloc(ui32 slotsInChunk) { - TChain chain("vdisk", slotsInChunk, 1); + TChain chain("vdisk", slotsInChunk); TVector<NPrivate::TChunkSlot> arr; TVector<ui32> chunks; @@ -171,13 +171,17 @@ namespace NKikimr { TStringStream serialized; - TChain chain("vdisk", slotsInChunk, 1); - PreliminaryAllocate(24, chain, arr); - FreeChunksScenary(chain, arr, chunks); - chain.Save(&serialized); - - TStringInput str(serialized.Str()); - TChain chain2 = TChain::Load(&str, "vdisk", 1 /*appendBlockSize*/, slotsInChunk); + { + 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); + } } Y_UNIT_TEST(AllocFreeAllocTest) { @@ -244,6 +248,38 @@ 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<ui32, ui32> p = all.GetTablesSize(); + TVector<NPrivate::TChainLayoutBuilder::TSeg> 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<ui32, ui32>(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<NPrivate::TChainLayoutBuilder::TSeg> 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) { @@ -254,9 +290,8 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); - heap.FinishRecovery(); ui32 hugeBlobSize = 6u << 20u; heap.AddChunk(5); @@ -279,10 +314,9 @@ namespace NKikimr { // just serialize/deserialize TString serialized = heap.Serialize(); - THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); newHeap.ParseFromString(serialized); - newHeap.FinishRecovery(); } void AllocateScenary(THeap &heap, ui32 hugeBlobSize, TVector<THugeSlot> &arr) { @@ -323,9 +357,8 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); - heap.FinishRecovery(); TVector<THugeSlot> arr; AllocateScenary(heap, 6u << 20u, arr); @@ -339,21 +372,16 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); - heap.FinishRecovery(); TVector<THugeSlot> 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, + THeap newHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); newHeap.ParseFromString(serialized); - newHeap.FinishRecovery(); - TString heap2 = newHeap.ToString(); - UNIT_ASSERT_VALUES_EQUAL(heap1, heap2); FreeScenary(newHeap, arr); } @@ -364,9 +392,8 @@ namespace NKikimr { ui32 maxBlobInBytes = 10u << 20u; ui32 overhead = 8; ui32 freeChunksReservation = 0; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); - heap.FinishRecovery(); heap.RecoveryModeAddChunk(2); heap.RecoveryModeAddChunk(34); @@ -394,9 +421,8 @@ namespace NKikimr { ui32 maxBlobInBytes = MaxVDiskBlobSize; ui32 overhead = 8u; ui32 freeChunksReservation = 1; - THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, + THeap heap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, minHugeBlobInBytes, minHugeBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); - heap.FinishRecovery(); THugeSlot hugeSlot; ui32 slotSize; @@ -412,34 +438,52 @@ namespace NKikimr { RollbackFrom_New_To_Old, }; - Y_UNIT_TEST(WriteRestore) { + void Write_SaveEntryPoint_Restart(EWrite_SaveEntryPoint_Restart mode) { 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; - THeap oldHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + 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, maxBlobInBytes, overhead, freeChunksReservation); - oldHeap.FinishRecovery(); - THeap fromHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + THeap fromHeap("vdisk", chunkSize, appendBlockSize, fromMin, oldMinHugeBlobInBytes, mileStoneBlobInBytes, maxBlobInBytes, overhead, freeChunksReservation); fromHeap.ParseFromString(oldHeap.Serialize()); - fromHeap.FinishRecovery(); TVector<THugeSlot> arr; AllocateScenary(fromHeap, 6u << 20u, arr); TString serialized = fromHeap.Serialize(); UNIT_ASSERT(THeap::CheckEntryPoint(serialized)); - THeap toHeap("vdisk", chunkSize, appendBlockSize, minHugeBlobInBytes, mileStoneBlobInBytes, + THeap toHeap("vdisk", chunkSize, appendBlockSize, toMin, oldMinHugeBlobInBytes, 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 f5aa9d561f..898271ef6d 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.cpp +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.cpp @@ -64,6 +64,7 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, + const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -72,11 +73,10 @@ namespace NKikimr { : VCtx(std::move(vctx)) , LogPos(THullHugeRecoveryLogPos::Default()) , Heap(new NHuge::THeap(VCtx->VDiskLogPrefix, chunkSize, appendBlockSize, - minHugeBlobInBytes, milestoneHugeBlobInBytes, + minHugeBlobInBytes, oldMinHugeBlobInBytes, 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,6 +86,7 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, + const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -96,13 +97,12 @@ namespace NKikimr { : VCtx(std::move(vctx)) , LogPos(THullHugeRecoveryLogPos::Default()) , Heap(new NHuge::THeap(VCtx->VDiskLogPrefix, chunkSize, appendBlockSize, - minHugeBlobInBytes, milestoneHugeBlobInBytes, + minHugeBlobInBytes, oldMinHugeBlobInBytes, 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,6 +113,7 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, + const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -123,13 +124,12 @@ namespace NKikimr { : VCtx(std::move(vctx)) , LogPos(THullHugeRecoveryLogPos::Default()) , Heap(new NHuge::THeap(VCtx->VDiskLogPrefix, chunkSize, appendBlockSize, - minHugeBlobInBytes, milestoneHugeBlobInBytes, + minHugeBlobInBytes, oldMinHugeBlobInBytes, 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 cea53fbe31..548a4997c2 100644 --- a/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.h +++ b/ydb/core/blobstorage/vdisk/huge/blobstorage_hullhugerecovery.h @@ -92,6 +92,7 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, + const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -101,6 +102,7 @@ namespace NKikimr { const ui32 chunkSize, const ui32 appendBlockSize, const ui32 minHugeBlobInBytes, + const ui32 oldMinHugeBlobInBytes, const ui32 milestoneHugeBlobInBytes, const ui32 maxBlobInBytes, const ui32 overhead, @@ -112,6 +114,7 @@ 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 7d1028e64e..cda349b521 100644 --- a/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp +++ b/ydb/core/blobstorage/vdisk/localrecovery/localrecovery_public.cpp @@ -425,6 +425,12 @@ namespace NKikimr { const ui32 blocksInChunk = LocRecCtx->PDiskCtx->Dsk->ChunkSize / LocRecCtx->PDiskCtx->Dsk->AppendBlockSize; Y_ABORT_UNLESS(LocRecCtx->PDiskCtx->Dsk->AppendBlockSize * blocksInChunk == LocRecCtx->PDiskCtx->Dsk->ChunkSize); + ui32 MaxLogoBlobDataSizeInBlocks = Config->MaxLogoBlobDataSize / LocRecCtx->PDiskCtx->Dsk->AppendBlockSize; + MaxLogoBlobDataSizeInBlocks += !!(Config->MaxLogoBlobDataSize - + MaxLogoBlobDataSizeInBlocks * LocRecCtx->PDiskCtx->Dsk->AppendBlockSize); + const ui32 slotsInChunk = blocksInChunk / MaxLogoBlobDataSizeInBlocks; + Y_ABORT_UNLESS(slotsInChunk > 1); + auto logFunc = [&] (const TString &msg) { LOG_DEBUG(ctx, BS_HULLHUGE, msg); }; @@ -438,8 +444,9 @@ namespace NKikimr { LocRecCtx->PDiskCtx->Dsk->ChunkSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, + Config->OldMinHugeBlobInBytes, Config->MilestoneHugeBlobInBytes, - Config->MaxLogoBlobDataSize + TDiskBlob::HeaderSize, + Config->MaxLogoBlobDataSize, Config->HugeBlobOverhead, Config->HugeBlobsFreeChunkReservation, logFunc); @@ -459,8 +466,9 @@ namespace NKikimr { LocRecCtx->PDiskCtx->Dsk->ChunkSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, LocRecCtx->PDiskCtx->Dsk->AppendBlockSize, + Config->OldMinHugeBlobInBytes, Config->MilestoneHugeBlobInBytes, - Config->MaxLogoBlobDataSize + TDiskBlob::HeaderSize, + Config->MaxLogoBlobDataSize, Config->HugeBlobOverhead, Config->HugeBlobsFreeChunkReservation, lsn, entryPoint, logFunc); |