aboutsummaryrefslogtreecommitdiff
path: root/zenserver/cache/structuredcachestore.cpp
diff options
context:
space:
mode:
authorPer Larsson <[email protected]>2021-12-09 16:17:56 +0100
committerPer Larsson <[email protected]>2021-12-09 16:17:56 +0100
commitd9dad00a33657c8759ca3551de4139136d16143b (patch)
tree2757d5fbf6b442766b4d6ea269ba2ca8cbbe537c /zenserver/cache/structuredcachestore.cpp
parentGC default off. (diff)
downloadzen-d9dad00a33657c8759ca3551de4139136d16143b.tar.xz
zen-d9dad00a33657c8759ca3551de4139136d16143b.zip
Added options for Z$ max duration and whether to collect small objects.
Diffstat (limited to 'zenserver/cache/structuredcachestore.cpp')
-rw-r--r--zenserver/cache/structuredcachestore.cpp289
1 files changed, 134 insertions, 155 deletions
diff --git a/zenserver/cache/structuredcachestore.cpp b/zenserver/cache/structuredcachestore.cpp
index d97e2d250..71e946432 100644
--- a/zenserver/cache/structuredcachestore.cpp
+++ b/zenserver/cache/structuredcachestore.cpp
@@ -491,7 +491,7 @@ private:
RwLock m_IndexLock;
IndexMap m_Index;
- uint64_t m_WriteCursor = 0;
+ uint64_t m_SobsCursor = 0;
std::atomic_uint64_t m_TotalSize{};
void BuildPath(WideStringBuilderBase& Path, const IoHash& HashKey);
@@ -499,6 +499,7 @@ private:
bool GetStandaloneCacheValue(const DiskLocation& Loc, const IoHash& HashKey, ZenCacheValue& OutValue);
void DeleteStandaloneCacheValue(const DiskLocation& Loc, const IoHash& HashKey, const fs::path& Path, std::error_code& Ec);
bool GetInlineCacheValue(const DiskLocation& Loc, ZenCacheValue& OutValue);
+ void OpenLog(const fs::path& BucketDir, const bool IsNew);
// These locks are here to avoid contention on file creation, therefore it's sufficient
// that we take the same lock for the same hash
@@ -539,11 +540,7 @@ ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir, bo
CreateDirectories(BucketDir);
- m_BucketDir = BucketDir;
-
- std::filesystem::path ManifestPath{m_BucketDir / "zen_manifest"};
- std::filesystem::path SobsPath{m_BucketDir / "zen.sobs"};
- std::filesystem::path SlogPath{m_BucketDir / "zen.slog"};
+ std::filesystem::path ManifestPath{BucketDir / "zen_manifest"};
bool IsNew = false;
@@ -569,56 +566,63 @@ ZenCacheDiskLayer::CacheBucket::OpenOrCreate(std::filesystem::path BucketDir, bo
return;
}
- // Initialize small object storage related files
+ OpenLog(BucketDir, IsNew);
- m_SobsFile.Open(SobsPath, IsNew);
+ for (CbFieldView Entry : Manifest["Timestamps"])
+ {
+ const CbObjectView Obj = Entry.AsObjectView();
+ const IoHash Key = Obj["Key"sv].AsHash();
- // Open and replay log
+ if (auto It = m_Index.find(Key); It != m_Index.end())
+ {
+ It.value().LastAccess = Obj["LastAccess"sv].AsInt64();
+ }
+ }
- m_SlogFile.Open(SlogPath, IsNew);
+ m_IsOk = true;
+}
+
+void
+ZenCacheDiskLayer::CacheBucket::OpenLog(const fs::path& BucketDir, const bool IsNew)
+{
+ m_BucketDir = BucketDir;
uint64_t MaxFileOffset = 0;
uint64_t InvalidEntryCount = 0;
+ m_SobsCursor = 0;
+ m_TotalSize = 0;
- if (RwLock::ExclusiveLockScope _(m_IndexLock); m_Index.empty())
- {
- m_SlogFile.Replay([&](const DiskIndexEntry& Entry) {
- if (Entry.Key == IoHash::Zero)
- {
- ++InvalidEntryCount;
- }
- else if (Entry.Location.IsFlagSet(DiskLocation::kTombStone))
- {
- m_TotalSize.fetch_sub(Entry.Location.Size(), std::memory_order::relaxed);
- }
- else
- {
- m_Index[Entry.Key] = {.Location = Entry.Location, .LastAccess = GcClock::TickCount()};
- m_TotalSize.fetch_add(Entry.Location.Size(), std::memory_order::relaxed);
- }
- MaxFileOffset = std::max<uint64_t>(MaxFileOffset, Entry.Location.Offset() + Entry.Location.Size());
- });
-
- if (InvalidEntryCount)
- {
- ZEN_WARN("found {} invalid entries in '{}'", InvalidEntryCount, SlogPath);
- }
+ m_Index.clear();
- m_WriteCursor = (MaxFileOffset + 15) & ~15;
- }
+ std::filesystem::path SobsPath{BucketDir / "zen.sobs"};
+ std::filesystem::path SlogPath{BucketDir / "zen.slog"};
- for (CbFieldView Entry : Manifest["Timestamps"])
- {
- const CbObjectView Obj = Entry.AsObjectView();
- const IoHash Key = Obj["Key"sv].AsHash();
+ m_SobsFile.Open(SobsPath, IsNew);
+ m_SlogFile.Open(SlogPath, IsNew);
- if (auto It = m_Index.find(Key); It != m_Index.end())
+ m_SlogFile.Replay([&](const DiskIndexEntry& Entry) {
+ if (Entry.Key == IoHash::Zero)
{
- It.value().LastAccess = Obj["LastAccess"sv].AsInt64();
+ ++InvalidEntryCount;
}
+ else if (Entry.Location.IsFlagSet(DiskLocation::kTombStone))
+ {
+ m_TotalSize.fetch_sub(Entry.Location.Size(), std::memory_order::relaxed);
+ }
+ else
+ {
+ m_Index[Entry.Key] = {.Location = Entry.Location, .LastAccess = GcClock::TickCount()};
+ m_TotalSize.fetch_add(Entry.Location.Size(), std::memory_order::relaxed);
+ }
+ MaxFileOffset = std::max<uint64_t>(MaxFileOffset, Entry.Location.Offset() + Entry.Location.Size());
+ });
+
+ if (InvalidEntryCount)
+ {
+ ZEN_WARN("found {} invalid entries in '{}'", InvalidEntryCount, SlogPath);
}
- m_IsOk = true;
+ m_SobsCursor = (MaxFileOffset + 15) & ~15;
}
void
@@ -740,9 +744,9 @@ ZenCacheDiskLayer::CacheBucket::Put(const IoHash& HashKey, const ZenCacheValue&
RwLock::ExclusiveLockScope _(m_IndexLock);
- DiskLocation Loc(m_WriteCursor, Value.Value.Size(), 0, EntryFlags);
+ DiskLocation Loc(m_SobsCursor, Value.Value.Size(), 0, EntryFlags);
- m_WriteCursor = RoundUp(m_WriteCursor + Loc.Size(), 16);
+ m_SobsCursor = RoundUp(m_SobsCursor + Loc.Size(), 16);
if (auto It = m_Index.find(HashKey); It == m_Index.end())
{
@@ -993,9 +997,34 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
}
}
- if (GcCtx.IsContainerGcEnabled() && !ExpiredEntries.empty())
+ if (GcCtx.CollectSmallObjects() && !ExpiredEntries.empty())
{
- // super naive GC implementation for small object container file
+ // Super naive GC implementation for small objects. Needs enough free
+ // disk space to store intermediate sobs files along side the
+ // old files
+
+ const auto ResetSobStorage = [this, &ValidEntries]() {
+ m_SobsFile.Close();
+ m_SlogFile.Close();
+
+ const bool IsNew = true;
+ m_SobsFile.Open(m_BucketDir / "zen.sobs", IsNew);
+ m_SlogFile.Open(m_BucketDir / "zen.slog", IsNew);
+
+ m_SobsCursor = 0;
+ m_TotalSize = 0;
+ m_Index.clear();
+
+ for (const auto& Entry : ValidEntries)
+ {
+ if (Entry.Loc.IsFlagSet(DiskLocation::kStandaloneFile))
+ {
+ m_SlogFile.Append({.Key = Entry.Key, .Location = Entry.Loc});
+ m_Index.insert({Entry.Key, {Entry.Loc, GcClock::TickCount()}});
+ m_TotalSize.fetch_add(Entry.Loc.Size(), std::memory_order::relaxed);
+ }
+ }
+ };
uint64_t NewContainerSize{};
for (const auto& Entry : ValidEntries)
@@ -1006,133 +1035,83 @@ ZenCacheDiskLayer::CacheBucket::CollectGarbage(GcContext& GcCtx)
}
}
- if (NewContainerSize > 0)
+ if (NewContainerSize == 0)
{
- const uint64_t DiskSpaceMargin = (256 << 10);
+ ResetSobStorage();
+ return;
+ }
- std::error_code Ec;
- DiskSpace Space = DiskSpaceInfo(m_BucketDir, Ec);
- if (Ec || Space.Free < NewContainerSize + DiskSpaceMargin)
- {
- ZEN_WARN("garbage collect z$ bucket '{}' FAILED, not enough disk space {}/{} (required/free)",
- m_BucketDir,
- NiceBytes(NewContainerSize),
- NiceBytes(Space.Free));
- return;
- }
+ const uint64_t DiskSpaceMargin = (256 << 10);
+
+ std::error_code Ec;
+ DiskSpace Space = DiskSpaceInfo(m_BucketDir, Ec);
+ if (Ec || Space.Free < NewContainerSize + DiskSpaceMargin)
+ {
+ ZEN_WARN("garbage collect z$ bucket '{}' FAILED, not enough disk space {}/{} (required/free)",
+ m_BucketDir,
+ NiceBytes(NewContainerSize),
+ NiceBytes(Space.Free));
+ return;
+ }
- // Copy non expired entries to temporary container file
+ std::filesystem::path TmpSobsPath{m_BucketDir / "zen.sobs.tmp"};
+ std::filesystem::path TmpSlogPath{m_BucketDir / "zen.slog.tmp"};
- std::filesystem::path TmpSobsPath{m_BucketDir / "zen.sobs.tmp"};
- std::filesystem::path TmpSlogPath{m_BucketDir / "zen.slog.tmp"};
+ // Copy non expired sob(s) to temporary file(s)
+
+ {
+ BasicFile TmpSobs;
TCasLogFile<DiskIndexEntry> TmpLog;
- IndexMap TmpIndex;
uint64_t TmpCursor{};
- uint64_t TmpTotalSize{};
+ std::vector<uint8_t> Chunk;
+
+ TmpSobs.Open(TmpSobsPath, true);
+ TmpLog.Open(TmpSlogPath, true);
+ for (const auto& Entry : ValidEntries)
{
- BasicFile TmpSobs;
- TmpSobs.Open(TmpSobsPath, true);
- std::vector<uint8_t> Chunk;
+ DiskLocation NewLoc;
- for (const auto& Entry : ValidEntries)
+ if (Entry.Loc.IsFlagSet(DiskLocation::kStandaloneFile))
{
- DiskLocation NewLoc;
-
- if (Entry.Loc.IsFlagSet(DiskLocation::kStandaloneFile))
- {
- NewLoc = DiskLocation(0, Entry.Loc.Size(), 0, DiskLocation::kStandaloneFile);
- }
- else
- {
- Chunk.resize(Entry.Loc.Size());
- m_SobsFile.Read(Chunk.data(), Chunk.size(), Entry.Loc.Offset());
-
- NewLoc = DiskLocation(TmpCursor, Chunk.size(), 0, 0);
- TmpSobs.Write(Chunk.data(), Chunk.size(), TmpCursor);
- TmpCursor = RoundUp(TmpCursor + Chunk.size(), 16);
- }
-
- TmpLog.Append({.Key = Entry.Key, .Location = NewLoc});
- TmpIndex.insert({Entry.Key, {NewLoc, GcClock::TickCount()}});
-
- TmpTotalSize += NewLoc.Size();
+ NewLoc = DiskLocation(0, Entry.Loc.Size(), 0, DiskLocation::kStandaloneFile);
}
- }
-
- // Swap state
- try
- {
- fs::path SobsPath{m_BucketDir / "zen.sobs"};
- fs::path SlogPath{m_BucketDir / "zen.slog"};
-
- m_SobsFile.Close();
- m_SlogFile.Close();
-
- fs::remove(SobsPath);
- fs::remove(SlogPath);
-
- fs::rename(TmpSobsPath, SobsPath);
- fs::rename(TmpSlogPath, SlogPath);
-
- const bool IsNew = false;
- m_SobsFile.Open(SobsPath, IsNew);
- m_SlogFile.Open(SobsPath, IsNew);
-
- std::swap(m_Index, TmpIndex);
- std::swap(m_WriteCursor, TmpCursor);
- m_TotalSize = TmpTotalSize;
- }
- catch (std::exception& Err)
- {
- ZEN_ERROR("garbage collection FAILED, reason '{}'", Err.what());
-
- // Reset the container file and add standalone file(s)
- m_SobsFile.Close();
- m_SlogFile.Close();
-
- const bool IsNew = true;
- m_SobsFile.Open(m_BucketDir / "zen.sobs", IsNew);
- m_SlogFile.Open(m_BucketDir / "zen.slog", IsNew);
-
- m_Index = IndexMap();
- m_WriteCursor = 0;
- m_TotalSize = 0;
-
- for (const auto& Entry : ValidEntries)
+ else
{
- if (Entry.Loc.IsFlagSet(DiskLocation::kStandaloneFile))
- {
- DiskLocation Loc(0, Entry.Loc.Size(), 0, DiskLocation::kStandaloneFile);
-
- m_SlogFile.Append({.Key = Entry.Key, .Location = Loc});
- m_Index.insert({Entry.Key, {Loc, GcClock::TickCount()}});
- m_TotalSize.fetch_add(Loc.Size(), std::memory_order::relaxed);
- }
+ Chunk.resize(Entry.Loc.Size());
+ m_SobsFile.Read(Chunk.data(), Chunk.size(), Entry.Loc.Offset());
+
+ NewLoc = DiskLocation(TmpCursor, Chunk.size(), 0, 0);
+ TmpSobs.Write(Chunk.data(), Chunk.size(), TmpCursor);
+ TmpCursor = RoundUp(TmpCursor + Chunk.size(), 16);
}
+
+ TmpLog.Append(DiskIndexEntry{.Key = Entry.Key, .Location = NewLoc});
}
}
- else
+
+ // Swap state
+ try
{
- uint64_t SobSize{};
- for (const auto& Entry : ExpiredEntries)
- {
- if (Entry.Loc.IsFlagSet(DiskLocation::kStandaloneFile) == false)
- {
- m_Index.erase(Entry.Key);
- SobSize += Entry.Loc.Size();
- }
- }
+ fs::path SobsPath{m_BucketDir / "zen.sobs"};
+ fs::path SlogPath{m_BucketDir / "zen.slog"};
m_SobsFile.Close();
m_SlogFile.Close();
- const bool IsNew = true;
- m_SobsFile.Open(m_BucketDir / "zen.sobs", IsNew);
- m_SlogFile.Open(m_BucketDir / "zen.slog", IsNew);
+ fs::remove(SobsPath);
+ fs::remove(SlogPath);
- m_WriteCursor = 0;
- m_TotalSize.fetch_sub(SobSize, std::memory_order::relaxed);
+ fs::rename(TmpSobsPath, SobsPath);
+ fs::rename(TmpSlogPath, SlogPath);
+
+ const bool IsNew = false;
+ OpenLog(m_BucketDir, IsNew);
+ }
+ catch (std::exception& Err)
+ {
+ ZEN_ERROR("garbage collection FAILED, reason '{}'", Err.what());
+ ResetSobStorage();
}
}
}
@@ -1766,7 +1745,7 @@ TEST_CASE("zcache.gc")
}
}
- SUBCASE("gc removes small objects (container)")
+ SUBCASE("gc removes small objects")
{
ScopedTemporaryDirectory TempDir;
CasGc Gc;
@@ -1785,7 +1764,7 @@ TEST_CASE("zcache.gc")
{
GcContext GcCtx;
GcCtx.MaxCacheDuration(std::chrono::hours(2));
- GcCtx.SetContainerGcEnabled(true);
+ GcCtx.CollectSmallObjects(true);
Zcs.CollectGarbage(GcCtx);
@@ -1801,7 +1780,7 @@ TEST_CASE("zcache.gc")
{
GcContext GcCtx(CurrentTime + std::chrono::hours(2));
GcCtx.MaxCacheDuration(std::chrono::minutes(2));
- GcCtx.SetContainerGcEnabled(true);
+ GcCtx.CollectSmallObjects(true);
Zcs.CollectGarbage(GcCtx);