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>,
20 typename Equal = std::equal_to<T>>
36 NWayLRU(size_t ways, size_t way_size,
const Hash& hash = Hash(),
37 const Equal& equal = Equal());
39 void Put(
const T& key, U value);
41 template <
typename Validator>
42 std::optional<U> Get(
const T& key, Validator validator);
44 std::optional<U> Get(
const T& key) {
45 return Get(key, [](
const U&) {
return true; });
48 U GetOr(
const T& key,
const U& default_value);
52 void InvalidateByKey(
const T& key);
55 template <
typename Function>
58 size_t GetSize()
const;
73 Way(Way&& other)
noexcept : cache(std::move(other.cache)) {}
76 Way(
const Hash& hash,
const Equal& equal) : cache(1, hash, equal) {}
78 mutable engine::Mutex mutex;
79 LruMap<T, U, Hash, Equal> cache;
82 Way& GetWay(
const T& key);
86 std::vector<Way> caches_;
88 std::shared_ptr<
dump::Dumper> dumper_{
nullptr};
91template <
typename T,
typename U,
typename Hash,
typename Eq>
92NWayLRU<T, U, Hash, Eq>::
NWayLRU(size_t ways, size_t way_size,
const Hash& hash,
95 caches_.reserve(ways);
96 for (size_t i = 0; i < ways; ++i) caches_.emplace_back(hash, equal);
97 if (ways == 0)
throw std::logic_error(
"Ways must be positive");
99 for (
auto& way : caches_) way.cache.SetMaxSize(way_size);
102template <
typename T,
typename U,
typename Hash,
typename Eq>
103void NWayLRU<T, U, Hash, Eq>::Put(
const T& key, U value) {
104 auto& way = GetWay(key);
106 std::unique_lock<engine::Mutex> lock(way.mutex);
107 way.cache.Put(key, std::move(value));
112template <
typename T,
typename U,
typename Hash,
typename Eq>
113template <
typename Validator>
114std::optional<U> NWayLRU<T, U, Hash, Eq>::Get(
const T& key,
115 Validator validator) {
116 auto& way = GetWay(key);
117 std::unique_lock<engine::Mutex> lock(way.mutex);
118 auto* value = way.cache.Get(key);
121 if (validator(*value))
return *value;
122 way.cache.Erase(key);
128template <
typename T,
typename U,
typename Hash,
typename Eq>
129void NWayLRU<T, U, Hash, Eq>::InvalidateByKey(
const T& key) {
130 auto& way = GetWay(key);
132 std::unique_lock<engine::Mutex> lock(way.mutex);
133 way.cache.Erase(key);
138template <
typename T,
typename U,
typename Hash,
typename Eq>
139U NWayLRU<T, U, Hash, Eq>::GetOr(
const T& key,
const U& default_value) {
140 auto& way = GetWay(key);
141 std::unique_lock<engine::Mutex> lock(way.mutex);
142 return way.cache.GetOr(key, default_value);
145template <
typename T,
typename U,
typename Hash,
typename Eq>
146void NWayLRU<T, U, Hash, Eq>::Invalidate() {
147 for (
auto& way : caches_) {
148 std::unique_lock<engine::Mutex> lock(way.mutex);
154template <
typename T,
typename U,
typename Hash,
typename Eq>
155template <
typename Function>
156void NWayLRU<T, U, Hash, Eq>::
VisitAll(Function func)
const {
157 for (
const auto& way : caches_) {
158 std::unique_lock<engine::Mutex> lock(way.mutex);
159 way.cache.VisitAll(func);
163template <
typename T,
typename U,
typename Hash,
typename Eq>
164size_t NWayLRU<T, U, Hash, Eq>::GetSize()
const {
166 for (
const auto& way : caches_) {
167 std::unique_lock<engine::Mutex> lock(way.mutex);
168 size += way.cache.GetSize();
173template <
typename T,
typename U,
typename Hash,
typename Eq>
174void NWayLRU<T, U, Hash, Eq>::
UpdateWaySize(size_t way_size) {
175 for (
auto& way : caches_) {
176 std::unique_lock<engine::Mutex> lock(way.mutex);
177 way.cache.SetMaxSize(way_size);
181template <
typename T,
typename U,
typename Hash,
typename Eq>
182typename NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
188 auto seed = hash_fn_(key);
189 boost::hash_combine(seed, 0);
190 auto n = seed % caches_.size();
194template <
typename T,
typename U,
typename Hash,
typename Equal>
195void NWayLRU<T, U, Hash, Equal>::Write(
dump::
Writer& writer)
const {
196 writer.Write(caches_.size());
198 for (
const Way& way : caches_) {
199 std::unique_lock<engine::Mutex> lock(way.mutex);
201 writer.Write(way.cache.GetSize());
203 way.cache.VisitAll([&writer](
const T& key,
const U& value) {
210template <
typename T,
typename U,
typename Hash,
typename Equal>
211void NWayLRU<T, U, Hash, Equal>::Read(
dump::
Reader& reader) {
214 const auto ways = reader.Read<std::size_t>();
215 for (std::size_t i = 0; i < ways; ++i) {
216 const auto elements_in_way = reader.Read<std::size_t>();
217 for (std::size_t j = 0; j < elements_in_way; ++j) {
218 auto key = reader.Read<T>();
219 auto value = reader.Read<U>();
220 Put(std::move(key), std::move(value));
225template <
typename T,
typename U,
typename Hash,
typename Equal>
226void NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
227 if (dumper_ !=
nullptr) {
228 dumper_->OnUpdateCompleted();
232template <
typename T,
typename U,
typename Hash,
typename Equal>
233void NWayLRU<T, U, Hash, Equal>::
SetDumper(
234 std::shared_ptr<
dump::Dumper> dumper) {
235 dumper_ = std::move(dumper);