userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
nway_lru_cache.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file userver/cache/nway_lru_cache.hpp
4
/// @brief @copybrief cache::NWayLRU
5
6
#
include
<
functional
>
7
#
include
<
optional
>
8
#
include
<
vector
>
9
10
#
include
<
boost
/
container_hash
/
hash
.
hpp
>
11
12
#
include
<
userver
/
cache
/
lru_map
.
hpp
>
13
#
include
<
userver
/
dump
/
dumper
.
hpp
>
14
#
include
<
userver
/
dump
/
operations
.
hpp
>
15
#
include
<
userver
/
engine
/
mutex
.
hpp
>
16
17
USERVER_NAMESPACE_BEGIN
18
19
namespace
cache
{
20
21
/// @brief N-way LRU cache with per-way mutexes and optional dump support.
22
/// @ingroup userver_containers
23
///
24
/// @snippet core/src/cache/nway_lru_cache_test.cpp NWayLRU basic
25
template
<
typename
T,
typename
U,
typename
Hash = std::hash<T>,
typename
Equal = std::equal_to<T>>
26
class
NWayLRU final {
27
public
:
28
/// @brief Constructs an N-way LRU cache with the given number of ways and
29
/// maximum elements per way.
30
///
31
/// The maximum total number of elements is `ways * way_size`.
32
///
33
/// @param ways Number of ways (a.k.a. shards, internal hash-maps), into
34
/// which elements are distributed based on their hash. Each shard is
35
/// protected by an individual mutex. Larger \p ways means more internal
36
/// hash-map instances and more memory usage, but less contention. A good
37
/// starting point is `ways=16`. If you encounter contention, you can
38
/// increase \p ways to something on the order of `256` or whatever your
39
/// RAM constraints allow.
40
/// @param way_size Maximum allowed amount of elements per way. When the
41
/// size of a way reaches this number, existing elements are deleted
42
/// according to the LRU policy.
43
/// @param hash Instance of \p Hash function to use, in case of a custom
44
/// stateful Hash.
45
/// @param equal Instance of \p Equal function to use, in case of a custom
46
/// stateful Equal.
47
/// @throws std::logic_error if \p ways is zero.
48
NWayLRU
(size_t ways, size_t way_size,
const
Hash& hash = Hash(),
const
Equal& equal = Equal());
49
50
/// @brief Stores value by key.
51
/// @param key Key to store the value under.
52
/// @param value Value to store.
53
void
Put
(
const
T& key, U value);
54
55
/// @brief Returns cached value by key if validator returns true for it.
56
/// @param key Key to look up.
57
/// @param validator Callable that takes the cached value and returns false
58
/// to invalidate the entry.
59
/// @return Cached value if present and validator returned true, otherwise
60
/// std::nullopt.
61
/// @note If validator returns false, the entry is removed from the cache.
62
template
<
typename
Validator>
63
std::optional<U>
Get
(
const
T& key, Validator validator);
64
65
/// @brief Returns cached value by key if present; otherwise std::nullopt.
66
/// @param key Key to look up.
67
/// @return Cached value if present, otherwise std::nullopt.
68
/// @note Equivalent to Get(key, [](const U&) { return true; }).
69
std::optional<U>
Get
(
const
T& key) {
70
return
Get(key, [](
const
U&) {
return
true
; });
71
}
72
73
/// @brief Returns cached value by key if present; otherwise returns
74
/// default_value.
75
/// @param key Key to look up.
76
/// @param default_value Value to return when key is not found.
77
/// @return Cached value if present, otherwise \p default_value.
78
U
GetOr
(
const
T& key,
const
U& default_value);
79
80
/// @brief Removes all entries from the cache.
81
void
Invalidate
();
82
83
/// @brief Removes the entry for the given key if present.
84
/// @param key Key whose entry is to be removed.
85
void
InvalidateByKey
(
const
T& key);
86
87
/// @brief Iterates over all items, invoking \p func for each (key, value).
88
/// @param func Callable to invoke for each item (e.g. void(const T&, const U&)).
89
/// @note May be slow for big caches.
90
template
<
typename
Function>
91
void
VisitAll
(Function func)
const
;
92
93
/// @brief Returns the total number of elements in the cache across all ways.
94
/// @return Total number of elements.
95
size_t
GetSize
()
const
;
96
97
/// @brief Updates maximum elements per way.
98
/// @param way_size New maximum elements per way; see
99
/// @ref cache::NWayLRU::NWayLRU "NWayLRU constructor" for the semantics.
100
void
UpdateWaySize
(size_t way_size);
101
102
/// @brief Serializes the cache contents to the dump writer.
103
/// @param writer Target @ref dump::Writer.
104
void
Write
(
dump
::
Writer
& writer)
const
;
105
106
/// @brief Deserializes the cache contents from the dump reader.
107
/// @param reader Source @ref dump::Reader.
108
/// @note Clears existing entries before loading.
109
void
Read
(
dump
::
Reader
& reader);
110
111
/// @brief Sets the dumper; it will be notified of any cache updates.
112
/// @param dumper Instance of @ref dump::Dumper, or nullptr to clear.
113
/// @note This method is not thread-safe.
114
void
SetDumper
(std::shared_ptr<
dump
::Dumper> dumper);
115
116
private
:
117
struct
Way {
118
Way(Way&& other)
noexcept
: cache(std::move(other.cache)) {}
119
120
// max_size is not used, will be reset by Resize() in NWayLRU::NWayLRU
121
Way(
const
Hash& hash,
const
Equal& equal)
122
: cache(1, hash, equal)
123
{}
124
125
mutable
engine::Mutex mutex;
126
LruMap<T, U, Hash, Equal> cache;
127
};
128
129
Way& GetWay(
const
T& key);
130
131
void
NotifyDumper();
132
133
std::vector<Way> caches_;
134
[[no_unique_address]] Hash hash_fn_;
135
std::shared_ptr<
dump
::Dumper> dumper_{
nullptr
};
136
};
137
138
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
139
NWayLRU<T, U, Hash, Eq>::
NWayLRU
(size_t ways, size_t way_size,
const
Hash& hash,
const
Eq& equal)
140
: caches_(),
141
hash_fn_(hash)
142
{
143
caches_.reserve(ways);
144
for
(size_t i = 0; i < ways; ++i) {
145
caches_.emplace_back(hash, equal);
146
}
147
if
(ways == 0) {
148
throw
std::logic_error(
"Ways must be positive"
);
149
}
150
151
for
(
auto
& way : caches_) {
152
way.cache.SetMaxSize(way_size);
153
}
154
}
155
156
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
157
void
NWayLRU<T, U, Hash, Eq>::
Put
(
const
T& key, U value) {
158
auto
& way = GetWay(key);
159
{
160
const
std::unique_lock lock{way.mutex};
161
way.cache.Put(key, std::move(value));
162
}
163
NotifyDumper();
164
}
165
166
template
<
typename
T
,
typename
U
,
typename
Hash
,
typename
Eq
>
167
template
<
typename
Validator>
168
std::optional<U> NWayLRU<T, U, Hash, Eq>::
Get
(
const
T& key, Validator validator) {
169
auto
& way = GetWay(key);
170
const
std::unique_lock lock{way.mutex};
171
auto
* value = way.cache.Get(key);
172
173
if
(value) {
174
if
(validator(*value)) {
175
return
*value;
176
}
177
way.cache.Erase(key);
178
}
179
180
return
std::nullopt;
181
}
182
183
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
184
void
NWayLRU<T, U, Hash, Eq>::
InvalidateByKey
(
const
T& key) {
185
auto
& way = GetWay(key);
186
{
187
const
std::unique_lock lock{way.mutex};
188
way.cache.Erase(key);
189
}
190
NotifyDumper();
191
}
192
193
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
194
U NWayLRU<T, U, Hash, Eq>::
GetOr
(
const
T& key,
const
U& default_value) {
195
auto
& way = GetWay(key);
196
const
std::unique_lock lock{way.mutex};
197
return
way.cache.GetOr(key, default_value);
198
}
199
200
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
201
void
NWayLRU<T, U, Hash, Eq>::
Invalidate
() {
202
for
(
auto
& way : caches_) {
203
const
std::unique_lock lock{way.mutex};
204
way.cache.Clear();
205
}
206
NotifyDumper();
207
}
208
209
template
<
typename
T
,
typename
U
,
typename
Hash
,
typename
Eq
>
210
template
<
typename
Function>
211
void
NWayLRU<T, U, Hash, Eq>::
VisitAll
(Function func)
const
{
212
for
(
const
auto
& way : caches_) {
213
const
std::unique_lock lock{way.mutex};
214
way.cache.VisitAll(func);
215
}
216
}
217
218
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
219
size_t NWayLRU<T, U, Hash, Eq>::
GetSize
()
const
{
220
size_t size{0};
221
for
(
const
auto
& way : caches_) {
222
const
std::unique_lock lock{way.mutex};
223
size += way.cache.GetSize();
224
}
225
return
size;
226
}
227
228
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
229
void
NWayLRU<T, U, Hash, Eq>::
UpdateWaySize
(size_t way_size) {
230
for
(
auto
& way : caches_) {
231
const
std::unique_lock lock{way.mutex};
232
way.cache.SetMaxSize(way_size);
233
}
234
}
235
236
template
<
typename
T,
typename
U,
typename
Hash,
typename
Eq>
237
typename
NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
const
T& key) {
238
/// It is needed to twist hash because there is hash map in LruMap. Otherwise
239
/// nodes will fall into one bucket. According to
240
/// https://www.boost.org/doc/libs/1_83_0/libs/container_hash/doc/html/hash.html#notes_hash_combine
241
/// hash_combine can be treated as hash itself
242
auto
seed = hash_fn_(key);
243
boost::hash_combine(seed, 0);
244
auto
n = seed % caches_.size();
245
return
caches_[n];
246
}
247
248
template
<
typename
T,
typename
U,
typename
Hash,
typename
Equal>
249
void
NWayLRU<T, U, Hash, Equal>::
Write
(
dump
::
Writer
& writer)
const
{
250
writer.Write(caches_.size());
251
252
for
(
const
Way& way : caches_) {
253
const
std::unique_lock lock{way.mutex};
254
255
writer.Write(way.cache.GetSize());
256
257
way.cache.VisitAll([&writer](
const
T& key,
const
U& value) {
258
writer.Write(key);
259
writer.Write(value);
260
});
261
}
262
}
263
264
template
<
typename
T,
typename
U,
typename
Hash,
typename
Equal>
265
void
NWayLRU<T, U, Hash, Equal>::
Read
(
dump
::
Reader
& reader) {
266
Invalidate
(
)
;
267
268
const
auto
ways = reader
.
Read
<
std
::
size_t
>
(
)
;
269
for
(std::size_t i = 0; i < ways; ++i) {
270
const
auto
elements_in_way = reader
.
Read
<
std
::
size_t
>
(
)
;
271
for
(std::size_t j = 0; j < elements_in_way; ++j) {
272
auto
key = reader.Read<T>();
273
auto
value = reader.Read<U>();
274
Put
(
std::move(key)
,
std::move(value)
)
;
275
}
276
}
277
}
278
279
template
<
typename
T,
typename
U,
typename
Hash,
typename
Equal>
280
void
NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
281
if
(dumper_ !=
nullptr
) {
282
dumper_->
OnUpdateCompleted
(
)
;
283
}
284
}
285
286
template
<
typename
T,
typename
U,
typename
Hash,
typename
Equal>
287
void
NWayLRU<T, U, Hash, Equal>::
SetDumper
(std::shared_ptr<
dump
::Dumper> dumper) {
288
dumper_ = std::move(dumper);
289
}
290
291
}
// namespace cache
292
293
USERVER_NAMESPACE_END
userver
cache
nway_lru_cache.hpp
Generated on
for userver by
Doxygen
1.17.0