diff options
| author | Per Larsson <[email protected]> | 2021-12-09 16:17:56 +0100 |
|---|---|---|
| committer | Per Larsson <[email protected]> | 2021-12-09 16:17:56 +0100 |
| commit | d9dad00a33657c8759ca3551de4139136d16143b (patch) | |
| tree | 2757d5fbf6b442766b4d6ea269ba2ca8cbbe537c /zenserver/cache/structuredcachestore.cpp | |
| parent | GC default off. (diff) | |
| download | zen-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.cpp | 289 |
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); |