1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "cacheshared.h"
#include <zencore/iohash.h>
#include <zencore/stats.h>
#include <zenstore/gc.h>
#include <inttypes.h>
#include <string_view>
#include <vector>
ZEN_THIRD_PARTY_INCLUDES_START
#include <tsl/robin_map.h>
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<uint64_t>(std::chrono::seconds(std::chrono::days(1)).count());
};
struct BucketInfo
{
uint64_t EntryCount = 0;
uint64_t TotalSize = 0;
};
struct Info
{
Configuration Config;
std::vector<std::string> 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<BucketInfo> 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<AccessTime> m_AccessTimes;
std::vector<BucketPayload> m_Payloads;
tsl::robin_map<IoHash, uint32_t> 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<zen::access_tracking::KeyAccessTime>& AccessTimes);
inline uint64_t TotalSize() const { return m_TotalSize; }
uint64_t EntryCount() const;
void GetUsageByAccess(GcClock::TimePoint TickStart, GcClock::Duration SectionLength, std::vector<uint64_t>& InOutUsageSlots);
};
JobQueue& m_JobQueue;
mutable RwLock m_Lock;
std::unordered_map<std::string, std::unique_ptr<CacheBucket>> m_Buckets;
std::vector<std::unique_ptr<CacheBucket>> m_DroppedBuckets;
Configuration m_Configuration;
std::atomic_uint64_t m_TotalSize{};
std::atomic_bool m_IsTrimming = false;
std::atomic<GcClock::Tick> m_LastTickTrim;
ZenCacheMemoryLayer(const ZenCacheMemoryLayer&) = delete;
ZenCacheMemoryLayer& operator=(const ZenCacheMemoryLayer&) = delete;
};
} // namespace zen
|