diff options
author | Alexey Efimov <[email protected]> | 2022-06-01 20:41:24 +0300 |
---|---|---|
committer | Alexey Efimov <[email protected]> | 2022-06-01 20:41:24 +0300 |
commit | 3d80db4ae60a700529a4e45d782862bb7d58e004 (patch) | |
tree | ae59ae9f47e61af61bbf3376877e70b8b86dd528 | |
parent | 81eb0537a61f07cbf6835310688daff656c8b69c (diff) |
add not so simple cache with lifetime checking KIKIMR-15015
ref:607a2c25f091e77893b82640122a16daa6ae3b1a
-rw-r--r-- | ydb/core/protos/auth.proto | 4 | ||||
-rw-r--r-- | ydb/core/util/simple_cache.h | 93 | ||||
-rw-r--r-- | ydb/core/util/simple_cache_ut.cpp | 31 |
3 files changed, 126 insertions, 2 deletions
diff --git a/ydb/core/protos/auth.proto b/ydb/core/protos/auth.proto index 7ba2bc4bd54..ced6fc9bcc4 100644 --- a/ydb/core/protos/auth.proto +++ b/ydb/core/protos/auth.proto @@ -15,8 +15,8 @@ message TAuthConfig { optional uint64 GrpcErrorLifeTime = 18 [default = 10000]; // ms optional bool UseBlackBox = 20 [default = true]; optional bool UseAccessService = 21 [default = false]; - optional bool CacheAccessServiceAuthentication = 22 [default = true]; - optional bool CacheAccessServiceAuthorization = 23 [default = true]; + optional bool CacheAccessServiceAuthentication = 22 [default = false]; + optional bool CacheAccessServiceAuthorization = 23 [default = false]; optional bool UseStaff = 25 [default = true]; optional bool UseUserAccountService = 26 [default = false]; optional bool UseServiceAccountService = 27 [default = false]; diff --git a/ydb/core/util/simple_cache.h b/ydb/core/util/simple_cache.h index 4190e8bb762..5083ba369e2 100644 --- a/ydb/core/util/simple_cache.h +++ b/ydb/core/util/simple_cache.h @@ -71,4 +71,97 @@ protected: TList List_; }; +template <typename TKey, typename TValue> +class TNotSoSimpleCache { +public: + size_t MaxSize = 1024; + + struct TItem; + + using TMap = std::unordered_map<TKey, TItem>; + using TMapIterator = typename TMap::iterator; + using TList = typename std::list<TMapIterator>; + using TListIterator = typename TList::iterator; + + struct TItem { + TValue Value; + TListIterator ListIterator; + }; + + TValue* FindPtr(TKey key) { + auto it = Map_.find(key); + if (it != Map_.end()) { + Touch(it); + return &it->second.Value; + } else { + return nullptr; + } + } + + TValue& Update(TKey key, TValue value = {}) { + auto it = Map_.find(key); // we don't use blind emplace to avoid coping value + if (it == Map_.end()) { + it = Map_.emplace(key, TItem{std::move(value), List_.end()}).first; + List_.emplace_back(it); + it->second.ListIterator = std::prev(List_.end()); + Shrink(); + } else { + it->second.Value = std::move(value); + Touch(it); + } + return it->second.Value; + } + + void Erase(TKey key) { + auto it = Map_.find(key); + if (it != Map_.end()) { + List_.remove(it); // log(N) + Map_.erase(it); + } + } + +protected: + void Touch(TMapIterator it) { + List_.splice(List_.end(), List_, it->second.ListIterator); + it->second.ListIterator = std::prev(List_.end()); + } + + template<typename TVal, typename Enable = void> + struct TReleaseChecker { + bool operator ()(TVal&) const { + return true; + } + }; + + template<typename TVal> + struct TReleaseChecker<TVal, typename std::enable_if_t<std::is_invocable_v<decltype(&TVal::IsSafeToRelease), TVal&>>> { + bool operator ()(TVal& val) const { + return val.IsSafeToRelease(); + } + }; + + static bool IsSafeToRelease(TValue& val) { + return TReleaseChecker<TValue>()(val); + } + + void Shrink() { + size_t maxDepth = std::min<size_t>(MaxSize + 1, 10); + auto itList = List_.begin(); + while (List_.size() > MaxSize && itList != List_.end()) { + if (IsSafeToRelease((*itList)->second.Value)) { + Map_.erase(*itList); + itList = List_.erase(itList); + } else { + ++itList; + if (--maxDepth == 0) { + break; + } + } + } + } + + TMap Map_; + TList List_; +}; + } // NKikimr diff --git a/ydb/core/util/simple_cache_ut.cpp b/ydb/core/util/simple_cache_ut.cpp index 340d5226634..e799faeab60 100644 --- a/ydb/core/util/simple_cache_ut.cpp +++ b/ydb/core/util/simple_cache_ut.cpp @@ -22,6 +22,37 @@ Y_UNIT_TEST_SUITE(TSimpleCacheTest) { ptr = cache.FindPtr("2"); UNIT_ASSERT(ptr == nullptr); } + + struct TValue { + TString Str; + bool SafeToRelease = true; + + bool IsSafeToRelease() { + return SafeToRelease; + } + }; + + Y_UNIT_TEST(TestNotSoSimpleCache) { + TNotSoSimpleCache<TString, TValue> cache; + + cache.MaxSize = 3; + TValue* ptr = cache.FindPtr("1"); + UNIT_ASSERT(ptr == nullptr); + cache.Update("1", {"one"}); + ptr = cache.FindPtr("1"); + UNIT_ASSERT(ptr != nullptr); + UNIT_ASSERT(ptr->Str == "one"); + cache.Update("2", {"two", false}); + cache.Update("3", {"three"}); + ptr = cache.FindPtr("1"); + UNIT_ASSERT(ptr != nullptr); + UNIT_ASSERT(ptr->Str == "one"); + cache.Update("4", {"four"}); // we evicting oldest one - "three" (but not "two" - it's not safe to release) + ptr = cache.FindPtr("2"); + UNIT_ASSERT(ptr != nullptr); + ptr = cache.FindPtr("3"); + UNIT_ASSERT(ptr == nullptr); + } } } // NKikimr |