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..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 @@ -25,12 +26,23 @@ class Container { template auto emplace(Args&&... args) { auto& seq_index = get_sequenced(); - auto result = seq_index.emplace_front(std::forward(args)...); + std::pair result; + + 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 { + 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(); + free_nodes_.emplace_back(seq_index.extract(std::prev(seq_index.end()))); } return result; } @@ -91,7 +103,10 @@ class Container { template bool erase(const Key& key) { - return get_index().erase(key) > 0; + auto it = find_no_update(key); + if (it == end()) return false; + free_nodes_.emplace_back(container_.template get().extract(it)); + return true; } std::size_t size() const noexcept { return container_.size(); } @@ -102,7 +117,7 @@ class Container { max_size_ = new_capacity; auto& seq_index = get_sequenced(); while (container_.size() > max_size_) { - seq_index.pop_back(); + free_nodes_.emplace_back(seq_index.extract(std::prev(seq_index.end()))); } } @@ -122,6 +137,7 @@ class Container { BoostContainer container_; std::size_t max_size_; + std::vector free_nodes_; auto& get_sequenced() noexcept { return container_.template get<0>(); } 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..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,42 @@ UTEST_F(ExpirableUsersTest, ThreadSafetyBasic) { EXPECT_LE(cache.size(), 100); } +#ifdef NDEBUG + +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 + ); +} +#endif + } // namespace USERVER_NAMESPACE_END