7#include <boost/container_hash/hash.hpp>
9#include <userver/cache/lru_map.hpp>
10#include <userver/dump/dumper.hpp>
11#include <userver/dump/operations.hpp>
12#include <userver/engine/mutex.hpp>
14USERVER_NAMESPACE_BEGIN
19template <
typename T,
typename U,
typename Hash = std::hash<T>,
typename Equal = std::equal_to<T>>
35 NWayLRU(size_t ways, size_t way_size,
const Hash& hash = Hash(),
const Equal& equal = Equal());
37 void Put(
const T& key, U value);
39 template <
typename Validator>
40 std::optional<U> Get(
const T& key, Validator validator);
42 std::optional<U> Get(
const T& key) {
43 return Get(key, [](
const U&) {
return true; });
46 U GetOr(
const T& key,
const U& default_value);
50 void InvalidateByKey(
const T& key);
53 template <
typename Function>
56 size_t GetSize()
const;
71 Way(Way&& other)
noexcept : cache(std::move(other.cache)) {}
74 Way(
const Hash& hash,
const Equal& equal) : cache(1, hash, equal) {}
76 mutable engine::Mutex mutex;
77 LruMap<T, U, Hash, Equal> cache;
80 Way& GetWay(
const T& key);
84 std::vector<Way> caches_;
86 std::shared_ptr<
dump::Dumper> dumper_{
nullptr};
89template <
typename T,
typename U,
typename Hash,
typename Eq>
90NWayLRU<T, U, Hash, Eq>::
NWayLRU(size_t ways, size_t way_size,
const Hash& hash,
const Eq& equal)
92 caches_.reserve(ways);
93 for (size_t i = 0; i < ways; ++i) caches_.emplace_back(hash, equal);
94 if (ways == 0)
throw std::logic_error(
"Ways must be positive");
96 for (
auto& way : caches_) way.cache.SetMaxSize(way_size);
99template <
typename T,
typename U,
typename Hash,
typename Eq>
100void NWayLRU<T, U, Hash, Eq>::Put(
const T& key, U value) {
101 auto& way = GetWay(key);
103 std::unique_lock<engine::Mutex> lock(way.mutex);
104 way.cache.Put(key, std::move(value));
109template <
typename T,
typename U,
typename Hash,
typename Eq>
110template <
typename Validator>
111std::optional<U> NWayLRU<T, U, Hash, Eq>::Get(
const T& key, Validator validator) {
112 auto& way = GetWay(key);
113 std::unique_lock<engine::Mutex> lock(way.mutex);
114 auto* value = way.cache.Get(key);
117 if (validator(*value))
return *value;
118 way.cache.Erase(key);
124template <
typename T,
typename U,
typename Hash,
typename Eq>
125void NWayLRU<T, U, Hash, Eq>::InvalidateByKey(
const T& key) {
126 auto& way = GetWay(key);
128 std::unique_lock<engine::Mutex> lock(way.mutex);
129 way.cache.Erase(key);
134template <
typename T,
typename U,
typename Hash,
typename Eq>
135U NWayLRU<T, U, Hash, Eq>::GetOr(
const T& key,
const U& default_value) {
136 auto& way = GetWay(key);
137 std::unique_lock<engine::Mutex> lock(way.mutex);
138 return way.cache.GetOr(key, default_value);
141template <
typename T,
typename U,
typename Hash,
typename Eq>
142void NWayLRU<T, U, Hash, Eq>::Invalidate() {
143 for (
auto& way : caches_) {
144 std::unique_lock<engine::Mutex> lock(way.mutex);
150template <
typename T,
typename U,
typename Hash,
typename Eq>
151template <
typename Function>
152void NWayLRU<T, U, Hash, Eq>::
VisitAll(Function func)
const {
153 for (
const auto& way : caches_) {
154 std::unique_lock<engine::Mutex> lock(way.mutex);
155 way.cache.VisitAll(func);
159template <
typename T,
typename U,
typename Hash,
typename Eq>
160size_t NWayLRU<T, U, Hash, Eq>::GetSize()
const {
162 for (
const auto& way : caches_) {
163 std::unique_lock<engine::Mutex> lock(way.mutex);
164 size += way.cache.GetSize();
169template <
typename T,
typename U,
typename Hash,
typename Eq>
170void NWayLRU<T, U, Hash, Eq>::
UpdateWaySize(size_t way_size) {
171 for (
auto& way : caches_) {
172 std::unique_lock<engine::Mutex> lock(way.mutex);
173 way.cache.SetMaxSize(way_size);
177template <
typename T,
typename U,
typename Hash,
typename Eq>
178typename NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
const T& key) {
183 auto seed = hash_fn_(key);
184 boost::hash_combine(seed, 0);
185 auto n = seed % caches_.size();
189template <
typename T,
typename U,
typename Hash,
typename Equal>
190void NWayLRU<T, U, Hash, Equal>::Write(
dump::
Writer& writer)
const {
191 writer.Write(caches_.size());
193 for (
const Way& way : caches_) {
194 std::unique_lock<engine::Mutex> lock(way.mutex);
196 writer.Write(way.cache.GetSize());
198 way.cache.VisitAll([&writer](
const T& key,
const U& value) {
205template <
typename T,
typename U,
typename Hash,
typename Equal>
206void NWayLRU<T, U, Hash, Equal>::Read(
dump::
Reader& reader) {
209 const auto ways = reader.Read<std::size_t>();
210 for (std::size_t i = 0; i < ways; ++i) {
211 const auto elements_in_way = reader.Read<std::size_t>();
212 for (std::size_t j = 0; j < elements_in_way; ++j) {
213 auto key = reader.Read<T>();
214 auto value = reader.Read<U>();
215 Put(std::move(key), std::move(value));
220template <
typename T,
typename U,
typename Hash,
typename Equal>
221void NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
222 if (dumper_ !=
nullptr) {
223 dumper_->OnUpdateCompleted();
227template <
typename T,
typename U,
typename Hash,
typename Equal>
228void NWayLRU<T, U, Hash, Equal>::
SetDumper(std::shared_ptr<
dump::Dumper> dumper) {
229 dumper_ = std::move(dumper);