From 05178f7c18a48b21b9e260de282a86b91df26955 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Tue, 21 Nov 2023 15:06:25 +0100 Subject: compact separate for gc referencer (#533) - Refactor GCV2 so GcReferencer::RemoveExpiredData returns a store compactor, moving out the actual disk work from deleting items in the index. - Refactor GCV2 GcResult to reuse GcCompactStoreStats and GcStats - Make Compacting of stores non-parallell to not eat all the disk I/O when running GC --- src/zenstore/blockstore.cpp | 107 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 21 deletions(-) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index 063d38707..ec299092d 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -15,6 +15,7 @@ ZEN_THIRD_PARTY_INCLUDES_START #include #include +#include ZEN_THIRD_PARTY_INCLUDES_END #if ZEN_WITH_TESTS @@ -267,6 +268,59 @@ BlockStore::SyncExistingBlocksOnDisk(const std::vector& Know } } +std::vector +BlockStore::GetBlocksToCompact(const std::unordered_map& BlockUsage, uint32_t BlockUsageThresholdPercent) +{ + std::unordered_set Result; + { + RwLock::SharedLockScope InsertLock(m_InsertLock); + for (const auto& It : m_ChunkBlocks) + { + uint32_t BlockIndex = It.first; + if (std::find(m_ActiveWriteBlocks.begin(), m_ActiveWriteBlocks.end(), BlockIndex) != m_ActiveWriteBlocks.end()) + { + continue; + } + uint64_t BlockSize = It.second ? It.second->FileSize() : 0u; + if (BlockSize == 0) + { + Result.insert(BlockIndex); + continue; + } + + uint64_t UsedSize = 0; + if (auto UsageIt = BlockUsage.find(BlockIndex); UsageIt != BlockUsage.end()) + { + UsedSize = UsageIt->second; + } + + if (BlockUsageThresholdPercent == 100) + { + if (UsedSize < BlockSize) + { + Result.insert(BlockIndex); + } + } + else if (BlockUsageThresholdPercent == 0) + { + if (UsedSize == 0) + { + Result.insert(BlockIndex); + } + } + else + { + const uint32_t UsedPercent = UsedSize < BlockSize ? gsl::narrow((100 * UsedSize) / BlockSize) : 100u; + if (UsedPercent < BlockUsageThresholdPercent) + { + Result.insert(BlockIndex); + } + } + } + } + return std::vector(Result.begin(), Result.end()); +} + void BlockStore::Close() { @@ -971,7 +1025,7 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, Stopwatch TotalTimer; const auto _ = MakeGuard([&] { - ZEN_DEBUG("compact blocks for '{}' DONE after {}, deleted {} and moved {} chunks ({}) ", + ZEN_DEBUG("Compact blocks for '{}' DONE after {}, deleted {} and moved {} chunks ({}) ", m_BlocksBasePath, NiceTimeSpanMs(TotalTimer.GetElapsedTimeMs()), NiceBytes(DeletedSize), @@ -983,13 +1037,14 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, uint32_t NewBlockIndex = 0; MovedChunksArray MovedChunks; + uint64_t AddedSize = 0; uint64_t RemovedSize = 0; Ref NewBlockFile; auto NewBlockFileGuard = MakeGuard([&]() { if (NewBlockFile) { - ZEN_DEBUG("dropping incomplete cas block store file '{}'", NewBlockFile->GetPath()); + ZEN_DEBUG("Dropping incomplete cas block store file '{}'", NewBlockFile->GetPath()); { RwLock::ExclusiveLockScope _l(m_InsertLock); if (m_ChunkBlocks[NewBlockIndex] == NewBlockFile) @@ -1001,6 +1056,18 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, } }); + auto ReportChanges = [&]() { + if (!MovedChunks.empty() || RemovedSize > 0) + { + ChangeCallback(MovedChunks, RemovedSize > AddedSize ? RemovedSize - AddedSize : 0); + DeletedSize += RemovedSize; + RemovedSize = 0; + AddedSize = 0; + MovedCount += MovedChunks.size(); + MovedChunks.clear(); + } + }; + std::vector RemovedBlocks; CompactState.IterateBlocks( @@ -1030,12 +1097,23 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, uint64_t OldBlockSize = OldBlockFile->FileSize(); - // TODO: Add heuristics for determining if it is worth to compact a block (if only a very small part is removed) - std::vector Chunk; for (const size_t& ChunkIndex : KeepChunkIndexes) { const BlockStoreLocation ChunkLocation = ChunkLocations[ChunkIndex]; + if (ChunkLocation.Offset + ChunkLocation.Size > OldBlockSize) + { + ZEN_WARN( + "Compact Block skipping chunk outside of block range in '{}', Chunk start {}, Chunk size {} in Block {}, Block " + "size {}", + m_BlocksBasePath, + ChunkLocation.Offset, + ChunkLocation.Size, + OldBlockFile->GetPath(), + OldBlockSize); + continue; + } + Chunk.resize(ChunkLocation.Size); OldBlockFile->Read(Chunk.data(), Chunk.size(), ChunkLocation.Offset); @@ -1113,18 +1191,11 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, NewBlockFile->Write(Chunk.data(), Chunk.size(), WriteOffset); MovedChunks.push_back({ChunkIndex, {.BlockIndex = NewBlockIndex, .Offset = WriteOffset, .Size = Chunk.size()}}); WriteOffset = RoundUp(WriteOffset + Chunk.size(), PayloadAlignment); + AddedSize += Chunk.size(); } Chunk.clear(); - // Report what we have moved so we can purge the old block - if (!MovedChunks.empty() || RemovedSize > 0) - { - ChangeCallback(MovedChunks, RemovedSize); - DeletedSize += RemovedSize; - RemovedSize = 0; - MovedCount += MovedChunks.size(); - MovedChunks.clear(); - } + ReportChanges(); { RwLock::ExclusiveLockScope InsertLock(m_InsertLock); @@ -1135,6 +1206,7 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, RemovedSize += OldBlockSize; } }); + if (NewBlockFile) { NewBlockFile->Flush(); @@ -1142,14 +1214,7 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, NewBlockFile = nullptr; } - if (!MovedChunks.empty() || RemovedSize > 0) - { - ChangeCallback(MovedChunks, RemovedSize); - DeletedSize += RemovedSize; - RemovedSize = 0; - MovedCount += MovedChunks.size(); - MovedChunks.clear(); - } + ReportChanges(); } const char* -- cgit v1.2.3 From 254d2f89c110fc5f14e658505559a7e7534a984d Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 24 Nov 2023 13:26:51 +0100 Subject: Add GC Cancel/Stop (#568) - GcScheduler will now cancel any running GC when it shuts down. - Old GC is rather limited in *when* it reacts to cancel of GC. GCv2 is more responsive. --- src/zenstore/blockstore.cpp | 255 ++++++++++++++++++++++++-------------------- 1 file changed, 141 insertions(+), 114 deletions(-) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index ec299092d..e4a66daf4 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -1056,156 +1056,172 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, } }); - auto ReportChanges = [&]() { + auto ReportChanges = [&]() -> bool { + bool Continue = true; if (!MovedChunks.empty() || RemovedSize > 0) { - ChangeCallback(MovedChunks, RemovedSize > AddedSize ? RemovedSize - AddedSize : 0); + Continue = ChangeCallback(MovedChunks, RemovedSize > AddedSize ? RemovedSize - AddedSize : 0); DeletedSize += RemovedSize; RemovedSize = 0; AddedSize = 0; MovedCount += MovedChunks.size(); MovedChunks.clear(); } + return Continue; }; std::vector RemovedBlocks; - CompactState.IterateBlocks( - [&](uint32_t BlockIndex, const std::vector& KeepChunkIndexes, const std::vector& ChunkLocations) { - Ref OldBlockFile; + CompactState.IterateBlocks([&](uint32_t BlockIndex, + const std::vector& KeepChunkIndexes, + const std::vector& ChunkLocations) -> bool { + Ref OldBlockFile; + { + RwLock::SharedLockScope _(m_InsertLock); + if ((BlockIndex == m_WriteBlockIndex.load()) && m_WriteBlock) { - RwLock::SharedLockScope _(m_InsertLock); - if ((BlockIndex == m_WriteBlockIndex.load()) && m_WriteBlock) - { - // You are trying to collect the currently writing block, Report error? - return; - } - auto It = m_ChunkBlocks.find(BlockIndex); - if (It == m_ChunkBlocks.end()) - { - // This block has unknown, we can't move anything. Report error? - return; - } - if (!It->second) - { - // This block has been removed, we can't move anything. Report error? - return; - } - OldBlockFile = It->second; + ZEN_ERROR("Compact Block was requested to rewrite the currently active write block in '{}', Block index {}", + m_BlocksBasePath, + BlockIndex); + return false; } - ZEN_ASSERT(OldBlockFile); + auto It = m_ChunkBlocks.find(BlockIndex); + if (It == m_ChunkBlocks.end()) + { + ZEN_WARN("Compact Block was requested to rewrite an unknown block in '{}', Block index {}", m_BlocksBasePath, BlockIndex); + return true; + } + if (!It->second) + { + ZEN_WARN("Compact Block was requested to rewrite a deleted block in '{}', Block index {}", m_BlocksBasePath, BlockIndex); + return true; + } + OldBlockFile = It->second; + } + ZEN_ASSERT(OldBlockFile); - uint64_t OldBlockSize = OldBlockFile->FileSize(); + uint64_t OldBlockSize = OldBlockFile->FileSize(); - std::vector Chunk; - for (const size_t& ChunkIndex : KeepChunkIndexes) + std::vector Chunk; + for (const size_t& ChunkIndex : KeepChunkIndexes) + { + const BlockStoreLocation ChunkLocation = ChunkLocations[ChunkIndex]; + if (ChunkLocation.Offset + ChunkLocation.Size > OldBlockSize) { - const BlockStoreLocation ChunkLocation = ChunkLocations[ChunkIndex]; - if (ChunkLocation.Offset + ChunkLocation.Size > OldBlockSize) - { - ZEN_WARN( - "Compact Block skipping chunk outside of block range in '{}', Chunk start {}, Chunk size {} in Block {}, Block " - "size {}", - m_BlocksBasePath, - ChunkLocation.Offset, - ChunkLocation.Size, - OldBlockFile->GetPath(), - OldBlockSize); - continue; - } + ZEN_WARN( + "Compact Block skipping chunk outside of block range in '{}', Chunk start {}, Chunk size {} in Block {}, Block " + "size {}", + m_BlocksBasePath, + ChunkLocation.Offset, + ChunkLocation.Size, + OldBlockFile->GetPath(), + OldBlockSize); + continue; + } - Chunk.resize(ChunkLocation.Size); - OldBlockFile->Read(Chunk.data(), Chunk.size(), ChunkLocation.Offset); + Chunk.resize(ChunkLocation.Size); + OldBlockFile->Read(Chunk.data(), Chunk.size(), ChunkLocation.Offset); - if ((WriteOffset + Chunk.size()) > m_MaxBlockSize) + if ((WriteOffset + Chunk.size()) > m_MaxBlockSize) + { + if (NewBlockFile) { - if (NewBlockFile) - { - NewBlockFile->Flush(); - MovedSize += NewBlockFile->FileSize(); - NewBlockFile = nullptr; + NewBlockFile->Flush(); + MovedSize += NewBlockFile->FileSize(); + NewBlockFile = nullptr; - ZEN_ASSERT(!MovedChunks.empty() || RemovedSize > 0); // We should not have a new block if we haven't moved anything + ZEN_ASSERT(!MovedChunks.empty() || RemovedSize > 0); // We should not have a new block if we haven't moved anything - ChangeCallback(MovedChunks, RemovedSize); - DeletedSize += RemovedSize; - RemovedSize = 0; - MovedCount += MovedChunks.size(); - MovedChunks.clear(); + if (!ReportChanges()) + { + return false; } + } - uint32_t NextBlockIndex = m_WriteBlockIndex.load(std::memory_order_relaxed); + uint32_t NextBlockIndex = m_WriteBlockIndex.load(std::memory_order_relaxed); + { + RwLock::ExclusiveLockScope InsertLock(m_InsertLock); + std::filesystem::path NewBlockPath; + NextBlockIndex = GetFreeBlockIndex(NextBlockIndex, InsertLock, NewBlockPath); + if (NextBlockIndex == (uint32_t)m_MaxBlockCount) { - RwLock::ExclusiveLockScope InsertLock(m_InsertLock); - std::filesystem::path NewBlockPath; - NextBlockIndex = GetFreeBlockIndex(NextBlockIndex, InsertLock, NewBlockPath); - if (NextBlockIndex == (uint32_t)m_MaxBlockCount) - { - ZEN_ERROR("unable to allocate a new block in '{}', count limit {} exeeded", - m_BlocksBasePath, - static_cast(std::numeric_limits::max()) + 1); - return; - } - - NewBlockFile = new BlockStoreFile(NewBlockPath); - m_ChunkBlocks[NextBlockIndex] = NewBlockFile; + ZEN_ERROR("unable to allocate a new block in '{}', count limit {} exeeded", + m_BlocksBasePath, + static_cast(std::numeric_limits::max()) + 1); + return false; } - ZEN_ASSERT(NewBlockFile); - std::error_code Error; - DiskSpace Space = DiskSpaceInfo(m_BlocksBasePath, Error); - if (Error) + NewBlockFile = new BlockStoreFile(NewBlockPath); + m_ChunkBlocks[NextBlockIndex] = NewBlockFile; + } + ZEN_ASSERT(NewBlockFile); + + std::error_code Error; + DiskSpace Space = DiskSpaceInfo(m_BlocksBasePath, Error); + if (Error) + { + ZEN_ERROR("get disk space in '{}' FAILED, reason: '{}'", m_BlocksBasePath, Error.message()); { - ZEN_ERROR("get disk space in '{}' FAILED, reason: '{}'", m_BlocksBasePath, Error.message()); - return; + RwLock::ExclusiveLockScope _l(m_InsertLock); + ZEN_ASSERT(m_ChunkBlocks[NextBlockIndex] == NewBlockFile); + m_ChunkBlocks.erase(NextBlockIndex); } + NewBlockFile->MarkAsDeleteOnClose(); + NewBlockFile = nullptr; + return false; + } - if (Space.Free < m_MaxBlockSize) + if (Space.Free < m_MaxBlockSize) + { + uint64_t ReclaimedSpace = DiskReserveCallback(); + if (Space.Free + ReclaimedSpace < m_MaxBlockSize) { - uint64_t ReclaimedSpace = DiskReserveCallback(); - if (Space.Free + ReclaimedSpace < m_MaxBlockSize) - { - ZEN_WARN("garbage collect for '{}' FAILED, required disk space {}, free {}", - m_BlocksBasePath, - m_MaxBlockSize, - NiceBytes(Space.Free + ReclaimedSpace)); - { - RwLock::ExclusiveLockScope _l(m_InsertLock); - ZEN_ASSERT(m_ChunkBlocks[NextBlockIndex] == NewBlockFile); - m_ChunkBlocks.erase(NextBlockIndex); - } - NewBlockFile = nullptr; - return; - } - - ZEN_INFO("using gc reserve for '{}', reclaimed {}, disk free {}", + ZEN_WARN("garbage collect for '{}' FAILED, required disk space {}, free {}", m_BlocksBasePath, - ReclaimedSpace, + m_MaxBlockSize, NiceBytes(Space.Free + ReclaimedSpace)); + { + RwLock::ExclusiveLockScope _l(m_InsertLock); + ZEN_ASSERT(m_ChunkBlocks[NextBlockIndex] == NewBlockFile); + m_ChunkBlocks.erase(NextBlockIndex); + } + NewBlockFile->MarkAsDeleteOnClose(); + NewBlockFile = nullptr; + return false; } - NewBlockFile->Create(m_MaxBlockSize); - NewBlockIndex = NextBlockIndex; - WriteOffset = 0; - } - NewBlockFile->Write(Chunk.data(), Chunk.size(), WriteOffset); - MovedChunks.push_back({ChunkIndex, {.BlockIndex = NewBlockIndex, .Offset = WriteOffset, .Size = Chunk.size()}}); - WriteOffset = RoundUp(WriteOffset + Chunk.size(), PayloadAlignment); - AddedSize += Chunk.size(); + ZEN_INFO("using gc reserve for '{}', reclaimed {}, disk free {}", + m_BlocksBasePath, + ReclaimedSpace, + NiceBytes(Space.Free + ReclaimedSpace)); + } + NewBlockFile->Create(m_MaxBlockSize); + NewBlockIndex = NextBlockIndex; + WriteOffset = 0; } - Chunk.clear(); - ReportChanges(); + NewBlockFile->Write(Chunk.data(), Chunk.size(), WriteOffset); + MovedChunks.push_back({ChunkIndex, {.BlockIndex = NewBlockIndex, .Offset = WriteOffset, .Size = Chunk.size()}}); + WriteOffset = RoundUp(WriteOffset + Chunk.size(), PayloadAlignment); + AddedSize += Chunk.size(); + } + Chunk.clear(); + + if (!ReportChanges()) + { + return false; + } - { - RwLock::ExclusiveLockScope InsertLock(m_InsertLock); - ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex); - OldBlockFile->MarkAsDeleteOnClose(); - m_ChunkBlocks.erase(BlockIndex); - m_TotalSize.fetch_sub(OldBlockSize); - RemovedSize += OldBlockSize; - } - }); + { + RwLock::ExclusiveLockScope InsertLock(m_InsertLock); + ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex); + OldBlockFile->MarkAsDeleteOnClose(); + m_ChunkBlocks.erase(BlockIndex); + m_TotalSize.fetch_sub(OldBlockSize); + RemovedSize += OldBlockSize; + } + return true; + }); if (NewBlockFile) { @@ -1825,7 +1841,10 @@ TEST_CASE("blockstore.compact.blocks") Store.CompactBlocks( State, Alignment, - [&](const BlockStore::MovedChunksArray&, uint64_t) { CHECK(false); }, + [&](const BlockStore::MovedChunksArray&, uint64_t) { + CHECK(false); + return true; + }, []() { CHECK(false); return 0; @@ -1850,6 +1869,7 @@ TEST_CASE("blockstore.compact.blocks") [&](const BlockStore::MovedChunksArray& Moved, uint64_t Removed) { RemovedSize += Removed; CHECK(Moved.empty()); + return true; }, []() { return 0; }); CHECK_EQ(RemovedSize, PreSize); @@ -1875,6 +1895,7 @@ TEST_CASE("blockstore.compact.blocks") [&](const BlockStore::MovedChunksArray& Moved, uint64_t Removed) { RemovedSize += Removed; CHECK(Moved.empty()); + return true; }, []() { return 0; }); CHECK_EQ(Store.TotalSize() + RemovedSize, PreSize); @@ -1895,7 +1916,10 @@ TEST_CASE("blockstore.compact.blocks") Store.CompactBlocks( State, Alignment, - [&](const BlockStore::MovedChunksArray&, uint64_t) { CHECK(false); }, + [&](const BlockStore::MovedChunksArray&, uint64_t) { + CHECK(false); + return true; + }, []() { CHECK(false); return 0; @@ -1927,6 +1951,7 @@ TEST_CASE("blockstore.compact.blocks") [&](const BlockStore::MovedChunksArray& Moved, uint64_t Removed) { CHECK(Moved.empty()); RemovedSize += Removed; + return true; }, []() { CHECK(false); @@ -1970,6 +1995,7 @@ TEST_CASE("blockstore.compact.blocks") (*It) = Move.second; } RemovedSize += Removed; + return true; }, []() { CHECK(false); @@ -2046,6 +2072,7 @@ TEST_CASE("blockstore.compact.blocks") (*It) = Move.second; } RemovedSize += Removed; + return true; }, []() { CHECK(false); -- cgit v1.2.3 From 4d95b578350ebfbbf6d54407c9403547b01cac4c Mon Sep 17 00:00:00 2001 From: Stefan Boberg Date: Mon, 27 Nov 2023 14:32:19 +0100 Subject: optimized index snapshot reading/writing (#561) the previous implementation of in-memory index snapshots serialise data to memory before writing to disk and vice versa when reading. This leads to some memory spikes which end up pushing useful data out of system cache and also cause stalls on I/O operations. this change moves more code to a streaming serialisation approach which scales better from a memory usage perspective and also performs much better --- src/zenstore/blockstore.cpp | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index e4a66daf4..89774f26d 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -227,7 +227,17 @@ BlockStore::Initialize(const std::filesystem::path& BlocksBasePath, uint64_t Max } void -BlockStore::SyncExistingBlocksOnDisk(const std::vector& KnownLocations) +BlockStore::BlockIndexSet::Add(uint32_t BlockIndex) +{ + if (!std::binary_search(begin(BlockIndexes), end(BlockIndexes), BlockIndex)) + { + auto It = std::lower_bound(begin(BlockIndexes), end(BlockIndexes), BlockIndex); + BlockIndexes.insert(It, BlockIndex); + } +} + +void +BlockStore::SyncExistingBlocksOnDisk(const BlockIndexSet& KnownLocations) { ZEN_TRACE_CPU("BlockStore::SyncExistingBlocksOnDisk"); @@ -240,14 +250,18 @@ BlockStore::SyncExistingBlocksOnDisk(const std::vector& Know { DeleteBlocks.insert(It.first); } - for (const auto& Entry : KnownLocations) + + for (const uint32_t BlockIndex : KnownLocations.GetBlockIndices()) { - DeleteBlocks.erase(Entry.BlockIndex); - if (auto It = m_ChunkBlocks.find(Entry.BlockIndex); It != m_ChunkBlocks.end() && !It->second.IsNull()) + DeleteBlocks.erase(BlockIndex); + if (auto It = m_ChunkBlocks.find(BlockIndex); It != m_ChunkBlocks.end() && !It->second.IsNull()) { continue; } - MissingBlocks.insert(Entry.BlockIndex); + else + { + MissingBlocks.insert(BlockIndex); + } } for (std::uint32_t BlockIndex : MissingBlocks) { @@ -1473,7 +1487,12 @@ TEST_CASE("blockstore.clean.stray.blocks") CHECK(!ThirdChunk); // Recreate a fake block for a missing chunk location - Store.SyncExistingBlocksOnDisk({FirstChunkLocation, SecondChunkLocation, ThirdChunkLocation}); + BlockStore::BlockIndexSet KnownBlocks; + KnownBlocks.Add(FirstChunkLocation.BlockIndex); + KnownBlocks.Add(SecondChunkLocation.BlockIndex); + KnownBlocks.Add(ThirdChunkLocation.BlockIndex); + Store.SyncExistingBlocksOnDisk(KnownBlocks); + // We create a fake block for the location - we should still not be able to get the chunk CHECK(GetDirectoryContent(RootDirectory / "store", true, false).size() == 2); ThirdChunk = Store.TryGetChunk(ThirdChunkLocation); -- cgit v1.2.3 From 765855296a28df198572d97207ac917f3d249014 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 27 Nov 2023 09:38:06 -0500 Subject: gcv2 tests for project store and bugfixes (#571) * gcv2 tests for project store and bugfixes --- src/zenstore/blockstore.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index 89774f26d..03c7f4b95 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -291,6 +291,10 @@ BlockStore::GetBlocksToCompact(const std::unordered_map& Blo for (const auto& It : m_ChunkBlocks) { uint32_t BlockIndex = It.first; + if ((BlockIndex == m_WriteBlockIndex.load()) && m_WriteBlock) + { + continue; + } if (std::find(m_ActiveWriteBlocks.begin(), m_ActiveWriteBlocks.end(), BlockIndex) != m_ActiveWriteBlocks.end()) { continue; -- cgit v1.2.3 From 1bbdc86732464170c2e7c6145a5a19cdb48fe396 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 1 Dec 2023 04:48:58 -0500 Subject: add separate PreCache step for GcReferenceChecker (#578) - Improvement: GCv2: Use separate PreCache step to improve concurrency when checking references - Improvement: GCv2: Improved verbose logging - Improvement: GCv2: Sort chunks to read by block/offset when finding references - Improvement: GCv2: Exit as soon as no more unreferenced items are left --- src/zenstore/blockstore.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index 03c7f4b95..cc727787f 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -1274,6 +1274,17 @@ BlockStore::GetBlockPath(const std::filesystem::path& BlocksBasePath, const uint return Path.ToPath(); } +Ref +BlockStore::GetBlockFile(uint32_t BlockIndex) +{ + RwLock::SharedLockScope _(m_InsertLock); + if (auto It = m_ChunkBlocks.find(BlockIndex); It != m_ChunkBlocks.end()) + { + return It->second; + } + return {}; +} + #if ZEN_WITH_TESTS TEST_CASE("blockstore.blockstoredisklocation") -- cgit v1.2.3 From 2755b0dbfd47237e018048598e9c85c71b9a0736 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Fri, 1 Dec 2023 07:57:13 -0500 Subject: use 32 bit offset and size in BlockStoreLocation (#581) - Improvement: Reduce memory usage in GC and diskbucket flush --- src/zenstore/blockstore.cpp | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index cc727787f..918f464ac 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -384,7 +384,7 @@ BlockStore::GetFreeBlockIndex(uint32_t ProbeIndex, RwLock::ExclusiveLockScope&, } void -BlockStore::WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, const WriteChunkCallback& Callback) +BlockStore::WriteChunk(const void* Data, uint64_t Size, uint32_t Alignment, const WriteChunkCallback& Callback) { ZEN_TRACE_CPU("BlockStore::WriteChunk"); @@ -393,12 +393,14 @@ BlockStore::WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, cons ZEN_ASSERT(Size <= m_MaxBlockSize); ZEN_ASSERT(Alignment > 0u); + uint32_t ChunkSize = gsl::narrow(Size); + RwLock::ExclusiveLockScope InsertLock(m_InsertLock); uint32_t WriteBlockIndex = m_WriteBlockIndex.load(std::memory_order_acquire); bool IsWriting = !!m_WriteBlock; - uint64_t AlignedInsertOffset = RoundUp(m_CurrentInsertOffset, Alignment); - if (!IsWriting || (AlignedInsertOffset + Size) > m_MaxBlockSize) + uint32_t AlignedInsertOffset = RoundUp(m_CurrentInsertOffset, Alignment); + if (!IsWriting || (AlignedInsertOffset + ChunkSize) > m_MaxBlockSize) { if (m_WriteBlock) { @@ -423,16 +425,16 @@ BlockStore::WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, cons m_CurrentInsertOffset = 0; AlignedInsertOffset = 0; } - uint64_t AlignedWriteSize = AlignedInsertOffset - m_CurrentInsertOffset + Size; - m_CurrentInsertOffset = AlignedInsertOffset + Size; + uint32_t AlignedWriteSize = AlignedInsertOffset - m_CurrentInsertOffset + ChunkSize; + m_CurrentInsertOffset = AlignedInsertOffset + ChunkSize; Ref WriteBlock = m_WriteBlock; m_ActiveWriteBlocks.push_back(WriteBlockIndex); InsertLock.ReleaseNow(); - WriteBlock->Write(Data, Size, AlignedInsertOffset); + WriteBlock->Write(Data, ChunkSize, AlignedInsertOffset); m_TotalSize.fetch_add(AlignedWriteSize, std::memory_order::relaxed); - Callback({.BlockIndex = WriteBlockIndex, .Offset = AlignedInsertOffset, .Size = Size}); + Callback({.BlockIndex = WriteBlockIndex, .Offset = AlignedInsertOffset, .Size = ChunkSize}); { RwLock::ExclusiveLockScope _(m_InsertLock); @@ -505,7 +507,7 @@ void BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot, const std::vector& ChunkLocations, const ChunkIndexArray& KeepChunkIndexes, - uint64_t PayloadAlignment, + uint32_t PayloadAlignment, bool DryRun, const ReclaimCallback& ChangeCallback, const ClaimDiskReserveCallback& DiskReserveCallback) @@ -754,9 +756,9 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot, { const BlockStoreLocation ChunkLocation = ChunkLocations[ChunkIndex]; Chunk.resize(ChunkLocation.Size); - OldBlockFile->Read(Chunk.data(), Chunk.size(), ChunkLocation.Offset); + OldBlockFile->Read(Chunk.data(), ChunkLocation.Size, ChunkLocation.Offset); - if (!NewBlockFile || (WriteOffset + Chunk.size() > m_MaxBlockSize)) + if (!NewBlockFile || (WriteOffset + ChunkLocation.Size > m_MaxBlockSize)) { uint32_t NextBlockIndex = m_WriteBlockIndex.load(std::memory_order_relaxed); @@ -830,10 +832,12 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot, WriteOffset = 0; } - NewBlockFile->Write(Chunk.data(), Chunk.size(), WriteOffset); - MovedChunks.push_back({ChunkIndex, {.BlockIndex = NewBlockIndex, .Offset = WriteOffset, .Size = Chunk.size()}}); + NewBlockFile->Write(Chunk.data(), ChunkLocation.Size, WriteOffset); + MovedChunks.push_back( + {ChunkIndex, + {.BlockIndex = NewBlockIndex, .Offset = gsl::narrow(WriteOffset), .Size = ChunkLocation.Size}}); uint64_t OldOffset = WriteOffset; - WriteOffset = RoundUp(WriteOffset + Chunk.size(), PayloadAlignment); + WriteOffset = RoundUp(WriteOffset + ChunkLocation.Size, PayloadAlignment); m_TotalSize.fetch_add(WriteOffset - OldOffset, std::memory_order::relaxed); } Chunk.clear(); @@ -1033,7 +1037,7 @@ BlockStore::IterateChunks(const std::vector& ChunkLocations, void BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, - uint64_t PayloadAlignment, + uint32_t PayloadAlignment, const CompactCallback& ChangeCallback, const ClaimDiskReserveCallback& DiskReserveCallback) { @@ -1218,9 +1222,10 @@ BlockStore::CompactBlocks(const BlockStoreCompactState& CompactState, WriteOffset = 0; } - NewBlockFile->Write(Chunk.data(), Chunk.size(), WriteOffset); - MovedChunks.push_back({ChunkIndex, {.BlockIndex = NewBlockIndex, .Offset = WriteOffset, .Size = Chunk.size()}}); - WriteOffset = RoundUp(WriteOffset + Chunk.size(), PayloadAlignment); + NewBlockFile->Write(Chunk.data(), ChunkLocation.Size, WriteOffset); + MovedChunks.push_back( + {ChunkIndex, {.BlockIndex = NewBlockIndex, .Offset = gsl::narrow(WriteOffset), .Size = ChunkLocation.Size}}); + WriteOffset = RoundUp(WriteOffset + ChunkLocation.Size, PayloadAlignment); AddedSize += Chunk.size(); } Chunk.clear(); @@ -1403,7 +1408,7 @@ TEST_CASE("blockstore.blockfile") } namespace blockstore::impl { - BlockStoreLocation WriteStringAsChunk(BlockStore& Store, std::string_view String, size_t PayloadAlignment) + BlockStoreLocation WriteStringAsChunk(BlockStore& Store, std::string_view String, uint32_t PayloadAlignment) { BlockStoreLocation Location; Store.WriteChunk(String.data(), String.length(), PayloadAlignment, [&](const BlockStoreLocation& L) { Location = L; }); -- cgit v1.2.3 From 8269e0616cf4333fd1007ccd8a7b1dac09743e11 Mon Sep 17 00:00:00 2001 From: Dan Engelbrecht Date: Mon, 4 Dec 2023 08:37:05 -0500 Subject: reserve vectors in gcv2 upfront / load factor for robin_map (#582) * reserve vectors in gcv2 upfront * set max load factor for robin_map indexes to reduce memory usage * set min load factor for robin_map indexes to allow them to shrink --- src/zenstore/blockstore.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'src/zenstore/blockstore.cpp') diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp index 918f464ac..71e306eca 100644 --- a/src/zenstore/blockstore.cpp +++ b/src/zenstore/blockstore.cpp @@ -282,10 +282,10 @@ BlockStore::SyncExistingBlocksOnDisk(const BlockIndexSet& KnownLocations) } } -std::vector -BlockStore::GetBlocksToCompact(const std::unordered_map& BlockUsage, uint32_t BlockUsageThresholdPercent) +BlockStore::BlockEntryCountMap +BlockStore::GetBlocksToCompact(const BlockUsageMap& BlockUsage, uint32_t BlockUsageThresholdPercent) { - std::unordered_set Result; + BlockEntryCountMap Result; { RwLock::SharedLockScope InsertLock(m_InsertLock); for (const auto& It : m_ChunkBlocks) @@ -299,31 +299,34 @@ BlockStore::GetBlocksToCompact(const std::unordered_map& Blo { continue; } - uint64_t BlockSize = It.second ? It.second->FileSize() : 0u; - if (BlockSize == 0) + + uint64_t UsedSize = 0; + uint32_t UsedCount = 0; + if (auto UsageIt = BlockUsage.find(BlockIndex); UsageIt != BlockUsage.end()) { - Result.insert(BlockIndex); - continue; + UsedSize = UsageIt->second.DiskUsage; + UsedCount = UsageIt->second.EntryCount; } - uint64_t UsedSize = 0; - if (auto UsageIt = BlockUsage.find(BlockIndex); UsageIt != BlockUsage.end()) + uint64_t BlockSize = It.second ? It.second->FileSize() : 0u; + if (BlockSize == 0) { - UsedSize = UsageIt->second; + Result.insert_or_assign(BlockIndex, UsedCount); + continue; } if (BlockUsageThresholdPercent == 100) { if (UsedSize < BlockSize) { - Result.insert(BlockIndex); + Result.insert_or_assign(BlockIndex, UsedCount); } } else if (BlockUsageThresholdPercent == 0) { if (UsedSize == 0) { - Result.insert(BlockIndex); + Result.insert_or_assign(BlockIndex, UsedCount); } } else @@ -331,12 +334,12 @@ BlockStore::GetBlocksToCompact(const std::unordered_map& Blo const uint32_t UsedPercent = UsedSize < BlockSize ? gsl::narrow((100 * UsedSize) / BlockSize) : 100u; if (UsedPercent < BlockUsageThresholdPercent) { - Result.insert(BlockIndex); + Result.insert_or_assign(BlockIndex, UsedCount); } } } } - return std::vector(Result.begin(), Result.end()); + return Result; } void -- cgit v1.2.3