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#include <userver/utils/datetime.hpp>
11
12USERVER_NAMESPACE_BEGIN
13
14namespace multi_index_lru {
15
16/// @ingroup userver_containers
17///
18/// @brief MultiIndex LRU expirable container
19template <typename Value, typename IndexSpecifierList, typename Allocator>
20class ExpirableContainer {
21public:
22 explicit ExpirableContainer(size_t max_size, std::chrono::milliseconds ttl)
23 : container_(max_size),
24 ttl_(ttl)
25 {
26 UINVARIANT(ttl.count() > 0, "ttl must be positive");
27 UINVARIANT(max_size > 0, "capacity must be positive");
28 }
29
30 template <typename... Args>
31 auto emplace(Args&&... args) {
32 const auto now = utils::datetime::SteadyClock::now();
33 auto result = container_.emplace(now, std::forward<Args>(args)...);
34
35 if (!result.second) {
36 result.first->last_accessed = now;
37 }
38
39 return result;
40 }
41
42 bool insert(const Value& value) { return emplace(value).second; }
43
44 bool insert(Value&& value) { return emplace(std::move(value)).second; }
45
46 template <typename Tag, typename Key>
47 auto find(const Key& key) {
48 const auto now = utils::datetime::SteadyClock::now();
49 auto it = container_.template find<Tag, Key>(key);
50
51 if (it != container_.template end<Tag>()) {
52 if (now > it->last_accessed + ttl_) {
53 container_.erase(it);
54 return end<Tag>();
55 } else {
56 it->last_accessed = now;
57 }
58 }
59
60 return impl::IteratorToValue{it};
61 }
62
63 template <typename Tag, typename Key>
64 auto find_no_update(const Key& key) const {
65 auto it = container_.template find_no_update<Tag, Key>(key);
66 return impl::IteratorToValue{it};
67 }
68
69 template <typename Tag, typename Key>
70 auto equal_range(const Key& key) {
71 const auto now = utils::datetime::SteadyClock::now();
72 auto range = container_.template equal_range<Tag, Key>(key);
73
74 auto it = range.first;
75 bool changed = false;
76
77 while (it != range.second) {
78 if (now > it->last_accessed + ttl_) {
79 it = container_.erase(it);
80 changed = true;
81 } else {
82 it->last_accessed = now;
83 ++it;
84 }
85 }
86 if (changed) {
87 range = container_.template equal_range_no_update<Tag, Key>(key);
88 }
89 return std::pair{impl::IteratorToValue{range.first}, impl::IteratorToValue{range.second}};
90 }
91
92 template <typename Tag, typename Key>
93 auto equal_range_no_update(const Key& key) const {
94 auto [begin, end] = container_.template equal_range_no_update<Tag, Key>(key);
95 return std::pair{impl::IteratorToValue{begin}, impl::IteratorToValue{end}};
96 }
97
98 template <typename Tag, typename Key>
99 bool contains(const Key& key) {
100 return this->template find<Tag, Key>(key) != this->template end<Tag>();
101 }
102
103 template <typename Tag, typename Key>
104 bool contains_no_update(const Key& key) const {
105 return this->template find_no_update<Tag, Key>(key) != this->template end<Tag>();
106 }
107
108 /// Removes the @b it from container, leaving the node in an internal pool. The key and value are not destroyed
109 /// and are reused on next insertion.
110 template <typename IteratorType>
111 bool erase(IteratorType it) {
112 return container_.erase(it);
113 }
114
115 /// Removes the @b key from container, leaving the node in an internal pool. The key and value are not destroyed
116 /// and are reused on next insertion.
117 template <typename Tag, typename Key>
118 bool erase(const Key& key) {
119 return container_.template erase<Tag, Key>(key);
120 }
121
122 std::size_t size() const noexcept { return container_.size(); }
123
124 bool empty() const noexcept { return container_.empty(); }
125
126 std::size_t capacity() const noexcept { return container_.capacity(); }
127
128 void set_capacity(std::size_t new_capacity) { container_.set_capacity(new_capacity); }
129
130 /// Clears the internal nodes pool
131 void shrink_to_fit() { container_.shrink_to_fit(); }
132
133 /// Removes all elements from the container, leaving the node in an internal pool. The keys and values are
134 /// not destroyed and are reused on next insertion.
135 void clear() { container_.clear(); }
136
137 template <typename Tag>
138 auto end() {
139 return impl::IteratorToValue{container_.template end<Tag>()};
140 }
141
142 /// @brief Removes all expired items from container
144 const auto now = utils::datetime::SteadyClock::now();
145
146 while (!container_.empty()) {
147 const auto it = container_.find_last_accessed_no_update();
148 if (now > it->last_accessed + ttl_) {
149 container_.erase(it);
150 } else {
151 break;
152 }
153 }
154 }
155
156private:
157 using CacheItem = impl::TimestampedValue<Value>;
158 using CacheContainer = Container<CacheItem, IndexSpecifierList, Allocator>;
159
160 CacheContainer container_;
161 std::chrono::milliseconds ttl_;
162};
163
164} // namespace multi_index_lru
165
166USERVER_NAMESPACE_END