diff options
| author | Stefan Boberg <[email protected]> | 2023-12-19 21:49:55 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-12-19 21:49:55 +0100 |
| commit | 8a90a40b4517d198855c1e52740a7e7fb21ecc20 (patch) | |
| tree | ffd8524bcebf8841b69725934f31d438a03e7746 /src/zenserver/cache/structuredcachestore.cpp | |
| parent | 0.2.38 (diff) | |
| download | zen-8a90a40b4517d198855c1e52740a7e7fb21ecc20.tar.xz zen-8a90a40b4517d198855c1e52740a7e7fb21ecc20.zip | |
move cachedisklayer and structuredcachestore into zenstore (#624)
Diffstat (limited to 'src/zenserver/cache/structuredcachestore.cpp')
| -rw-r--r-- | src/zenserver/cache/structuredcachestore.cpp | 2456 |
1 files changed, 0 insertions, 2456 deletions
diff --git a/src/zenserver/cache/structuredcachestore.cpp b/src/zenserver/cache/structuredcachestore.cpp deleted file mode 100644 index 9155e209c..000000000 --- a/src/zenserver/cache/structuredcachestore.cpp +++ /dev/null @@ -1,2456 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -#include "structuredcachestore.h" - -#include <zencore/compactbinarybuilder.h> -#include <zencore/compactbinarypackage.h> -#include <zencore/compactbinaryvalidation.h> -#include <zencore/compress.h> -#include <zencore/except.h> -#include <zencore/filesystem.h> -#include <zencore/fmtutils.h> -#include <zencore/logging.h> -#include <zencore/scopeguard.h> -#include <zencore/string.h> -#include <zencore/thread.h> -#include <zencore/timer.h> -#include <zencore/trace.h> -#include <zencore/workthreadpool.h> -#include <zennet/statsdclient.h> -#include <zenstore/scrubcontext.h> -#include <zenutil/cache/cache.h> - -#include <future> -#include <limits> - -#if ZEN_PLATFORM_WINDOWS -# include <zencore/windows.h> -#endif - -ZEN_THIRD_PARTY_INCLUDES_START -#include <fmt/core.h> -#include <xxhash.h> -#include <gsl/gsl-lite.hpp> -ZEN_THIRD_PARTY_INCLUDES_END - -#if ZEN_WITH_TESTS -# include <zencore/jobqueue.h> -# include <zencore/testing.h> -# include <zencore/testutils.h> -# include <zencore/workthreadpool.h> -# include <zenstore/cidstore.h> -# include <random> -# include <unordered_map> -#endif - -namespace zen { - -bool -IsKnownBadBucketName(std::string_view Bucket) -{ - if (Bucket.size() == 32) - { - uint8_t BucketHex[16]; - if (ParseHexBytes(Bucket, BucketHex)) - { - return true; - } - } - - return false; -} - -ZenCacheNamespace::ZenCacheNamespace(GcManager& Gc, JobQueue& JobQueue, const std::filesystem::path& RootDir, const Configuration& Config) -: m_Gc(Gc) -, m_JobQueue(JobQueue) -, m_RootDir(RootDir) -, m_Configuration(Config) -, m_DiskLayer(m_Gc, m_JobQueue, m_RootDir, m_Configuration.DiskLayerConfig) -{ - ZEN_INFO("initializing structured cache at '{}'", m_RootDir); - CreateDirectories(m_RootDir); - - m_DiskLayer.DiscoverBuckets(); - - m_Gc.AddGcContributor(this); - m_Gc.AddGcStorage(this); -} - -ZenCacheNamespace::~ZenCacheNamespace() -{ - m_Gc.RemoveGcStorage(this); - m_Gc.RemoveGcContributor(this); -} - -bool -ZenCacheNamespace::Get(std::string_view InBucket, const IoHash& HashKey, ZenCacheValue& OutValue) -{ - ZEN_TRACE_CPU("Z$::Namespace::Get"); - - metrics::RequestStats::Scope StatsScope(m_GetOps, 0); - - bool Ok = m_DiskLayer.Get(InBucket, HashKey, OutValue); - - if (Ok) - { - ZEN_ASSERT(OutValue.Value.Size()); - StatsScope.SetBytes(OutValue.Value.Size()); - - m_HitCount++; - return true; - } - - m_MissCount++; - return false; -} - -void -ZenCacheNamespace::Put(std::string_view InBucket, const IoHash& HashKey, const ZenCacheValue& Value, std::span<IoHash> References) -{ - ZEN_TRACE_CPU("Z$::Namespace::Put"); - - metrics::RequestStats::Scope $(m_PutOps, Value.Value.Size()); - - // Store value and index - - ZEN_ASSERT(Value.Value.Size()); - - m_DiskLayer.Put(InBucket, HashKey, Value, References); - m_WriteCount++; -} - -bool -ZenCacheNamespace::DropBucket(std::string_view Bucket) -{ - ZEN_INFO("dropping bucket '{}'", Bucket); - - const bool Dropped = m_DiskLayer.DropBucket(Bucket); - - ZEN_INFO("bucket '{}' was {}", Bucket, Dropped ? "dropped" : "not found"); - - return Dropped; -} - -void -ZenCacheNamespace::EnumerateBucketContents(std::string_view Bucket, - std::function<void(const IoHash& Key, const CacheValueDetails::ValueDetails& Details)>& Fn) const -{ - m_DiskLayer.EnumerateBucketContents(Bucket, Fn); -} - -bool -ZenCacheNamespace::Drop() -{ - return m_DiskLayer.Drop(); -} - -void -ZenCacheNamespace::Flush() -{ - m_DiskLayer.Flush(); -} - -void -ZenCacheNamespace::ScrubStorage(ScrubContext& Ctx) -{ - if (m_LastScrubTime == Ctx.ScrubTimestamp()) - { - return; - } - - ZEN_INFO("scrubbing '{}'", m_RootDir); - - m_LastScrubTime = Ctx.ScrubTimestamp(); - - m_DiskLayer.ScrubStorage(Ctx); -} - -void -ZenCacheNamespace::GatherReferences(GcContext& GcCtx) -{ - ZEN_TRACE_CPU("Z$::ZenCacheNamespace::GatherReferences"); - - Stopwatch Timer; - const auto Guard = - MakeGuard([&] { ZEN_DEBUG("cache gathered all references from '{}' in {}", m_RootDir, NiceTimeSpanMs(Timer.GetElapsedTimeMs())); }); - - m_DiskLayer.GatherReferences(GcCtx); -} - -void -ZenCacheNamespace::CollectGarbage(GcContext& GcCtx) -{ - ZEN_TRACE_CPU("Z$::Namespace::CollectGarbage"); - - m_DiskLayer.CollectGarbage(GcCtx); -} - -GcStorageSize -ZenCacheNamespace::StorageSize() const -{ - return m_DiskLayer.StorageSize(); -} - -ZenCacheNamespace::Info -ZenCacheNamespace::GetInfo() const -{ - ZenCacheNamespace::Info Info = {.RootDir = m_RootDir, .Config = m_Configuration, .DiskLayerInfo = m_DiskLayer.GetInfo()}; - std::unordered_set<std::string> BucketNames; - for (const std::string& BucketName : Info.DiskLayerInfo.BucketNames) - { - BucketNames.insert(BucketName); - } - Info.BucketNames.insert(Info.BucketNames.end(), BucketNames.begin(), BucketNames.end()); - return Info; -} - -std::optional<ZenCacheNamespace::BucketInfo> -ZenCacheNamespace::GetBucketInfo(std::string_view Bucket) const -{ - std::optional<ZenCacheDiskLayer::BucketInfo> DiskBucketInfo = m_DiskLayer.GetBucketInfo(Bucket); - if (!DiskBucketInfo.has_value()) - { - return {}; - } - ZenCacheNamespace::BucketInfo Info = {.DiskLayerInfo = *DiskBucketInfo}; - return Info; -} - -ZenCacheNamespace::NamespaceStats -ZenCacheNamespace::Stats() -{ - return ZenCacheNamespace::NamespaceStats{.HitCount = m_HitCount, - .MissCount = m_MissCount, - .WriteCount = m_WriteCount, - .PutOps = m_PutOps.Snapshot(), - .GetOps = m_GetOps.Snapshot(), - .DiskStats = m_DiskLayer.Stats()}; -} - -CacheValueDetails::NamespaceDetails -ZenCacheNamespace::GetValueDetails(const std::string_view BucketFilter, const std::string_view ValueFilter) const -{ - return m_DiskLayer.GetValueDetails(BucketFilter, ValueFilter); -} - -#if ZEN_WITH_TESTS -void -ZenCacheNamespace::SetAccessTime(std::string_view Bucket, const IoHash& HashKey, GcClock::TimePoint Time) -{ - m_DiskLayer.SetAccessTime(Bucket, HashKey, Time); -} -#endif // ZEN_WITH_TESTS - -//////////////////////////// ZenCacheStore - -ZEN_DEFINE_LOG_CATEGORY_STATIC(LogCacheActivity, "z$"); - -static constinit std::string_view UE4DDCNamespaceName = "ue4.ddc"; - -ZenCacheStore::ZenCacheStore(GcManager& Gc, - JobQueue& JobQueue, - const std::filesystem::path& BasePath, - const Configuration& Configuration, - const DiskWriteBlocker* InDiskWriteBlocker) -: m_DiskWriteBlocker(InDiskWriteBlocker) -, m_Gc(Gc) -, m_JobQueue(JobQueue) -, m_BasePath(BasePath) -, m_Configuration(Configuration) -, m_ExitLogging(false) -{ - SetLoggingConfig(m_Configuration.Logging); - CreateDirectories(m_BasePath); - - ZEN_INFO("initializing cache store at '{}'", m_BasePath); - - DirectoryContent DirContent; - GetDirectoryContent(m_BasePath, DirectoryContent::IncludeDirsFlag, DirContent); - - std::vector<std::string> Namespaces; - for (const std::filesystem::path& DirPath : DirContent.Directories) - { - std::string DirName = PathToUtf8(DirPath.filename()); - if (DirName.starts_with(NamespaceDiskPrefix)) - { - Namespaces.push_back(DirName.substr(NamespaceDiskPrefix.length())); - continue; - } - } - - ZEN_INFO("Found {} namespaces in '{}'", Namespaces.size(), m_BasePath); - - if (std::find(Namespaces.begin(), Namespaces.end(), UE4DDCNamespaceName) == Namespaces.end()) - { - // default (unspecified) and ue4-ddc namespace points to the same namespace instance - - std::filesystem::path DefaultNamespaceFolder = m_BasePath / fmt::format("{}{}", NamespaceDiskPrefix, UE4DDCNamespaceName); - CreateDirectories(DefaultNamespaceFolder); - Namespaces.push_back(std::string(UE4DDCNamespaceName)); - } - - for (const std::string& NamespaceName : Namespaces) - { - m_Namespaces[NamespaceName] = - std::make_unique<ZenCacheNamespace>(Gc, - m_JobQueue, - m_BasePath / fmt::format("{}{}", NamespaceDiskPrefix, NamespaceName), - m_Configuration.NamespaceConfig); - } -} - -ZenCacheStore::~ZenCacheStore() -{ - ZEN_INFO("closing cache store at '{}'", m_BasePath); - SetLoggingConfig({.EnableWriteLog = false, .EnableAccessLog = false}); - m_Namespaces.clear(); -} - -void -ZenCacheStore::LogWorker() -{ - SetCurrentThreadName("ZenCacheStore::LogWorker"); - - LoggerRef ZCacheLog(logging::Get("z$")); - - auto Log = [&ZCacheLog]() -> LoggerRef { return ZCacheLog; }; - - std::vector<AccessLogItem> Items; - while (true) - { - try - { - m_LogQueueLock.WithExclusiveLock([this, &Items]() { Items.swap(m_LogQueue); }); - if (m_DiskWriteBlocker == nullptr || m_DiskWriteBlocker->AreDiskWritesAllowed()) - { - for (const auto& Item : Items) - { - if (Item.Value.Value) - { - const bool IsCbObject = Item.Value.Value.GetContentType() == ZenContentType::kCbObject; - - try - { - const IoHash ObjectHash = IsCbObject ? IoHash::HashBuffer(Item.Value.Value.GetView()) : Item.Value.RawHash; - const size_t ObjectSize = IsCbObject ? Item.Value.Value.GetSize() : Item.Value.RawSize; - - ZEN_LOG_INFO(LogCacheActivity, - "{} [{}] {}/{}/{} -> {} {} {}", - Item.Op, - Item.Context, - Item.Namespace, - Item.Bucket, - Item.HashKey, - ObjectHash, - ObjectSize, - ToString(Item.Value.Value.GetContentType())) - } - catch (std::exception& Ex) - { - ZEN_LOG_INFO(LogCacheActivity, - "{} [{}] {}/{}/{} failed: Reason: '{}'", - Item.Op, - Item.Context, - Item.Namespace, - Item.Bucket, - Item.HashKey, - Ex.what()) - } - } - else - { - ZEN_LOG_INFO(LogCacheActivity, - "{} [{}] {}/{}/{}", - Item.Op, - Item.Context, - Item.Namespace, - Item.Bucket, - Item.HashKey); - } - } - } - if (!Items.empty()) - { - Items.resize(0); - continue; - } - if (m_ExitLogging) - { - break; - } - m_LogEvent.Wait(); - m_LogEvent.Reset(); - } - catch (std::exception& Ex) - { - ZEN_WARN("Log writer failed: '{}'", Ex.what()); - } - } -} - -bool -ZenCacheStore::Get(const CacheRequestContext& Context, - std::string_view Namespace, - std::string_view Bucket, - const IoHash& HashKey, - ZenCacheValue& OutValue) -{ - // Ad hoc rejection of known bad usage patterns for DDC bucket names - - if (IsKnownBadBucketName(Bucket)) - { - m_RejectedReadCount++; - return false; - } - - ZEN_TRACE_CPU("Z$::Get"); - - metrics::RequestStats::Scope OpScope(m_GetOps, 0); - - if (ZenCacheNamespace* Store = GetNamespace(Namespace); Store) - { - bool Result = Store->Get(Bucket, HashKey, OutValue); - - if (m_AccessLogEnabled) - { - ZEN_TRACE_CPU("Z$::Get::AccessLog"); - bool Signal = false; - m_LogQueueLock.WithExclusiveLock([&]() { - Signal = m_LogQueue.empty(); - m_LogQueue.emplace_back(AccessLogItem{.Op = Result ? "GET HIT " : "GET MISS", - .Context = Context, - .Namespace = std::string(Namespace), - .Bucket = std::string(Bucket), - .HashKey = HashKey, - .Value = OutValue /*, - .Result = Result*/}); - }); - if (Signal) - { - m_LogEvent.Set(); - } - } - if (Result) - { - m_HitCount++; - OpScope.SetBytes(OutValue.Value.GetSize()); - return true; - } - - m_MissCount++; - return false; - } - ZEN_WARN("request for unknown namespace '{}' in ZenCacheStore::Get [{}], bucket '{}', key '{}'", - Context, - Namespace, - Bucket, - HashKey.ToHexString()); - - m_MissCount++; - return false; -} - -void -ZenCacheStore::Put(const CacheRequestContext& Context, - std::string_view Namespace, - std::string_view Bucket, - const IoHash& HashKey, - const ZenCacheValue& Value, - std::span<IoHash> References) -{ - // Ad hoc rejection of known bad usage patterns for DDC bucket names - - if (IsKnownBadBucketName(Bucket)) - { - m_RejectedWriteCount++; - return; - } - - ZEN_TRACE_CPU("Z$::Put"); - - metrics::RequestStats::Scope $(m_PutOps, Value.Value.GetSize()); - - if (m_WriteLogEnabled) - { - ZEN_TRACE_CPU("Z$::Get::WriteLog"); - bool Signal = false; - m_LogQueueLock.WithExclusiveLock([&]() { - Signal = m_LogQueue.empty(); - m_LogQueue.emplace_back(AccessLogItem{.Op = "PUT ", - .Context = Context, - .Namespace = std::string(Namespace), - .Bucket = std::string(Bucket), - .HashKey = HashKey, - .Value = Value /*, - .Result = true*/}); - }); - if (Signal) - { - m_LogEvent.Set(); - } - } - - if (ZenCacheNamespace* Store = GetNamespace(Namespace); Store) - { - Store->Put(Bucket, HashKey, Value, References); - m_WriteCount++; - return; - } - - ZEN_WARN("request for unknown namespace '{}' in ZenCacheStore::Put [{}] bucket '{}', key '{}'", - Context, - Namespace, - Bucket, - HashKey.ToHexString()); -} - -bool -ZenCacheStore::DropBucket(std::string_view Namespace, std::string_view Bucket) -{ - if (ZenCacheNamespace* Store = GetNamespace(Namespace); Store) - { - return Store->DropBucket(Bucket); - } - ZEN_WARN("request for unknown namespace '{}' in ZenCacheStore::DropBucket, bucket '{}'", Namespace, Bucket); - return false; -} - -bool -ZenCacheStore::DropNamespace(std::string_view InNamespace) -{ - RwLock::SharedLockScope _(m_NamespacesLock); - if (auto It = m_Namespaces.find(std::string(InNamespace)); It != m_Namespaces.end()) - { - ZenCacheNamespace& Namespace = *It->second; - m_DroppedNamespaces.push_back(std::move(It->second)); - m_Namespaces.erase(It); - return Namespace.Drop(); - } - ZEN_WARN("request for unknown namespace '{}' in ZenCacheStore::DropNamespace", InNamespace); - return false; -} - -void -ZenCacheStore::Flush() -{ - ZEN_INFO("flushing cache store at '{}'", m_BasePath); - IterateNamespaces([&](std::string_view, ZenCacheNamespace& Store) { Store.Flush(); }); -} - -void -ZenCacheStore::ScrubStorage(ScrubContext& Ctx) -{ - IterateNamespaces([&](std::string_view, ZenCacheNamespace& Store) { Store.ScrubStorage(Ctx); }); -} - -CacheValueDetails -ZenCacheStore::GetValueDetails(const std::string_view NamespaceFilter, - const std::string_view BucketFilter, - const std::string_view ValueFilter) const -{ - CacheValueDetails Details; - if (NamespaceFilter.empty()) - { - IterateNamespaces([&](std::string_view Namespace, ZenCacheNamespace& Store) { - Details.Namespaces[std::string(Namespace)] = Store.GetValueDetails(BucketFilter, ValueFilter); - }); - } - else if (const ZenCacheNamespace* Store = FindNamespace(NamespaceFilter); Store != nullptr) - { - Details.Namespaces[std::string(NamespaceFilter)] = Store->GetValueDetails(BucketFilter, ValueFilter); - } - return Details; -} - -void -ZenCacheStore::EnumerateBucketContents(std::string_view Namespace, - std::string_view Bucket, - std::function<void(const IoHash& Key, const CacheValueDetails::ValueDetails& Details)>&& Fn) -{ - if (const ZenCacheNamespace* Ns = FindNamespace(Namespace)) - { - Ns->EnumerateBucketContents(Bucket, Fn); - } -} - -ZenCacheNamespace* -ZenCacheStore::GetNamespace(std::string_view Namespace) -{ - RwLock::SharedLockScope _(m_NamespacesLock); - if (auto It = m_Namespaces.find(std::string(Namespace)); It != m_Namespaces.end()) - { - return It->second.get(); - } - if (Namespace == DefaultNamespace) - { - if (auto It = m_Namespaces.find(std::string(UE4DDCNamespaceName)); It != m_Namespaces.end()) - { - return It->second.get(); - } - } - _.ReleaseNow(); - - if (!m_Configuration.AllowAutomaticCreationOfNamespaces) - { - return nullptr; - } - - RwLock::ExclusiveLockScope __(m_NamespacesLock); - if (auto It = m_Namespaces.find(std::string(Namespace)); It != m_Namespaces.end()) - { - return It->second.get(); - } - - auto NewNamespace = - m_Namespaces.insert_or_assign(std::string(Namespace), - std::make_unique<ZenCacheNamespace>(m_Gc, - m_JobQueue, - m_BasePath / fmt::format("{}{}", NamespaceDiskPrefix, Namespace), - m_Configuration.NamespaceConfig)); - return NewNamespace.first->second.get(); -} - -const ZenCacheNamespace* -ZenCacheStore::FindNamespace(std::string_view Namespace) const -{ - RwLock::SharedLockScope _(m_NamespacesLock); - if (auto It = m_Namespaces.find(std::string(Namespace)); It != m_Namespaces.end()) - { - return It->second.get(); - } - if (Namespace == DefaultNamespace) - { - if (auto It = m_Namespaces.find(std::string(UE4DDCNamespaceName)); It != m_Namespaces.end()) - { - return It->second.get(); - } - } - return nullptr; -} - -std::vector<std::string> -ZenCacheStore::GetNamespaces() -{ - std::vector<std::string> Namespaces; - - IterateNamespaces([&](std::string_view Namespace, ZenCacheNamespace&) { Namespaces.push_back(std::string(Namespace)); }); - - return Namespaces; -} - -void -ZenCacheStore::IterateNamespaces(const std::function<void(std::string_view Namespace, ZenCacheNamespace& Store)>& Callback) const -{ - std::vector<std::pair<std::string, ZenCacheNamespace&>> Namespaces; - { - RwLock::SharedLockScope _(m_NamespacesLock); - Namespaces.reserve(m_Namespaces.size()); - for (const auto& Entry : m_Namespaces) - { - if (Entry.first == DefaultNamespace) - { - continue; - } - Namespaces.push_back({Entry.first, *Entry.second}); - } - } - for (auto& Entry : Namespaces) - { - Callback(Entry.first, Entry.second); - } -} - -GcStorageSize -ZenCacheStore::StorageSize() const -{ - GcStorageSize Size; - IterateNamespaces([&](std::string_view, ZenCacheNamespace& Store) { - GcStorageSize StoreSize = Store.StorageSize(); - Size.MemorySize += StoreSize.MemorySize; - Size.DiskSize += StoreSize.DiskSize; - }); - return Size; -} - -ZenCacheStore::CacheStoreStats -ZenCacheStore::Stats(bool IncludeNamespaceStats) -{ - ZenCacheStore::CacheStoreStats Result{.HitCount = m_HitCount, - .MissCount = m_MissCount, - .WriteCount = m_WriteCount, - .RejectedWriteCount = m_RejectedWriteCount, - .RejectedReadCount = m_RejectedReadCount, - .PutOps = m_PutOps.Snapshot(), - .GetOps = m_GetOps.Snapshot()}; - - if (IncludeNamespaceStats) - { - IterateNamespaces([&](std::string_view NamespaceName, ZenCacheNamespace& Store) { - Result.NamespaceStats.emplace_back(NamedNamespaceStats{.NamespaceName = std::string(NamespaceName), .Stats = Store.Stats()}); - }); - } - - return Result; -} - -void -ZenCacheStore::ReportMetrics(StatsMetrics& Statsd) -{ - const bool IncludeNamespaceStats = false; - const CacheStoreStats Now = Stats(IncludeNamespaceStats); - const CacheStoreStats& Old = m_LastReportedMetrics; - - Statsd.Meter("zen.cache_hits", Now.HitCount - Old.HitCount); - Statsd.Meter("zen.cache_misses", Now.MissCount - Old.MissCount); - Statsd.Meter("zen.cache_writes", Now.WriteCount - Old.WriteCount); - - m_LastReportedMetrics = Now; -} - -void -ZenCacheStore::SetLoggingConfig(const Configuration::LogConfig& Loggingconfig) -{ - if (!Loggingconfig.EnableAccessLog && !Loggingconfig.EnableWriteLog) - { - m_AccessLogEnabled.store(false); - m_WriteLogEnabled.store(false); - m_ExitLogging.store(true); - m_LogEvent.Set(); - if (m_AsyncLoggingThread.joinable()) - { - m_AsyncLoggingThread.join(); - } - m_Configuration.Logging = Loggingconfig; - return; - } - if (!m_AccessLogEnabled.load() && !m_WriteLogEnabled.load()) - { - m_AsyncLoggingThread = std::thread(&ZenCacheStore::LogWorker, this); - } - m_WriteLogEnabled.store(Loggingconfig.EnableWriteLog); - m_AccessLogEnabled.store(Loggingconfig.EnableAccessLog); - m_Configuration.Logging = Loggingconfig; -} - -ZenCacheStore::Info -ZenCacheStore::GetInfo() const -{ - ZenCacheStore::Info Info = {.Config = m_Configuration, .StorageSize = StorageSize()}; - - IterateNamespaces([&Info](std::string_view NamespaceName, ZenCacheNamespace& Namespace) { - Info.NamespaceNames.push_back(std::string(NamespaceName)); - ZenCacheNamespace::Info NamespaceInfo = Namespace.GetInfo(); - Info.DiskEntryCount += NamespaceInfo.DiskLayerInfo.EntryCount; - }); - - return Info; -} - -std::optional<ZenCacheNamespace::Info> -ZenCacheStore::GetNamespaceInfo(std::string_view NamespaceName) -{ - if (const ZenCacheNamespace* Namespace = FindNamespace(NamespaceName); Namespace) - { - return Namespace->GetInfo(); - } - return {}; -} - -std::optional<ZenCacheNamespace::BucketInfo> -ZenCacheStore::GetBucketInfo(std::string_view NamespaceName, std::string_view BucketName) -{ - if (const ZenCacheNamespace* Namespace = FindNamespace(NamespaceName); Namespace) - { - return Namespace->GetBucketInfo(BucketName); - } - return {}; -} - -////////////////////////////////////////////////////////////////////////// - -#if ZEN_WITH_TESTS - -using namespace std::literals; - -namespace testutils { - IoHash CreateKey(size_t KeyValue) { return IoHash::HashBuffer(&KeyValue, sizeof(size_t)); } - - IoHash ToIoHash(const Oid& Id) - { - char OIdString[24 + 1]; - Id.ToString(OIdString); - IoHash Key = IoHash::HashBuffer(OIdString, 24); - return Key; - } - - std::pair<Oid, IoBuffer> CreateBinaryBlob(size_t Size) { return {Oid::NewOid(), CreateRandomBlob(Size)}; } - - std::vector<std::pair<Oid, CompressedBuffer>> CreateCompressedAttachment(CidStore& Store, const std::span<const size_t>& Sizes) - { - std::vector<std::pair<Oid, CompressedBuffer>> Result; - Result.reserve(Sizes.size()); - for (size_t Size : Sizes) - { - auto Blob = CreateBinaryBlob(Size); - CompressedBuffer Compressed = CompressedBuffer::Compress(SharedBuffer::MakeView(Blob.second.Data(), Blob.second.Size())); - CHECK(!Store.ContainsChunk(Compressed.DecodeRawHash())); - Result.emplace_back(std::pair<Oid, CompressedBuffer>(Blob.first, Compressed)); - } - return Result; - } - - std::pair<IoHash, IoBuffer> CreateRecord(std::span<std::pair<Oid, CompressedBuffer>> Attachments) - { - Oid Id = Oid::NewOid(); - IoHash Key = ToIoHash(Id); - CbObjectWriter Record; - Record << "Key"sv << Id; - - for (size_t Idx = 0; auto& Cid : Attachments) - { - Record.AddBinaryAttachment(fmt::format("attachment-{}", Idx++), Cid.second.DecodeRawHash()); - } - - IoBuffer Buffer = Record.Save().GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - return {Key, Buffer}; - } - - struct FalseType - { - static const bool Enabled = false; - }; - struct TrueType - { - static const bool Enabled = true; - }; - -} // namespace testutils - -TEST_CASE_TEMPLATE("z$.store", ReferenceCaching, testutils::FalseType, testutils::TrueType) -{ - ScopedTemporaryDirectory TempDir; - - GcManager Gc; - - auto JobQueue = MakeJobQueue(1, "testqueue"); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - const int kIterationCount = 100; - - for (int i = 0; i < kIterationCount; ++i) - { - const IoHash Key = IoHash::HashBuffer(&i, sizeof i); - - CbObjectWriter Cbo; - Cbo << "hey" << i; - CbObject Obj = Cbo.Save(); - - ZenCacheValue Value; - Value.Value = Obj.GetBuffer().AsIoBuffer(); - Value.Value.SetContentType(ZenContentType::kCbObject); - - Zcs.Put("test_bucket"sv, Key, Value, {}); - } - - for (int i = 0; i < kIterationCount; ++i) - { - const IoHash Key = IoHash::HashBuffer(&i, sizeof i); - - ZenCacheValue Value; - Zcs.Get("test_bucket"sv, Key, /* out */ Value); - - REQUIRE(Value.Value); - CHECK(Value.Value.GetContentType() == ZenContentType::kCbObject); - CHECK_EQ(ValidateCompactBinary(Value.Value, CbValidateMode::All), CbValidateError::None); - CbObject Obj = LoadCompactBinaryObject(Value.Value); - CHECK_EQ(Obj["hey"].AsInt32(), i); - } -} - -TEST_CASE_TEMPLATE("z$.size", ReferenceCaching, testutils::FalseType, testutils::TrueType) -{ - auto JobQueue = MakeJobQueue(1, "testqueue"); - - const auto CreateCacheValue = [](size_t Size) -> CbObject { - std::vector<uint8_t> Buf; - Buf.resize(Size); - - CbObjectWriter Writer; - Writer.AddBinary("Binary"sv, Buf.data(), Buf.size()); - return Writer.Save(); - }; - - SUBCASE("mem/disklayer") - { - const size_t Count = 16; - ScopedTemporaryDirectory TempDir; - - GcStorageSize CacheSize; - - { - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - CbObject CacheValue = CreateCacheValue(Zcs.GetConfig().DiskLayerConfig.BucketConfig.MemCacheSizeThreshold - 256); - - IoBuffer Buffer = CacheValue.GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - std::vector<std::pair<std::string, IoHash>> Keys; - - for (size_t Key = 0; Key < Count; ++Key) - { - const size_t Bucket = Key % 4; - std::string BucketName = fmt::format("test_bucket-{}", Bucket); - IoHash Hash = IoHash::HashBuffer(&Key, sizeof(uint32_t)); - Zcs.Put(BucketName, Hash, ZenCacheValue{.Value = Buffer}, {}); - Keys.push_back({BucketName, Hash}); - } - CacheSize = Zcs.StorageSize(); - CHECK_LE(CacheValue.GetSize() * Count, CacheSize.DiskSize); - CHECK_EQ(0, CacheSize.MemorySize); - - for (const auto& Key : Keys) - { - ZenCacheValue _; - Zcs.Get(Key.first, Key.second, _); - } - - CacheSize = Zcs.StorageSize(); - CHECK_LE(CacheValue.GetSize() * Count, CacheSize.DiskSize); - CHECK_LE(CacheValue.GetSize() * Count, CacheSize.MemorySize); - } - - { - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - const GcStorageSize SerializedSize = Zcs.StorageSize(); - CHECK_EQ(SerializedSize.MemorySize, 0); - CHECK_LE(SerializedSize.DiskSize, CacheSize.DiskSize); - - for (size_t Bucket = 0; Bucket < 4; ++Bucket) - { - Zcs.DropBucket(fmt::format("test_bucket-{}", Bucket)); - } - CHECK_EQ(0, Zcs.StorageSize().DiskSize); - CHECK_EQ(0, Zcs.StorageSize().MemorySize); - } - } - - SUBCASE("disklayer") - { - const size_t Count = 16; - ScopedTemporaryDirectory TempDir; - - GcStorageSize CacheSize; - - { - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - CbObject CacheValue = CreateCacheValue(Zcs.GetConfig().DiskLayerConfig.BucketConfig.MemCacheSizeThreshold + 64); - - IoBuffer Buffer = CacheValue.GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - for (size_t Key = 0; Key < Count; ++Key) - { - const size_t Bucket = Key % 4; - Zcs.Put(fmt::format("test_bucket-{}", Bucket), IoHash::HashBuffer(&Key, sizeof(uint32_t)), {.Value = Buffer}, {}); - } - - CacheSize = Zcs.StorageSize(); - CHECK_LE(CacheValue.GetSize() * Count, CacheSize.DiskSize); - CHECK_EQ(0, CacheSize.MemorySize); - } - - { - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - const GcStorageSize SerializedSize = Zcs.StorageSize(); - CHECK_EQ(SerializedSize.MemorySize, 0); - CHECK_LE(SerializedSize.DiskSize, CacheSize.DiskSize); - - for (size_t Bucket = 0; Bucket < 4; ++Bucket) - { - Zcs.DropBucket(fmt::format("test_bucket-{}", Bucket)); - } - CHECK_EQ(0, Zcs.StorageSize().DiskSize); - } - } -} - -TEST_CASE_TEMPLATE("z$.gc", ReferenceCaching, testutils::FalseType, testutils::TrueType) -{ - using namespace testutils; - - auto JobQueue = MakeJobQueue(1, "testqueue"); - - SUBCASE("gather references does NOT add references for expired cache entries") - { - ScopedTemporaryDirectory TempDir; - std::vector<IoHash> Cids{CreateKey(1), CreateKey(2), CreateKey(3)}; - - const auto CollectAndFilter = [](GcManager& Gc, - GcClock::TimePoint Time, - GcClock::Duration MaxDuration, - std::span<const IoHash> Cids, - std::vector<IoHash>& OutKeep) { - GcContext GcCtx(Time - MaxDuration, Time - MaxDuration); - Gc.CollectGarbage(GcCtx); - OutKeep.clear(); - GcCtx.FilterCids(Cids, [&OutKeep](const IoHash& Hash) { OutKeep.push_back(Hash); }); - }; - - { - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - const auto Bucket = "teardrinker"sv; - - // Create a cache record - const IoHash Key = CreateKey(42); - CbObjectWriter Record; - Record << "Key"sv - << "SomeRecord"sv; - - for (size_t Idx = 0; auto& Cid : Cids) - { - Record.AddBinaryAttachment(fmt::format("attachment-{}", Idx++), Cid); - } - - IoBuffer Buffer = Record.Save().GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - Zcs.Put(Bucket, Key, {.Value = Buffer}, Cids); - - std::vector<IoHash> Keep; - - // Collect garbage with 1 hour max cache duration - { - CollectAndFilter(Gc, GcClock::Now(), std::chrono::hours(1), Cids, Keep); - CHECK_EQ(Cids.size(), Keep.size()); - } - - // Move forward in time - { - CollectAndFilter(Gc, GcClock::Now() + std::chrono::hours(2), std::chrono::hours(1), Cids, Keep); - CHECK_EQ(0, Keep.size()); - } - } - - // Expect timestamps to be serialized - { - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - std::vector<IoHash> Keep; - - // Collect garbage with 1 hour max cache duration - { - CollectAndFilter(Gc, GcClock::Now(), std::chrono::hours(1), Cids, Keep); - CHECK_EQ(3, Keep.size()); - } - - // Move forward in time - { - CollectAndFilter(Gc, GcClock::Now() + std::chrono::hours(2), std::chrono::hours(1), Cids, Keep); - CHECK_EQ(0, Keep.size()); - } - } - } - - SUBCASE("gc removes standalone values") - { - ScopedTemporaryDirectory TempDir; - GcManager Gc; - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - const auto Bucket = "fortysixandtwo"sv; - const GcClock::TimePoint CurrentTime = GcClock::Now(); - - std::vector<IoHash> Keys{CreateKey(1), CreateKey(2), CreateKey(3)}; - - for (const auto& Key : Keys) - { - IoBuffer Value = CreateRandomBlob(128 << 10); - Zcs.Put(Bucket, Key, {.Value = Value}, {}); - } - - { - GcContext GcCtx(CurrentTime - std::chrono::hours(46), CurrentTime - std::chrono::hours(46)); - - Gc.CollectGarbage(GcCtx); - - for (const auto& Key : Keys) - { - ZenCacheValue CacheValue; - const bool Exists = Zcs.Get(Bucket, Key, CacheValue); - CHECK(Exists); - } - } - - // Move forward in time and collect again - { - GcContext GcCtx(CurrentTime + std::chrono::minutes(2), CurrentTime + std::chrono::minutes(2)); - Gc.CollectGarbage(GcCtx); - - for (const auto& Key : Keys) - { - ZenCacheValue CacheValue; - const bool Exists = Zcs.Get(Bucket, Key, CacheValue); - CHECK(!Exists); - } - - CHECK_EQ(0, Zcs.StorageSize().DiskSize); - } - } - - SUBCASE("gc removes small objects") - { - ScopedTemporaryDirectory TempDir; - GcManager Gc; - { - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - const auto Bucket = "rightintwo"sv; - - std::vector<IoHash> Keys{CreateKey(1), CreateKey(2), CreateKey(3)}; - - for (const auto& Key : Keys) - { - IoBuffer Value = CreateRandomBlob(128); - Zcs.Put(Bucket, Key, {.Value = Value}, {}); - } - - { - GcContext GcCtx(GcClock::Now() - std::chrono::hours(2), GcClock::Now() - std::chrono::hours(2)); - GcCtx.CollectSmallObjects(true); - - Gc.CollectGarbage(GcCtx); - - for (const auto& Key : Keys) - { - ZenCacheValue CacheValue; - const bool Exists = Zcs.Get(Bucket, Key, CacheValue); - CHECK(Exists); - } - } - - // Move forward in time and collect again - { - GcContext GcCtx(GcClock::Now() + std::chrono::minutes(2), GcClock::Now() + std::chrono::minutes(2)); - GcCtx.CollectSmallObjects(true); - - Zcs.Flush(); - Gc.CollectGarbage(GcCtx); - - for (const auto& Key : Keys) - { - ZenCacheValue CacheValue; - const bool Exists = Zcs.Get(Bucket, Key, CacheValue); - CHECK(!Exists); - } - // GC could not remove the currently written block so size will not be zero - CHECK_NE(0, Zcs.StorageSize().DiskSize); - } - } - { - // Unreferenced blocks will be pruned so size should now be zero - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(0, Zcs.StorageSize().DiskSize); - } - } -} - -TEST_CASE_TEMPLATE("z$.threadedinsert", ReferenceCaching, testutils::FalseType, testutils::TrueType) // * doctest::skip(true)) -{ - // for (uint32_t i = 0; i < 100; ++i) - { - ScopedTemporaryDirectory TempDir; - - const uint64_t kChunkSize = 1048; - const int32_t kChunkCount = 8192; - - struct Chunk - { - std::string Bucket; - IoBuffer Buffer; - }; - std::unordered_map<IoHash, Chunk, IoHash::Hasher> Chunks; - Chunks.reserve(kChunkCount); - - const std::string Bucket1 = "rightinone"; - const std::string Bucket2 = "rightintwo"; - - for (int32_t Idx = 0; Idx < kChunkCount; ++Idx) - { - while (true) - { - IoBuffer Chunk = CreateRandomBlob(kChunkSize); - IoHash Hash = HashBuffer(Chunk); - if (Chunks.contains(Hash)) - { - continue; - } - Chunks[Hash] = {.Bucket = Bucket1, .Buffer = Chunk}; - break; - } - while (true) - { - IoBuffer Chunk = CreateRandomBlob(kChunkSize); - IoHash Hash = HashBuffer(Chunk); - if (Chunks.contains(Hash)) - { - continue; - } - Chunks[Hash] = {.Bucket = Bucket2, .Buffer = Chunk}; - break; - } - } - - CreateDirectories(TempDir.Path()); - - WorkerThreadPool ThreadPool(4); - GcManager Gc; - auto JobQueue = MakeJobQueue(1, "testqueue"); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path(), - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - { - std::atomic<size_t> WorkCompleted = 0; - for (const auto& Chunk : Chunks) - { - ThreadPool.ScheduleWork([&Zcs, &WorkCompleted, &Chunk]() { - Zcs.Put(Chunk.second.Bucket, Chunk.first, {.Value = Chunk.second.Buffer}, {}); - WorkCompleted.fetch_add(1); - }); - } - while (WorkCompleted < Chunks.size()) - { - Sleep(1); - } - } - - const uint64_t TotalSize = Zcs.StorageSize().DiskSize; - CHECK_LE(kChunkSize * Chunks.size(), TotalSize); - - { - std::atomic<size_t> WorkCompleted = 0; - for (const auto& Chunk : Chunks) - { - ThreadPool.ScheduleWork([&Zcs, &WorkCompleted, &Chunk]() { - std::string Bucket = Chunk.second.Bucket; - IoHash ChunkHash = Chunk.first; - ZenCacheValue CacheValue; - - CHECK(Zcs.Get(Bucket, ChunkHash, CacheValue)); - IoHash Hash = IoHash::HashBuffer(CacheValue.Value); - CHECK(ChunkHash == Hash); - WorkCompleted.fetch_add(1); - }); - } - while (WorkCompleted < Chunks.size()) - { - Sleep(1); - } - } - std::unordered_map<IoHash, std::string, IoHash::Hasher> GcChunkHashes; - GcChunkHashes.reserve(Chunks.size()); - for (const auto& Chunk : Chunks) - { - GcChunkHashes[Chunk.first] = Chunk.second.Bucket; - } - { - std::unordered_map<IoHash, Chunk, IoHash::Hasher> NewChunks; - - for (int32_t Idx = 0; Idx < kChunkCount; ++Idx) - { - { - IoBuffer Chunk = CreateRandomBlob(kChunkSize); - IoHash Hash = HashBuffer(Chunk); - NewChunks[Hash] = {.Bucket = Bucket1, .Buffer = Chunk}; - } - { - IoBuffer Chunk = CreateRandomBlob(kChunkSize); - IoHash Hash = HashBuffer(Chunk); - NewChunks[Hash] = {.Bucket = Bucket2, .Buffer = Chunk}; - } - } - - std::atomic<size_t> WorkCompleted = 0; - std::atomic_uint32_t AddedChunkCount = 0; - for (const auto& Chunk : NewChunks) - { - ThreadPool.ScheduleWork([&Zcs, &WorkCompleted, Chunk, &AddedChunkCount]() { - Zcs.Put(Chunk.second.Bucket, Chunk.first, {.Value = Chunk.second.Buffer}, {}); - AddedChunkCount.fetch_add(1); - WorkCompleted.fetch_add(1); - }); - } - - for (const auto& Chunk : Chunks) - { - ThreadPool.ScheduleWork([&Zcs, &WorkCompleted, Chunk]() { - ZenCacheValue CacheValue; - if (Zcs.Get(Chunk.second.Bucket, Chunk.first, CacheValue)) - { - CHECK(Chunk.first == IoHash::HashBuffer(CacheValue.Value)); - } - WorkCompleted.fetch_add(1); - }); - } - while (AddedChunkCount.load() < NewChunks.size()) - { - // Need to be careful since we might GC blocks we don't know outside of RwLock::ExclusiveLockScope - for (const auto& Chunk : NewChunks) - { - ZenCacheValue CacheValue; - if (Zcs.Get(Chunk.second.Bucket, Chunk.first, CacheValue)) - { - GcChunkHashes[Chunk.first] = Chunk.second.Bucket; - } - } - std::vector<IoHash> KeepHashes; - KeepHashes.reserve(GcChunkHashes.size()); - for (const auto& Entry : GcChunkHashes) - { - KeepHashes.push_back(Entry.first); - } - size_t C = 0; - while (C < KeepHashes.size()) - { - if (C % 155 == 0) - { - if (C < KeepHashes.size() - 1) - { - KeepHashes[C] = KeepHashes[KeepHashes.size() - 1]; - KeepHashes.pop_back(); - } - if (C + 3 < KeepHashes.size() - 1) - { - KeepHashes[C + 3] = KeepHashes[KeepHashes.size() - 1]; - KeepHashes.pop_back(); - } - } - C++; - } - - GcContext GcCtx(GcClock::Now() - std::chrono::hours(24), GcClock::Now() - std::chrono::hours(24)); - GcCtx.CollectSmallObjects(true); - GcCtx.AddRetainedCids(KeepHashes); - Zcs.CollectGarbage(GcCtx); - const HashKeySet& Deleted = GcCtx.DeletedCids(); - Deleted.IterateHashes([&GcChunkHashes](const IoHash& ChunkHash) { GcChunkHashes.erase(ChunkHash); }); - } - - while (WorkCompleted < NewChunks.size() + Chunks.size()) - { - Sleep(1); - } - - { - // Need to be careful since we might GC blocks we don't know outside of RwLock::ExclusiveLockScope - for (const auto& Chunk : NewChunks) - { - ZenCacheValue CacheValue; - if (Zcs.Get(Chunk.second.Bucket, Chunk.first, CacheValue)) - { - GcChunkHashes[Chunk.first] = Chunk.second.Bucket; - } - } - std::vector<IoHash> KeepHashes; - KeepHashes.reserve(GcChunkHashes.size()); - for (const auto& Entry : GcChunkHashes) - { - KeepHashes.push_back(Entry.first); - } - size_t C = 0; - while (C < KeepHashes.size()) - { - if (C % 155 == 0) - { - if (C < KeepHashes.size() - 1) - { - KeepHashes[C] = KeepHashes[KeepHashes.size() - 1]; - KeepHashes.pop_back(); - } - if (C + 3 < KeepHashes.size() - 1) - { - KeepHashes[C + 3] = KeepHashes[KeepHashes.size() - 1]; - KeepHashes.pop_back(); - } - } - C++; - } - - GcContext GcCtx(GcClock::Now() - std::chrono::hours(24), GcClock::Now() - std::chrono::hours(24)); - GcCtx.CollectSmallObjects(true); - GcCtx.AddRetainedCids(KeepHashes); - Zcs.CollectGarbage(GcCtx); - const HashKeySet& Deleted = GcCtx.DeletedCids(); - Deleted.IterateHashes([&GcChunkHashes](const IoHash& ChunkHash) { GcChunkHashes.erase(ChunkHash); }); - } - } - { - std::atomic<size_t> WorkCompleted = 0; - for (const auto& Chunk : GcChunkHashes) - { - ThreadPool.ScheduleWork([&Zcs, &WorkCompleted, Chunk]() { - ZenCacheValue CacheValue; - CHECK(Zcs.Get(Chunk.second, Chunk.first, CacheValue)); - CHECK(Chunk.first == IoHash::HashBuffer(CacheValue.Value)); - WorkCompleted.fetch_add(1); - }); - } - while (WorkCompleted < GcChunkHashes.size()) - { - Sleep(1); - } - } - } -} - -TEST_CASE("z$.namespaces") -{ - using namespace testutils; - - const auto CreateCacheValue = [](size_t Size) -> CbObject { - std::vector<uint8_t> Buf; - Buf.resize(Size); - - CbObjectWriter Writer; - Writer.AddBinary("Binary"sv, Buf.data(), Buf.size()); - return Writer.Save(); - }; - - auto JobQueue = MakeJobQueue(1, "testqueue"); - - ScopedTemporaryDirectory TempDir; - CreateDirectories(TempDir.Path()); - - const CacheRequestContext Context; - IoHash Key1; - IoHash Key2; - { - GcManager Gc; - ZenCacheStore Zcs(Gc, *JobQueue, TempDir.Path() / "cache", {.AllowAutomaticCreationOfNamespaces = false}, nullptr); - const auto Bucket = "teardrinker"sv; - const auto CustomNamespace = "mynamespace"sv; - - // Create a cache record - Key1 = CreateKey(42); - CbObject CacheValue = CreateCacheValue(4096); - - IoBuffer Buffer = CacheValue.GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - ZenCacheValue PutValue = {.Value = Buffer}; - Zcs.Put(Context, ZenCacheStore::DefaultNamespace, Bucket, Key1, PutValue, {}); - - ZenCacheValue GetValue; - CHECK(Zcs.Get(Context, ZenCacheStore::DefaultNamespace, Bucket, Key1, GetValue)); - CHECK(!Zcs.Get(Context, CustomNamespace, Bucket, Key1, GetValue)); - - // This should just be dropped as we don't allow creating of namespaces on the fly - Zcs.Put(Context, CustomNamespace, Bucket, Key1, PutValue, {}); - CHECK(!Zcs.Get(Context, CustomNamespace, Bucket, Key1, GetValue)); - } - - { - GcManager Gc; - ZenCacheStore Zcs(Gc, *JobQueue, TempDir.Path() / "cache", {.AllowAutomaticCreationOfNamespaces = true}, nullptr); - const auto Bucket = "teardrinker"sv; - const auto CustomNamespace = "mynamespace"sv; - - Key2 = CreateKey(43); - CbObject CacheValue2 = CreateCacheValue(4096); - - IoBuffer Buffer2 = CacheValue2.GetBuffer().AsIoBuffer(); - Buffer2.SetContentType(ZenContentType::kCbObject); - ZenCacheValue PutValue2 = {.Value = Buffer2}; - Zcs.Put(Context, CustomNamespace, Bucket, Key2, PutValue2, {}); - - ZenCacheValue GetValue; - CHECK(!Zcs.Get(Context, ZenCacheStore::DefaultNamespace, Bucket, Key2, GetValue)); - CHECK(Zcs.Get(Context, ZenCacheStore::DefaultNamespace, Bucket, Key1, GetValue)); - CHECK(!Zcs.Get(Context, CustomNamespace, Bucket, Key1, GetValue)); - CHECK(Zcs.Get(Context, CustomNamespace, Bucket, Key2, GetValue)); - } -} - -TEST_CASE("z$.drop.bucket") -{ - using namespace testutils; - - const auto CreateCacheValue = [](size_t Size) -> CbObject { - std::vector<uint8_t> Buf; - Buf.resize(Size); - - CbObjectWriter Writer; - Writer.AddBinary("Binary"sv, Buf.data(), Buf.size()); - return Writer.Save(); - }; - - auto JobQueue = MakeJobQueue(1, "testqueue"); - - ScopedTemporaryDirectory TempDir; - CreateDirectories(TempDir.Path()); - - const CacheRequestContext Context; - IoHash Key1; - IoHash Key2; - - auto PutValue = [&CreateCacheValue, - &Context](ZenCacheStore& Zcs, std::string_view Namespace, std::string_view Bucket, size_t KeyIndex, size_t Size) { - // Create a cache record - IoHash Key = CreateKey(KeyIndex); - CbObject CacheValue = CreateCacheValue(Size); - - IoBuffer Buffer = CacheValue.GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - ZenCacheValue PutValue = {.Value = Buffer}; - Zcs.Put(Context, Namespace, Bucket, Key, PutValue, {}); - return Key; - }; - auto GetValue = [&Context](ZenCacheStore& Zcs, std::string_view Namespace, std::string_view Bucket, const IoHash& Key) { - ZenCacheValue GetValue; - Zcs.Get(Context, Namespace, Bucket, Key, GetValue); - return GetValue; - }; - WorkerThreadPool Workers(1); - { - GcManager Gc; - ZenCacheStore Zcs(Gc, *JobQueue, TempDir.Path() / "cache", {.AllowAutomaticCreationOfNamespaces = true}, nullptr); - const auto Bucket = "teardrinker"sv; - const auto Namespace = "mynamespace"sv; - - Key1 = PutValue(Zcs, Namespace, Bucket, 42, 4096); - Key2 = PutValue(Zcs, Namespace, Bucket, 43, 2048); - - ZenCacheValue Value1 = GetValue(Zcs, Namespace, Bucket, Key1); - CHECK(Value1.Value); - - std::atomic_bool WorkComplete = false; - Workers.ScheduleWork([&]() { - zen::Sleep(100); - Value1.Value = IoBuffer{}; - WorkComplete = true; - }); - // On Windows, DropBucket() will be blocked as long as we hold a reference to a buffer in the bucket - // Our DropBucket execution blocks any incoming request from completing until we are done with the drop - CHECK(Zcs.DropBucket(Namespace, Bucket)); - while (!WorkComplete) - { - zen::Sleep(1); - } - - // Entire bucket should be dropped, but doing a request should will re-create the namespace but it must still be empty - Value1 = GetValue(Zcs, Namespace, Bucket, Key1); - CHECK(!Value1.Value); - ZenCacheValue Value2 = GetValue(Zcs, Namespace, Bucket, Key2); - CHECK(!Value2.Value); - } -} - -TEST_CASE("z$.drop.namespace") -{ - using namespace testutils; - - const CacheRequestContext Context; - - const auto CreateCacheValue = [](size_t Size) -> CbObject { - std::vector<uint8_t> Buf; - Buf.resize(Size); - - CbObjectWriter Writer; - Writer.AddBinary("Binary"sv, Buf.data(), Buf.size()); - return Writer.Save(); - }; - - auto JobQueue = MakeJobQueue(1, "testqueue"); - - ScopedTemporaryDirectory TempDir; - CreateDirectories(TempDir.Path()); - - auto PutValue = [&CreateCacheValue, - &Context](ZenCacheStore& Zcs, std::string_view Namespace, std::string_view Bucket, size_t KeyIndex, size_t Size) { - // Create a cache record - IoHash Key = CreateKey(KeyIndex); - CbObject CacheValue = CreateCacheValue(Size); - - IoBuffer Buffer = CacheValue.GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - ZenCacheValue PutValue = {.Value = Buffer}; - Zcs.Put(Context, Namespace, Bucket, Key, PutValue, {}); - return Key; - }; - auto GetValue = [&Context](ZenCacheStore& Zcs, std::string_view Namespace, std::string_view Bucket, const IoHash& Key) { - ZenCacheValue GetValue; - Zcs.Get(Context, Namespace, Bucket, Key, GetValue); - return GetValue; - }; - WorkerThreadPool Workers(1); - { - GcManager Gc; - ZenCacheStore Zcs(Gc, *JobQueue, TempDir.Path() / "cache", {.AllowAutomaticCreationOfNamespaces = true}, nullptr); - const auto Bucket1 = "teardrinker1"sv; - const auto Bucket2 = "teardrinker2"sv; - const auto Namespace1 = "mynamespace1"sv; - const auto Namespace2 = "mynamespace2"sv; - - IoHash Key1 = PutValue(Zcs, Namespace1, Bucket1, 42, 4096); - IoHash Key2 = PutValue(Zcs, Namespace1, Bucket2, 43, 2048); - IoHash Key3 = PutValue(Zcs, Namespace2, Bucket1, 44, 4096); - IoHash Key4 = PutValue(Zcs, Namespace2, Bucket2, 45, 2048); - - ZenCacheValue Value1 = GetValue(Zcs, Namespace1, Bucket1, Key1); - CHECK(Value1.Value); - ZenCacheValue Value2 = GetValue(Zcs, Namespace1, Bucket2, Key2); - CHECK(Value2.Value); - ZenCacheValue Value3 = GetValue(Zcs, Namespace2, Bucket1, Key3); - CHECK(Value3.Value); - ZenCacheValue Value4 = GetValue(Zcs, Namespace2, Bucket2, Key4); - CHECK(Value4.Value); - - std::atomic_bool WorkComplete = false; - Workers.ScheduleWork([&]() { - zen::Sleep(100); - Value1.Value = IoBuffer{}; - Value2.Value = IoBuffer{}; - Value3.Value = IoBuffer{}; - Value4.Value = IoBuffer{}; - WorkComplete = true; - }); - // On Windows, DropBucket() will be blocked as long as we hold a reference to a buffer in the bucket - // Our DropBucket execution blocks any incoming request from completing until we are done with the drop - CHECK(Zcs.DropNamespace(Namespace1)); - while (!WorkComplete) - { - zen::Sleep(1); - } - - // Entire namespace should be dropped, but doing a request should will re-create the namespace but it must still be empty - Value1 = GetValue(Zcs, Namespace1, Bucket1, Key1); - CHECK(!Value1.Value); - Value2 = GetValue(Zcs, Namespace1, Bucket2, Key2); - CHECK(!Value2.Value); - Value3 = GetValue(Zcs, Namespace2, Bucket1, Key3); - CHECK(Value3.Value); - Value4 = GetValue(Zcs, Namespace2, Bucket2, Key4); - CHECK(Value4.Value); - } -} - -TEST_CASE_TEMPLATE("z$.blocked.disklayer.put", ReferenceCaching, testutils::FalseType, testutils::TrueType) -{ - ScopedTemporaryDirectory TempDir; - - GcStorageSize CacheSize; - - const auto CreateCacheValue = [](size_t Size) -> CbObject { - std::vector<uint8_t> Buf; - Buf.resize(Size, Size & 0xff); - - CbObjectWriter Writer; - Writer.AddBinary("Binary"sv, Buf.data(), Buf.size()); - return Writer.Save(); - }; - - GcManager Gc; - auto JobQueue = MakeJobQueue(1, "testqueue"); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - CbObject CacheValue = CreateCacheValue(64 * 1024 + 64); - - IoBuffer Buffer = CacheValue.GetBuffer().AsIoBuffer(); - Buffer.SetContentType(ZenContentType::kCbObject); - - size_t Key = Buffer.Size(); - IoHash HashKey = IoHash::HashBuffer(&Key, sizeof(uint32_t)); - Zcs.Put("test_bucket", HashKey, {.Value = Buffer}, {}); - - ZenCacheValue BufferGet; - CHECK(Zcs.Get("test_bucket", HashKey, BufferGet)); - - CbObject CacheValue2 = CreateCacheValue(64 * 1024 + 64 + 1); - IoBuffer Buffer2 = CacheValue2.GetBuffer().AsIoBuffer(); - Buffer2.SetContentType(ZenContentType::kCbObject); - - // We should be able to overwrite even if the file is open for read - Zcs.Put("test_bucket", HashKey, {.Value = Buffer2}, {}); - - MemoryView OldView = BufferGet.Value.GetView(); - - ZenCacheValue BufferGet2; - CHECK(Zcs.Get("test_bucket", HashKey, BufferGet2)); - MemoryView NewView = BufferGet2.Value.GetView(); - - // Make sure file openend for read before we wrote it still have old data - CHECK(OldView.GetSize() == Buffer.GetSize()); - CHECK(memcmp(OldView.GetData(), Buffer.GetData(), OldView.GetSize()) == 0); - - // Make sure we get the new data when reading after we write new data - CHECK(NewView.GetSize() == Buffer2.GetSize()); - CHECK(memcmp(NewView.GetData(), Buffer2.GetData(), NewView.GetSize()) == 0); -} - -TEST_CASE_TEMPLATE("z$.scrub", ReferenceCaching, testutils::FalseType, testutils::TrueType) -{ - ScopedTemporaryDirectory TempDir; - - using namespace testutils; - - struct CacheRecord - { - IoBuffer Record; - std::vector<CompressedBuffer> Attachments; - }; - - auto CreateCacheRecord = [](bool Structured, std::string_view Bucket, const IoHash& Key, const std::vector<size_t>& AttachmentSizes) { - CacheRecord Result; - if (Structured) - { - Result.Attachments.resize(AttachmentSizes.size()); - CbObjectWriter Record; - Record.BeginObject("Key"sv); - { - Record << "Bucket"sv << Bucket; - Record << "Hash"sv << Key; - } - Record.EndObject(); - for (size_t Index = 0; Index < AttachmentSizes.size(); Index++) - { - IoBuffer AttachmentData = CreateRandomBlob(AttachmentSizes[Index]); - CompressedBuffer CompressedAttachmentData = CompressedBuffer::Compress(SharedBuffer(AttachmentData)); - Record.AddBinaryAttachment(fmt::format("attachment-{}", Index), CompressedAttachmentData.DecodeRawHash()); - Result.Attachments[Index] = CompressedAttachmentData; - } - Result.Record = Record.Save().GetBuffer().AsIoBuffer(); - Result.Record.SetContentType(ZenContentType::kCbObject); - } - else - { - std::string RecordData = fmt::format("{}:{}", Bucket, Key.ToHexString()); - size_t TotalSize = RecordData.length() + 1; - for (size_t AttachmentSize : AttachmentSizes) - { - TotalSize += AttachmentSize; - } - Result.Record = IoBuffer(TotalSize); - char* DataPtr = (char*)Result.Record.MutableData(); - memcpy(DataPtr, RecordData.c_str(), RecordData.length() + 1); - DataPtr += RecordData.length() + 1; - for (size_t AttachmentSize : AttachmentSizes) - { - IoBuffer AttachmentData = CreateRandomBlob(AttachmentSize); - memcpy(DataPtr, AttachmentData.GetData(), AttachmentData.GetSize()); - DataPtr += AttachmentData.GetSize(); - } - } - return Result; - }; - - GcManager Gc; - CidStore CidStore(Gc); - auto JobQueue = MakeJobQueue(1, "testqueue"); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CidStoreConfiguration CidConfig = {.RootDirectory = TempDir.Path() / "cas", .TinyValueThreshold = 1024, .HugeValueThreshold = 4096}; - CidStore.Initialize(CidConfig); - - auto CreateRecords = - [&](bool IsStructured, std::string_view BucketName, const std::vector<IoHash>& Cids, const std::vector<size_t>& AttachmentSizes) { - for (const IoHash& Cid : Cids) - { - CacheRecord Record = CreateCacheRecord(IsStructured, BucketName, Cid, AttachmentSizes); - std::vector<IoHash> AttachmentHashes; - for (const CompressedBuffer& Attachment : Record.Attachments) - { - AttachmentHashes.push_back(Attachment.DecodeRawHash()); - CidStore.AddChunk(Attachment.GetCompressed().Flatten().AsIoBuffer(), AttachmentHashes.back()); - } - Zcs.Put("mybucket", Cid, {.Value = Record.Record}, AttachmentHashes); - } - }; - - std::vector<size_t> AttachmentSizes = {16, 1000, 2000, 4000, 8000, 64000, 80000}; - - std::vector<IoHash> UnstructuredCids{CreateKey(4), CreateKey(5), CreateKey(6)}; - CreateRecords(false, "mybucket"sv, UnstructuredCids, AttachmentSizes); - - std::vector<IoHash> StructuredCids{CreateKey(1), CreateKey(2), CreateKey(3)}; - CreateRecords(true, "mybucket"sv, StructuredCids, AttachmentSizes); - - WorkerThreadPool ThreadPool{1}; - ScrubContext ScrubCtx{ThreadPool}; - Zcs.ScrubStorage(ScrubCtx); - CidStore.ScrubStorage(ScrubCtx); - CHECK(ScrubCtx.ScrubbedChunks() == (StructuredCids.size() + StructuredCids.size() * AttachmentSizes.size()) + UnstructuredCids.size()); - CHECK(ScrubCtx.BadCids().GetSize() == 0); -} - -TEST_CASE_TEMPLATE("z$.newgc.basics", ReferenceCaching, testutils::FalseType, testutils::TrueType) -{ - using namespace testutils; - - ScopedTemporaryDirectory TempDir; - - auto JobQueue = MakeJobQueue(1, "testqueue"); - - struct CacheEntry - { - IoBuffer Data; - std::vector<std::pair<Oid, CompressedBuffer>> Attachments; - }; - - std::unordered_map<IoHash, CacheEntry> CacheEntries; - - auto CreateCacheRecord = - [&](ZenCacheNamespace& Zcs, CidStore& CidStore, std::string_view Bucket, std::span<std::pair<Oid, CompressedBuffer>> Attachments) { - std::vector<IoHash> AttachmentKeys; - for (const auto& Attachment : Attachments) - { - AttachmentKeys.push_back(Attachment.second.DecodeRawHash()); - } - auto Record = CreateRecord(Attachments); - Zcs.Put(Bucket, - Record.first, - {.Value = Record.second, - .RawSize = Record.second.GetSize(), - .RawHash = IoHash::HashBuffer(Record.second.GetData(), Record.second.GetSize())}, - AttachmentKeys); - for (const auto& Attachment : Attachments) - { - CidStore.AddChunk(Attachment.second.GetCompressed().Flatten().AsIoBuffer(), Attachment.second.DecodeRawHash()); - } - CacheEntries.insert({Record.first, CacheEntry{.Data = Record.second, .Attachments = {Attachments.begin(), Attachments.end()}}}); - return Record.first; - }; - auto CreateCacheValue = [&](ZenCacheNamespace& Zcs, std::string_view Bucket, size_t Size) { - std::pair<Oid, IoBuffer> CacheValue = CreateBinaryBlob(Size); - IoHash Key = ToIoHash(CacheValue.first); - Zcs.Put(Bucket, - Key, - {.Value = CacheValue.second, - .RawSize = CacheValue.second.GetSize(), - .RawHash = IoHash::HashBuffer(CacheValue.second.GetData(), CacheValue.second.GetSize())}, - {}); - CacheEntries.insert({Key, CacheEntry{CacheValue.second, {}}}); - return Key; - }; - - auto ValidateCacheEntry = [&](ZenCacheNamespace& Zcs, - CidStore& CidStore, - std::string_view Bucket, - const IoHash& Key, - bool ExpectCacheEntry, - bool ExpectAttachments) { - const CacheEntry& Entry = CacheEntries[Key]; - ZenCacheValue Value; - bool CacheExists = Zcs.Get(Bucket, Key, Value); - if (ExpectCacheEntry) - { - if (!CacheExists) - { - return false; - } - if (Value.Value.GetSize() != Entry.Data.GetSize()) - { - return false; - } - if (!Value.Value.GetView().EqualBytes(Entry.Data.GetView())) - { - return false; - } - } - else if (CacheExists) - { - return false; - } - - if (ExpectAttachments) - { - for (const auto& Attachment : Entry.Attachments) - { - IoHash AttachmentHash = Attachment.second.DecodeRawHash(); - IoBuffer StoredData = CidStore.FindChunkByCid(AttachmentHash); - if (!StoredData) - { - return false; - } - if (!StoredData.GetView().EqualBytes(Attachment.second.GetCompressed().Flatten().GetView())) - { - return false; - } - } - } - else - { - for (const auto& Attachment : Entry.Attachments) - { - IoHash AttachmentHash = Attachment.second.DecodeRawHash(); - if (CidStore.ContainsChunk(AttachmentHash)) - { - return false; - } - } - } - return true; - }; - - std::vector<IoHash> CacheRecords; - std::vector<IoHash> UnstructuredCacheValues; - - const auto TearDrinkerBucket = "teardrinker"sv; - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - - // Create some basic data - { - // Structured record with attachments - auto Attachments1 = CreateCompressedAttachment(CidStore, std::vector<size_t>{77, 1024 * 1024 * 2, 99, 1024 * 1024 * 2 + 87}); - CacheRecords.emplace_back(CreateCacheRecord(Zcs, CidStore, TearDrinkerBucket, Attachments1)); - - // Structured record with reuse of attachments - auto Attachments2 = CreateCompressedAttachment(CidStore, std::vector<size_t>{971}); - Attachments2.push_back(Attachments1[0]); - Attachments2.push_back(Attachments1[1]); - CacheRecords.emplace_back(CreateCacheRecord(Zcs, CidStore, TearDrinkerBucket, Attachments2)); - } - - CacheRecords.emplace_back(CreateCacheRecord(Zcs, CidStore, TearDrinkerBucket, {})); - - { - // Unstructured cache values - UnstructuredCacheValues.push_back(CreateCacheValue(Zcs, TearDrinkerBucket, 84)); - UnstructuredCacheValues.push_back(CreateCacheValue(Zcs, TearDrinkerBucket, 591)); - UnstructuredCacheValues.push_back(CreateCacheValue(Zcs, TearDrinkerBucket, 1024 * 1024 * 3 + 7)); - UnstructuredCacheValues.push_back(CreateCacheValue(Zcs, TearDrinkerBucket, 71)); - } - } - - SUBCASE("expire nothing") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() - std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() - std::chrono::hours(1), - .CollectSmallObjects = false, - .IsDeleteMode = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_EQ(0u, Result.CompactStoresStatSum.RemovedDisk); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], true, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - SUBCASE("expire all large objects, delete nothing") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = false, - .IsDeleteMode = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(1u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_EQ(0u, Result.CompactStoresStatSum.RemovedDisk); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], true, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - SUBCASE("expire all object, delete nothing") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_EQ(0u, Result.CompactStoresStatSum.RemovedDisk); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], true, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - SUBCASE("expire all large objects, skip cid") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = false, - .IsDeleteMode = true, - .SkipCidDelete = true, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(1u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(1u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_EQ(CacheEntries[UnstructuredCacheValues[2]].Data.GetSize(), Result.CompactStoresStatSum.RemovedDisk); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], true, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - SUBCASE("expire all objects, skip cid") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = true, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_GE(Result.CompactStoresStatSum.RemovedDisk, 0); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], false, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], false, true)); - } - SUBCASE("expire all large objects") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = false, - .IsDeleteMode = true, - .SkipCidDelete = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(1u, - Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); // Only one cache value is pruned/deleted as that is the only - // large item in the cache (all other large items as in cas) - CHECK_EQ(1u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, - Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats - .FoundCount); // We won't remove any references since all referencers are small which retains all references - CHECK_EQ(0u, - Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats - .DeletedCount); // We won't remove any references since all referencers are small which retains all references - CHECK_EQ(CacheEntries[UnstructuredCacheValues[2]].Data.GetSize(), Result.CompactStoresStatSum.RemovedDisk); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], true, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - SUBCASE("expire all objects") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_GT(Result.CompactStoresStatSum.RemovedDisk, 0); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], false, false)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], false, false)); - } - - SUBCASE("keep 1 cache record, skip cid") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecords[0], GcClock::Now() + std::chrono::hours(2)); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = true, - .Verbose = true, - .CompactBlockUsageThresholdPercent = 100}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(6u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(6u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - uint64_t MinExpectedRemoveSize = CacheEntries[UnstructuredCacheValues[2]].Data.GetSize(); - CHECK_LT(MinExpectedRemoveSize, Result.CompactStoresStatSum.RemovedDisk); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], false, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], false, false)); - } - - SUBCASE("keep 2 cache records") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecords[0], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecords[1], GcClock::Now() + std::chrono::hours(2)); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(5u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(5u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_GT(Result.CompactStoresStatSum.RemovedDisk, 0); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], false, false)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], false, false)); - } - - SUBCASE("keep 3 cache value") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[1], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[2], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[3], GcClock::Now() + std::chrono::hours(2)); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = false, - .Verbose = true}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(4u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(4u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(5u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_GT(Result.CompactStoresStatSum.RemovedDisk, 0); - CHECK_EQ(0u, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], false, false)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - - SUBCASE("keep 3 cache value, skip cid") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - // Prime so we can check GC of memory layer - ZenCacheValue Dummy; - Zcs.Get(TearDrinkerBucket, CacheRecords[0], Dummy); - Zcs.Get(TearDrinkerBucket, CacheRecords[1], Dummy); - Zcs.Get(TearDrinkerBucket, CacheRecords[2], Dummy); - Zcs.Get(TearDrinkerBucket, UnstructuredCacheValues[0], Dummy); - Zcs.Get(TearDrinkerBucket, UnstructuredCacheValues[1], Dummy); - Zcs.Get(TearDrinkerBucket, UnstructuredCacheValues[2], Dummy); - Zcs.Get(TearDrinkerBucket, UnstructuredCacheValues[3], Dummy); - - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[1], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[2], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[3], GcClock::Now() + std::chrono::hours(2)); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = true, - .Verbose = true, - .CompactBlockUsageThresholdPercent = 100}); - CHECK_EQ(7u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(4u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(4u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(0u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_GT(Result.CompactStoresStatSum.RemovedDisk, 0); - uint64_t MemoryClean = CacheEntries[CacheRecords[0]].Data.GetSize() + CacheEntries[CacheRecords[1]].Data.GetSize() + - CacheEntries[CacheRecords[2]].Data.GetSize() + CacheEntries[UnstructuredCacheValues[0]].Data.GetSize(); - CHECK_EQ(MemoryClean, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[0], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[1], false, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, CacheRecords[2], false, true)); - - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[0], false, false)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[1], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[2], true, true)); - CHECK(ValidateCacheEntry(Zcs, CidStore, TearDrinkerBucket, UnstructuredCacheValues[3], true, true)); - } - - SUBCASE("leave write block") - { - GcManager Gc; - CidStore CidStore(Gc); - CidStore.Initialize({.RootDirectory = TempDir.Path() / "cas"}); - ZenCacheNamespace Zcs(Gc, - *JobQueue, - TempDir.Path() / "cache", - {.DiskLayerConfig = {.BucketConfig = {.EnableReferenceCaching = ReferenceCaching::Enabled}}}); - CHECK_EQ(7, Zcs.GetBucketInfo(TearDrinkerBucket).value().DiskLayerInfo.EntryCount); - - auto Attachments = - CreateCompressedAttachment(CidStore, std::vector<size_t>{177, 1024 * 1024 * 2 + 31, 8999, 1024 * 1024 * 2 + 187}); - IoHash CacheRecord = CreateCacheRecord(Zcs, CidStore, TearDrinkerBucket, Attachments); - { - // Do get so it ends up in memcache - ZenCacheValue _; - Zcs.Get(TearDrinkerBucket, CacheRecord, _); - } - - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecord, GcClock::Now() - std::chrono::hours(2)); - - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecords[0], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecords[1], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, CacheRecords[2], GcClock::Now() + std::chrono::hours(2)); - - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[0], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[1], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[2], GcClock::Now() + std::chrono::hours(2)); - Zcs.SetAccessTime(TearDrinkerBucket, UnstructuredCacheValues[3], GcClock::Now() + std::chrono::hours(2)); - - GcResult Result = Gc.CollectGarbage(GcSettings{.CacheExpireTime = GcClock::Now() + std::chrono::hours(1), - .ProjectStoreExpireTime = GcClock::Now() + std::chrono::hours(1), - .CollectSmallObjects = true, - .IsDeleteMode = true, - .SkipCidDelete = false, - .Verbose = true}); - // Write block can't be compacted so Compacted will be less than Deleted - CHECK_EQ(8u, Result.ReferencerStatSum.RemoveExpiredDataStats.CheckedCount); - CHECK_EQ(1u, Result.ReferencerStatSum.RemoveExpiredDataStats.FoundCount); - CHECK_EQ(1u, Result.ReferencerStatSum.RemoveExpiredDataStats.DeletedCount); - CHECK_EQ(9u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.CheckedCount); - CHECK_EQ(4u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.FoundCount); - CHECK_EQ(4u, Result.ReferenceStoreStatSum.RemoveUnreferencedDataStats.DeletedCount); - CHECK_EQ(Attachments[1].second.GetCompressed().GetSize() + Attachments[3].second.GetCompressed().GetSize(), - Result.CompactStoresStatSum.RemovedDisk); - uint64_t MemoryClean = CacheEntries[CacheRecord].Data.GetSize(); - CHECK_EQ(MemoryClean, Result.ReferencerStatSum.RemoveExpiredDataStats.FreedMemory); - } -} - -#endif - -void -z$_forcelink() -{ -} - -} // namespace zen |