From f940b8c3c461f6aaa14f1b68a781893e4739ff53 Mon Sep 17 00:00:00 2001 From: kungurtsev Date: Wed, 23 Oct 2024 17:19:46 +0200 Subject: [PATCH] Do not block all requests on Shared Cache policy switch (#10619) --- ydb/core/protos/shared_cache.proto | 2 +- ydb/core/tablet_flat/shared_cache_clock_pro.h | 8 +- ydb/core/tablet_flat/shared_cache_events.h | 11 - ydb/core/tablet_flat/shared_cache_s3fifo.h | 6 +- .../tablet_flat/shared_cache_switchable.h | 228 ++++++++ .../shared_cache_switchable_ut.cpp | 542 ++++++++++++++++++ ydb/core/tablet_flat/shared_sausagecache.cpp | 95 ++- ydb/core/tablet_flat/shared_sausagecache.h | 2 +- .../tablet_flat/ut/ut_shared_sausagecache.cpp | 28 +- ydb/core/tablet_flat/ut/ya.make | 1 + ydb/core/tablet_flat/ya.make | 1 + ydb/core/util/cache_cache.h | 4 + ydb/core/util/cache_cache_iface.h | 6 + 13 files changed, 856 insertions(+), 78 deletions(-) create mode 100644 ydb/core/tablet_flat/shared_cache_switchable.h create mode 100644 ydb/core/tablet_flat/shared_cache_switchable_ut.cpp diff --git a/ydb/core/protos/shared_cache.proto b/ydb/core/protos/shared_cache.proto index e4ad3952f778..8477e86c897d 100644 --- a/ydb/core/protos/shared_cache.proto +++ b/ydb/core/protos/shared_cache.proto @@ -14,5 +14,5 @@ message TSharedCacheConfig { optional uint32 ActivePagesReservationPercent = 4 [default = 50]; reserved 5; optional TReplacementPolicy ReplacementPolicy = 6 [default = ThreeLeveledLRU]; - optional uint32 ReplacementPolicySwitchUniformDelaySeconds = 7 [default = 3600]; + reserved 7; } diff --git a/ydb/core/tablet_flat/shared_cache_clock_pro.h b/ydb/core/tablet_flat/shared_cache_clock_pro.h index aa2cd3fbff14..70e98ab142f0 100644 --- a/ydb/core/tablet_flat/shared_cache_clock_pro.h +++ b/ydb/core/tablet_flat/shared_cache_clock_pro.h @@ -64,7 +64,7 @@ class TClockProCache : public ICacheCache { {} TIntrusiveList EvictNext() override { - if (SizeHot + SizeCold == 0) { + if (GetSize() == 0) { return {}; } @@ -118,7 +118,11 @@ class TClockProCache : public ICacheCache { } } - TString Dump() const { + ui64 GetSize() const override { + return SizeHot + SizeCold; + } + + TString Dump() const override { TStringBuilder result; size_t count = 0; diff --git a/ydb/core/tablet_flat/shared_cache_events.h b/ydb/core/tablet_flat/shared_cache_events.h index 2b7cb4bd37a6..87b74f980f1e 100644 --- a/ydb/core/tablet_flat/shared_cache_events.h +++ b/ydb/core/tablet_flat/shared_cache_events.h @@ -25,7 +25,6 @@ namespace NSharedCache { EvRequest, EvResult, EvUpdated, - EvReplacementPolicySwitch, EvEnd @@ -129,16 +128,6 @@ namespace NSharedCache { THashMap Actions; }; - - struct TEvReplacementPolicySwitch : public TEventLocal { - using TReplacementPolicy = NKikimrSharedCache::TReplacementPolicy; - - TReplacementPolicy ReplacementPolicy; - - TEvReplacementPolicySwitch(TReplacementPolicy replacementPolicy) - : ReplacementPolicy(replacementPolicy) - {} - }; } } diff --git a/ydb/core/tablet_flat/shared_cache_s3fifo.h b/ydb/core/tablet_flat/shared_cache_s3fifo.h index e8173a452b68..71a5c113067f 100644 --- a/ydb/core/tablet_flat/shared_cache_s3fifo.h +++ b/ydb/core/tablet_flat/shared_cache_s3fifo.h @@ -225,7 +225,11 @@ class TS3FIFOCache : public ICacheCache { GhostQueue.UpdateLimit(limit); } - TString Dump() const { + ui64 GetSize() const override { + return SmallQueue.Size + MainQueue.Size; + } + + TString Dump() const override { TStringBuilder result; auto dump = [&](const TQueue& queue) { diff --git a/ydb/core/tablet_flat/shared_cache_switchable.h b/ydb/core/tablet_flat/shared_cache_switchable.h new file mode 100644 index 000000000000..48d28cc90b41 --- /dev/null +++ b/ydb/core/tablet_flat/shared_cache_switchable.h @@ -0,0 +1,228 @@ +#pragma once +#include "defs.h" +#include +#include +#include + +namespace NKikimr::NCache { + +template +class TSwitchableCache : public ICacheCache { + using TCounterPtr = ::NMonitoring::TDynamicCounters::TCounterPtr; + + static const ui32 MaxCachesCount = 3; + static const ui32 RotatePagesPerCallCount = 10; + static_assert(MaxCachesCount < (1 << 4)); + + class TCacheHolder { + public: + TCacheHolder(ui32 id, THolder>&& cache, TCounterPtr& sizeCounter) + : Id(id) + , Cache(std::move(cache)) + , SizeCounter(sizeCounter) + { + Y_ABORT_UNLESS(GetSize() == 0); + } + + TIntrusiveList EvictNext() { + return ProcessEvictedList(Cache->EvictNext()); + } + + TIntrusiveList Touch(TPage* page) { + ui32 cacheId = TPageTraits::GetCacheId(page); + if (cacheId == 0) { + TPageTraits::SetCacheId(page, Id); + SizeCounter->Add(TPageTraits::GetSize(page)); + } else { + Y_ABORT_UNLESS(cacheId == Id); + } + + return ProcessEvictedList(Cache->Touch(page)); + } + + void Erase(TPage* page) { + ui32 cacheId = TPageTraits::GetCacheId(page); + if (cacheId != 0) { + Y_ABORT_UNLESS(cacheId == Id); + SizeCounter->Sub(TPageTraits::GetSize(page)); + TPageTraits::SetCacheId(page, 0); + } + + Cache->Erase(page); + } + + void UpdateLimit(ui64 limit) { + Cache->UpdateLimit(limit); + } + + ui64 GetSize() const { + return Cache->GetSize(); + } + + TString Dump() const { + return Cache->Dump(); + } + + private: + TIntrusiveList ProcessEvictedList(TIntrusiveList&& evictedList) { + ui64 evictedSize = 0; + + for (auto& page_ : evictedList) { + TPage* page = &page_; + Y_ABORT_UNLESS(TPageTraits::GetCacheId(page) == Id); + TPageTraits::SetCacheId(page, 0); + evictedSize += TPageTraits::GetSize(page); + } + + SizeCounter->Sub(evictedSize); + + return evictedList; + } + + public: + const ui32 Id; // in [1 .. MaxCachesCount] range + + private: + const THolder> Cache; + const TCounterPtr SizeCounter; + }; + +public: + TSwitchableCache(ui64 limit, THolder>&& cache, TCounterPtr sizeCounter) { + Caches.emplace_back(1, std::move(cache), sizeCounter); + UpdateLimit(limit); + } + + TIntrusiveList Switch(THolder>&& cache, TCounterPtr sizeCounter) Y_WARN_UNUSED_RESULT { + ui32 cacheId = Caches.back().Id + 1; + if (cacheId > MaxCachesCount) { + cacheId -= MaxCachesCount; + } + + Caches.emplace_back(cacheId, std::move(cache), sizeCounter) + .UpdateLimit(Limit); + + TIntrusiveList evictedList; + + while (Caches.size() > 1 && Caches.front().Id == cacheId) { // MaxCachesCount is exceeded + RotatePages(evictedList); + } + + return evictedList; + } + + TIntrusiveList EvictNext() override { + while (Y_UNLIKELY(Caches.size() > 1)) { + auto result = Caches.front().EvictNext(); + if (!result) { + Y_ABORT_UNLESS(Caches.front().GetSize() == 0); + Caches.pop_front(); + } else { + return result; + } + } + + return Caches.back().EvictNext(); + } + + TIntrusiveList Touch(TPage* page) override { + if (Y_LIKELY(Caches.size() == 1)) { + return Caches.back().Touch(page); + } + + ui32 cacheId = TPageTraits::GetCacheId(page); + if (cacheId > 0 && cacheId != Caches.back().Id) { + // rotate the current page first: + GetCache(cacheId).Erase(page); + } + + TIntrusiveList evictedList = Caches.back().Touch(page); + + RotatePages(evictedList); + + while (GetSize() > Limit && Caches.size() > 1) { + evictedList.Append(EvictNext()); + } + + return evictedList; + } + + void Erase(TPage* page) override { + if (Y_LIKELY(Caches.size() == 1)) { + Caches.back().Erase(page); + return; + } + + GetCache(TPageTraits::GetCacheId(page)) + .Erase(page); + } + + void UpdateLimit(ui64 limit) override { + Limit = limit; + for (auto& cache : Caches) { + cache.UpdateLimit(limit); + } + } + + ui64 GetSize() const override { + ui64 result = 0; + for (const auto& cache : Caches) { + result += cache.GetSize(); + } + return result; + } + + TString Dump() const override { + TStringBuilder result; + size_t count = 0; + + for (const auto& cache : Caches) { + if (count) result << "; "; + result << cache.Dump(); + count++; + } + + return result; + } + +private: + TCacheHolder& GetCache(ui32 cacheId) { + if (cacheId == 0) { + // use the most-recent cache by default + return Caches.back(); + } else { + // Note: this loop might be replaced with formula + // but it seems useless and error-prone + for (auto& cache : Caches) { + if (cache.Id == cacheId) { + return cache; + } + } + Y_ABORT("Failed to locate page cache"); + } + } + + void RotatePages(TIntrusiveList& evictedList) { + ui32 rotatedPagesCount = 0; + while (Caches.size() > 1 && rotatedPagesCount < RotatePagesPerCallCount) { + auto rotatedList = Caches.front().EvictNext(); + if (!rotatedList) { + Y_ABORT_UNLESS(Caches.front().GetSize() == 0); + Caches.pop_front(); + continue; + } + + while (!rotatedList.Empty()) { + TPage* page = rotatedList.PopFront(); + evictedList.Append(Caches.back().Touch(page)); + rotatedPagesCount++; + } + } + } + +private: + ui64 Limit; + TDeque Caches; +}; + +} diff --git a/ydb/core/tablet_flat/shared_cache_switchable_ut.cpp b/ydb/core/tablet_flat/shared_cache_switchable_ut.cpp new file mode 100644 index 000000000000..74c9b43867ed --- /dev/null +++ b/ydb/core/tablet_flat/shared_cache_switchable_ut.cpp @@ -0,0 +1,542 @@ +#include +#include +#include + +namespace NKikimr::NCache { + +namespace { + + using TCounterPtr = ::NMonitoring::TDynamicCounters::TCounterPtr; + + struct TPage : public TIntrusiveListItem { + ui32 Id; + size_t Size; + + TPage(ui32 id, size_t size) + : Id(id), Size(size) + {} + + ui32 CacheId : 4 = 0; + }; + + struct TPageTraits { + struct TPageKey { + ui32 Id; + + TPageKey(ui32 id) + : Id(id) + {} + }; + + static ui64 GetSize(const TPage* page) { + return page->Size; + } + + static ui32 GetCacheId(const TPage* page) { + return page->CacheId; + } + + static void SetCacheId(TPage* page, ui32 id) { + Y_ABORT_UNLESS(id < (1 << 4)); + page->CacheId = id; + } + }; + + class TSimpleCache : public ICacheCache { + public: + TIntrusiveList EvictNext() override { + TIntrusiveList result; + + if (!List.empty()) { + TPage* page = List.front(); + List.pop_front(); + Map.erase(page->Id); + result.PushBack(page); + }; + + return result; + } + + TIntrusiveList Touch(TPage* page) override { + if (Map.contains(page->Id)) { + List.erase(Map[page->Id]); + } + List.push_back(page); + Map[page->Id] = prev(List.end()); + + TIntrusiveList evictedList; + + while (GetSize() > Limit) { + TPage* page = List.front(); + List.pop_front(); + Map.erase(page->Id); + evictedList.PushBack(page); + } + + return evictedList; + } + + void Erase(TPage* page) override { + if (Map.contains(page->Id)) { + List.erase(Map[page->Id]); + Map.erase(page->Id); + } + } + + void UpdateLimit(ui64 limit) override { + Limit = limit; + } + + ui64 GetSize() const override { + ui64 size = 0; + for (auto page : List) { + size += page->Size; + } + return size; + } + + TString Dump() const override { + TStringBuilder result; + size_t count = 0; + for (auto it = List.begin(); it != List.end(); it++) { + TPage* page = *it; + if (count != 0) result << ", "; + result << "{" << page->Id << " " << page->Size << "b}"; + count++; + Y_ABORT_UNLESS(*Map.FindPtr(page->Id) == it); + } + Y_ABORT_UNLESS(Map.size() == count); + return result; + } + + private: + ui64 Limit = 0; + TList List; + THashMap::iterator> Map; + }; + +} + +Y_UNIT_TEST_SUITE(TSwitchableCache) { + + TVector Touch(auto& cache, TPage& page) { + auto evicted = cache.Touch(&page); + TVector result; + for (auto& p : evicted) { + UNIT_ASSERT_VALUES_EQUAL(p.CacheId, 0); + result.push_back(p.Id); + } + return result; + } + + void Erase(auto& cache, TPage& page) { + cache.Erase(&page); + UNIT_ASSERT_VALUES_EQUAL(page.CacheId, 0); + } + + TVector EvictNext(auto& cache) { + auto evicted = cache.EvictNext(); + TVector result; + for (auto& p : evicted) { + UNIT_ASSERT_VALUES_EQUAL(p.CacheId, 0); + result.push_back(p.Id); + } + return result; + } + + TVector Switch(auto& cache, auto&& cache2, auto& counter) { + auto evicted = cache.Switch(std::move(cache2), counter); + TVector result; + for (auto& p : evicted) { + UNIT_ASSERT_VALUES_EQUAL(p.CacheId, 0); + result.push_back(p.Id); + } + return result; + } + + Y_UNIT_TEST(Touch) { + TCounterPtr counter = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter); + + TPage page1{1, 2}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + TPage page2{2, 3}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + TPage page3{3, 4}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + TPage page4{4, 1}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + TPage page5{5, 4}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page5), (TVector{1, 2})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{3 4b}, {4 1b}, {5 4b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), (TVector{3})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{4 1b}, {5 4b}, {2 3b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page5), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{4 1b}, {2 3b}, {5 4b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + } + + Y_UNIT_TEST(Erase) { + TCounterPtr counter = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter); + + TPage page1{1, 2}; + TPage page2{2, 3}; + TPage page3{3, 4}; + TPage page4{4, 1}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + Erase(cache, page2); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + TPage page5{5, 4}; + Erase(cache, page5); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + } + + Y_UNIT_TEST(EvictNext) { + TCounterPtr counter = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter); + + TPage page1{1, 2}; + TPage page2{2, 3}; + TPage page3{3, 4}; + TPage page4{4, 1}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), (TVector{1})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{2 3b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), (TVector{2})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), (TVector{3})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), (TVector{4})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), ""); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), (TVector{})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), ""); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + } + + Y_UNIT_TEST(UpdateLimit) { + TCounterPtr counter = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter); + + TPage page1{1, 2}; + TPage page2{2, 3}; + TPage page3{3, 4}; + TPage page4{4, 1}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}, {4 1b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + + cache.UpdateLimit(6); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), (TVector{1, 3})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{4 1b}, {2 3b}"); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), cache.GetSize()); + } + + Y_UNIT_TEST(Switch_Touch_RotatePages_All) { + TCounterPtr counter1 = new NMonitoring::TCounterForPtr; + TCounterPtr counter2 = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter1); + + TPage page1{1, 2}; + TPage page2{2, 3}; + TPage page3{3, 4}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}"); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 0); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}; "); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 0); + + TPage page4{4, 1}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{4 1b}, {1 2b}, {2 3b}, {3 4b}"); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 10); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 10); + } + + Y_UNIT_TEST(Switch_Touch_RotatePages_Parts) { + TCounterPtr counter1 = new NMonitoring::TCounterForPtr; + TCounterPtr counter2 = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(50, MakeHolder(), counter1); + + TVector> pages; + for (ui32 pageId : xrange(50)) { + pages.push_back(MakeHolder(pageId, 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{}); + } + + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 0); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 0); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{10}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 39); // [11 .. 49] + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); // [50, 0 .. 9] + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{21}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 28); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 22); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{32}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 17); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 33); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{43}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 6); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 44); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{50}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 50); + } + + Y_UNIT_TEST(Switch_RotatePages_Force) { + TCounterPtr counter = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter); + + TPage page1{1, 2}; + TPage page2{2, 3}; + TPage page3{3, 4}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}"); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), 9); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}; "); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), 9); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}; ; "); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), 9); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}"); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), 9); + } + + Y_UNIT_TEST(Switch_RotatePages_Evicts) { + TCounterPtr counter = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(10, MakeHolder(), counter); + + TPage page1{1, 2}; + TPage page2{2, 3}; + TPage page3{3, 4}; + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector{}); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter), TVector{}); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{1 2b}, {2 3b}, {3 4b}; ; "); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 9); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), 9); + + cache.UpdateLimit(5); + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter), (TVector{1, 2})); + UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), "{3 4b}"); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 4); + UNIT_ASSERT_VALUES_EQUAL(counter->Val(), 4); + } + + Y_UNIT_TEST(Switch_Touch) { + TCounterPtr counter1 = new NMonitoring::TCounterForPtr; + TCounterPtr counter2 = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(50, MakeHolder(), counter1); + + TVector> pages; + for (ui32 pageId : xrange(50)) { + pages.push_back(MakeHolder(pageId, 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{}); + } + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter2), TVector{}); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{10}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 39); // [11 .. 49] + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); // [50, 0 .. 9] + + Touch(cache, *pages[23]); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 28); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 22); + + Touch(cache, *pages[7]); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 18); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 32); + } + + Y_UNIT_TEST(Switch_Erase) { + TCounterPtr counter1 = new NMonitoring::TCounterForPtr; + TCounterPtr counter2 = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(50, MakeHolder(), counter1); + + TVector> pages; + for (ui32 pageId : xrange(50)) { + pages.push_back(MakeHolder(pageId, 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{}); + } + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter2), TVector{}); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{10}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 39); // [11 .. 49] + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); // [50, 0 .. 9] + + Erase(cache, *pages[23]); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 49); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 38); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); + + Erase(cache, *pages[7]); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 48); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 38); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 10); + } + + Y_UNIT_TEST(Switch_EvictNext) { + TCounterPtr counter1 = new NMonitoring::TCounterForPtr; + TCounterPtr counter2 = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(50, MakeHolder(), counter1); + + TVector> pages; + for (ui32 pageId : xrange(50)) { + pages.push_back(MakeHolder(pageId, 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{}); + } + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter2), TVector{}); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{10}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 39); // [11 .. 49] + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); // [50, 0 .. 9] + + for (ui32 i : xrange(39)) { + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), TVector{i + 11}); + } + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 11); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); + + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), TVector{50}); + for (ui32 i : xrange(10)) { + UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), TVector{i}); + } + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 0); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 0); + } + + Y_UNIT_TEST(Switch_UpdateLimit) { + TCounterPtr counter1 = new NMonitoring::TCounterForPtr; + TCounterPtr counter2 = new NMonitoring::TCounterForPtr; + TSwitchableCache cache(50, MakeHolder(), counter1); + + TVector> pages; + for (ui32 pageId : xrange(50)) { + pages.push_back(MakeHolder(pageId, 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{}); + } + + UNIT_ASSERT_VALUES_EQUAL(Switch(cache, MakeHolder(), counter2), TVector{}); + + pages.push_back(MakeHolder(pages.size(), 1)); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages.back()), TVector{10}); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 50); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 39); // [11 .. 49] + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 11); // [50, 0 .. 9] + + cache.UpdateLimit(40); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages[23]), (TVector{21, 22, 24, 25, 26, 27, 28, 29, 30, 31})); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 40); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 18); // [32 .. 49] + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 22); // [50, 0 .. 9, 23, 11 .. 20] + + cache.UpdateLimit(7); + UNIT_ASSERT_VALUES_EQUAL(Touch(cache, *pages[7]).size(), 33); + UNIT_ASSERT_VALUES_EQUAL(cache.GetSize(), 7); + UNIT_ASSERT_VALUES_EQUAL(counter1->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counter2->Val(), 7); + } + +} + +} diff --git a/ydb/core/tablet_flat/shared_sausagecache.cpp b/ydb/core/tablet_flat/shared_sausagecache.cpp index 5d9686e9534f..4ae4d5595341 100644 --- a/ydb/core/tablet_flat/shared_sausagecache.cpp +++ b/ydb/core/tablet_flat/shared_sausagecache.cpp @@ -4,6 +4,7 @@ #include "flat_bio_actor.h" #include "util_fmt_logger.h" #include +#include #include #include #include @@ -36,8 +37,8 @@ TSharedPageCacheCounters::TSharedPageCacheCounters(const TIntrusivePtr<::NMonito , LoadInFlyBytes(counters->GetCounter("LoadInFlyBytes")) { } -TSharedPageCacheCounters::TCounterPtr TSharedPageCacheCounters::ReplacementPolicy(TReplacementPolicy policy) { - return Counters->GetCounter(TStringBuilder() << "ReplacementPolicy/" << policy); +TSharedPageCacheCounters::TCounterPtr TSharedPageCacheCounters::ReplacementPolicySize(TReplacementPolicy policy) { + return Counters->GetCounter(TStringBuilder() << "ReplacementPolicySize/" << policy); } } @@ -76,6 +77,7 @@ class TSharedPageCache : public TActorBootstrapped { , public TIntrusiveListItem { ui32 State : 4 = PageStateNo; + ui32 CacheId : 4 = 0; ui32 CacheFlags1 : 4 = 0; ui32 CacheFlags2 : 4 = 0; @@ -110,11 +112,14 @@ class TSharedPageCache : public TActorBootstrapped { } void EnsureNoCacheFlags() { + Y_VERIFY_S(CacheId == 0, "Unexpected page " << CacheId << " cache id"); Y_VERIFY_S(CacheFlags1 == 0, "Unexpected page " << CacheFlags1 << " cache flags 1"); Y_VERIFY_S(CacheFlags2 == 0, "Unexpected page " << CacheFlags2 << " cache flags 2"); } }; + static_assert(sizeof(TPage) == 104); + struct TCacheCachePageTraits { static ui64 GetWeight(const TPage* page) { return sizeof(TPage) + page->Size; @@ -230,6 +235,21 @@ class TSharedPageCache : public TActorBootstrapped { } }; + struct TCompositeCachePageTraits { + static ui64 GetSize(const TPage* page) { + return sizeof(TPage) + page->Size; + } + + static ui32 GetCacheId(const TPage* page) { + return page->CacheId; + } + + static void SetCacheId(TPage* page, ui32 id) { + Y_ABORT_UNLESS(id < (1 << 4)); + page->CacheId = id; + } + }; + struct TRequest : public TSimpleRefCount { TRequest(TIntrusiveConstPtr pageCollection, NWilson::TTraceId &&traceId) : Label(pageCollection->Label()) @@ -298,7 +318,7 @@ class TSharedPageCache : public TActorBootstrapped { TRequestQueue ScanRequests; THolder Config; - THolder> Cache; + TSwitchableCache Cache; ui64 StatBioReqs = 0; ui64 StatHitPages = 0; @@ -336,7 +356,7 @@ class TSharedPageCache : public TActorBootstrapped { // limit of cache depends only on config and mem because passive pages may go in and out arbitrary // we may have some passive bytes, so if we fully fill this Cache we may exceed the limit // because of that DoGC should be called to ensure limits - Cache->UpdateLimit(limitBytes); + Cache.UpdateLimit(limitBytes); if (Config->Counters) { Config->Counters->ConfigLimitBytes->Set(Config->LimitBytes.value_or(0)); @@ -355,7 +375,7 @@ class TSharedPageCache : public TActorBootstrapped { THashSet recheck; while (GetStatAllBytes() > MemLimitBytes || GetStatAllBytes() > Config->LimitBytes.value_or(Max()) && StatActiveBytes > configActiveReservedBytes) { - TIntrusiveList pages = Cache->EvictNext(); + TIntrusiveList pages = Cache.EvictNext(); if (pages.Empty()) { break; } @@ -391,44 +411,6 @@ class TSharedPageCache : public TActorBootstrapped { DoGC(); } - void Handle(NSharedCache::TEvReplacementPolicySwitch::TPtr &ev) { - auto *msg = ev->Get(); - - if (msg->ReplacementPolicy == Config->ReplacementPolicy) { - return; - } - - if (auto logl = Logger->Log(ELnLev::Info)) { - logl << "Replacement policy switch from " << Config->ReplacementPolicy << " to " << msg->ReplacementPolicy; - } - if (Config->Counters) { - Config->Counters->ReplacementPolicy(Config->ReplacementPolicy)->Set(0); - } - - Config->ReplacementPolicy = msg->ReplacementPolicy; - auto oldCache = CreateCache(); - Cache.Swap(oldCache); - ActualizeCacheSizeLimit(); - - while (auto pages = oldCache->EvictNext()) { - while (!pages.Empty()) { - TPage* page = pages.PopFront(); - page->EnsureNoCacheFlags(); - - // touch each page multiple times to make it warm - for (ui32 touchTimes = 0; touchTimes < 3; touchTimes++) { - Evict(Cache->Touch(page)); - } - } - } - - DoGC(); - - if (Config->Counters) { - Config->Counters->ReplacementPolicy(Config->ReplacementPolicy)->Set(1); - } - } - void Registered(TActorSystem *sys, const TActorId &owner) { NActors::TActorBootstrapped::Registered(sys, owner); @@ -543,7 +525,7 @@ class TSharedPageCache : public TActorBootstrapped { ++*Config->Counters->CacheHitPages; *Config->Counters->CacheHitBytes += page->Size; } - Evict(Cache->Touch(page)); + Evict(Cache.Touch(page)); break; case PageStateNo: ++pagesToLoad; @@ -825,7 +807,7 @@ class TSharedPageCache : public TActorBootstrapped { AddActivePage(page); [[fallthrough]]; case PageStateLoaded: - Evict(Cache->Touch(page)); + Evict(Cache.Touch(page)); break; default: Y_ABORT("unknown load state"); @@ -926,7 +908,7 @@ class TSharedPageCache : public TActorBootstrapped { page->Initialize(std::move(paged.Data)); BodyProvided(collection, paged.PageId, page); - Evict(Cache->Touch(page)); + Evict(Cache.Touch(page)); } } @@ -1078,7 +1060,7 @@ class TSharedPageCache : public TActorBootstrapped { for (const auto &kv : collection.PageMap) { auto* page = kv.second.Get(); - Cache->Erase(page); + Cache.Erase(page); page->EnsureNoCacheFlags(); if (page->State == PageStateLoaded) { @@ -1216,9 +1198,15 @@ class TSharedPageCache : public TActorBootstrapped { } if (msg->Record.GetReplacementPolicy() != Config->ReplacementPolicy) { - // Note: use random delay to prevent the whole cluster lag and storage ddos - ui32 delaySeconds = RandomNumber(msg->Record.GetReplacementPolicySwitchUniformDelaySeconds() + 1); - Schedule(TDuration::Seconds(delaySeconds), new NSharedCache::TEvReplacementPolicySwitch(msg->Record.GetReplacementPolicy())); + if (auto logl = Logger->Log(ELnLev::Info)) { + logl << "Replacement policy switch from " << Config->ReplacementPolicy << " to " << msg->Record.GetReplacementPolicy(); + } + Config->ReplacementPolicy = msg->Record.GetReplacementPolicy(); + Evict(Cache.Switch(CreateCache(), Config->Counters->ReplacementPolicySize(Config->ReplacementPolicy))); + DoGC(); + if (auto logl = Logger->Log(ELnLev::Info)) { + logl << "Replacement policy switch to " << Config->ReplacementPolicy << " finished"; + } } } @@ -1280,12 +1268,8 @@ class TSharedPageCache : public TActorBootstrapped { public: TSharedPageCache(THolder config) : Config(std::move(config)) - , Cache(CreateCache()) + , Cache(1, CreateCache(), Config->Counters->ReplacementPolicySize(Config->ReplacementPolicy)) { - if (Config->Counters) { - Config->Counters->ReplacementPolicy(Config->ReplacementPolicy)->Set(1); - } - AsyncRequests.Limit = Config->TotalAsyncQueueInFlyLimit; ScanRequests.Limit = Config->TotalScanQueueInFlyLimit; } @@ -1314,7 +1298,6 @@ class TSharedPageCache : public TActorBootstrapped { hFunc(NMemory::TEvConsumerRegistered, Handle); hFunc(NMemory::TEvConsumerLimit, Handle); - hFunc(NSharedCache::TEvReplacementPolicySwitch, Handle); } } diff --git a/ydb/core/tablet_flat/shared_sausagecache.h b/ydb/core/tablet_flat/shared_sausagecache.h index 3451bf7c805c..680b3d75bef6 100644 --- a/ydb/core/tablet_flat/shared_sausagecache.h +++ b/ydb/core/tablet_flat/shared_sausagecache.h @@ -48,7 +48,7 @@ struct TSharedPageCacheCounters final : public TAtomicRefCount &group); - TCounterPtr ReplacementPolicy(TReplacementPolicy policy); + TCounterPtr ReplacementPolicySize(TReplacementPolicy policy); }; // TODO: use protobuf configs diff --git a/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp b/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp index 1ba3d5a886e9..6b93f474b866 100644 --- a/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp +++ b/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp @@ -128,6 +128,13 @@ void RestartAndClearCache(TMyEnvBase& env) { env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp)); } +void SwitchPolicy(TMyEnvBase& env, NKikimrSharedCache::TReplacementPolicy policy) { + auto configure = MakeHolder(); + configure->Record.SetReplacementPolicy(policy); + env->Send(MakeSharedPageCacheId(), TActorId{}, configure.Release()); + WaitEvent(env, TEvSharedPageCache::EvConfigure); +} + Y_UNIT_TEST(Limits) { TMyEnvBase env; auto counters = MakeIntrusive(env->GetDynamicCounters()); @@ -294,8 +301,7 @@ Y_UNIT_TEST(S3FIFO) { env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp)); env.SendSync(new NFake::TEvExecute{ new TTxInitSchema() }); - env->Send(MakeSharedPageCacheId(), TActorId{}, new TEvReplacementPolicySwitch(NKikimrSharedCache::S3FIFO)); - WaitEvent(env, NSharedCache::EvReplacementPolicySwitch); + SwitchPolicy(env, NKikimrSharedCache::S3FIFO); // write 100 rows, each ~100KB (~10MB) for (i64 key = 0; key < 100; ++key) { @@ -393,8 +399,7 @@ Y_UNIT_TEST(ClockPro) { env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp)); env.SendSync(new NFake::TEvExecute{ new TTxInitSchema() }); - env->Send(MakeSharedPageCacheId(), TActorId{}, new TEvReplacementPolicySwitch(NKikimrSharedCache::ClockPro)); - WaitEvent(env, NSharedCache::EvReplacementPolicySwitch); + SwitchPolicy(env, NKikimrSharedCache::ClockPro); // write 100 rows, each ~100KB (~10MB) for (i64 key = 0; key < 100; ++key) { @@ -525,14 +530,25 @@ Y_UNIT_TEST(ReplacementPolicySwitch) { } UNIT_ASSERT_VALUES_EQUAL(retried, (TVector{3, 3, 1, 1})); - env->Send(MakeSharedPageCacheId(), TActorId{}, new TEvReplacementPolicySwitch(NKikimrSharedCache::S3FIFO)); - WaitEvent(env, NSharedCache::EvReplacementPolicySwitch); + UNIT_ASSERT_GT(counters->ReplacementPolicySize(NKikimrSharedCache::ThreeLeveledLRU)->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counters->ReplacementPolicySize(NKikimrSharedCache::S3FIFO)->Val(), 0); + + SwitchPolicy(env, NKikimrSharedCache::S3FIFO); retried = {}; for (i64 key = 0; key < 3; ++key) { env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true); } UNIT_ASSERT_VALUES_EQUAL(retried, (TVector{3})); + + retried = {}; + for (i64 key = 90; key < 93; ++key) { + env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true); + } + UNIT_ASSERT_VALUES_EQUAL(retried, (TVector{3, 3, 2, 1})); + + UNIT_ASSERT_GT(counters->ReplacementPolicySize(NKikimrSharedCache::S3FIFO)->Val(), 0); + UNIT_ASSERT_VALUES_EQUAL(counters->ReplacementPolicySize(NKikimrSharedCache::ThreeLeveledLRU)->Val(), 0); } } // Y_UNIT_TEST_SUITE(TSharedPageCache) diff --git a/ydb/core/tablet_flat/ut/ya.make b/ydb/core/tablet_flat/ut/ya.make index 1cdc75a39068..ae100d0c57dc 100644 --- a/ydb/core/tablet_flat/ut/ya.make +++ b/ydb/core/tablet_flat/ut/ya.make @@ -27,6 +27,7 @@ SRCS( flat_test_db_helpers.h shared_cache_s3fifo_ut.cpp shared_cache_clock_pro_ut.cpp + shared_cache_switchable_ut.cpp shared_handle_ut.cpp ut_btree_index_nodes.cpp ut_btree_index_iter_charge.cpp diff --git a/ydb/core/tablet_flat/ya.make b/ydb/core/tablet_flat/ya.make index fe64c4a525ac..0598cc00d0e8 100644 --- a/ydb/core/tablet_flat/ya.make +++ b/ydb/core/tablet_flat/ya.make @@ -110,6 +110,7 @@ PEERDIR( ydb/core/protos ydb/core/tablet ydb/core/tablet_flat/protos + ydb/core/util ydb/library/binary_json ydb/library/dynumber ydb/library/mkql_proto/protos diff --git a/ydb/core/util/cache_cache.h b/ydb/core/util/cache_cache.h index e211b26db65b..058fbfda6b72 100644 --- a/ydb/core/util/cache_cache.h +++ b/ydb/core/util/cache_cache.h @@ -139,6 +139,10 @@ class TCacheCache : public ICacheCache { Config.SetLimit(limit); } + ui64 GetSize() const override { + return FreshWeight + StagingWeight + WarmWeight; + } + private: void Unlink(TItem *item, ui64 &weight) { item->Unlink(); diff --git a/ydb/core/util/cache_cache_iface.h b/ydb/core/util/cache_cache_iface.h index 6ead4def43f4..b914d4b78162 100644 --- a/ydb/core/util/cache_cache_iface.h +++ b/ydb/core/util/cache_cache_iface.h @@ -17,6 +17,12 @@ struct ICacheCache { // WARN: do not evict items virtual void UpdateLimit(ui64 limit) = 0; + virtual ui64 GetSize() const = 0; + + virtual TString Dump() const { + return {}; + } + virtual ~ICacheCache() = default; };