aboutsummaryrefslogtreecommitdiff
path: root/zenstore/blockstore.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-05-02 10:01:47 +0200
committerGitHub <[email protected]>2023-05-02 10:01:47 +0200
commit075d17f8ada47e990fe94606c3d21df409223465 (patch)
treee50549b766a2f3c354798a54ff73404217b4c9af /zenstore/blockstore.cpp
parentfix: bundle shouldn't append content zip to zen (diff)
downloadzen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz
zen-075d17f8ada47e990fe94606c3d21df409223465.zip
moved source directories into `/src` (#264)
* moved source directories into `/src` * updated bundle.lua for new `src` path * moved some docs, icon * removed old test trees
Diffstat (limited to 'zenstore/blockstore.cpp')
-rw-r--r--zenstore/blockstore.cpp1312
1 files changed, 0 insertions, 1312 deletions
diff --git a/zenstore/blockstore.cpp b/zenstore/blockstore.cpp
deleted file mode 100644
index 5dfa10c91..000000000
--- a/zenstore/blockstore.cpp
+++ /dev/null
@@ -1,1312 +0,0 @@
-// Copyright Epic Games, Inc. All Rights Reserved.
-
-#include <zenstore/blockstore.h>
-
-#include <zencore/fmtutils.h>
-#include <zencore/logging.h>
-#include <zencore/scopeguard.h>
-#include <zencore/timer.h>
-
-#include <algorithm>
-
-#if ZEN_WITH_TESTS
-# include <zencore/compactbinarybuilder.h>
-# include <zencore/testing.h>
-# include <zencore/testutils.h>
-# include <zencore/workthreadpool.h>
-# include <random>
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-
-namespace zen {
-
-//////////////////////////////////////////////////////////////////////////
-
-BlockStoreFile::BlockStoreFile(const std::filesystem::path& BlockPath) : m_Path(BlockPath)
-{
-}
-
-BlockStoreFile::~BlockStoreFile()
-{
- m_IoBuffer = IoBuffer();
- m_File.Detach();
-}
-
-const std::filesystem::path&
-BlockStoreFile::GetPath() const
-{
- return m_Path;
-}
-
-void
-BlockStoreFile::Open()
-{
- m_File.Open(m_Path, BasicFile::Mode::kDelete);
- void* FileHandle = m_File.Handle();
- m_IoBuffer = IoBuffer(IoBuffer::File, FileHandle, 0, m_File.FileSize());
-}
-
-void
-BlockStoreFile::Create(uint64_t InitialSize)
-{
- auto ParentPath = m_Path.parent_path();
- if (!std::filesystem::is_directory(ParentPath))
- {
- CreateDirectories(ParentPath);
- }
-
- m_File.Open(m_Path, BasicFile::Mode::kTruncateDelete);
- void* FileHandle = m_File.Handle();
-
- // We map our m_IoBuffer beyond the file size as we will grow it over time and want
- // to be able to create sub-buffers of all the written range later
- m_IoBuffer = IoBuffer(IoBuffer::File, FileHandle, 0, InitialSize);
-}
-
-uint64_t
-BlockStoreFile::FileSize()
-{
- return m_File.FileSize();
-}
-
-void
-BlockStoreFile::MarkAsDeleteOnClose()
-{
- m_IoBuffer.MarkAsDeleteOnClose();
-}
-
-IoBuffer
-BlockStoreFile::GetChunk(uint64_t Offset, uint64_t Size)
-{
- return IoBuffer(m_IoBuffer, Offset, Size);
-}
-
-void
-BlockStoreFile::Read(void* Data, uint64_t Size, uint64_t FileOffset)
-{
- m_File.Read(Data, Size, FileOffset);
-}
-
-void
-BlockStoreFile::Write(const void* Data, uint64_t Size, uint64_t FileOffset)
-{
- m_File.Write(Data, Size, FileOffset);
-}
-
-void
-BlockStoreFile::Flush()
-{
- m_File.Flush();
-}
-
-BasicFile&
-BlockStoreFile::GetBasicFile()
-{
- return m_File;
-}
-
-void
-BlockStoreFile::StreamByteRange(uint64_t FileOffset, uint64_t Size, std::function<void(const void* Data, uint64_t Size)>&& ChunkFun)
-{
- m_File.StreamByteRange(FileOffset, Size, std::move(ChunkFun));
-}
-
-constexpr uint64_t ScrubSmallChunkWindowSize = 4 * 1024 * 1024;
-
-void
-BlockStore::Initialize(const std::filesystem::path& BlocksBasePath,
- uint64_t MaxBlockSize,
- uint64_t MaxBlockCount,
- const std::vector<BlockStoreLocation>& KnownLocations)
-{
- ZEN_ASSERT(MaxBlockSize > 0);
- ZEN_ASSERT(MaxBlockCount > 0);
- ZEN_ASSERT(IsPow2(MaxBlockCount));
-
- m_TotalSize = 0;
- m_BlocksBasePath = BlocksBasePath;
- m_MaxBlockSize = MaxBlockSize;
-
- m_ChunkBlocks.clear();
-
- std::unordered_set<uint32_t> KnownBlocks;
- for (const auto& Entry : KnownLocations)
- {
- KnownBlocks.insert(Entry.BlockIndex);
- }
-
- if (std::filesystem::is_directory(m_BlocksBasePath))
- {
- std::vector<std::filesystem::path> FoldersToScan;
- FoldersToScan.push_back(m_BlocksBasePath);
- size_t FolderOffset = 0;
- while (FolderOffset < FoldersToScan.size())
- {
- for (const std::filesystem::directory_entry& Entry : std::filesystem::directory_iterator(FoldersToScan[FolderOffset]))
- {
- if (Entry.is_directory())
- {
- FoldersToScan.push_back(Entry.path());
- continue;
- }
- if (Entry.is_regular_file())
- {
- const std::filesystem::path Path = Entry.path();
- if (Path.extension() != GetBlockFileExtension())
- {
- continue;
- }
- std::string FileName = PathToUtf8(Path.stem());
- uint32_t BlockIndex;
- bool OK = ParseHexNumber(FileName, BlockIndex);
- if (!OK)
- {
- continue;
- }
- if (!KnownBlocks.contains(BlockIndex))
- {
- // Log removing unreferenced block
- // Clear out unused blocks
- ZEN_DEBUG("removing unused block at '{}'", Path);
- std::error_code Ec;
- std::filesystem::remove(Path, Ec);
- if (Ec)
- {
- ZEN_WARN("Failed to delete file '{}' reason: '{}'", Path, Ec.message());
- }
- continue;
- }
- Ref<BlockStoreFile> BlockFile{new BlockStoreFile(Path)};
- BlockFile->Open();
- m_TotalSize.fetch_add(BlockFile->FileSize(), std::memory_order::relaxed);
- m_ChunkBlocks[BlockIndex] = BlockFile;
- }
- }
- ++FolderOffset;
- }
- }
- else
- {
- CreateDirectories(m_BlocksBasePath);
- }
-}
-
-void
-BlockStore::Close()
-{
- RwLock::ExclusiveLockScope InsertLock(m_InsertLock);
- m_WriteBlock = nullptr;
- m_CurrentInsertOffset = 0;
- m_WriteBlockIndex = 0;
-
- m_ChunkBlocks.clear();
- m_BlocksBasePath.clear();
-}
-
-void
-BlockStore::WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, const WriteChunkCallback& Callback)
-{
- ZEN_ASSERT(Data != nullptr);
- ZEN_ASSERT(Size > 0u);
- ZEN_ASSERT(Size <= m_MaxBlockSize);
- ZEN_ASSERT(Alignment > 0u);
-
- RwLock::ExclusiveLockScope InsertLock(m_InsertLock);
-
- uint32_t WriteBlockIndex = m_WriteBlockIndex.load(std::memory_order_acquire);
- bool IsWriting = !!m_WriteBlock;
- if (!IsWriting || (m_CurrentInsertOffset + Size) > m_MaxBlockSize)
- {
- if (m_WriteBlock)
- {
- m_WriteBlock = nullptr;
- }
-
- if (m_ChunkBlocks.size() == m_MaxBlockCount)
- {
- throw std::runtime_error(fmt::format("unable to allocate a new block in '{}'", m_BlocksBasePath));
- }
-
- WriteBlockIndex += IsWriting ? 1 : 0;
- while (m_ChunkBlocks.contains(WriteBlockIndex))
- {
- WriteBlockIndex = (WriteBlockIndex + 1) & (m_MaxBlockCount - 1);
- }
-
- std::filesystem::path BlockPath = GetBlockPath(m_BlocksBasePath, WriteBlockIndex);
-
- Ref<BlockStoreFile> NewBlockFile(new BlockStoreFile(BlockPath));
- NewBlockFile->Create(m_MaxBlockSize);
-
- m_ChunkBlocks[WriteBlockIndex] = NewBlockFile;
- m_WriteBlock = NewBlockFile;
- m_WriteBlockIndex.store(WriteBlockIndex, std::memory_order_release);
- m_CurrentInsertOffset = 0;
- }
- uint64_t InsertOffset = m_CurrentInsertOffset;
- m_CurrentInsertOffset = RoundUp(InsertOffset + Size, Alignment);
- uint64_t AlignedWriteSize = m_CurrentInsertOffset - InsertOffset;
- Ref<BlockStoreFile> WriteBlock = m_WriteBlock;
- m_ActiveWriteBlocks.push_back(WriteBlockIndex);
- InsertLock.ReleaseNow();
-
- WriteBlock->Write(Data, Size, InsertOffset);
- m_TotalSize.fetch_add(AlignedWriteSize, std::memory_order::relaxed);
-
- Callback({.BlockIndex = WriteBlockIndex, .Offset = InsertOffset, .Size = Size});
-
- {
- RwLock::ExclusiveLockScope _(m_InsertLock);
- m_ActiveWriteBlocks.erase(std::find(m_ActiveWriteBlocks.begin(), m_ActiveWriteBlocks.end(), WriteBlockIndex));
- }
-}
-
-BlockStore::ReclaimSnapshotState
-BlockStore::GetReclaimSnapshotState()
-{
- ReclaimSnapshotState State;
- RwLock::SharedLockScope _(m_InsertLock);
- for (uint32_t BlockIndex : m_ActiveWriteBlocks)
- {
- State.m_ActiveWriteBlocks.insert(BlockIndex);
- }
- if (m_WriteBlock)
- {
- State.m_ActiveWriteBlocks.insert(m_WriteBlockIndex);
- }
- State.BlockCount = m_ChunkBlocks.size();
- return State;
-}
-
-IoBuffer
-BlockStore::TryGetChunk(const BlockStoreLocation& Location) const
-{
- RwLock::SharedLockScope InsertLock(m_InsertLock);
- if (auto BlockIt = m_ChunkBlocks.find(Location.BlockIndex); BlockIt != m_ChunkBlocks.end())
- {
- if (const Ref<BlockStoreFile>& Block = BlockIt->second; Block)
- {
- return Block->GetChunk(Location.Offset, Location.Size);
- }
- }
- return IoBuffer();
-}
-
-void
-BlockStore::Flush()
-{
- RwLock::ExclusiveLockScope _(m_InsertLock);
- if (m_CurrentInsertOffset > 0)
- {
- uint32_t WriteBlockIndex = m_WriteBlockIndex.load(std::memory_order_acquire);
- WriteBlockIndex = (WriteBlockIndex + 1) & (m_MaxBlockCount - 1);
- m_WriteBlock = nullptr;
- m_WriteBlockIndex.store(WriteBlockIndex, std::memory_order_release);
- m_CurrentInsertOffset = 0;
- }
-}
-
-void
-BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
- const std::vector<BlockStoreLocation>& ChunkLocations,
- const ChunkIndexArray& KeepChunkIndexes,
- uint64_t PayloadAlignment,
- bool DryRun,
- const ReclaimCallback& ChangeCallback,
- const ClaimDiskReserveCallback& DiskReserveCallback)
-{
- if (ChunkLocations.empty())
- {
- return;
- }
- uint64_t WriteBlockTimeUs = 0;
- uint64_t WriteBlockLongestTimeUs = 0;
- uint64_t ReadBlockTimeUs = 0;
- uint64_t ReadBlockLongestTimeUs = 0;
- uint64_t TotalChunkCount = ChunkLocations.size();
- uint64_t DeletedSize = 0;
- uint64_t OldTotalSize = 0;
- uint64_t NewTotalSize = 0;
-
- uint64_t MovedCount = 0;
- uint64_t DeletedCount = 0;
-
- Stopwatch TotalTimer;
- const auto _ = MakeGuard([&] {
- ZEN_DEBUG(
- "reclaim space for '{}' DONE after {}, write lock: {} ({}), read lock: {} ({}), collected {} bytes, deleted {} and moved "
- "{} "
- "of {} "
- "chunks ({}).",
- m_BlocksBasePath,
- NiceTimeSpanMs(TotalTimer.GetElapsedTimeMs()),
- NiceLatencyNs(WriteBlockTimeUs),
- NiceLatencyNs(WriteBlockLongestTimeUs),
- NiceLatencyNs(ReadBlockTimeUs),
- NiceLatencyNs(ReadBlockLongestTimeUs),
- NiceBytes(DeletedSize),
- DeletedCount,
- MovedCount,
- TotalChunkCount,
- NiceBytes(OldTotalSize));
- });
-
- size_t BlockCount = Snapshot.BlockCount;
- if (BlockCount == 0)
- {
- ZEN_DEBUG("garbage collect for '{}' SKIPPED, no blocks to process", m_BlocksBasePath);
- return;
- }
-
- std::unordered_set<size_t> KeepChunkMap;
- KeepChunkMap.reserve(KeepChunkIndexes.size());
- for (size_t KeepChunkIndex : KeepChunkIndexes)
- {
- KeepChunkMap.insert(KeepChunkIndex);
- }
-
- std::unordered_map<uint32_t, size_t> BlockIndexToChunkMapIndex;
- std::vector<ChunkIndexArray> BlockKeepChunks;
- std::vector<ChunkIndexArray> BlockDeleteChunks;
-
- BlockIndexToChunkMapIndex.reserve(BlockCount);
- BlockKeepChunks.reserve(BlockCount);
- BlockDeleteChunks.reserve(BlockCount);
- size_t GuesstimateCountPerBlock = TotalChunkCount / BlockCount / 2;
-
- size_t DeleteCount = 0;
- for (size_t Index = 0; Index < TotalChunkCount; ++Index)
- {
- const BlockStoreLocation& Location = ChunkLocations[Index];
- OldTotalSize += Location.Size;
- if (Snapshot.m_ActiveWriteBlocks.contains(Location.BlockIndex))
- {
- continue;
- }
-
- auto BlockIndexPtr = BlockIndexToChunkMapIndex.find(Location.BlockIndex);
- size_t ChunkMapIndex = 0;
- if (BlockIndexPtr == BlockIndexToChunkMapIndex.end())
- {
- ChunkMapIndex = BlockKeepChunks.size();
- BlockIndexToChunkMapIndex[Location.BlockIndex] = ChunkMapIndex;
- BlockKeepChunks.resize(ChunkMapIndex + 1);
- BlockKeepChunks.back().reserve(GuesstimateCountPerBlock);
- BlockDeleteChunks.resize(ChunkMapIndex + 1);
- BlockDeleteChunks.back().reserve(GuesstimateCountPerBlock);
- }
- else
- {
- ChunkMapIndex = BlockIndexPtr->second;
- }
-
- if (KeepChunkMap.contains(Index))
- {
- ChunkIndexArray& IndexMap = BlockKeepChunks[ChunkMapIndex];
- IndexMap.push_back(Index);
- NewTotalSize += Location.Size;
- continue;
- }
- ChunkIndexArray& IndexMap = BlockDeleteChunks[ChunkMapIndex];
- IndexMap.push_back(Index);
- DeleteCount++;
- }
-
- std::unordered_set<uint32_t> BlocksToReWrite;
- BlocksToReWrite.reserve(BlockIndexToChunkMapIndex.size());
- for (const auto& Entry : BlockIndexToChunkMapIndex)
- {
- uint32_t BlockIndex = Entry.first;
- size_t ChunkMapIndex = Entry.second;
- const ChunkIndexArray& ChunkMap = BlockDeleteChunks[ChunkMapIndex];
- if (ChunkMap.empty())
- {
- continue;
- }
- BlocksToReWrite.insert(BlockIndex);
- }
-
- if (DryRun)
- {
- ZEN_DEBUG("garbage collect for '{}' DISABLED, found {} {} chunks of total {} {}",
- m_BlocksBasePath,
- DeleteCount,
- NiceBytes(OldTotalSize - NewTotalSize),
- TotalChunkCount,
- OldTotalSize);
- return;
- }
-
- Ref<BlockStoreFile> NewBlockFile;
- try
- {
- uint64_t WriteOffset = 0;
- uint32_t NewBlockIndex = 0;
- for (uint32_t BlockIndex : BlocksToReWrite)
- {
- const size_t ChunkMapIndex = BlockIndexToChunkMapIndex[BlockIndex];
-
- Ref<BlockStoreFile> OldBlockFile;
- {
- RwLock::SharedLockScope _i(m_InsertLock);
- Stopwatch Timer;
- const auto __ = MakeGuard([&] {
- uint64_t ElapsedUs = Timer.GetElapsedTimeUs();
- WriteBlockTimeUs += ElapsedUs;
- WriteBlockLongestTimeUs = std::max(ElapsedUs, WriteBlockLongestTimeUs);
- });
- OldBlockFile = m_ChunkBlocks[BlockIndex];
- }
-
- if (!OldBlockFile)
- {
- // If the block file pointed to does not exist, move them all to deleted list
- BlockDeleteChunks[ChunkMapIndex].insert(BlockDeleteChunks[ChunkMapIndex].end(),
- BlockKeepChunks[ChunkMapIndex].begin(),
- BlockKeepChunks[ChunkMapIndex].end());
- BlockKeepChunks[ChunkMapIndex].clear();
- }
-
- const ChunkIndexArray& KeepMap = BlockKeepChunks[ChunkMapIndex];
- if (KeepMap.empty())
- {
- const ChunkIndexArray& DeleteMap = BlockDeleteChunks[ChunkMapIndex];
- for (size_t DeleteIndex : DeleteMap)
- {
- DeletedSize += ChunkLocations[DeleteIndex].Size;
- }
- ChangeCallback({}, DeleteMap);
- DeletedCount += DeleteMap.size();
- {
- RwLock::ExclusiveLockScope _i(m_InsertLock);
- Stopwatch Timer;
- const auto __ = MakeGuard([&] {
- uint64_t ElapsedUs = Timer.GetElapsedTimeUs();
- ReadBlockTimeUs += ElapsedUs;
- ReadBlockLongestTimeUs = std::max(ElapsedUs, ReadBlockLongestTimeUs);
- });
- if (OldBlockFile)
- {
- m_ChunkBlocks[BlockIndex] = nullptr;
- ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex);
- m_TotalSize.fetch_sub(OldBlockFile->FileSize(), std::memory_order::relaxed);
- OldBlockFile->MarkAsDeleteOnClose();
- }
- }
- continue;
- }
-
- ZEN_ASSERT(OldBlockFile);
-
- MovedChunksArray MovedChunks;
- std::vector<uint8_t> Chunk;
- for (const size_t& ChunkIndex : KeepMap)
- {
- const BlockStoreLocation ChunkLocation = ChunkLocations[ChunkIndex];
- Chunk.resize(ChunkLocation.Size);
- OldBlockFile->Read(Chunk.data(), Chunk.size(), ChunkLocation.Offset);
-
- if (!NewBlockFile || (WriteOffset + Chunk.size() > m_MaxBlockSize))
- {
- uint32_t NextBlockIndex = m_WriteBlockIndex.load(std::memory_order_relaxed);
-
- if (NewBlockFile)
- {
- NewBlockFile->Flush();
- NewBlockFile = nullptr;
- }
- {
- ChangeCallback(MovedChunks, {});
- MovedCount += KeepMap.size();
- MovedChunks.clear();
- RwLock::ExclusiveLockScope __(m_InsertLock);
- Stopwatch Timer;
- const auto ___ = MakeGuard([&] {
- uint64_t ElapsedUs = Timer.GetElapsedTimeUs();
- ReadBlockTimeUs += ElapsedUs;
- ReadBlockLongestTimeUs = std::max(ElapsedUs, ReadBlockLongestTimeUs);
- });
- if (m_ChunkBlocks.size() == m_MaxBlockCount)
- {
- ZEN_ERROR("unable to allocate a new block in '{}', count limit {} exeeded",
- m_BlocksBasePath,
- static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1);
- return;
- }
- while (m_ChunkBlocks.contains(NextBlockIndex))
- {
- NextBlockIndex = (NextBlockIndex + 1) & (m_MaxBlockCount - 1);
- }
- std::filesystem::path NewBlockPath = GetBlockPath(m_BlocksBasePath, NextBlockIndex);
- NewBlockFile = new BlockStoreFile(NewBlockPath);
- m_ChunkBlocks[NextBlockIndex] = 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());
- return;
- }
- if (Space.Free < 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);
- Stopwatch Timer;
- const auto __ = MakeGuard([&] {
- uint64_t ElapsedUs = Timer.GetElapsedTimeUs();
- ReadBlockTimeUs += ElapsedUs;
- ReadBlockLongestTimeUs = std::max(ElapsedUs, ReadBlockLongestTimeUs);
- });
- m_ChunkBlocks.erase(NextBlockIndex);
- return;
- }
-
- ZEN_INFO("using gc reserve for '{}', reclaimed {}, disk free {}",
- m_BlocksBasePath,
- ReclaimedSpace,
- NiceBytes(Space.Free + ReclaimedSpace));
- }
- 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()}});
- uint64_t OldOffset = WriteOffset;
- WriteOffset = RoundUp(WriteOffset + Chunk.size(), PayloadAlignment);
- m_TotalSize.fetch_add(WriteOffset - OldOffset, std::memory_order::relaxed);
- }
- Chunk.clear();
- if (NewBlockFile)
- {
- NewBlockFile->Flush();
- NewBlockFile = nullptr;
- }
-
- const ChunkIndexArray& DeleteMap = BlockDeleteChunks[ChunkMapIndex];
- for (size_t DeleteIndex : DeleteMap)
- {
- DeletedSize += ChunkLocations[DeleteIndex].Size;
- }
-
- ChangeCallback(MovedChunks, DeleteMap);
- MovedCount += KeepMap.size();
- DeletedCount += DeleteMap.size();
- MovedChunks.clear();
- {
- RwLock::ExclusiveLockScope __(m_InsertLock);
- Stopwatch Timer;
- const auto ___ = MakeGuard([&] {
- uint64_t ElapsedUs = Timer.GetElapsedTimeUs();
- ReadBlockTimeUs += ElapsedUs;
- ReadBlockLongestTimeUs = std::max(ElapsedUs, ReadBlockLongestTimeUs);
- });
- m_ChunkBlocks[BlockIndex] = nullptr;
- ZEN_DEBUG("marking cas block store file '{}' for delete, block #{}", OldBlockFile->GetPath(), BlockIndex);
- m_TotalSize.fetch_sub(OldBlockFile->FileSize(), std::memory_order::relaxed);
- OldBlockFile->MarkAsDeleteOnClose();
- }
- }
- }
- catch (std::exception& ex)
- {
- ZEN_ERROR("reclaiming space for '{}' failed with: '{}'", m_BlocksBasePath, ex.what());
- if (NewBlockFile)
- {
- ZEN_DEBUG("dropping incomplete cas block store file '{}'", NewBlockFile->GetPath());
- m_TotalSize.fetch_sub(NewBlockFile->FileSize(), std::memory_order::relaxed);
- NewBlockFile->MarkAsDeleteOnClose();
- }
- }
-}
-
-void
-BlockStore::IterateChunks(const std::vector<BlockStoreLocation>& ChunkLocations,
- const IterateChunksSmallSizeCallback& SmallSizeCallback,
- const IterateChunksLargeSizeCallback& LargeSizeCallback)
-{
- std::vector<size_t> LocationIndexes;
- LocationIndexes.reserve(ChunkLocations.size());
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkLocations.size(); ++ChunkIndex)
- {
- LocationIndexes.push_back(ChunkIndex);
- }
- std::sort(LocationIndexes.begin(), LocationIndexes.end(), [&](size_t IndexA, size_t IndexB) -> bool {
- const BlockStoreLocation& LocationA = ChunkLocations[IndexA];
- const BlockStoreLocation& LocationB = ChunkLocations[IndexB];
- if (LocationA.BlockIndex < LocationB.BlockIndex)
- {
- return true;
- }
- else if (LocationA.BlockIndex > LocationB.BlockIndex)
- {
- return false;
- }
- return LocationA.Offset < LocationB.Offset;
- });
-
- IoBuffer ReadBuffer{ScrubSmallChunkWindowSize};
- void* BufferBase = ReadBuffer.MutableData();
-
- RwLock::SharedLockScope _(m_InsertLock);
-
- auto GetNextRange = [&](size_t StartIndexOffset) {
- size_t ChunkCount = 0;
- size_t StartIndex = LocationIndexes[StartIndexOffset];
- const BlockStoreLocation& StartLocation = ChunkLocations[StartIndex];
- uint64_t StartOffset = StartLocation.Offset;
- while (StartIndexOffset + ChunkCount < LocationIndexes.size())
- {
- size_t NextIndex = LocationIndexes[StartIndexOffset + ChunkCount];
- const BlockStoreLocation& Location = ChunkLocations[NextIndex];
- if (Location.BlockIndex != StartLocation.BlockIndex)
- {
- break;
- }
- if ((Location.Offset + Location.Size) - StartOffset > ScrubSmallChunkWindowSize)
- {
- break;
- }
- ++ChunkCount;
- }
- return ChunkCount;
- };
-
- size_t LocationIndexOffset = 0;
- while (LocationIndexOffset < LocationIndexes.size())
- {
- size_t ChunkIndex = LocationIndexes[LocationIndexOffset];
- const BlockStoreLocation& FirstLocation = ChunkLocations[ChunkIndex];
-
- const Ref<BlockStoreFile>& BlockFile = m_ChunkBlocks[FirstLocation.BlockIndex];
- if (!BlockFile)
- {
- while (ChunkLocations[ChunkIndex].BlockIndex == FirstLocation.BlockIndex)
- {
- SmallSizeCallback(ChunkIndex, nullptr, 0);
- LocationIndexOffset++;
- if (LocationIndexOffset == LocationIndexes.size())
- {
- break;
- }
- ChunkIndex = LocationIndexes[LocationIndexOffset];
- }
- continue;
- }
- size_t BlockSize = BlockFile->FileSize();
- size_t RangeCount = GetNextRange(LocationIndexOffset);
- if (RangeCount > 0)
- {
- size_t LastChunkIndex = LocationIndexes[LocationIndexOffset + RangeCount - 1];
- const BlockStoreLocation& LastLocation = ChunkLocations[LastChunkIndex];
- uint64_t Size = LastLocation.Offset + LastLocation.Size - FirstLocation.Offset;
- BlockFile->Read(BufferBase, Size, FirstLocation.Offset);
- for (size_t RangeIndex = 0; RangeIndex < RangeCount; ++RangeIndex)
- {
- size_t NextChunkIndex = LocationIndexes[LocationIndexOffset + RangeIndex];
- const BlockStoreLocation& ChunkLocation = ChunkLocations[NextChunkIndex];
- if (ChunkLocation.Size == 0 || (ChunkLocation.Offset + ChunkLocation.Size > BlockSize))
- {
- SmallSizeCallback(NextChunkIndex, nullptr, 0);
- continue;
- }
- void* BufferPtr = &((char*)BufferBase)[ChunkLocation.Offset - FirstLocation.Offset];
- SmallSizeCallback(NextChunkIndex, BufferPtr, ChunkLocation.Size);
- }
- LocationIndexOffset += RangeCount;
- continue;
- }
- if (FirstLocation.Size == 0 || (FirstLocation.Offset + FirstLocation.Size > BlockSize))
- {
- SmallSizeCallback(ChunkIndex, nullptr, 0);
- LocationIndexOffset++;
- continue;
- }
- LargeSizeCallback(ChunkIndex, *BlockFile.Get(), FirstLocation.Offset, FirstLocation.Size);
- LocationIndexOffset++;
- }
-}
-
-const char*
-BlockStore::GetBlockFileExtension()
-{
- return ".ucas";
-}
-
-std::filesystem::path
-BlockStore::GetBlockPath(const std::filesystem::path& BlocksBasePath, const uint32_t BlockIndex)
-{
- ExtendablePathBuilder<256> Path;
-
- char BlockHexString[9];
- ToHexNumber(BlockIndex, BlockHexString);
-
- Path.Append(BlocksBasePath);
- Path.AppendSeparator();
- Path.AppendAsciiRange(BlockHexString, BlockHexString + 4);
- Path.AppendSeparator();
- Path.Append(BlockHexString);
- Path.Append(GetBlockFileExtension());
- return Path.ToPath();
-}
-
-#if ZEN_WITH_TESTS
-
-TEST_CASE("blockstore.blockstoredisklocation")
-{
- BlockStoreLocation Zero = BlockStoreLocation{.BlockIndex = 0, .Offset = 0, .Size = 0};
- CHECK(Zero == BlockStoreDiskLocation(Zero, 4).Get(4));
-
- BlockStoreLocation MaxBlockIndex = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex, .Offset = 0, .Size = 0};
- CHECK(MaxBlockIndex == BlockStoreDiskLocation(MaxBlockIndex, 4).Get(4));
-
- BlockStoreLocation MaxOffset = BlockStoreLocation{.BlockIndex = 0, .Offset = BlockStoreDiskLocation::MaxOffset * 4, .Size = 0};
- CHECK(MaxOffset == BlockStoreDiskLocation(MaxOffset, 4).Get(4));
-
- BlockStoreLocation MaxSize = BlockStoreLocation{.BlockIndex = 0, .Offset = 0, .Size = std::numeric_limits<uint32_t>::max()};
- CHECK(MaxSize == BlockStoreDiskLocation(MaxSize, 4).Get(4));
-
- BlockStoreLocation MaxBlockIndexAndOffset =
- BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex, .Offset = BlockStoreDiskLocation::MaxOffset * 4, .Size = 0};
- CHECK(MaxBlockIndexAndOffset == BlockStoreDiskLocation(MaxBlockIndexAndOffset, 4).Get(4));
-
- BlockStoreLocation MaxAll = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex,
- .Offset = BlockStoreDiskLocation::MaxOffset * 4,
- .Size = std::numeric_limits<uint32_t>::max()};
- CHECK(MaxAll == BlockStoreDiskLocation(MaxAll, 4).Get(4));
-
- BlockStoreLocation MaxAll4096 = BlockStoreLocation{.BlockIndex = BlockStoreDiskLocation::MaxBlockIndex,
- .Offset = BlockStoreDiskLocation::MaxOffset * 4096,
- .Size = std::numeric_limits<uint32_t>::max()};
- CHECK(MaxAll4096 == BlockStoreDiskLocation(MaxAll4096, 4096).Get(4096));
-
- BlockStoreLocation Middle = BlockStoreLocation{.BlockIndex = (BlockStoreDiskLocation::MaxBlockIndex) / 2,
- .Offset = ((BlockStoreDiskLocation::MaxOffset) / 2) * 4,
- .Size = std::numeric_limits<uint32_t>::max() / 2};
- CHECK(Middle == BlockStoreDiskLocation(Middle, 4).Get(4));
-}
-
-TEST_CASE("blockstore.blockfile")
-{
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path() / "blocks";
- CreateDirectories(RootDirectory);
-
- {
- BlockStoreFile File1(RootDirectory / "1");
- File1.Create(16384);
- CHECK(File1.FileSize() == 0);
- File1.Write("data", 5, 0);
- IoBuffer DataChunk = File1.GetChunk(0, 5);
- File1.Write("boop", 5, 5);
- IoBuffer BoopChunk = File1.GetChunk(5, 5);
- const char* Data = static_cast<const char*>(DataChunk.GetData());
- CHECK(std::string(Data) == "data");
- const char* Boop = static_cast<const char*>(BoopChunk.GetData());
- CHECK(std::string(Boop) == "boop");
- File1.Flush();
- CHECK(File1.FileSize() == 10);
- }
- {
- BlockStoreFile File1(RootDirectory / "1");
- File1.Open();
-
- char DataRaw[5];
- File1.Read(DataRaw, 5, 0);
- CHECK(std::string(DataRaw) == "data");
- IoBuffer DataChunk = File1.GetChunk(0, 5);
-
- char BoopRaw[5];
- File1.Read(BoopRaw, 5, 5);
- CHECK(std::string(BoopRaw) == "boop");
-
- IoBuffer BoopChunk = File1.GetChunk(5, 5);
- const char* Data = static_cast<const char*>(DataChunk.GetData());
- CHECK(std::string(Data) == "data");
- const char* Boop = static_cast<const char*>(BoopChunk.GetData());
- CHECK(std::string(Boop) == "boop");
- }
-
- {
- IoBuffer DataChunk;
- IoBuffer BoopChunk;
-
- {
- BlockStoreFile File1(RootDirectory / "1");
- File1.Open();
- DataChunk = File1.GetChunk(0, 5);
- BoopChunk = File1.GetChunk(5, 5);
- }
-
- CHECK(std::filesystem::exists(RootDirectory / "1"));
-
- const char* Data = static_cast<const char*>(DataChunk.GetData());
- CHECK(std::string(Data) == "data");
- const char* Boop = static_cast<const char*>(BoopChunk.GetData());
- CHECK(std::string(Boop) == "boop");
- }
- CHECK(std::filesystem::exists(RootDirectory / "1"));
-
- {
- IoBuffer DataChunk;
- IoBuffer BoopChunk;
-
- {
- BlockStoreFile File1(RootDirectory / "1");
- File1.Open();
- File1.MarkAsDeleteOnClose();
- DataChunk = File1.GetChunk(0, 5);
- BoopChunk = File1.GetChunk(5, 5);
- }
-
- const char* Data = static_cast<const char*>(DataChunk.GetData());
- CHECK(std::string(Data) == "data");
- const char* Boop = static_cast<const char*>(BoopChunk.GetData());
- CHECK(std::string(Boop) == "boop");
- }
- CHECK(!std::filesystem::exists(RootDirectory / "1"));
-}
-
-namespace blockstore::impl {
- BlockStoreLocation WriteStringAsChunk(BlockStore& Store, std::string_view String, size_t PayloadAlignment)
- {
- BlockStoreLocation Location;
- Store.WriteChunk(String.data(), String.length(), PayloadAlignment, [&](const BlockStoreLocation& L) { Location = L; });
- CHECK(Location.Size == String.length());
- return Location;
- };
-
- std::string ReadChunkAsString(BlockStore& Store, const BlockStoreLocation& Location)
- {
- IoBuffer ChunkData = Store.TryGetChunk(Location);
- if (!ChunkData)
- {
- return "";
- }
- std::string AsString((const char*)ChunkData.Data(), ChunkData.Size());
- return AsString;
- };
-
- std::vector<std::filesystem::path> GetDirectoryContent(std::filesystem::path RootDir, bool Files, bool Directories)
- {
- DirectoryContent DirectoryContent;
- GetDirectoryContent(RootDir,
- DirectoryContent::RecursiveFlag | (Files ? DirectoryContent::IncludeFilesFlag : 0) |
- (Directories ? DirectoryContent::IncludeDirsFlag : 0),
- DirectoryContent);
- std::vector<std::filesystem::path> Result;
- Result.insert(Result.end(), DirectoryContent.Directories.begin(), DirectoryContent.Directories.end());
- Result.insert(Result.end(), DirectoryContent.Files.begin(), DirectoryContent.Files.end());
- return Result;
- };
-
- static IoBuffer CreateChunk(uint64_t Size)
- {
- static std::random_device rd;
- static std::mt19937 g(rd());
-
- std::vector<uint8_t> Values;
- Values.resize(Size);
- for (size_t Idx = 0; Idx < Size; ++Idx)
- {
- Values[Idx] = static_cast<uint8_t>(Idx);
- }
- std::shuffle(Values.begin(), Values.end(), g);
-
- return IoBufferBuilder::MakeCloneFromMemory(Values.data(), Values.size());
- }
-} // namespace blockstore::impl
-
-TEST_CASE("blockstore.chunks")
-{
- using namespace blockstore::impl;
-
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path();
-
- BlockStore Store;
- Store.Initialize(RootDirectory, 128, 1024, {});
- IoBuffer BadChunk = Store.TryGetChunk({.BlockIndex = 0, .Offset = 0, .Size = 512});
- CHECK(!BadChunk);
-
- std::string FirstChunkData = "This is the data of the first chunk that we will write";
- BlockStoreLocation FirstChunkLocation = WriteStringAsChunk(Store, FirstChunkData, 4);
- std::string SecondChunkData = "This is the data for the second chunk that we will write";
- BlockStoreLocation SecondChunkLocation = WriteStringAsChunk(Store, SecondChunkData, 4);
-
- CHECK(ReadChunkAsString(Store, FirstChunkLocation) == FirstChunkData);
- CHECK(ReadChunkAsString(Store, SecondChunkLocation) == SecondChunkData);
-
- std::string ThirdChunkData =
- "This is a much longer string that will not fit in the first block so it should be placed in the second block";
- BlockStoreLocation ThirdChunkLocation = WriteStringAsChunk(Store, ThirdChunkData, 4);
- CHECK(ThirdChunkLocation.BlockIndex != FirstChunkLocation.BlockIndex);
-
- CHECK(ReadChunkAsString(Store, FirstChunkLocation) == FirstChunkData);
- CHECK(ReadChunkAsString(Store, SecondChunkLocation) == SecondChunkData);
- CHECK(ReadChunkAsString(Store, ThirdChunkLocation) == ThirdChunkData);
-}
-
-TEST_CASE("blockstore.clean.stray.blocks")
-{
- using namespace blockstore::impl;
-
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path();
-
- BlockStore Store;
- Store.Initialize(RootDirectory / "store", 128, 1024, {});
-
- std::string FirstChunkData = "This is the data of the first chunk that we will write";
- BlockStoreLocation FirstChunkLocation = WriteStringAsChunk(Store, FirstChunkData, 4);
- std::string SecondChunkData = "This is the data for the second chunk that we will write";
- BlockStoreLocation SecondChunkLocation = WriteStringAsChunk(Store, SecondChunkData, 4);
- std::string ThirdChunkData =
- "This is a much longer string that will not fit in the first block so it should be placed in the second block";
- WriteStringAsChunk(Store, ThirdChunkData, 4);
-
- Store.Close();
-
- // Not referencing the second block means that we should be deleted
- Store.Initialize(RootDirectory / "store", 128, 1024, {FirstChunkLocation, SecondChunkLocation});
-
- CHECK(GetDirectoryContent(RootDirectory / "store", true, false).size() == 1);
-}
-
-TEST_CASE("blockstore.flush.forces.new.block")
-{
- using namespace blockstore::impl;
-
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path();
-
- BlockStore Store;
- Store.Initialize(RootDirectory / "store", 128, 1024, {});
-
- std::string FirstChunkData = "This is the data of the first chunk that we will write";
- WriteStringAsChunk(Store, FirstChunkData, 4);
- Store.Flush();
- std::string SecondChunkData = "This is the data for the second chunk that we will write";
- WriteStringAsChunk(Store, SecondChunkData, 4);
- Store.Flush();
- std::string ThirdChunkData =
- "This is a much longer string that will not fit in the first block so it should be placed in the second block";
- WriteStringAsChunk(Store, ThirdChunkData, 4);
-
- CHECK(GetDirectoryContent(RootDirectory / "store", true, false).size() == 3);
-}
-
-TEST_CASE("blockstore.iterate.chunks")
-{
- using namespace blockstore::impl;
-
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path();
-
- BlockStore Store;
- Store.Initialize(RootDirectory / "store", ScrubSmallChunkWindowSize * 2, 1024, {});
- IoBuffer BadChunk = Store.TryGetChunk({.BlockIndex = 0, .Offset = 0, .Size = 512});
- CHECK(!BadChunk);
-
- std::string FirstChunkData = "This is the data of the first chunk that we will write";
- BlockStoreLocation FirstChunkLocation = WriteStringAsChunk(Store, FirstChunkData, 4);
-
- std::string SecondChunkData = "This is the data for the second chunk that we will write";
- BlockStoreLocation SecondChunkLocation = WriteStringAsChunk(Store, SecondChunkData, 4);
- Store.Flush();
-
- std::string VeryLargeChunk(ScrubSmallChunkWindowSize * 2, 'L');
- BlockStoreLocation VeryLargeChunkLocation = WriteStringAsChunk(Store, VeryLargeChunk, 4);
-
- BlockStoreLocation BadLocationZeroSize = {.BlockIndex = 0, .Offset = 0, .Size = 0};
- BlockStoreLocation BadLocationOutOfRange = {.BlockIndex = 0,
- .Offset = ScrubSmallChunkWindowSize,
- .Size = ScrubSmallChunkWindowSize * 2};
- BlockStoreLocation BadBlockIndex = {.BlockIndex = 0xfffff, .Offset = 1024, .Size = 1024};
-
- Store.IterateChunks(
- {FirstChunkLocation, SecondChunkLocation, VeryLargeChunkLocation, BadLocationZeroSize, BadLocationOutOfRange, BadBlockIndex},
- [&](size_t ChunkIndex, const void* Data, uint64_t Size) {
- switch (ChunkIndex)
- {
- case 0:
- CHECK(Data);
- CHECK(Size == FirstChunkData.size());
- CHECK(std::string((const char*)Data, Size) == FirstChunkData);
- break;
- case 1:
- CHECK(Data);
- CHECK(Size == SecondChunkData.size());
- CHECK(std::string((const char*)Data, Size) == SecondChunkData);
- break;
- case 2:
- CHECK(false);
- break;
- case 3:
- CHECK(!Data);
- break;
- case 4:
- CHECK(!Data);
- break;
- case 5:
- CHECK(!Data);
- break;
- default:
- CHECK(false);
- break;
- }
- },
- [&](size_t ChunkIndex, BlockStoreFile& File, uint64_t Offset, uint64_t Size) {
- switch (ChunkIndex)
- {
- case 0:
- case 1:
- CHECK(false);
- break;
- case 2:
- {
- CHECK(Size == VeryLargeChunk.size());
- char* Buffer = new char[Size];
- size_t HashOffset = 0;
- File.StreamByteRange(Offset, Size, [&](const void* Data, uint64_t Size) {
- memcpy(&Buffer[HashOffset], Data, Size);
- HashOffset += Size;
- });
- CHECK(memcmp(Buffer, VeryLargeChunk.data(), Size) == 0);
- delete[] Buffer;
- }
- break;
- case 3:
- CHECK(false);
- break;
- case 4:
- CHECK(false);
- break;
- case 5:
- CHECK(false);
- break;
- default:
- CHECK(false);
- break;
- }
- });
-}
-
-TEST_CASE("blockstore.reclaim.space")
-{
- using namespace blockstore::impl;
-
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path();
-
- BlockStore Store;
- Store.Initialize(RootDirectory / "store", 512, 1024, {});
-
- constexpr size_t ChunkCount = 200;
- constexpr size_t Alignment = 8;
- std::vector<BlockStoreLocation> ChunkLocations;
- std::vector<IoHash> ChunkHashes;
- ChunkLocations.reserve(ChunkCount);
- ChunkHashes.reserve(ChunkCount);
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- IoBuffer Chunk = CreateChunk(57 + ChunkIndex);
-
- Store.WriteChunk(Chunk.Data(), Chunk.Size(), Alignment, [&](const BlockStoreLocation& L) { ChunkLocations.push_back(L); });
- ChunkHashes.push_back(IoHash::HashBuffer(Chunk.Data(), Chunk.Size()));
- }
-
- std::vector<size_t> ChunksToKeep;
- ChunksToKeep.reserve(ChunkLocations.size());
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- ChunksToKeep.push_back(ChunkIndex);
- }
-
- Store.Flush();
- BlockStore::ReclaimSnapshotState State1 = Store.GetReclaimSnapshotState();
- Store.ReclaimSpace(State1, ChunkLocations, ChunksToKeep, Alignment, true);
-
- // If we keep all the chunks we should not get any callbacks on moved/deleted stuff
- Store.ReclaimSpace(
- State1,
- ChunkLocations,
- ChunksToKeep,
- Alignment,
- false,
- [](const BlockStore::MovedChunksArray&, const BlockStore::ChunkIndexArray&) { CHECK(false); },
- []() {
- CHECK(false);
- return 0;
- });
-
- size_t DeleteChunkCount = 38;
- ChunksToKeep.clear();
- for (size_t ChunkIndex = DeleteChunkCount; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- ChunksToKeep.push_back(ChunkIndex);
- }
-
- std::vector<BlockStoreLocation> NewChunkLocations = ChunkLocations;
- size_t MovedChunkCount = 0;
- size_t DeletedChunkCount = 0;
- Store.ReclaimSpace(
- State1,
- ChunkLocations,
- ChunksToKeep,
- Alignment,
- false,
- [&](const BlockStore::MovedChunksArray& MovedChunks, const BlockStore::ChunkIndexArray& DeletedChunks) {
- for (const auto& MovedChunk : MovedChunks)
- {
- CHECK(MovedChunk.first >= DeleteChunkCount);
- NewChunkLocations[MovedChunk.first] = MovedChunk.second;
- }
- MovedChunkCount += MovedChunks.size();
- for (size_t DeletedIndex : DeletedChunks)
- {
- CHECK(DeletedIndex < DeleteChunkCount);
- }
- DeletedChunkCount += DeletedChunks.size();
- },
- []() {
- CHECK(false);
- return 0;
- });
- CHECK(MovedChunkCount <= DeleteChunkCount);
- CHECK(DeletedChunkCount == DeleteChunkCount);
- ChunkLocations = std::vector<BlockStoreLocation>(NewChunkLocations.begin() + DeleteChunkCount, NewChunkLocations.end());
-
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- IoBuffer ChunkBlock = Store.TryGetChunk(NewChunkLocations[ChunkIndex]);
- if (ChunkIndex >= DeleteChunkCount)
- {
- IoBuffer VerifyChunk = Store.TryGetChunk(NewChunkLocations[ChunkIndex]);
- CHECK(VerifyChunk);
- IoHash VerifyHash = IoHash::HashBuffer(VerifyChunk.Data(), VerifyChunk.Size());
- CHECK(VerifyHash == ChunkHashes[ChunkIndex]);
- }
- }
-
- NewChunkLocations = ChunkLocations;
- MovedChunkCount = 0;
- DeletedChunkCount = 0;
- Store.ReclaimSpace(
- State1,
- ChunkLocations,
- {},
- Alignment,
- false,
- [&](const BlockStore::MovedChunksArray& MovedChunks, const BlockStore::ChunkIndexArray& DeletedChunks) {
- CHECK(MovedChunks.empty());
- DeletedChunkCount += DeletedChunks.size();
- },
- []() {
- CHECK(false);
- return 0;
- });
- CHECK(DeletedChunkCount == ChunkCount - DeleteChunkCount);
-}
-
-TEST_CASE("blockstore.thread.read.write")
-{
- using namespace blockstore::impl;
-
- ScopedTemporaryDirectory TempDir;
- auto RootDirectory = TempDir.Path();
-
- BlockStore Store;
- Store.Initialize(RootDirectory / "store", 1088, 1024, {});
-
- constexpr size_t ChunkCount = 1000;
- constexpr size_t Alignment = 8;
- std::vector<IoBuffer> Chunks;
- std::vector<IoHash> ChunkHashes;
- Chunks.reserve(ChunkCount);
- ChunkHashes.reserve(ChunkCount);
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- IoBuffer Chunk = CreateChunk(57 + ChunkIndex / 2);
- Chunks.push_back(Chunk);
- ChunkHashes.push_back(IoHash::HashBuffer(Chunk.Data(), Chunk.Size()));
- }
-
- std::vector<BlockStoreLocation> ChunkLocations;
- ChunkLocations.resize(ChunkCount);
-
- WorkerThreadPool WorkerPool(8);
- std::atomic<size_t> WorkCompleted = 0;
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- WorkerPool.ScheduleWork([&Store, ChunkIndex, &Chunks, &ChunkLocations, &WorkCompleted]() {
- IoBuffer& Chunk = Chunks[ChunkIndex];
- Store.WriteChunk(Chunk.Data(), Chunk.Size(), Alignment, [&](const BlockStoreLocation& L) { ChunkLocations[ChunkIndex] = L; });
- WorkCompleted.fetch_add(1);
- });
- }
- while (WorkCompleted < Chunks.size())
- {
- Sleep(1);
- }
-
- WorkCompleted = 0;
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- WorkerPool.ScheduleWork([&Store, ChunkIndex, &ChunkLocations, &ChunkHashes, &WorkCompleted]() {
- IoBuffer VerifyChunk = Store.TryGetChunk(ChunkLocations[ChunkIndex]);
- CHECK(VerifyChunk);
- IoHash VerifyHash = IoHash::HashBuffer(VerifyChunk.Data(), VerifyChunk.Size());
- CHECK(VerifyHash == ChunkHashes[ChunkIndex]);
- WorkCompleted.fetch_add(1);
- });
- }
- while (WorkCompleted < Chunks.size())
- {
- Sleep(1);
- }
-
- std::vector<BlockStoreLocation> SecondChunkLocations;
- SecondChunkLocations.resize(ChunkCount);
- WorkCompleted = 0;
- for (size_t ChunkIndex = 0; ChunkIndex < ChunkCount; ++ChunkIndex)
- {
- WorkerPool.ScheduleWork([&Store, ChunkIndex, &Chunks, &SecondChunkLocations, &WorkCompleted]() {
- IoBuffer& Chunk = Chunks[ChunkIndex];
- Store.WriteChunk(Chunk.Data(), Chunk.Size(), Alignment, [&](const BlockStoreLocation& L) {
- SecondChunkLocations[ChunkIndex] = L;
- });
- WorkCompleted.fetch_add(1);
- });
- WorkerPool.ScheduleWork([&Store, ChunkIndex, &ChunkLocations, &ChunkHashes, &WorkCompleted]() {
- IoBuffer VerifyChunk = Store.TryGetChunk(ChunkLocations[ChunkIndex]);
- CHECK(VerifyChunk);
- IoHash VerifyHash = IoHash::HashBuffer(VerifyChunk.Data(), VerifyChunk.Size());
- CHECK(VerifyHash == ChunkHashes[ChunkIndex]);
- WorkCompleted.fetch_add(1);
- });
- }
- while (WorkCompleted < Chunks.size() * 2)
- {
- Sleep(1);
- }
-}
-
-#endif
-
-void
-blockstore_forcelink()
-{
-}
-
-} // namespace zen