// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "cacheshared.h" #include #include #include #include #include #include ZEN_THIRD_PARTY_INCLUDES_START #include ZEN_THIRD_PARTY_INCLUDES_END namespace zen { class JobQueue; /** In-memory cache storage Intended for small values which are frequently accessed This should have a better memory management policy to maintain reasonable footprint. */ class ZenCacheMemoryLayer { public: struct Configuration { uint64_t TargetFootprintBytes = 512 * 1024 * 1024; uint64_t TrimIntervalSeconds = 60; uint64_t MaxAgeSeconds = gsl::narrow(std::chrono::seconds(std::chrono::days(1)).count()); }; struct BucketInfo { uint64_t EntryCount = 0; uint64_t TotalSize = 0; }; struct Info { Configuration Config; std::vector BucketNames; uint64_t EntryCount = 0; uint64_t TotalSize = 0; }; ZenCacheMemoryLayer(JobQueue& JobQueue, const Configuration& Config); ~ZenCacheMemoryLayer(); bool Get(std::string_view Bucket, const IoHash& HashKey, ZenCacheValue& OutValue); void Put(std::string_view Bucket, const IoHash& HashKey, const ZenCacheValue& Value); uint64_t CollectGarbage(GcClock::TimePoint ExpireTime); void Drop(); bool DropBucket(std::string_view Bucket); void ScrubStorage(ScrubContext& Ctx); void GatherAccessTimes(zen::access_tracking::AccessTimes& AccessTimes); uint64_t TotalSize() const; Info GetInfo() const; std::optional GetBucketInfo(std::string_view Bucket) const; const Configuration& GetConfiguration() const { return m_Configuration; } void SetConfiguration(const Configuration& NewConfig) { m_Configuration = NewConfig; } private: void Trim(); struct CacheBucket { #pragma pack(push) #pragma pack(1) struct BucketPayload { IoBuffer Payload; // 8 uint32_t RawSize; // 4 IoHash RawHash; // 20 }; #pragma pack(pop) static_assert(sizeof(BucketPayload) == 32u); static_assert(sizeof(AccessTime) == 4u); metrics::OperationTiming m_PutOps; metrics::OperationTiming m_GetOps; mutable RwLock m_BucketLock; std::vector m_AccessTimes; std::vector m_Payloads; tsl::robin_map m_CacheMap; std::atomic_uint64_t m_TotalSize{}; bool Get(const IoHash& HashKey, ZenCacheValue& OutValue); int64_t Put(const IoHash& HashKey, const ZenCacheValue& Value); uint64_t Trim(GcClock::TimePoint ExpireTime); void Drop(); void ScrubStorage(ScrubContext& Ctx); void GatherAccessTimes(std::vector& AccessTimes); inline uint64_t TotalSize() const { return m_TotalSize; } uint64_t EntryCount() const; void GetUsageByAccess(GcClock::TimePoint TickStart, GcClock::Duration SectionLength, std::vector& InOutUsageSlots); }; JobQueue& m_JobQueue; mutable RwLock m_Lock; std::unordered_map> m_Buckets; std::vector> m_DroppedBuckets; Configuration m_Configuration; std::atomic_uint64_t m_TotalSize{}; std::atomic_bool m_IsTrimming = false; std::atomic m_LastTickTrim; ZenCacheMemoryLayer(const ZenCacheMemoryLayer&) = delete; ZenCacheMemoryLayer& operator=(const ZenCacheMemoryLayer&) = delete; }; } // namespace zen