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>>
38 NWayLRU(size_t ways, size_t way_size,
const Hash& hash = Hash(),
const Equal& equal = Equal());
40 void Put(
const T& key, U value);
42 template <
typename Validator>
43 std::optional<U> Get(
const T& key, Validator validator);
45 std::optional<U> Get(
const T& key) {
46 return Get(key, [](
const U&) {
return true; });
49 U GetOr(
const T& key,
const U& default_value);
53 void InvalidateByKey(
const T& key);
56 template <
typename Function>
59 size_t GetSize()
const;
74 Way(Way&& other)
noexcept : cache(std::move(other.cache)) {}
77 Way(
const Hash& hash,
const Equal& equal)
78 : cache(1, hash, equal)
81 mutable engine::Mutex mutex;
82 LruMap<T, U, Hash, Equal> cache;
85 Way& GetWay(
const T& key);
89 std::vector<Way> caches_;
91 std::shared_ptr<
dump::Dumper> dumper_{
nullptr};
94template <
typename T,
typename U,
typename Hash,
typename Eq>
95NWayLRU<T, U, Hash, Eq>::
NWayLRU(size_t ways, size_t way_size,
const Hash& hash,
const Eq& equal)
99 caches_.reserve(ways);
100 for (size_t i = 0; i < ways; ++i) {
101 caches_.emplace_back(hash, equal);
104 throw std::logic_error(
"Ways must be positive");
107 for (
auto& way : caches_) {
108 way.cache.SetMaxSize(way_size);
112template <
typename T,
typename U,
typename Hash,
typename Eq>
113void NWayLRU<T, U, Hash, Eq>::Put(
const T& key, U value) {
114 auto& way = GetWay(key);
116 const std::unique_lock<
engine::Mutex> lock(way.mutex);
117 way.cache.Put(key, std::move(value));
122template <
typename T,
typename U,
typename Hash,
typename Eq>
123template <
typename Validator>
124std::optional<U> NWayLRU<T, U, Hash, Eq>::Get(
const T& key, Validator validator) {
125 auto& way = GetWay(key);
126 const std::unique_lock<
engine::Mutex> lock(way.mutex);
127 auto* value = way.cache.Get(key);
130 if (validator(*value)) {
133 way.cache.Erase(key);
139template <
typename T,
typename U,
typename Hash,
typename Eq>
140void NWayLRU<T, U, Hash, Eq>::InvalidateByKey(
const T& key) {
141 auto& way = GetWay(key);
143 const std::unique_lock<
engine::Mutex> lock(way.mutex);
144 way.cache.Erase(key);
149template <
typename T,
typename U,
typename Hash,
typename Eq>
150U NWayLRU<T, U, Hash, Eq>::GetOr(
const T& key,
const U& default_value) {
151 auto& way = GetWay(key);
152 std::unique_lock<
engine::Mutex> lock(way.mutex);
153 return way.cache.GetOr(key, default_value);
156template <
typename T,
typename U,
typename Hash,
typename Eq>
157void NWayLRU<T, U, Hash, Eq>::Invalidate() {
158 for (
auto& way : caches_) {
159 const std::unique_lock<
engine::Mutex> lock(way.mutex);
165template <
typename T,
typename U,
typename Hash,
typename Eq>
166template <
typename Function>
167void NWayLRU<T, U, Hash, Eq>::
VisitAll(Function func)
const {
168 for (
const auto& way : caches_) {
169 std::unique_lock<
engine::Mutex> lock(way.mutex);
170 way.cache.VisitAll(func);
174template <
typename T,
typename U,
typename Hash,
typename Eq>
175size_t NWayLRU<T, U, Hash, Eq>::GetSize()
const {
177 for (
const auto& way : caches_) {
178 const std::unique_lock<
engine::Mutex> lock(way.mutex);
179 size += way.cache.GetSize();
184template <
typename T,
typename U,
typename Hash,
typename Eq>
186 for (
auto& way : caches_) {
187 const std::unique_lock<
engine::Mutex> lock(way.mutex);
188 way.cache.SetMaxSize(way_size);
192template <
typename T,
typename U,
typename Hash,
typename Eq>
193typename NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
const T& key) {
198 auto seed = hash_fn_(key);
199 boost::hash_combine(seed, 0);
200 auto n = seed % caches_.size();
204template <
typename T,
typename U,
typename Hash,
typename Equal>
205void NWayLRU<T, U, Hash, Equal>::Write(
dump::
Writer& writer)
const {
206 writer.Write(caches_.size());
208 for (
const Way& way : caches_) {
209 const std::unique_lock<
engine::Mutex> lock(way.mutex);
211 writer.Write(way.cache.GetSize());
213 way.cache.VisitAll([&writer](
const T& key,
const U& value) {
220template <
typename T,
typename U,
typename Hash,
typename Equal>
221void NWayLRU<T, U, Hash, Equal>::Read(
dump::
Reader& reader) {
225 for (std::size_t i = 0; i < ways; ++i) {
226 const auto elements_in_way = reader
.Read<std
::size_t
>();
227 for (std::size_t j = 0; j < elements_in_way; ++j) {
228 auto key = reader.Read<T>();
229 auto value = reader.Read<U>();
230 Put(std::move(key), std::move(value));
235template <
typename T,
typename U,
typename Hash,
typename Equal>
236void NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
237 if (dumper_ !=
nullptr) {
242template <
typename T,
typename U,
typename Hash,
typename Equal>
243void NWayLRU<T, U, Hash, Equal>::
SetDumper(std::shared_ptr<
dump::Dumper> dumper) {
244 dumper_ = std::move(dumper);