userver: /data/code/userver/libraries/multi-index-lru/include/userver/multi-index-lru/expirable_container.hpp Source File
Loading...
Searching...
No Matches
expirable_container.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/multi-index-lru/expirable_container.hpp
4/// @brief @copybrief multi_index_lru::ExpirableContainer
5
6#include "container.hpp"
7#include "impl/mpl_helpers.hpp"
8
9#include <userver/utils/assert.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace multi_index_lru {
14
15/// @ingroup userver_containers
16///
17/// @brief MultiIndex LRU expirable container
18template <typename Value, typename IndexSpecifierList, typename Allocator>
19class ExpirableContainer {
20public:
21 explicit ExpirableContainer(size_t max_size, std::chrono::milliseconds ttl)
22 : container_(max_size),
23 ttl_(ttl)
24 {
25 UASSERT_MSG(ttl.count() > 0, "ttl must be positive");
26 }
27
28 template <typename... Args>
29 auto emplace(Args&&... args) {
30 auto result = container_.emplace(std::forward<Args>(args)...);
31
32 if (!result.second) {
33 result.first->last_accessed = std::chrono::steady_clock::now();
34 }
35
36 return result;
37 }
38
39 bool insert(const Value& value) { return emplace(value).second; }
40
41 bool insert(Value&& value) { return emplace(std::move(value)).second; }
42
43 template <typename Tag, typename Key>
44 auto find(const Key& key) {
45 const auto now = std::chrono::steady_clock::now();
46 auto it = container_.template find<Tag, Key>(key);
47
48 if (it != container_.template end<Tag>()) {
49 if (now > it->last_accessed + ttl_) {
50 container_.template get_index<Tag>().erase(it);
51 return end<Tag>();
52 } else {
53 it->last_accessed = now;
54 }
55 }
56
57 return impl::IteratorToValue{it};
58 }
59
60 template <typename Tag, typename Key>
61 auto find_no_update(const Key& key) const {
62 auto it = container_.template find_no_update<Tag, Key>(key);
63 return impl::IteratorToValue{it};
64 }
65
66 template <typename Tag, typename Key>
67 auto equal_range(const Key& key) {
68 const auto now = std::chrono::steady_clock::now();
69 auto& index = container_.template get_index<Tag>();
70 auto range = container_.template equal_range<Tag, Key>(key);
71
72 auto it = range.first;
73 bool changed = false;
74
75 while (it != range.second) {
76 if (now > it->last_accessed + ttl_) {
77 it = index.erase(it);
78 changed = true;
79 } else {
80 it->last_accessed = now;
81 ++it;
82 }
83 }
84 if (changed) {
85 range = index.equal_range(key);
86 }
87 return std::pair{impl::IteratorToValue{range.first}, impl::IteratorToValue{range.second}};
88 }
89
90 template <typename Tag, typename Key>
91 auto equal_range_no_update(const Key& key) const {
92 auto [begin, end] = container_.template equal_range_no_update<Tag, Key>(key);
93 return std::pair{impl::IteratorToValue{begin}, impl::IteratorToValue{end}};
94 }
95
96 template <typename Tag, typename Key>
97 bool contains(const Key& key) {
98 return this->template find<Tag, Key>(key) != this->template end<Tag>();
99 }
100
101 template <typename Tag, typename Key>
102 bool contains_no_update(const Key& key) const {
103 return this->template find_no_update<Tag, Key>(key) != this->template end<Tag>();
104 }
105
106 template <typename Tag, typename Key>
107 bool erase(const Key& key) {
108 return container_.template erase<Tag, Key>(key);
109 }
110
111 std::size_t size() const noexcept { return container_.size(); }
112 bool empty() const noexcept { return container_.empty(); }
113 std::size_t capacity() const noexcept { return container_.capacity(); }
114
115 void set_capacity(std::size_t new_capacity) { container_.set_capacity(new_capacity); }
116
117 void clear() { container_.clear(); }
118
119 template <typename Tag>
120 auto end() {
121 return impl::IteratorToValue{container_.template end<Tag>()};
122 }
123
124 void cleanup_expired() {
125 const auto now = std::chrono::steady_clock::now();
126 auto& seq_index = container_.get_sequenced();
127
128 while (!seq_index.empty()) {
129 const auto it = seq_index.rbegin();
130 if (now > it->last_accessed + ttl_) {
131 seq_index.pop_back();
132 } else {
133 break;
134 }
135 }
136 }
137
138private:
139 using CacheItem = impl::TimestampedValue<Value>;
140 using CacheContainer = Container<CacheItem, IndexSpecifierList, Allocator>;
141
142 CacheContainer container_;
143 std::chrono::milliseconds ttl_;
144};
145
146} // namespace multi_index_lru
147
148USERVER_NAMESPACE_END