aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/zenserver/cache/cachedisklayer.cpp60
-rw-r--r--src/zenserver/cache/structuredcachestore.cpp8
-rw-r--r--src/zenstore/blockstore.cpp129
-rw-r--r--src/zenstore/compactcas.cpp51
-rw-r--r--src/zenstore/include/zenstore/blockstore.h12
5 files changed, 113 insertions, 147 deletions
diff --git a/src/zenserver/cache/cachedisklayer.cpp b/src/zenserver/cache/cachedisklayer.cpp
index ebefa2e54..3cd725a27 100644
--- a/src/zenserver/cache/cachedisklayer.cpp
+++ b/src/zenserver/cache/cachedisklayer.cpp
@@ -604,8 +604,7 @@ ZenCacheDiskLayer::CacheBucket::OpenLog(const bool IsNew)
CreateDirectories(m_BucketDir);
- std::unordered_map<uint32_t, uint64_t> BlockSizes =
- m_BlockStore.Initialize(m_BlocksBasePath, MaxBlockSize, BlockStoreDiskLocation::MaxBlockIndex + 1);
+ m_BlockStore.Initialize(m_BlocksBasePath, MaxBlockSize, BlockStoreDiskLocation::MaxBlockIndex + 1);
if (std::filesystem::is_regular_file(IndexPath))
{
@@ -636,7 +635,6 @@ ZenCacheDiskLayer::CacheBucket::OpenLog(const bool IsNew)
std::vector<BlockStoreLocation> KnownLocations;
KnownLocations.reserve(m_Index.size());
- std::vector<DiskIndexEntry> BadEntries;
for (const auto& Entry : m_Index)
{
size_t EntryIndex = Entry.second;
@@ -649,48 +647,10 @@ ZenCacheDiskLayer::CacheBucket::OpenLog(const bool IsNew)
continue;
}
const BlockStoreLocation& BlockLocation = Location.GetBlockLocation(m_PayloadAlignment);
-
- auto BlockIt = BlockSizes.find(BlockLocation.BlockIndex);
- if (BlockIt == BlockSizes.end())
- {
- ZEN_WARN("Unknown block {} for entry {} in '{}'", BlockLocation.BlockIndex, Entry.first.ToHexString(), m_BucketDir);
- }
- else
- {
- uint64_t BlockSize = BlockIt->second;
- if (BlockLocation.Offset + BlockLocation.Size > BlockSize)
- {
- ZEN_WARN("Range is outside of block {} for entry {} in '{}'",
- BlockLocation.BlockIndex,
- Entry.first.ToHexString(),
- m_BucketDir);
- }
- else
- {
- KnownLocations.push_back(BlockLocation);
- continue;
- }
- }
-
- DiskLocation NewLocation = Payload.Location;
- NewLocation.Flags |= DiskLocation::kTombStone;
- BadEntries.push_back(DiskIndexEntry{.Key = Entry.first, .Location = NewLocation});
+ KnownLocations.push_back(BlockLocation);
}
- if (!BadEntries.empty())
- {
- m_SlogFile.Append(BadEntries);
- m_SlogFile.Flush();
-
- LogEntryCount += BadEntries.size();
-
- for (const DiskIndexEntry& BadEntry : BadEntries)
- {
- m_Index.erase(BadEntry.Key);
- }
- }
-
- m_BlockStore.Prune(KnownLocations);
+ m_BlockStore.CreateMissingBlocks(KnownLocations);
if (IsNew || LogEntryCount > 0)
{
@@ -1558,11 +1518,6 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
}
DeleteCacheKeys.push_back(ChunkHash);
});
- if (DeleteCacheKeys.empty())
- {
- ZEN_DEBUG("garbage collect SKIPPED, for '{}', no expired cache keys found", m_BucketDir);
- return;
- }
auto __ = MakeGuard([&]() {
if (!DeletedChunks.empty())
@@ -1638,11 +1593,6 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
WriteBlockTimeUs += ElapsedUs;
WriteBlockLongestTimeUs = std::max(ElapsedUs, WriteBlockLongestTimeUs);
});
- if (m_Index.empty())
- {
- ZEN_DEBUG("garbage collect SKIPPED, for '{}', container is empty", m_BucketDir);
- return;
- }
BlockStoreState = m_BlockStore.GetReclaimSnapshotState();
@@ -1769,10 +1719,6 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
}
}
- if (TotalChunkHashes.empty())
- {
- return;
- }
TotalChunkCount = TotalChunkHashes.size();
std::vector<BlockStoreLocation> ChunkLocations;
diff --git a/src/zenserver/cache/structuredcachestore.cpp b/src/zenserver/cache/structuredcachestore.cpp
index 786053adc..207323451 100644
--- a/src/zenserver/cache/structuredcachestore.cpp
+++ b/src/zenserver/cache/structuredcachestore.cpp
@@ -1123,7 +1123,15 @@ TEST_CASE("z$.gc")
}
}
{
+ // GC could not remove the currently written block so size will not be zero
ZenCacheNamespace Zcs(Gc, *JobQueue, TempDir.Path() / "cache", true);
+ CHECK_NE(0, Zcs.StorageSize().DiskSize);
+
+ // GC again and now the stray write block should be removed
+ GcContext GcCtx(GcClock::Now() + std::chrono::minutes(2), GcClock::Now() + std::chrono::minutes(2));
+ GcCtx.CollectSmallObjects(true);
+ Gc.CollectGarbage(GcCtx);
+
CHECK_EQ(0, Zcs.StorageSize().DiskSize);
}
}
diff --git a/src/zenstore/blockstore.cpp b/src/zenstore/blockstore.cpp
index 12ea24752..c4453a39f 100644
--- a/src/zenstore/blockstore.cpp
+++ b/src/zenstore/blockstore.cpp
@@ -109,6 +109,10 @@ BlockStoreFile::MarkAsDeleteOnClose()
IoBuffer
BlockStoreFile::GetChunk(uint64_t Offset, uint64_t Size)
{
+ if (Offset + Size > m_IoBuffer.GetSize())
+ {
+ return {};
+ }
return IoBuffer(m_IoBuffer, Offset, Size);
}
@@ -154,7 +158,7 @@ BlockStore::~BlockStore()
{
}
-std::unordered_map<uint32_t, uint64_t>
+void
BlockStore::Initialize(const std::filesystem::path& BlocksBasePath, uint64_t MaxBlockSize, uint64_t MaxBlockCount)
{
ZEN_TRACE_CPU("BlockStore::Initialize");
@@ -217,40 +221,30 @@ BlockStore::Initialize(const std::filesystem::path& BlocksBasePath, uint64_t Max
{
CreateDirectories(m_BlocksBasePath);
}
- return FoundBlocks;
}
void
-BlockStore::Prune(const std::vector<BlockStoreLocation>& KnownLocations)
+BlockStore::CreateMissingBlocks(const std::vector<BlockStoreLocation>& KnownLocations)
{
- ZEN_TRACE_CPU("BlockStore::Prune");
+ ZEN_TRACE_CPU("BlockStore::CreateMissingBlocks");
RwLock::ExclusiveLockScope InsertLock(m_InsertLock);
- tsl::robin_set<uint32_t> KnownBlocks;
+ tsl::robin_set<uint32_t> MissingBlocks;
for (const auto& Entry : KnownLocations)
{
- KnownBlocks.insert(Entry.BlockIndex);
- }
- std::vector<uint32_t> BlocksToDelete;
- for (auto It = m_ChunkBlocks.begin(); It != m_ChunkBlocks.end(); ++It)
- {
- uint32_t BlockIndex = It->first;
- if (!KnownBlocks.contains(BlockIndex))
+ if (auto It = m_ChunkBlocks.find(Entry.BlockIndex); It != m_ChunkBlocks.end() && !It->second.IsNull())
{
- Ref<BlockStoreFile> BlockFile = m_ChunkBlocks[BlockIndex];
- BlocksToDelete.push_back(BlockIndex);
+ continue;
}
+ MissingBlocks.insert(Entry.BlockIndex);
}
-
- for (uint32_t BlockIndex : BlocksToDelete)
+ for (std::uint32_t BlockIndex : MissingBlocks)
{
- // Clear out unused blocks
- Ref<BlockStoreFile> BlockFile = m_ChunkBlocks[BlockIndex];
- m_TotalSize.fetch_sub(BlockFile->FileSize(), std::memory_order::relaxed);
- m_ChunkBlocks.erase(BlockIndex);
- ZEN_DEBUG("marking block store file '{}' for delete, block #{}", BlockFile->GetPath(), BlockIndex);
- BlockFile->MarkAsDeleteOnClose();
+ std::filesystem::path BlockPath = GetBlockPath(m_BlocksBasePath, BlockIndex);
+ Ref<BlockStoreFile> NewBlockFile(new BlockStoreFile(BlockPath));
+ NewBlockFile->Create(0);
+ m_ChunkBlocks[BlockIndex] = NewBlockFile;
}
}
@@ -367,7 +361,10 @@ BlockStore::GetReclaimSnapshotState()
{
State.m_ActiveWriteBlocks.insert(m_WriteBlockIndex);
}
- State.BlockCount = m_ChunkBlocks.size();
+ for (auto It : m_ChunkBlocks)
+ {
+ State.m_BlockIndexes.insert(It.first);
+ }
return State;
}
@@ -421,11 +418,6 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
const ReclaimCallback& ChangeCallback,
const ClaimDiskReserveCallback& DiskReserveCallback)
{
- if (ChunkLocations.empty())
- {
- return;
- }
-
ZEN_TRACE_CPU("BlockStore::ReclaimSpace");
uint64_t WriteBlockTimeUs = 0;
@@ -460,7 +452,7 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
NiceBytes(OldTotalSize));
});
- size_t BlockCount = Snapshot.BlockCount;
+ size_t BlockCount = Snapshot.m_BlockIndexes.size();
if (BlockCount == 0)
{
ZEN_DEBUG("garbage collect for '{}' SKIPPED, no blocks to process", m_BlocksBasePath);
@@ -487,6 +479,11 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
for (size_t Index = 0; Index < TotalChunkCount; ++Index)
{
const BlockStoreLocation& Location = ChunkLocations[Index];
+ if (!Snapshot.m_BlockIndexes.contains(Location.BlockIndex))
+ {
+ // We did not know about the block when we took the snapshot, don't touch it
+ continue;
+ }
OldTotalSize += Location.Size;
auto BlockIndexPtr = BlockIndexToChunkMapIndex.find(Location.BlockIndex);
size_t ChunkMapIndex = 0;
@@ -516,7 +513,7 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
DeleteCount++;
}
- tsl::robin_set<uint32_t> BlocksToReWrite;
+ std::vector<uint32_t> BlocksToReWrite;
BlocksToReWrite.reserve(BlockIndexToChunkMapIndex.size());
for (const auto& Entry : BlockIndexToChunkMapIndex)
{
@@ -527,7 +524,33 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
{
continue;
}
- BlocksToReWrite.insert(BlockIndex);
+ BlocksToReWrite.push_back(BlockIndex);
+ }
+
+ {
+ // Any known block not referenced should be added as well
+ RwLock::SharedLockScope __(m_InsertLock);
+ for (std::uint32_t BlockIndex : Snapshot.m_BlockIndexes)
+ {
+ if (!m_ChunkBlocks.contains(BlockIndex))
+ {
+ continue;
+ }
+ bool WasActiveWriteBlock = Snapshot.m_ActiveWriteBlocks.contains(BlockIndex);
+ if (WasActiveWriteBlock)
+ {
+ continue;
+ }
+ if (BlockIndexToChunkMapIndex.contains(BlockIndex))
+ {
+ continue;
+ }
+ size_t ChunkMapIndex = ChunkMapIndex = BlockKeepChunks.size();
+ BlockIndexToChunkMapIndex[BlockIndex] = ChunkMapIndex;
+ BlockKeepChunks.resize(ChunkMapIndex + 1);
+ BlockDeleteChunks.resize(ChunkMapIndex + 1);
+ BlocksToReWrite.push_back(BlockIndex);
+ }
}
if (DryRun)
@@ -618,6 +641,17 @@ BlockStore::ReclaimSpace(const ReclaimSnapshotState& Snapshot,
BlockDeleteChunks[ChunkMapIndex].insert(BlockDeleteChunks[ChunkMapIndex].end(), KeepMap.begin(), KeepMap.end());
KeepMap.clear();
}
+ else if (OldBlockFile && (OldBlockFile->FileSize() == 0))
+ {
+ // Block created to accommodate missing blocks
+ ZEN_WARN("Missing block {} in {} - backing data for locations is missing, marking {} entries as deleted.",
+ BlockIndex,
+ m_BlocksBasePath,
+ KeepMap.size());
+
+ BlockDeleteChunks[ChunkMapIndex].insert(BlockDeleteChunks[ChunkMapIndex].end(), KeepMap.begin(), KeepMap.end());
+ KeepMap.clear();
+ }
MovedChunksArray MovedChunks;
if (OldBlockFile)
@@ -1141,15 +1175,30 @@ TEST_CASE("blockstore.clean.stray.blocks")
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);
+ BlockStoreLocation ThirdChunkLocation = WriteStringAsChunk(Store, ThirdChunkData, 4);
Store.Close();
- // Not referencing the second block means that we should be deleted
Store.Initialize(RootDirectory / "store", 128, 1024);
- Store.Prune({FirstChunkLocation, SecondChunkLocation});
-
+ CHECK(GetDirectoryContent(RootDirectory / "store", true, false).size() == 2);
+ IoBuffer ThirdChunk = Store.TryGetChunk(ThirdChunkLocation);
+ CHECK(ThirdChunk);
+
+ // Reclaim space should delete unreferenced block
+ Store.ReclaimSpace(Store.GetReclaimSnapshotState(), {FirstChunkLocation, SecondChunkLocation}, {0, 1}, 4, false);
+ // Block lives on as long as we reference it via ThirdChunk
+ CHECK(GetDirectoryContent(RootDirectory / "store", true, false).size() == 2);
+ ThirdChunk = {};
CHECK(GetDirectoryContent(RootDirectory / "store", true, false).size() == 1);
+ ThirdChunk = Store.TryGetChunk(ThirdChunkLocation);
+ CHECK(!ThirdChunk);
+
+ // Recreate a fake block for an missing chunk location
+ Store.CreateMissingBlocks({FirstChunkLocation, SecondChunkLocation, ThirdChunkLocation});
+ // 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);
+ CHECK(!ThirdChunk);
}
TEST_CASE("blockstore.flush.force.new.block")
@@ -1368,11 +1417,13 @@ TEST_CASE("blockstore.reclaim.space")
}
}
- NewChunkLocations = ChunkLocations;
- MovedChunkCount = 0;
- DeletedChunkCount = 0;
+ // We need to take a new state since reclaim space add new block when compacting
+ BlockStore::ReclaimSnapshotState State2 = Store.GetReclaimSnapshotState();
+ NewChunkLocations = ChunkLocations;
+ MovedChunkCount = 0;
+ DeletedChunkCount = 0;
Store.ReclaimSpace(
- State1,
+ State2,
ChunkLocations,
{},
Alignment,
diff --git a/src/zenstore/compactcas.cpp b/src/zenstore/compactcas.cpp
index 98ad20b98..0468e95de 100644
--- a/src/zenstore/compactcas.cpp
+++ b/src/zenstore/compactcas.cpp
@@ -804,8 +804,7 @@ CasContainerStrategy::OpenContainer(bool IsNewStore)
CreateDirectories(BasePath);
- std::unordered_map<uint32_t, uint64_t> BlockSizes =
- m_BlockStore.Initialize(m_BlocksBasePath, m_MaxBlockSize, BlockStoreDiskLocation::MaxBlockIndex + 1);
+ m_BlockStore.Initialize(m_BlocksBasePath, m_MaxBlockSize, BlockStoreDiskLocation::MaxBlockIndex + 1);
std::filesystem::path LogPath = GetLogPath(m_RootDirectory, m_ContainerBaseName);
std::filesystem::path IndexPath = GetIndexPath(m_RootDirectory, m_ContainerBaseName);
@@ -839,54 +838,14 @@ CasContainerStrategy::OpenContainer(bool IsNewStore)
std::vector<BlockStoreLocation> KnownLocations;
KnownLocations.reserve(m_LocationMap.size());
- std::vector<CasDiskIndexEntry> BadEntries;
for (const auto& Entry : m_LocationMap)
{
- const BlockStoreDiskLocation& DiskLocation = m_Locations[Entry.second];
- uint32_t BlockIndex = DiskLocation.GetBlockIndex();
- auto BlockIt = BlockSizes.find(BlockIndex);
- if (BlockIt == BlockSizes.end())
- {
- ZEN_WARN("Unknown block {} for entry {} in '{}'", BlockIndex, Entry.first.ToHexString(), BasePath);
- }
- else
- {
- BlockStoreLocation BlockLocation = DiskLocation.Get(m_PayloadAlignment);
-
- uint64_t BlockSize = BlockIt->second;
- if (BlockLocation.Offset + BlockLocation.Size > BlockSize)
- {
- ZEN_WARN("Range is outside of block {} for entry {} in '{}'",
- BlockLocation.BlockIndex,
- Entry.first.ToHexString(),
- BasePath);
- }
- else
- {
- KnownLocations.emplace_back(std::move(BlockLocation));
- continue;
- }
- BadEntries.push_back({.Key = Entry.first, .Location = DiskLocation, .Flags = CasDiskIndexEntry::kTombstone});
- }
- }
-
- if (!BadEntries.empty())
- {
- m_CasLog.Append(BadEntries);
- m_CasLog.Flush();
-
- LogEntryCount += BadEntries.size();
-
- for (const CasDiskIndexEntry& BadEntry : BadEntries)
- {
- m_LocationMap.erase(BadEntry.Key);
- }
-
- RwLock::ExclusiveLockScope IndexLock(m_LocationMapLock);
- CompactIndex(IndexLock);
+ const BlockStoreDiskLocation& DiskLocation = m_Locations[Entry.second];
+ BlockStoreLocation BlockLocation = DiskLocation.Get(m_PayloadAlignment);
+ KnownLocations.emplace_back(std::move(BlockLocation));
}
- m_BlockStore.Prune(KnownLocations);
+ m_BlockStore.CreateMissingBlocks(KnownLocations);
if (IsNewStore || (LogEntryCount > 0))
{
diff --git a/src/zenstore/include/zenstore/blockstore.h b/src/zenstore/include/zenstore/blockstore.h
index a2dea86c1..48d33edbc 100644
--- a/src/zenstore/include/zenstore/blockstore.h
+++ b/src/zenstore/include/zenstore/blockstore.h
@@ -117,7 +117,7 @@ public:
struct ReclaimSnapshotState
{
std::unordered_set<uint32_t> m_ActiveWriteBlocks;
- size_t BlockCount;
+ std::unordered_set<uint32_t> m_BlockIndexes;
};
typedef std::vector<std::pair<size_t, BlockStoreLocation>> MovedChunksArray;
@@ -129,11 +129,13 @@ public:
typedef std::function<void(size_t ChunkIndex, BlockStoreFile& File, uint64_t Offset, uint64_t Size)> IterateChunksLargeSizeCallback;
typedef std::function<void(const BlockStoreLocation& Location)> WriteChunkCallback;
- std::unordered_map<uint32_t, uint64_t> Initialize(const std::filesystem::path& BlocksBasePath,
- uint64_t MaxBlockSize,
- uint64_t MaxBlockCount);
+ void Initialize(const std::filesystem::path& BlocksBasePath, uint64_t MaxBlockSize, uint64_t MaxBlockCount);
+
+ // Ask the store to create empty blocks for all locations that does not have a block
+ // This is to make sure we don't have locations that points to a block that will be written
+ // in the future which would result in us getting garbage data
+ void CreateMissingBlocks(const std::vector<BlockStoreLocation>& KnownLocations);
- void Prune(const std::vector<BlockStoreLocation>& KnownLocations);
void Close();
void WriteChunk(const void* Data, uint64_t Size, uint64_t Alignment, const WriteChunkCallback& Callback);