Skip to content

Commit 079dc75

Browse files
hzhdlrpapolukhin
authored andcommitted
feat userver/libraries: lru multi index container prototype
Draft LRU cache prototype done on top of Boost.MultiIndex and Boost.Intrusive. More docs, tests, samples, features and cleanups to come soon... Relates: #451 --- Pull Request resolved: #1048 Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> Co-authored-by: antoshkka <[email protected]> commit_hash:53108456407815176e2233fa327b94aceaabb374
1 parent 53b2f4d commit 079dc75

File tree

11 files changed

+561
-0
lines changed

11 files changed

+561
-0
lines changed

.mapping.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,7 @@
562562
"cmake/install/userver-grpc-reflection-config.cmake":"taxi/uservices/userver/cmake/install/userver-grpc-reflection-config.cmake",
563563
"cmake/install/userver-kafka-config.cmake":"taxi/uservices/userver/cmake/install/userver-kafka-config.cmake",
564564
"cmake/install/userver-mongo-config.cmake":"taxi/uservices/userver/cmake/install/userver-mongo-config.cmake",
565+
"cmake/install/userver-multi-index-lru-config.cmake":"taxi/uservices/userver/cmake/install/userver-multi-index-lru-config.cmake",
565566
"cmake/install/userver-mysql-config.cmake":"taxi/uservices/userver/cmake/install/userver-mysql-config.cmake",
566567
"cmake/install/userver-odbc-config.cmake":"taxi/uservices/userver/cmake/install/userver-odbc-config.cmake",
567568
"cmake/install/userver-otlp-config.cmake":"taxi/uservices/userver/cmake/install/userver-otlp-config.cmake",
@@ -2688,6 +2689,12 @@
26882689
"libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.cpp":"taxi/uservices/userver/libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.cpp",
26892690
"libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.hpp":"taxi/uservices/userver/libraries/grpc-reflection/src/grpc-reflection/proto_server_reflection.hpp",
26902691
"libraries/grpc-reflection/src/grpc-reflection/reflection_service_component.cpp":"taxi/uservices/userver/libraries/grpc-reflection/src/grpc-reflection/reflection_service_component.cpp",
2692+
"libraries/multi-index-lru/CMakeLists.txt":"taxi/uservices/userver/libraries/multi-index-lru/CMakeLists.txt",
2693+
"libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp":"taxi/uservices/userver/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp",
2694+
"libraries/multi-index-lru/library.yaml":"taxi/uservices/userver/libraries/multi-index-lru/library.yaml",
2695+
"libraries/multi-index-lru/src/container.cpp":"taxi/uservices/userver/libraries/multi-index-lru/src/container.cpp",
2696+
"libraries/multi-index-lru/src/main_benchmark.cpp":"taxi/uservices/userver/libraries/multi-index-lru/src/main_benchmark.cpp",
2697+
"libraries/multi-index-lru/src/main_test.cpp":"taxi/uservices/userver/libraries/multi-index-lru/src/main_test.cpp",
26912698
"libraries/proto-structs/codegen-tests/proto/box/autobox/cycles.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/cycles.proto",
26922699
"libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_nested.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_nested.proto",
26932700
"libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_self.proto":"taxi/uservices/userver/libraries/proto-structs/codegen-tests/proto/box/autobox/dependency_on_self.proto",
@@ -4236,6 +4243,7 @@
42364243
"scripts/docs/en/userver/kafka.md":"taxi/uservices/userver/scripts/docs/en/userver/kafka.md",
42374244
"scripts/docs/en/userver/libraries/easy.md":"taxi/uservices/userver/scripts/docs/en/userver/libraries/easy.md",
42384245
"scripts/docs/en/userver/libraries/grpc-reflection.md":"taxi/uservices/userver/scripts/docs/en/userver/libraries/grpc-reflection.md",
4246+
"scripts/docs/en/userver/libraries/multi_index_lru.md":"taxi/uservices/userver/scripts/docs/en/userver/libraries/multi_index_lru.md",
42394247
"scripts/docs/en/userver/libraries/s3api.md":"taxi/uservices/userver/scripts/docs/en/userver/libraries/s3api.md",
42404248
"scripts/docs/en/userver/log_level_running_service.md":"taxi/uservices/userver/scripts/docs/en/userver/log_level_running_service.md",
42414249
"scripts/docs/en/userver/logging.md":"taxi/uservices/userver/scripts/docs/en/userver/logging.md",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
include_guard(GLOBAL)
2+
3+
if(userver_multi_index_lru_FOUND)
4+
return()
5+
endif()
6+
7+
find_package(userver REQUIRED COMPONENTS core)
8+
9+
find_package(Boost REQUIRED)
10+
11+
set(userver_multi_index_lru_FOUND TRUE)

libraries/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ option(USERVER_FEATURE_EASY "Build easy HTTP server library" "${USERVER_LIB_ENAB
22
option(USERVER_FEATURE_S3API "Build S3 api client library" "${USERVER_LIB_ENABLED_DEFAULT}")
33
option(USERVER_FEATURE_GRPC_REFLECTION "Build grpc reflection library" "${USERVER_LIB_ENABLED_DEFAULT}")
44
option(USERVER_FEATURE_GRPC_PROTOVALIDATE "Build grpc protovalidate library" "OFF")
5+
option(USERVER_FEATURE_MULTI_INDEX_LRU "Build multi index lru library" "${USERVER_LIB_ENABLED_DEFAULT}")
56

67
if(USERVER_FEATURE_S3API)
78
add_subdirectory(s3api)
89
endif()
910

11+
if (USERVER_FEATURE_MULTI_INDEX_LRU)
12+
add_subdirectory(multi-index-lru)
13+
endif()
14+
1015
if(USERVER_FEATURE_EASY)
1116
if(NOT USERVER_FEATURE_POSTGRESQL)
1217
message(FATAL_ERROR "'USERVER_FEATURE_EASY' requires 'USERVER_FEATURE_POSTGRESQL=ON'")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
project(userver-multi-index-lru CXX)
2+
3+
find_package(Boost REQUIRED)
4+
5+
userver_module(
6+
multi-index-lru
7+
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
8+
UTEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*_test.cpp"
9+
UBENCH_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*_benchmark.cpp"
10+
DEPENDS core
11+
)
12+
13+
target_include_directories(userver-multi-index-lru
14+
PUBLIC
15+
${Boost_INCLUDE_DIRS}
16+
PRIVATE
17+
"${CMAKE_CURRENT_SOURCE_DIR}/tests/"
18+
)
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#pragma once
2+
3+
/// @file userver/multi-index-lru/container.hpp
4+
/// @brief @copybrief multi_index_lru::Container
5+
6+
#include <boost/intrusive/link_mode.hpp>
7+
#include <boost/intrusive/list.hpp>
8+
#include <boost/intrusive/list_hook.hpp>
9+
#include <boost/multi_index/hashed_index.hpp>
10+
#include <boost/multi_index/mem_fun.hpp>
11+
#include <boost/multi_index/member.hpp>
12+
#include <boost/multi_index/ordered_index.hpp>
13+
#include <boost/multi_index/tag.hpp>
14+
#include <boost/multi_index_container.hpp>
15+
16+
#include <cstddef>
17+
#include <utility>
18+
19+
USERVER_NAMESPACE_BEGIN
20+
21+
namespace multi_index_lru {
22+
23+
namespace impl {
24+
25+
template <typename Value>
26+
struct ValueWithHook {
27+
Value value;
28+
mutable boost::intrusive::list_member_hook<> list_hook;
29+
30+
const ValueWithHook* GetPointerToSelf() const { return this; };
31+
32+
explicit ValueWithHook(const Value& val) : value(val) {}
33+
34+
explicit ValueWithHook(Value&& val) : value(std::move(val)) {}
35+
36+
ValueWithHook() = delete;
37+
ValueWithHook(const ValueWithHook&) = delete;
38+
ValueWithHook(ValueWithHook&&) = delete;
39+
40+
ValueWithHook& operator=(const ValueWithHook&) = delete;
41+
ValueWithHook& operator=(ValueWithHook&&) = delete;
42+
43+
operator Value&() { return value; }
44+
operator const Value&() const { return value; }
45+
46+
Value* operator->() { return &value; }
47+
const Value* operator->() const { return &value; }
48+
49+
Value& get() { return value; }
50+
const Value& get() const { return value; }
51+
};
52+
53+
template <class List, class Node>
54+
void PushBackToList(List& lst, const Node& node) {
55+
lst.push_back(const_cast<Node&>(node)); // TODO:
56+
}
57+
58+
template <class List, class Node>
59+
void SpliceInList(List& lst, Node& node) {
60+
lst.splice(lst.end(), lst, lst.iterator_to(node));
61+
}
62+
63+
struct InternalPtrTag {};
64+
65+
} // namespace impl
66+
67+
/// @ingroup userver_containers
68+
///
69+
/// @brief MultiIndex LRU container
70+
template <typename Value, typename IndexSpecifierList, typename Allocator = std::allocator<impl::ValueWithHook<Value>>>
71+
class Container {
72+
public:
73+
explicit Container(size_t max_size) : max_size(max_size) {}
74+
75+
template <typename... Args>
76+
bool emplace(Args&&... args) {
77+
if (container.size() >= max_size) {
78+
EvictLru();
79+
}
80+
81+
auto result = container.emplace(std::forward<Args>(args)...);
82+
83+
auto& value = *result.first;
84+
if (result.second) {
85+
impl::PushBackToList(usage_list, value);
86+
} else {
87+
impl::SpliceInList(usage_list, value);
88+
}
89+
return result.second;
90+
}
91+
92+
bool insert(const Value& value) { return emplace(value); }
93+
94+
bool insert(Value&& value) { return emplace(std::move(value)); }
95+
96+
template <typename Tag, typename Key>
97+
auto find(const Key& key) {
98+
auto& primary_index = container.template get<Tag>();
99+
auto it = primary_index.find(key);
100+
101+
if (it != primary_index.end()) {
102+
impl::SpliceInList(usage_list, *it);
103+
}
104+
105+
return it;
106+
}
107+
108+
template <typename Tag>
109+
auto end() {
110+
return container.template get<Tag>().end();
111+
}
112+
113+
template <typename Tag, typename Key>
114+
bool contains(const Key& key) {
115+
return this->template find<Tag, Key>(key) != container.template get<Tag>().end();
116+
}
117+
118+
template <typename Tag, typename Key>
119+
bool erase(const Key& key) {
120+
auto& primary_index = container.template get<Tag>();
121+
auto it = primary_index.find(key);
122+
if (it != primary_index.end()) {
123+
usage_list.erase(usage_list.iterator_to(*it));
124+
}
125+
return container.template get<Tag>().erase(key) > 0;
126+
}
127+
128+
std::size_t size() const { return container.size(); }
129+
bool empty() const { return container.empty(); }
130+
std::size_t capacity() const { return max_size; }
131+
132+
void set_capacity(size_t new_capacity) {
133+
max_size = new_capacity;
134+
while (container.size() > max_size) {
135+
EvictLru();
136+
}
137+
}
138+
139+
void clear() { container.clear(); }
140+
141+
private:
142+
using CacheItem = impl::ValueWithHook<Value>;
143+
using List = boost::intrusive::list<
144+
CacheItem,
145+
boost::intrusive::member_hook<CacheItem, boost::intrusive::list_member_hook<>, &CacheItem::list_hook>>;
146+
147+
using ExtendedIndexSpecifierList = typename boost::mpl::push_back<
148+
IndexSpecifierList,
149+
boost::multi_index::hashed_unique<
150+
boost::multi_index::tag<impl::InternalPtrTag>,
151+
boost::multi_index::const_mem_fun<CacheItem, const CacheItem*, &CacheItem::GetPointerToSelf>>>::type;
152+
153+
using BoostContainer = boost::multi_index::multi_index_container<CacheItem, ExtendedIndexSpecifierList, Allocator>;
154+
155+
void EvictLru() {
156+
if (!usage_list.empty()) {
157+
CacheItem* ptr_to_erase = &*usage_list.begin();
158+
usage_list.erase(usage_list.begin());
159+
container.template get<impl::InternalPtrTag>().erase(ptr_to_erase);
160+
}
161+
}
162+
163+
BoostContainer container;
164+
std::size_t max_size;
165+
List usage_list;
166+
};
167+
} // namespace multi_index_lru
168+
169+
USERVER_NAMESPACE_END
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
project-name: userver-lib-multi-index-lru
2+
3+
description: multi index lru cache
4+
5+
maintainers:
6+
- Common components
7+
8+
libraries:
9+
- userver-core
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <userver/multi-index-lru/container.hpp>

0 commit comments

Comments
 (0)