Skip to content

Commit 7e3904f

Browse files
committed
refactoring
1 parent 6edff0d commit 7e3904f

File tree

4 files changed

+89
-21
lines changed

4 files changed

+89
-21
lines changed

libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ class Container {
110110
return container_.template get<Tag>();
111111
}
112112

113+
template <typename IterT>
114+
auto project_to_sequenced(IterT it) {
115+
return container_.template project<0>(it);
116+
}
117+
113118
template <typename V, typename I, typename A>
114119
friend class ExpirableContainer;
115120
};

libraries/multi-index-lru/include/userver/multi-index-lru/expirable_container.hpp

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <cassert>
88
#include <shared_mutex>
99
#include <mutex>
10-
#include <optional>
10+
#include <vector>
1111

1212
#include "impl/mpl_helpers.hpp"
1313
#include "container.hpp"
@@ -63,19 +63,33 @@ class ExpirableContainer {
6363
bool insert(Value&& value) { return emplace(std::move(value)).second; }
6464

6565
template <typename Tag, typename Key>
66-
std::optional<Value> get(const Key& key) {
66+
std::vector<Value> get(const Key& key) {
67+
auto now = std::chrono::steady_clock::now();
6768
std::lock_guard<userver::engine::SharedMutex> lock(mutex_);
68-
auto it = find<Tag, Key>(lock, key);
69-
if (it == end<Tag>()) {
70-
return std::nullopt;
69+
70+
std::vector<Value> result;
71+
auto& index = container_.template get_index<Tag>();
72+
73+
if constexpr (impl::is_unique_index<decltype(index)>::value) {
74+
auto it = find<Tag, Key>(lock, key, now);
75+
if (it != container_.template end<Tag>()) {
76+
result.push_back(it->value);
77+
}
78+
} else {
79+
auto range = find_range<Tag, Key>(lock, key, now);
80+
for (auto it = range.first; it != range.second; ++it) {
81+
result.push_back(it->value);
82+
}
7183
}
72-
return *it;
84+
85+
return result;
7386
}
7487

7588
template <typename Tag, typename Key>
7689
bool contains(const Key& key) {
90+
auto now = std::chrono::steady_clock::now();
7791
std::lock_guard<userver::engine::SharedMutex> lock(mutex_);
78-
return this->template find<Tag, Key>(lock, key) != container_.template end<Tag>();
92+
return find<Tag, Key>(lock, key, now) != container_.template end<Tag>();
7993
}
8094

8195
template <typename Tag, typename Key>
@@ -117,19 +131,55 @@ class ExpirableContainer {
117131
using CacheContainer = Container<CacheItem, IndexSpecifierList, Allocator>;
118132

119133
template <typename Tag, typename Key>
120-
auto find(std::lock_guard<userver::engine::SharedMutex>&, const Key& key) {
134+
auto find(std::lock_guard<userver::engine::SharedMutex>&,
135+
const Key& key,
136+
std::chrono::steady_clock::time_point now) {
121137
auto it = container_.template find<Tag, Key>(key);
122138

123-
if (it != container_.template end<Tag>()) {
124-
if (std::chrono::steady_clock::now() > it->last_accessed + ttl_) {
139+
if (it != end<Tag>()) {
140+
if (now > it->last_accessed + ttl_) {
125141
container_.template get_index<Tag>().erase(it);
126-
return impl::TimestampedIteratorWrapper{container_.template end<Tag>()};
142+
return end<Tag>();
143+
} else {
144+
it->last_accessed = now;
127145
}
128-
129-
it->last_accessed = std::chrono::steady_clock::now();
130146
}
147+
148+
return it;
149+
}
131150

132-
return impl::TimestampedIteratorWrapper{it};
151+
template <typename Tag, typename Key>
152+
auto find_range(std::lock_guard<userver::engine::SharedMutex>&,
153+
const Key& key,
154+
std::chrono::steady_clock::time_point now) {
155+
auto& index = container_.template get_index<Tag>();
156+
auto [begin, end] = index.equal_range(key);
157+
158+
auto it = begin;
159+
std::vector<decltype(it)> to_erase;
160+
std::vector<decltype(it)> to_move;
161+
162+
while (it != end) {
163+
if (now > it->last_accessed + ttl_) {
164+
to_erase.push_back(it);
165+
++it;
166+
} else {
167+
it->last_accessed = now;
168+
to_move.push_back(it);
169+
++it;
170+
}
171+
}
172+
173+
for (auto erase_it : to_erase) {
174+
index.erase(erase_it);
175+
}
176+
177+
auto& seq_index = container_.get_sequensed();
178+
for (auto move_it : to_move) {
179+
seq_index.relocate(seq_index.begin(), container_.project_to_sequenced(move_it));
180+
}
181+
182+
return index.equal_range(key);
133183
}
134184

135185
void cleanup() {
@@ -174,7 +224,6 @@ class ExpirableContainer {
174224
userver::engine::Task cleanup_task_;
175225
};
176226

177-
178227
} // namespace multi_index_lru
179228

180229
USERVER_NAMESPACE_END

libraries/multi-index-lru/include/userver/multi-index-lru/impl/mpl_helpers.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ class TimestampedIteratorWrapper : public Iterator {
9393
return *(this->Iterator::operator->());
9494
}
9595
};
96+
97+
template<typename T>
98+
struct is_unique_index {
99+
private:
100+
template<typename U>
101+
static auto test(int) -> decltype(std::declval<U>().find(std::declval<typename U::key_type>()),
102+
std::true_type{});
103+
104+
template<typename>
105+
static std::false_type test(...);
106+
107+
public:
108+
static constexpr bool value = decltype(test<T>(0))::value;
109+
};
96110
} // namespace impl
97111
} // namespace multi_index_lru
98112

libraries/multi-index-lru/src/expirable_container_test.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,18 @@ UTEST_F(ExpirableUsersTest, BasicOperations) {
5959

6060
// Test get by id
6161
auto by_id = cache.get<IdTag>(1);
62-
EXPECT_TRUE(by_id.has_value());
63-
EXPECT_EQ(by_id->name, "Alice");
62+
EXPECT_FALSE(by_id.empty());
63+
EXPECT_EQ(by_id.begin()->name, "Alice");
6464

6565
// Test get by email
6666
auto by_email = cache.get<EmailTag>("bob@test.com");
67-
EXPECT_TRUE(by_email.has_value());
68-
EXPECT_EQ(by_email->id, 2);
67+
EXPECT_FALSE(by_email.empty());
68+
EXPECT_EQ(by_email.begin()->id, 2);
6969

7070
// Test get by name
7171
auto by_name = cache.get<NameTag>("Charlie");
72-
EXPECT_TRUE(by_name.has_value());
73-
EXPECT_EQ(by_name->email, "charlie@test.com");
72+
EXPECT_FALSE(by_name.empty());
73+
EXPECT_EQ(by_name.begin()->email, "charlie@test.com");
7474
}
7575

7676
UTEST_F(ExpirableUsersTest, LRUEviction) {

0 commit comments

Comments
 (0)