From c8cf3a09197618155f71db9cfec2f0ed960c2352 Mon Sep 17 00:00:00 2001 From: Vadim Leonov Date: Tue, 10 Mar 2026 19:10:41 +0300 Subject: [PATCH 1/4] bondary tests --- .../multi-index-lru/expirable_container.hpp | 3 +- .../src/expirable_container_test.cpp | 33 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/libraries/multi-index-lru/include/userver/multi-index-lru/expirable_container.hpp b/libraries/multi-index-lru/include/userver/multi-index-lru/expirable_container.hpp index 07c5bbff091f..0f18ed8c000b 100644 --- a/libraries/multi-index-lru/include/userver/multi-index-lru/expirable_container.hpp +++ b/libraries/multi-index-lru/include/userver/multi-index-lru/expirable_container.hpp @@ -22,7 +22,8 @@ class ExpirableContainer { : container_(max_size), ttl_(ttl) { - UASSERT_MSG(ttl.count() > 0, "ttl must be positive"); + UINVARIANT(ttl.count() > 0, "ttl must be positive"); + UINVARIANT(max_size > 0, "capacity must be positive"); } template diff --git a/libraries/multi-index-lru/src/expirable_container_test.cpp b/libraries/multi-index-lru/src/expirable_container_test.cpp index d83ab4c3e7dc..12b4f75f36af 100644 --- a/libraries/multi-index-lru/src/expirable_container_test.cpp +++ b/libraries/multi-index-lru/src/expirable_container_test.cpp @@ -305,6 +305,39 @@ UTEST_F(ExpirableUsersTest, ThreadSafetyBasic) { EXPECT_LE(cache.size(), 100); } +UTEST_F(ExpirableUsersTest, ZeroTTL) { + using namespace std::chrono_literals; + + EXPECT_THROW( + { + UserCacheExpirable cache(10, 0ms); + }, + utils::InvariantError + ); +} + +UTEST_F(ExpirableUsersTest, ZeroCapacity) { + using namespace std::chrono_literals; + + EXPECT_THROW( + { + UserCacheExpirable cache(0, 10s); + }, + utils::InvariantError + ); +} + +UTEST_F(ExpirableUsersTest, NegativeTTL) { + using namespace std::chrono_literals; + + EXPECT_THROW( + { + UserCacheExpirable cache(10, -1ms); + }, + utils::InvariantError + ); +} + } // namespace USERVER_NAMESPACE_END From 3d0e37528bb28552e5673d957f4ed2d8379e2555 Mon Sep 17 00:00:00 2001 From: Vadim Leonov Date: Sun, 22 Mar 2026 00:22:55 +0300 Subject: [PATCH 2/4] nodes extraction for reusing! --- .../userver/multi-index-lru/container.hpp | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp b/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp index d5cda44a3491..62811c0182ac 100644 --- a/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp +++ b/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp @@ -24,13 +24,23 @@ class Container { template auto emplace(Args&&... args) { + auto& nodes_handler_index = nodes_handler_.template get<0>(); auto& seq_index = get_sequenced(); - auto result = seq_index.emplace_front(std::forward(args)...); + std::pair result; + + if (nodes_handler_.size() > 0) { + auto node = nodes_handler_index.extract(std::prev(nodes_handler_index.end())); + new (&node.value()) Value(std::forward(args)...); + auto ret = seq_index.insert(container_.end(), std::move(node)); + result = {ret.position, ret.inserted}; + } else { + result = seq_index.emplace_front(std::forward(args)...); + } if (!result.second) { seq_index.relocate(seq_index.begin(), result.first); } else if (seq_index.size() > max_size_) { - seq_index.pop_back(); + nodes_handler_index.insert(nodes_handler_index.end(), std::move(seq_index.extract(result.first))); } return result; } @@ -91,7 +101,10 @@ class Container { template bool erase(const Key& key) { - return get_index().erase(key) > 0; + auto it = find_no_update; + if (it == end()) return false; + nodes_handler_.insert(nodes_handler_.end(), std::move(container_.template get.extract(it))); + return true; } std::size_t size() const noexcept { return container_.size(); } @@ -102,7 +115,7 @@ class Container { max_size_ = new_capacity; auto& seq_index = get_sequenced(); while (container_.size() > max_size_) { - seq_index.pop_back(); + nodes_handler_.insert(nodes_handler_.end(), seq_index.extract(std::prev(seq_index.end()))); } } @@ -122,6 +135,7 @@ class Container { BoostContainer container_; std::size_t max_size_; + BoostContainer nodes_handler_; auto& get_sequenced() noexcept { return container_.template get<0>(); } From ce84fd37279118c5300c44abc119b43e14f66248 Mon Sep 17 00:00:00 2001 From: Vadim Leonov Date: Sun, 22 Mar 2026 00:34:30 +0300 Subject: [PATCH 3/4] fix --- .../include/userver/multi-index-lru/container.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp b/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp index 62811c0182ac..31b1f1b130c6 100644 --- a/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp +++ b/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp @@ -32,7 +32,7 @@ class Container { if (nodes_handler_.size() > 0) { auto node = nodes_handler_index.extract(std::prev(nodes_handler_index.end())); new (&node.value()) Value(std::forward(args)...); - auto ret = seq_index.insert(container_.end(), std::move(node)); + auto ret = seq_index.insert(seq_index.begin(), std::move(node)); result = {ret.position, ret.inserted}; } else { result = seq_index.emplace_front(std::forward(args)...); @@ -40,7 +40,7 @@ class Container { if (!result.second) { seq_index.relocate(seq_index.begin(), result.first); } else if (seq_index.size() > max_size_) { - nodes_handler_index.insert(nodes_handler_index.end(), std::move(seq_index.extract(result.first))); + nodes_handler_index.insert(nodes_handler_index.end(), std::move(seq_index.extract(std::prev(seq_index.end())))); } return result; } @@ -101,9 +101,9 @@ class Container { template bool erase(const Key& key) { - auto it = find_no_update; + auto it = find_no_update(key); if (it == end()) return false; - nodes_handler_.insert(nodes_handler_.end(), std::move(container_.template get.extract(it))); + nodes_handler_.insert(nodes_handler_.end(), std::move(container_.template get().extract(it))); return true; } From dddf87718c760c69e9b9daaec147f3e776e1c5bb Mon Sep 17 00:00:00 2001 From: Vadim Leonov Date: Sun, 22 Mar 2026 09:49:09 +0300 Subject: [PATCH 4/4] fxes --- .../userver/multi-index-lru/container.hpp | 18 ++++++++++-------- .../src/expirable_container_test.cpp | 3 +++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp b/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp index 31b1f1b130c6..e6fdac1b20fd 100644 --- a/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp +++ b/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp @@ -4,6 +4,7 @@ /// @brief @copybrief multi_index_lru::Container #include "impl/mpl_helpers.hpp" +#include USERVER_NAMESPACE_BEGIN @@ -24,14 +25,15 @@ class Container { template auto emplace(Args&&... args) { - auto& nodes_handler_index = nodes_handler_.template get<0>(); auto& seq_index = get_sequenced(); std::pair result; - if (nodes_handler_.size() > 0) { - auto node = nodes_handler_index.extract(std::prev(nodes_handler_index.end())); - new (&node.value()) Value(std::forward(args)...); + if (free_nodes_.size() > 0) { + auto node = std::move(free_nodes_.back()); + free_nodes_.pop_back(); + + node.value() = Value(std::forward(args)...); auto ret = seq_index.insert(seq_index.begin(), std::move(node)); result = {ret.position, ret.inserted}; } else { @@ -40,7 +42,7 @@ class Container { if (!result.second) { seq_index.relocate(seq_index.begin(), result.first); } else if (seq_index.size() > max_size_) { - nodes_handler_index.insert(nodes_handler_index.end(), std::move(seq_index.extract(std::prev(seq_index.end())))); + free_nodes_.emplace_back(seq_index.extract(std::prev(seq_index.end()))); } return result; } @@ -103,7 +105,7 @@ class Container { bool erase(const Key& key) { auto it = find_no_update(key); if (it == end()) return false; - nodes_handler_.insert(nodes_handler_.end(), std::move(container_.template get().extract(it))); + free_nodes_.emplace_back(container_.template get().extract(it)); return true; } @@ -115,7 +117,7 @@ class Container { max_size_ = new_capacity; auto& seq_index = get_sequenced(); while (container_.size() > max_size_) { - nodes_handler_.insert(nodes_handler_.end(), seq_index.extract(std::prev(seq_index.end()))); + free_nodes_.emplace_back(seq_index.extract(std::prev(seq_index.end()))); } } @@ -135,7 +137,7 @@ class Container { BoostContainer container_; std::size_t max_size_; - BoostContainer nodes_handler_; + std::vector free_nodes_; auto& get_sequenced() noexcept { return container_.template get<0>(); } diff --git a/libraries/multi-index-lru/src/expirable_container_test.cpp b/libraries/multi-index-lru/src/expirable_container_test.cpp index 12b4f75f36af..364487bb5b3a 100644 --- a/libraries/multi-index-lru/src/expirable_container_test.cpp +++ b/libraries/multi-index-lru/src/expirable_container_test.cpp @@ -305,6 +305,8 @@ UTEST_F(ExpirableUsersTest, ThreadSafetyBasic) { EXPECT_LE(cache.size(), 100); } +#ifdef NDEBUG + UTEST_F(ExpirableUsersTest, ZeroTTL) { using namespace std::chrono_literals; @@ -337,6 +339,7 @@ UTEST_F(ExpirableUsersTest, NegativeTTL) { utils::InvariantError ); } +#endif } // namespace