10#include <userver/cache/lru_cache_config.hpp>
11#include <userver/cache/lru_cache_statistics.hpp>
12#include <userver/cache/nway_lru_cache.hpp>
13#include <userver/concurrent/mutex_set.hpp>
14#include <userver/dump/common.hpp>
15#include <userver/dump/dumper.hpp>
16#include <userver/engine/async.hpp>
17#include <userver/utils/datetime.hpp>
18#include <userver/utils/impl/cached_time.hpp>
19#include <userver/utils/impl/wait_token_storage.hpp>
20#include <userver/utils/statistics/writer.hpp>
22USERVER_NAMESPACE_BEGIN
28template <
typename Value>
29struct ExpirableValue final {
31 std::chrono::steady_clock::time_point update_time;
34template <
typename Value>
35void Write(
dump::
Writer& writer,
const impl::ExpirableValue<Value>& value) {
36 const auto [now, steady_now] =
utils::impl::GetGlobalTime();
37 writer.Write(value.value);
38 writer.Write(value.update_time - steady_now + now);
41template <
typename Value>
42impl::ExpirableValue<Value> Read(
dump::
Reader& reader,
dump::
To<impl::ExpirableValue<Value>>) {
43 const auto [now, steady_now] =
utils::impl::GetGlobalTime();
45 return impl::ExpirableValue<Value>{
46 reader.Read<Value>(), reader
.Read<std
::chrono
::system_clock
::time_point
>() - now + steady_now};
58template <
typename Key,
typename Value,
typename Hash = std::hash<Key>,
typename Equal = std::equal_to<Key>>
59class ExpirableLruCache final {
61 using UpdateValueFunc = std::function<Value(
const Key&)>;
71 ExpirableLruCache(size_t ways, size_t way_size,
const Hash& hash = Hash(),
const Equal& equal = Equal());
79 std::chrono::milliseconds GetMaxLifetime()
const noexcept;
81 void SetMaxLifetime(std::chrono::milliseconds max_lifetime);
84
85
86
87
91
92
93
94
95 Value
Get(
const Key& key,
const UpdateValueFunc& update_func,
ReadMode read_mode =
ReadMode::kUseCache);
98
99
100
101
102
103 std::optional<Value>
GetOptional(
const Key& key,
const UpdateValueFunc& update_func);
106
107
108
109
113
114
115
116
120
121
124 void Put(
const Key& key,
const Value& value);
126 void Put(
const Key& key, Value&& value);
128 const impl::ExpirableLruCacheStatistics& GetStatistics()
const;
130 size_t GetSizeApproximate()
const;
139 template <
typename Predicate>
154 bool IsExpired(std::chrono::steady_clock::time_point update_time, std::chrono::steady_clock::time_point now)
const;
156 bool ShouldUpdate(std::chrono::steady_clock::time_point update_time, std::chrono::steady_clock::time_point now)
159 cache::NWayLRU<Key, impl::ExpirableValue<Value>, Hash, Equal> lru_;
160 std::atomic<std::chrono::milliseconds> max_lifetime_{std::chrono::milliseconds(0)};
161 std::atomic<BackgroundUpdateMode> background_update_mode_{BackgroundUpdateMode::kDisabled};
162 impl::ExpirableLruCacheStatistics stats_;
163 concurrent::MutexSet<Key, Hash, Equal> mutex_set_;
164 utils::impl::WaitTokenStorage wait_token_storage_;
59class ExpirableLruCache final {
…};
167template <
typename Key,
typename Value,
typename Hash,
typename Equal>
174 : lru_(ways, way_size, hash, equal), mutex_set_{ways, way_size, hash, equal} {}
176template <
typename Key,
typename Value,
typename Hash,
typename Equal>
177ExpirableLruCache<Key, Value, Hash, Equal>::~ExpirableLruCache() {
178 wait_token_storage_.WaitForAllTokens();
181template <
typename Key,
typename Value,
typename Hash,
typename Equal>
182void ExpirableLruCache<Key, Value, Hash, Equal>::
SetWaySize(size_t way_size) {
183 lru_.UpdateWaySize(way_size);
182void ExpirableLruCache<Key, Value, Hash, Equal>::
SetWaySize(size_t way_size) {
…}
186template <
typename Key,
typename Value,
typename Hash,
typename Equal>
187std::chrono::milliseconds ExpirableLruCache<Key, Value, Hash, Equal>::GetMaxLifetime()
const noexcept {
188 return max_lifetime_.load();
191template <
typename Key,
typename Value,
typename Hash,
typename Equal>
192void ExpirableLruCache<Key, Value, Hash, Equal>::SetMaxLifetime(std::chrono::milliseconds max_lifetime) {
193 max_lifetime_ = max_lifetime;
196template <
typename Key,
typename Value,
typename Hash,
typename Equal>
197void ExpirableLruCache<Key, Value, Hash, Equal>::
SetBackgroundUpdate(BackgroundUpdateMode background_update) {
198 background_update_mode_ = background_update;
197void ExpirableLruCache<Key, Value, Hash, Equal>::
SetBackgroundUpdate(BackgroundUpdateMode background_update) {
…}
201template <
typename Key,
typename Value,
typename Hash,
typename Equal>
202Value ExpirableLruCache<Key, Value, Hash, Equal>::
Get(
204 const UpdateValueFunc& update_func,
210 return std::move(*opt_old_value);
213 auto mutex = mutex_set_.GetMutexForKey(key);
214 std::lock_guard lock(mutex);
217 auto old_value = lru_.Get(key);
218 if (old_value && !IsExpired(old_value->update_time, now)) {
219 return std::move(old_value->value);
222 auto value = update_func(key);
223 if (read_mode ==
ReadMode::kUseCache) {
224 lru_.Put(key, {value, now});
202Value ExpirableLruCache<Key, Value, Hash, Equal>::
Get( {
…}
229template <
typename Key,
typename Value,
typename Hash,
typename Equal>
231ExpirableLruCache<Key, Value, Hash, Equal>::
GetOptional(
const Key& key,
const UpdateValueFunc& update_func) {
233 auto old_value = lru_.Get(key);
236 if (!IsExpired(old_value->update_time, now)) {
237 impl::CacheHit(stats_);
239 if (ShouldUpdate(old_value->update_time, now)) {
243 return std::move(old_value->value);
245 impl::CacheStale(stats_);
248 impl::CacheMiss(stats_);
231ExpirableLruCache<Key, Value, Hash, Equal>::
GetOptional(
const Key& key,
const UpdateValueFunc& update_func) {
…}
253template <
typename Key,
typename Value,
typename Hash,
typename Equal>
255 auto old_value = lru_.Get(key);
258 impl::CacheHit(stats_);
259 return old_value->value;
261 impl::CacheMiss(stats_);
266template <
typename Key,
typename Value,
typename Hash,
typename Equal>
269 const UpdateValueFunc& update_func
272 auto old_value = lru_.Get(key);
275 impl::CacheHit(stats_);
277 if (ShouldUpdate(old_value->update_time, now)) {
281 return old_value->value;
283 impl::CacheMiss(stats_);
288template <
typename Key,
typename Value,
typename Hash,
typename Equal>
291 auto old_value = lru_.Get(key);
294 if (!IsExpired(old_value->update_time, now)) {
295 impl::CacheHit(stats_);
297 return old_value->value;
299 impl::CacheStale(stats_);
302 impl::CacheMiss(stats_);
307template <
typename Key,
typename Value,
typename Hash,
typename Equal>
308void ExpirableLruCache<Key, Value, Hash, Equal>::Put(
const Key& key,
const Value& value) {
312template <
typename Key,
typename Value,
typename Hash,
typename Equal>
313void ExpirableLruCache<Key, Value, Hash, Equal>::Put(
const Key& key, Value&& value) {
317template <
typename Key,
typename Value,
typename Hash,
typename Equal>
318const impl::ExpirableLruCacheStatistics& ExpirableLruCache<Key, Value, Hash, Equal>::GetStatistics()
const {
322template <
typename Key,
typename Value,
typename Hash,
typename Equal>
323size_t ExpirableLruCache<Key, Value, Hash, Equal>::GetSizeApproximate()
const {
324 return lru_.GetSize();
327template <
typename Key,
typename Value,
typename Hash,
typename Equal>
328void ExpirableLruCache<Key, Value, Hash, Equal>::
Invalidate() {
332template <
typename Key,
typename Value,
typename Hash,
typename Equal>
334 lru_.InvalidateByKey(key);
338template <
typename Predicate>
339void ExpirableLruCache<Key, Value, Hash, Equal>::
InvalidateByKeyIf(
const Key& key, Predicate pred) {
342 auto mutex = mutex_set_.GetMutexForKey(key);
343 std::lock_guard lock(mutex);
344 const auto cur_value = lru_.Get(key);
346 if (cur_value.has_value() && !IsExpired(cur_value->update_time, now) && pred(cur_value->value)) {
339void ExpirableLruCache<Key, Value, Hash, Equal>::
InvalidateByKeyIf(
const Key& key, Predicate pred) {
…}
351template <
typename Key,
typename Value,
typename Hash,
typename Equal>
352void ExpirableLruCache<Key, Value, Hash, Equal>::
UpdateInBackground(
const Key& key, UpdateValueFunc update_func) {
353 impl::CacheBackgroundUpdate(stats_);
356 engine::AsyncNoSpan([token = wait_token_storage_.GetToken(),
this, key, update_func = std::move(update_func)] {
357 auto mutex = mutex_set_.GetMutexForKey(key);
358 std::unique_lock lock(mutex, std::try_to_lock);
365 auto value = update_func(key);
366 lru_.Put(key, {value, now});
352void ExpirableLruCache<Key, Value, Hash, Equal>::
UpdateInBackground(
const Key& key, UpdateValueFunc update_func) {
…}
370template <
typename Key,
typename Value,
typename Hash,
typename Equal>
371bool ExpirableLruCache<Key, Value, Hash, Equal>::IsExpired(
372 std::chrono::steady_clock::time_point update_time,
373 std::chrono::steady_clock::time_point now
375 auto max_lifetime = max_lifetime_.load();
376 return max_lifetime.count() != 0 && update_time + max_lifetime < now;
379template <
typename Key,
typename Value,
typename Hash,
typename Equal>
380bool ExpirableLruCache<Key, Value, Hash, Equal>::ShouldUpdate(
381 std::chrono::steady_clock::time_point update_time,
382 std::chrono::steady_clock::time_point now
384 auto max_lifetime = max_lifetime_.load();
385 return (background_update_mode_.load() == BackgroundUpdateMode::kEnabled) && max_lifetime.count() != 0 &&
386 update_time + max_lifetime / 2 < now;
389template <
typename Key,
typename Value,
typename Hash = std::hash<Key>,
typename Equal = std::equal_to<Key>>
390class LruCacheWrapper final {
392 using Cache = ExpirableLruCache<Key, Value, Hash, Equal>;
393 using ReadMode =
typename Cache::ReadMode;
395 LruCacheWrapper(std::shared_ptr<Cache> cache,
typename Cache::UpdateValueFunc update_func)
396 : cache_(std::move(cache)), update_func_(std::move(update_func)) {}
399 Value
Get(
const Key& key, ReadMode read_mode = ReadMode::kUseCache) {
400 return cache_->Get(key, update_func_, read_mode);
399 Value
Get(
const Key& key, ReadMode read_mode = ReadMode::kUseCache) {
…}
404 std::optional<Value>
GetOptional(
const Key& key) {
return cache_->GetOptional(key, update_func_); }
406 void InvalidateByKey(
const Key& key) { cache_->InvalidateByKey(key); }
408 template <
typename Predicate>
409 void InvalidateByKeyIf(
const Key& key, Predicate pred) {
410 cache_->InvalidateByKeyIf(key, pred);
417 std::shared_ptr<Cache>
GetCache() {
return cache_; }
420 std::shared_ptr<Cache> cache_;
421 typename Cache::UpdateValueFunc update_func_;
390class LruCacheWrapper final {
…};
424template <
typename Key,
typename Value,
typename Hash,
typename Equal>
425void ExpirableLruCache<Key, Value, Hash, Equal>::Write(
dump::
Writer& writer)
const {
426 utils::impl::UpdateGlobalTime();
430template <
typename Key,
typename Value,
typename Hash,
typename Equal>
431void ExpirableLruCache<Key, Value, Hash, Equal>::Read(
dump::
Reader& reader) {
432 utils::impl::UpdateGlobalTime();
436template <
typename Key,
typename Value,
typename Hash,
typename Equal>
437void ExpirableLruCache<Key, Value, Hash, Equal>::
SetDumper(std::shared_ptr<
dump::Dumper> dumper) {
438 lru_.SetDumper(std::move(dumper));
437void ExpirableLruCache<Key, Value, Hash, Equal>::
SetDumper(std::shared_ptr<
dump::Dumper> dumper) {
…}
441template <
typename Key,
typename Value,
typename Hash,
typename Equal>
442void DumpMetric(
utils::statistics::Writer& writer,
const ExpirableLruCache<Key, Value, Hash, Equal>& cache) {
443 writer
["current-documents-count"] = cache.GetSizeApproximate();
444 writer = cache.GetStatistics();