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
22template <
typename T,
typename U,
typename Hash = std::hash<T>,
typename Equal = std::equal_to<T>>
45 NWayLRU(size_t ways, size_t way_size,
const Hash& hash = Hash(),
const Equal& equal = Equal());
50 void Put(
const T& key, U value);
59 template <
typename Validator>
60 std::optional<U>
Get(
const T& key, Validator validator);
66 std::optional<U>
Get(
const T& key) {
67 return Get(key, [](
const U&) {
return true; });
75 U
GetOr(
const T& key,
const U& default_value);
87 template <
typename Function>
115 Way(Way&& other)
noexcept : cache(std::move(other.cache)) {}
118 Way(
const Hash& hash,
const Equal& equal)
119 : cache(1, hash, equal)
122 mutable engine::Mutex mutex;
123 LruMap<T, U, Hash, Equal> cache;
126 Way& GetWay(
const T& key);
130 std::vector<Way> caches_;
132 std::shared_ptr<
dump::Dumper> dumper_{
nullptr};
135template <
typename T,
typename U,
typename Hash,
typename Eq>
136NWayLRU<T, U, Hash, Eq>::
NWayLRU(size_t ways, size_t way_size,
const Hash& hash,
const Eq& equal)
140 caches_.reserve(ways);
141 for (size_t i = 0; i < ways; ++i) {
142 caches_.emplace_back(hash, equal);
145 throw std::logic_error(
"Ways must be positive");
148 for (
auto& way : caches_) {
149 way.cache.SetMaxSize(way_size);
153template <
typename T,
typename U,
typename Hash,
typename Eq>
154void NWayLRU<T, U, Hash, Eq>::
Put(
const T& key, U value) {
155 auto& way = GetWay(key);
157 const std::unique_lock lock{way.mutex};
158 way.cache.Put(key, std::move(value));
163template <
typename T,
typename U,
typename Hash,
typename Eq>
164template <
typename Validator>
165std::optional<U> NWayLRU<T, U, Hash, Eq>::
Get(
const T& key, Validator validator) {
166 auto& way = GetWay(key);
167 const std::unique_lock lock{way.mutex};
168 auto* value = way.cache.Get(key);
171 if (validator(*value)) {
174 way.cache.Erase(key);
180template <
typename T,
typename U,
typename Hash,
typename Eq>
182 auto& way = GetWay(key);
184 const std::unique_lock lock{way.mutex};
185 way.cache.Erase(key);
190template <
typename T,
typename U,
typename Hash,
typename Eq>
191U NWayLRU<T, U, Hash, Eq>::
GetOr(
const T& key,
const U& default_value) {
192 auto& way = GetWay(key);
193 const std::unique_lock lock{way.mutex};
194 return way.cache.GetOr(key, default_value);
197template <
typename T,
typename U,
typename Hash,
typename Eq>
199 for (
auto& way : caches_) {
200 const std::unique_lock lock{way.mutex};
206template <
typename T,
typename U,
typename Hash,
typename Eq>
207template <
typename Function>
208void NWayLRU<T, U, Hash, Eq>::
VisitAll(Function func)
const {
209 for (
const auto& way : caches_) {
210 const std::unique_lock lock{way.mutex};
211 way.cache.VisitAll(func);
215template <
typename T,
typename U,
typename Hash,
typename Eq>
216size_t NWayLRU<T, U, Hash, Eq>::
GetSize()
const {
218 for (
const auto& way : caches_) {
219 const std::unique_lock lock{way.mutex};
220 size += way.cache.GetSize();
225template <
typename T,
typename U,
typename Hash,
typename Eq>
227 for (
auto& way : caches_) {
228 const std::unique_lock lock{way.mutex};
229 way.cache.SetMaxSize(way_size);
233template <
typename T,
typename U,
typename Hash,
typename Eq>
234typename NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
const T& key) {
239 auto seed = hash_fn_(key);
240 boost::hash_combine(seed, 0);
241 auto n = seed % caches_.size();
245template <
typename T,
typename U,
typename Hash,
typename Equal>
247 writer.Write(caches_.size());
249 for (
const Way& way : caches_) {
250 const std::unique_lock lock{way.mutex};
252 writer.Write(way.cache.GetSize());
254 way.cache.VisitAll([&writer](
const T& key,
const U& value) {
261template <
typename T,
typename U,
typename Hash,
typename Equal>
266 for (std::size_t i = 0; i < ways; ++i) {
267 const auto elements_in_way = reader
.Read<std
::size_t
>();
268 for (std::size_t j = 0; j < elements_in_way; ++j) {
269 auto key = reader.Read<T>();
270 auto value = reader.Read<U>();
271 Put(std::move(key)
, std::move(value)
);
276template <
typename T,
typename U,
typename Hash,
typename Equal>
277void NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
278 if (dumper_ !=
nullptr) {
283template <
typename T,
typename U,
typename Hash,
typename Equal>
284void NWayLRU<T, U, Hash, Equal>::
SetDumper(std::shared_ptr<
dump::Dumper> dumper) {
285 dumper_ = std::move(dumper);