10#include <boost/container_hash/hash.hpp>
12#include <userver/cache/lru_map.hpp>
13#include <userver/dump/dumper.hpp>
14#include <userver/dump/operations.hpp>
15#include <userver/engine/mutex.hpp>
17USERVER_NAMESPACE_BEGIN
25template <
typename T,
typename U,
typename Hash = std::hash<T>,
typename Equal = std::equal_to<T>>
48 NWayLRU(size_t ways, size_t way_size,
const Hash& hash = Hash(),
const Equal& equal = Equal());
53 void Put(
const T& key, U value);
62 template <
typename Validator>
63 std::optional<U>
Get(
const T& key, Validator validator);
69 std::optional<U>
Get(
const T& key) {
70 return Get(key, [](
const U&) {
return true; });
78 U
GetOr(
const T& key,
const U& default_value);
90 template <
typename Function>
118 Way(Way&& other)
noexcept : cache(std::move(other.cache)) {}
121 Way(
const Hash& hash,
const Equal& equal)
122 : cache(1, hash, equal)
125 mutable engine::Mutex mutex;
126 LruMap<T, U, Hash, Equal> cache;
129 Way& GetWay(
const T& key);
133 std::vector<Way> caches_;
135 std::shared_ptr<
dump::Dumper> dumper_{
nullptr};
138template <
typename T,
typename U,
typename Hash,
typename Eq>
139NWayLRU<T, U, Hash, Eq>::
NWayLRU(size_t ways, size_t way_size,
const Hash& hash,
const Eq& equal)
143 caches_.reserve(ways);
144 for (size_t i = 0; i < ways; ++i) {
145 caches_.emplace_back(hash, equal);
148 throw std::logic_error(
"Ways must be positive");
151 for (
auto& way : caches_) {
152 way.cache.SetMaxSize(way_size);
156template <
typename T,
typename U,
typename Hash,
typename Eq>
157void NWayLRU<T, U, Hash, Eq>::
Put(
const T& key, U value) {
158 auto& way = GetWay(key);
160 const std::unique_lock lock{way.mutex};
161 way.cache.Put(key, std::move(value));
166template <
typename T,
typename U,
typename Hash,
typename Eq>
167template <
typename Validator>
168std::optional<U> NWayLRU<T, U, Hash, Eq>::
Get(
const T& key, Validator validator) {
169 auto& way = GetWay(key);
170 const std::unique_lock lock{way.mutex};
171 auto* value = way.cache.Get(key);
174 if (validator(*value)) {
177 way.cache.Erase(key);
183template <
typename T,
typename U,
typename Hash,
typename Eq>
185 auto& way = GetWay(key);
187 const std::unique_lock lock{way.mutex};
188 way.cache.Erase(key);
193template <
typename T,
typename U,
typename Hash,
typename Eq>
194U NWayLRU<T, U, Hash, Eq>::
GetOr(
const T& key,
const U& default_value) {
195 auto& way = GetWay(key);
196 const std::unique_lock lock{way.mutex};
197 return way.cache.GetOr(key, default_value);
200template <
typename T,
typename U,
typename Hash,
typename Eq>
202 for (
auto& way : caches_) {
203 const std::unique_lock lock{way.mutex};
209template <
typename T,
typename U,
typename Hash,
typename Eq>
210template <
typename Function>
211void NWayLRU<T, U, Hash, Eq>::
VisitAll(Function func)
const {
212 for (
const auto& way : caches_) {
213 const std::unique_lock lock{way.mutex};
214 way.cache.VisitAll(func);
218template <
typename T,
typename U,
typename Hash,
typename Eq>
219size_t NWayLRU<T, U, Hash, Eq>::
GetSize()
const {
221 for (
const auto& way : caches_) {
222 const std::unique_lock lock{way.mutex};
223 size += way.cache.GetSize();
228template <
typename T,
typename U,
typename Hash,
typename Eq>
230 for (
auto& way : caches_) {
231 const std::unique_lock lock{way.mutex};
232 way.cache.SetMaxSize(way_size);
236template <
typename T,
typename U,
typename Hash,
typename Eq>
237typename NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
const T& key) {
242 auto seed = hash_fn_(key);
243 boost::hash_combine(seed, 0);
244 auto n = seed % caches_.size();
248template <
typename T,
typename U,
typename Hash,
typename Equal>
250 writer.Write(caches_.size());
252 for (
const Way& way : caches_) {
253 const std::unique_lock lock{way.mutex};
255 writer.Write(way.cache.GetSize());
257 way.cache.VisitAll([&writer](
const T& key,
const U& value) {
264template <
typename T,
typename U,
typename Hash,
typename Equal>
269 for (std::size_t i = 0; i < ways; ++i) {
270 const auto elements_in_way = reader
.Read<std
::size_t
>();
271 for (std::size_t j = 0; j < elements_in_way; ++j) {
272 auto key = reader.Read<T>();
273 auto value = reader.Read<U>();
274 Put(std::move(key)
, std::move(value)
);
279template <
typename T,
typename U,
typename Hash,
typename Equal>
280void NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
281 if (dumper_ !=
nullptr) {
286template <
typename T,
typename U,
typename Hash,
typename Equal>
287void NWayLRU<T, U, Hash, Equal>::
SetDumper(std::shared_ptr<
dump::Dumper> dumper) {
288 dumper_ = std::move(dumper);