aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkungurtsev <kungasc@ydb.tech>2024-09-13 15:22:20 +0200
committerGitHub <noreply@github.com>2024-09-13 15:22:20 +0200
commit2416150911027a83489a7d169ac16035c6278405 (patch)
tree2db72e9ccedbad4d7a2c6cee2000c97852f95c8d
parentb52420ca2be9143a90d9ac7b39f640eba8c6d509 (diff)
downloadydb-2416150911027a83489a7d169ac16035c6278405.tar.gz
Shared Cache S3FIFO implementation (#8692)
-rw-r--r--ydb/core/driver_lib/run/kikimr_services_initializers.cpp5
-rw-r--r--ydb/core/protos/shared_cache.proto1
-rw-r--r--ydb/core/tablet_flat/shared_cache_events.h12
-rw-r--r--ydb/core/tablet_flat/shared_cache_s3fifo.h346
-rw-r--r--ydb/core/tablet_flat/shared_cache_s3fifo_ut.cpp406
-rw-r--r--ydb/core/tablet_flat/shared_sausagecache.cpp115
-rw-r--r--ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp269
-rw-r--r--ydb/core/tablet_flat/ut/ya.make1
-rw-r--r--ydb/core/util/cache_cache.h80
-rw-r--r--ydb/core/util/cache_cache_iface.h2
-rw-r--r--ydb/core/util/cache_cache_ut.cpp63
11 files changed, 1174 insertions, 126 deletions
diff --git a/ydb/core/driver_lib/run/kikimr_services_initializers.cpp b/ydb/core/driver_lib/run/kikimr_services_initializers.cpp
index 414cf6a0815..62c60f623ce 100644
--- a/ydb/core/driver_lib/run/kikimr_services_initializers.cpp
+++ b/ydb/core/driver_lib/run/kikimr_services_initializers.cpp
@@ -1093,10 +1093,7 @@ void TSharedCacheInitializer::InitializeServices(
config->TotalAsyncQueueInFlyLimit = cfg.GetAsyncQueueInFlyLimit();
config->TotalScanQueueInFlyLimit = cfg.GetScanQueueInFlyLimit();
config->ReplacementPolicy = cfg.GetReplacementPolicy();
-
- if (cfg.HasActivePagesReservationPercent()) {
- config->ActivePagesReservationPercent = cfg.GetActivePagesReservationPercent();
- }
+ config->ActivePagesReservationPercent = cfg.GetActivePagesReservationPercent();
TIntrusivePtr<::NMonitoring::TDynamicCounters> tabletGroup = GetServiceCounters(appData->Counters, "tablets");
TIntrusivePtr<::NMonitoring::TDynamicCounters> sausageGroup = tabletGroup->GetSubgroup("type", "S_CACHE");
diff --git a/ydb/core/protos/shared_cache.proto b/ydb/core/protos/shared_cache.proto
index d433c03b1cf..5835055a929 100644
--- a/ydb/core/protos/shared_cache.proto
+++ b/ydb/core/protos/shared_cache.proto
@@ -3,6 +3,7 @@ option java_package = "ru.yandex.kikimr.proto";
enum TReplacementPolicy {
ThreeLeveledLRU = 0;
+ S3FIFO = 1;
}
message TSharedCacheConfig {
diff --git a/ydb/core/tablet_flat/shared_cache_events.h b/ydb/core/tablet_flat/shared_cache_events.h
index f71462d2822..2b7cb4bd37a 100644
--- a/ydb/core/tablet_flat/shared_cache_events.h
+++ b/ydb/core/tablet_flat/shared_cache_events.h
@@ -3,6 +3,7 @@
#include "defs.h"
#include "flat_bio_events.h"
#include "shared_handle.h"
+#include <ydb/core/protos/shared_cache.pb.h>
#include <util/generic/map.h>
#include <util/generic/set.h>
@@ -24,6 +25,7 @@ namespace NSharedCache {
EvRequest,
EvResult,
EvUpdated,
+ EvReplacementPolicySwitch,
EvEnd
@@ -127,6 +129,16 @@ namespace NSharedCache {
THashMap<TLogoBlobID, TActions> Actions;
};
+
+ struct TEvReplacementPolicySwitch : public TEventLocal<TEvReplacementPolicySwitch, EvReplacementPolicySwitch> {
+ 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
new file mode 100644
index 00000000000..3644d133118
--- /dev/null
+++ b/ydb/core/tablet_flat/shared_cache_s3fifo.h
@@ -0,0 +1,346 @@
+#pragma once
+#include "defs.h"
+#include <ydb/core/util/cache_cache_iface.h>
+#include <ydb/library/yverify_stream/yverify_stream.h>
+#include <library/cpp/monlib/counters/counters.h>
+#include <library/cpp/monlib/dynamic_counters/counters.h>
+
+namespace NKikimr::NCache {
+
+// TODO: remove template args and make some page base class
+
+enum class ES3FIFOPageLocation {
+ None,
+ SmallQueue,
+ MainQueue
+};
+
+template <typename TPageTraits>
+class TS3FIFOGhostPageQueue {
+ using TPageKey = typename TPageTraits::TPageKey;
+
+ struct TGhostPage {
+ TPageKey Key;
+ ui64 Size; // zero size is tombstone
+
+ TGhostPage(const TPageKey& key, ui64 size)
+ : Key(key)
+ , Size(size)
+ {}
+ };
+
+ struct TGhostPageHash {
+ using is_transparent = void;
+
+ inline size_t operator()(const TGhostPage* ghost) const {
+ return TPageTraits::GetHash(ghost->Key);
+ }
+
+ inline size_t operator()(const TPageKey& key) const {
+ return TPageTraits::GetHash(key);
+ }
+ };
+
+ struct TGhostPageEqual {
+ using is_transparent = void;
+
+ inline bool operator()(const TGhostPage* left, const TGhostPage* right) const {
+ return TPageTraits::Equals(left->Key, right->Key);
+ }
+
+ inline bool operator()(const TGhostPage* left, const TPageKey& right) const {
+ return TPageTraits::Equals(left->Key, right);
+ }
+ };
+
+public:
+ TS3FIFOGhostPageQueue(ui64 limit)
+ : Limit(limit)
+ {}
+
+ void Add(const TPageKey& key, ui64 size) {
+ if (Y_UNLIKELY(size == 0)) {
+ Y_DEBUG_ABORT_S("Empty " << TPageTraits::ToString(key) << " page");
+ return;
+ }
+
+ TGhostPage* ghost = &GhostsQueue.emplace_back(key, size);
+ if (Y_UNLIKELY(!GhostsSet.emplace(ghost).second)) {
+ GhostsQueue.pop_back();
+ Y_DEBUG_ABORT_S("Duplicated " << TPageTraits::ToString(key) << " page");
+ return;
+ }
+
+ Size += ghost->Size;
+
+ EvictWhileFull();
+ }
+
+ bool Erase(const TPageKey& key, ui64 size) {
+ if (auto it = GhostsSet.find(key); it != GhostsSet.end()) {
+ TGhostPage* ghost = *it;
+ Y_DEBUG_ABORT_UNLESS(ghost->Size == size);
+ Y_ABORT_UNLESS(Size >= ghost->Size);
+ Size -= ghost->Size;
+ ghost->Size = 0; // mark as deleted
+ GhostsSet.erase(it);
+ return true;
+ }
+ return false;
+ }
+
+ void UpdateLimit(ui64 limit) {
+ Limit = limit;
+ EvictWhileFull();
+ }
+
+ TString Dump() const {
+ TStringBuilder result;
+ size_t count = 0;
+ ui64 size = 0;
+ for (auto it = GhostsQueue.begin(); it != GhostsQueue.end(); it++) {
+ const TGhostPage* ghost = &*it;
+ if (ghost->Size) { // isn't deleted
+ Y_DEBUG_ABORT_UNLESS(GhostsSet.contains(ghost));
+ if (count != 0) result << ", ";
+ result << "{" << TPageTraits::ToString(ghost->Key) << " " << ghost->Size << "b}";
+ count++;
+ size += ghost->Size;
+ }
+ }
+ Y_DEBUG_ABORT_UNLESS(GhostsSet.size() == count);
+ Y_DEBUG_ABORT_UNLESS(Size == size);
+ return result;
+ }
+
+private:
+ void EvictWhileFull() {
+ while (!GhostsQueue.empty() && Size > Limit) {
+ TGhostPage* ghost = &GhostsQueue.front();
+ if (ghost->Size) { // isn't deleted
+ Y_ABORT_UNLESS(Size >= ghost->Size);
+ Size -= ghost->Size;
+ bool erased = GhostsSet.erase(ghost);
+ Y_ABORT_UNLESS(erased);
+ }
+ GhostsQueue.pop_front();
+ }
+ }
+
+ ui64 Limit;
+ ui64 Size = 0;
+ // TODO: store ghost withing PageMap
+ THashSet<TGhostPage*, TGhostPageHash, TGhostPageEqual> GhostsSet;
+ TDeque<TGhostPage> GhostsQueue;
+};
+
+template <typename TPage, typename TPageTraits>
+class TS3FIFOCache : public ICacheCache<TPage> {
+ using TPageKey = typename TPageTraits::TPageKey;
+
+ struct TLimit {
+ ui64 SmallQueueLimit;
+ ui64 MainQueueLimit;
+
+ TLimit(ui64 limit)
+ : SmallQueueLimit(limit / 10)
+ , MainQueueLimit(limit - SmallQueueLimit)
+ {}
+ };
+
+ struct TQueue {
+ TQueue(ES3FIFOPageLocation location)
+ : Location(location)
+ {}
+
+ ES3FIFOPageLocation Location;
+ TIntrusiveList<TPage> Queue;
+ ui64 Size = 0;
+ };
+
+public:
+ TS3FIFOCache(ui64 limit)
+ : Limit(limit)
+ , SmallQueue(ES3FIFOPageLocation::SmallQueue)
+ , MainQueue(ES3FIFOPageLocation::MainQueue)
+ , GhostQueue(limit)
+ {}
+
+ TPage* EvictNext() override {
+ if (SmallQueue.Queue.Empty() && MainQueue.Queue.Empty()) {
+ return nullptr;
+ }
+
+ // TODO: account passive pages inside the cache
+ TLimit savedLimit = std::exchange(Limit, TLimit(SmallQueue.Size + MainQueue.Size - 1));
+
+ TPage* evictedPage = EvictOneIfFull();
+ Y_DEBUG_ABORT_UNLESS(evictedPage);
+
+ Limit = savedLimit;
+
+ return evictedPage;
+ }
+
+ TIntrusiveList<TPage> Touch(TPage* page) override {
+ const ES3FIFOPageLocation location = TPageTraits::GetLocation(page);
+ switch (location) {
+ case ES3FIFOPageLocation::SmallQueue:
+ case ES3FIFOPageLocation::MainQueue: {
+ TouchFast(page);
+ return {};
+ }
+ case ES3FIFOPageLocation::None:
+ return Insert(page);
+ default:
+ Y_ABORT("Unknown page location");
+ }
+ }
+
+ void Erase(TPage* page) override {
+ const ES3FIFOPageLocation location = TPageTraits::GetLocation(page);
+ switch (location) {
+ case ES3FIFOPageLocation::None:
+ EraseGhost(page);
+ break;
+ case ES3FIFOPageLocation::SmallQueue:
+ Erase(SmallQueue, page);
+ break;
+ case ES3FIFOPageLocation::MainQueue:
+ Erase(MainQueue, page);
+ break;
+ default:
+ Y_ABORT("Unknown page location");
+ }
+
+ TPageTraits::SetFrequency(page, 0);
+ }
+
+ void UpdateLimit(ui64 limit) override {
+ Limit = limit;
+ GhostQueue.UpdateLimit(limit);
+ }
+
+ TString Dump() const {
+ TStringBuilder result;
+
+ auto dump = [&](const TQueue& queue) {
+ size_t count = 0;
+ ui64 size = 0;
+ for (auto it = queue.Queue.begin(); it != queue.Queue.end(); it++) {
+ const TPage* page = &*it;
+ if (count != 0) result << ", ";
+ result << "{" << TPageTraits::GetKeyToString(page) << " " << TPageTraits::GetFrequency(page) << "f " << TPageTraits::GetSize(page) << "b}";
+ count++;
+ size += TPageTraits::GetSize(page);
+ }
+ Y_DEBUG_ABORT_UNLESS(queue.Size == size);
+ };
+
+ result << "SmallQueue: ";
+ dump(SmallQueue);
+ result << Endl << "MainQueue: ";
+ dump(MainQueue);
+ result << Endl << "GhostQueue: ";
+ result << GhostQueue.Dump();
+
+ return result;
+ }
+
+private:
+ TPage* EvictOneIfFull() {
+ while (true) {
+ if (!SmallQueue.Queue.Empty() && SmallQueue.Size > Limit.SmallQueueLimit) {
+ TPage* page = Pop(SmallQueue);
+ if (ui32 frequency = TPageTraits::GetFrequency(page); frequency > 1) { // load inserts, first read touches, second read touches
+ Push(MainQueue, page);
+ } else {
+ if (frequency) TPageTraits::SetFrequency(page, 0);
+ AddGhost(page);
+ return page;
+ }
+ } else if (!MainQueue.Queue.Empty() && MainQueue.Size > Limit.MainQueueLimit) {
+ TPage* page = Pop(MainQueue);
+ if (ui32 frequency = TPageTraits::GetFrequency(page); frequency > 0) {
+ TPageTraits::SetFrequency(page, frequency - 1);
+ Push(MainQueue, page);
+ } else {
+ return page;
+ }
+ } else {
+ break;
+ }
+ }
+
+ return nullptr;
+ }
+
+ void TouchFast(TPage* page) {
+ Y_DEBUG_ABORT_UNLESS(TPageTraits::GetLocation(page) != ES3FIFOPageLocation::None);
+
+ ui32 frequency = TPageTraits::GetFrequency(page);
+ if (frequency < 3) {
+ TPageTraits::SetFrequency(page, frequency + 1);
+ }
+ }
+
+ TIntrusiveList<TPage> Insert(TPage* page) {
+ Y_DEBUG_ABORT_UNLESS(TPageTraits::GetLocation(page) == ES3FIFOPageLocation::None);
+
+ Push(EraseGhost(page) ? MainQueue : SmallQueue, page);
+ TPageTraits::SetFrequency(page, 0);
+
+ TIntrusiveList<TPage> evictedList;
+ while (TPage* evictedPage = EvictOneIfFull()) {
+ evictedList.PushBack(evictedPage);
+ }
+
+ return evictedList;
+ }
+
+ TPage* Pop(TQueue& queue) {
+ Y_DEBUG_ABORT_UNLESS(!queue.Queue.Empty());
+ Y_ABORT_UNLESS(TPageTraits::GetLocation(queue.Queue.Front()) == queue.Location);
+ Y_ABORT_UNLESS(queue.Size >= TPageTraits::GetSize(queue.Queue.Front()));
+
+ TPage* page = queue.Queue.PopFront();
+ queue.Size -= TPageTraits::GetSize(page);
+ TPageTraits::SetLocation(page, ES3FIFOPageLocation::None);
+
+ return page;
+ }
+
+ void Push(TQueue& queue, TPage* page) {
+ Y_ABORT_UNLESS(TPageTraits::GetLocation(page) == ES3FIFOPageLocation::None);
+
+ queue.Queue.PushBack(page);
+ queue.Size += TPageTraits::GetSize(page);
+ TPageTraits::SetLocation(page, queue.Location);
+ }
+
+ void Erase(TQueue& queue, TPage* page) {
+ Y_ABORT_UNLESS(TPageTraits::GetLocation(page) == queue.Location);
+ Y_ABORT_UNLESS(queue.Size >= TPageTraits::GetSize(page));
+
+ page->Unlink();
+ queue.Size -= TPageTraits::GetSize(page);
+ TPageTraits::SetLocation(page, ES3FIFOPageLocation::None);
+ }
+
+ void AddGhost(const TPage* page) {
+ GhostQueue.Add(TPageTraits::GetKey(page), TPageTraits::GetSize(page));
+ }
+
+ bool EraseGhost(const TPage* page) {
+ return GhostQueue.Erase(TPageTraits::GetKey(page), TPageTraits::GetSize(page));
+ }
+
+private:
+ TLimit Limit;
+ TQueue SmallQueue;
+ TQueue MainQueue;
+ TS3FIFOGhostPageQueue<TPageTraits> GhostQueue;
+
+};
+
+}
diff --git a/ydb/core/tablet_flat/shared_cache_s3fifo_ut.cpp b/ydb/core/tablet_flat/shared_cache_s3fifo_ut.cpp
new file mode 100644
index 00000000000..96082a2811b
--- /dev/null
+++ b/ydb/core/tablet_flat/shared_cache_s3fifo_ut.cpp
@@ -0,0 +1,406 @@
+#include <library/cpp/testing/unittest/registar.h>
+#include "shared_cache_s3fifo.h"
+
+namespace NKikimr::NCache {
+
+namespace {
+
+ struct TPage : public TIntrusiveListItem<TPage> {
+ ui32 Id;
+ size_t Size;
+
+ TPage(ui32 id, size_t size)
+ : Id(id), Size(size)
+ {}
+
+ ui32 CacheFlags1 : 4 = 0;
+ ui32 CacheFlags2 : 4 = 0;
+ };
+
+ struct TPageTraits {
+ struct TPageKey {
+ ui32 Id;
+
+ TPageKey(ui32 id)
+ : Id(id)
+ {}
+ };
+
+ static ui64 GetSize(const TPage* page) {
+ return page->Size;
+ }
+
+ static TPageKey GetKey(const TPage* page) {
+ return {page->Id};
+ }
+
+ static size_t GetHash(const TPageKey& key) {
+ return std::hash<ui32>()(key.Id);
+ }
+
+ static bool Equals(const TPageKey& left, const TPageKey& right) {
+ return left.Id == right.Id;
+ }
+
+ static TString ToString(const TPageKey& key) {
+ return std::to_string(key.Id);
+ }
+
+ static TString GetKeyToString(const TPage* page) {
+ return ToString(GetKey(page));
+ }
+
+ static ES3FIFOPageLocation GetLocation(const TPage* page) {
+ return static_cast<ES3FIFOPageLocation>(page->CacheFlags1);
+ }
+
+ static void SetLocation(TPage* page, ES3FIFOPageLocation location) {
+ ui32 location_ = static_cast<ui32>(location);
+ Y_ABORT_UNLESS(location_ < (1 << 4));
+ page->CacheFlags1 = location_;
+ }
+
+ static ui32 GetFrequency(const TPage* page) {
+ return page->CacheFlags2;
+ }
+
+ static void SetFrequency(TPage* page, ui32 frequency) {
+ Y_ABORT_UNLESS(frequency < (1 << 4));
+ page->CacheFlags2 = frequency;
+ }
+ };
+
+}
+
+Y_UNIT_TEST_SUITE(TS3FIFOGhostQueue) {
+
+ Y_UNIT_TEST(Add) {
+ TS3FIFOGhostPageQueue<TPageTraits> queue(100);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "");
+
+ queue.Add(1, 10);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}");
+
+ queue.Add(2, 30);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {2 30b}");
+
+ queue.Add(3, 60);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {2 30b}, {3 60b}");
+
+ queue.Add(4, 1);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{2 30b}, {3 60b}, {4 1b}");
+
+ queue.Add(1, 3);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{2 30b}, {3 60b}, {4 1b}, {1 3b}");
+ }
+
+ Y_UNIT_TEST(Erase) {
+ TS3FIFOGhostPageQueue<TPageTraits> queue(100);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "");
+
+ queue.Add(1, 10);
+ queue.Add(2, 30);
+ queue.Add(3, 60);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {2 30b}, {3 60b}");
+
+ UNIT_ASSERT_VALUES_EQUAL(queue.Erase(5, 42), false);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {2 30b}, {3 60b}");
+
+ UNIT_ASSERT_VALUES_EQUAL(queue.Erase(2, 30), true);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {3 60b}");
+
+ queue.Add(4, 30);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {3 60b}, {4 30b}");
+
+ queue.Add(5, 1);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{3 60b}, {4 30b}, {5 1b}");
+ }
+
+ Y_UNIT_TEST(Erase_Add) {
+ TS3FIFOGhostPageQueue<TPageTraits> queue(100);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "");
+
+ queue.Add(1, 10);
+ queue.Add(2, 30);
+ queue.Add(3, 60);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {2 30b}, {3 60b}");
+
+ UNIT_ASSERT_VALUES_EQUAL(queue.Erase(2, 30), true);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {3 60b}");
+
+ queue.Add(2, 30);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {3 60b}, {2 30b}");
+
+ queue.Add(4, 70);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{2 30b}, {4 70b}");
+ }
+
+ Y_UNIT_TEST(Add_Big) {
+ TS3FIFOGhostPageQueue<TPageTraits> queue(100);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "");
+
+ queue.Add(1, 101);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "");
+ }
+
+ Y_UNIT_TEST(UpdateLimit) {
+ TS3FIFOGhostPageQueue<TPageTraits> queue(100);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "");
+
+ queue.Add(1, 10);
+ queue.Add(2, 20);
+ queue.Add(3, 30);
+ queue.Add(4, 40);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{1 10b}, {2 20b}, {3 30b}, {4 40b}");
+
+ queue.UpdateLimit(80);
+ UNIT_ASSERT_VALUES_EQUAL(queue.Dump(), "{3 30b}, {4 40b}");
+ }
+
+}
+
+Y_UNIT_TEST_SUITE(TS3FIFOCache) {
+
+ TVector<ui32> Touch(auto& cache, TPage& page) {
+ auto evicted = cache.Touch(&page);
+ TVector<ui32> result;
+ for (auto& p : evicted) {
+ UNIT_ASSERT_VALUES_EQUAL(p.CacheFlags1, 0);
+ UNIT_ASSERT_VALUES_EQUAL(p.CacheFlags2, 0);
+ result.push_back(p.Id);
+ }
+ return result;
+ }
+
+ std::optional<ui32> EvictNext(auto& cache) {
+ auto evicted = cache.EvictNext();
+ if (evicted) {
+ UNIT_ASSERT_VALUES_EQUAL(evicted->CacheFlags1, 0);
+ UNIT_ASSERT_VALUES_EQUAL(evicted->CacheFlags2, 0);
+ return evicted->Id;
+ }
+ return {};
+ }
+
+ Y_UNIT_TEST(Touch) {
+ TS3FIFOCache<TPage, TPageTraits> cache(100);
+
+ TPage page1{1, 2};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {1 0f 2b}" << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: "));
+
+ TPage page2{2, 3};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {1 0f 2b}, {2 0f 3b}" << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: "));
+
+ TPage page3{3, 4};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {1 0f 2b}, {2 0f 3b}, {3 0f 4b}" << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: "));
+
+ TPage page4{4, 1};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {1 0f 2b}, {2 0f 3b}, {3 0f 4b}, {4 0f 1b}" << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: "));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {1 2f 2b}, {2 1f 3b}, {3 2f 4b}, {4 0f 1b}" << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: "));
+
+ TPage page5{5, 8};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page5), TVector<ui32>{2});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {4 0f 1b}, {5 0f 8b}" << Endl
+ << "MainQueue: {1 2f 2b}, {3 2f 4b}" << Endl
+ << "GhostQueue: {2 3b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {4 0f 1b}, {5 0f 8b}" << Endl
+ << "MainQueue: {1 2f 2b}, {3 3f 4b}" << Endl
+ << "GhostQueue: {2 3b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {4 0f 1b}, {5 0f 8b}" << Endl
+ << "MainQueue: {1 2f 2b}, {3 3f 4b}, {2 0f 3b}" << Endl
+ << "GhostQueue: "));
+ }
+
+ Y_UNIT_TEST(Touch_MainQueue) {
+ TS3FIFOCache<TPage, TPageTraits> cache(100);
+
+ TPage page1{1, 20};
+ TPage page2{2, 30};
+ TPage page3{3, 40};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{1});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{2});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{3});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: {1 20b}, {2 30b}, {3 40b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {2 0f 30b}, {1 0f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: "));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {2 2f 30b}, {1 1f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: "));
+
+ TPage page4{4, 20};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector<ui32>{4});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {2 2f 30b}, {1 1f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: {4 20b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector<ui32>{3});
+ // MainQueue: {2 2f 30b}, {1 1f 20b}, {3 0f 40b}, {4 0f 20b}
+ // MainQueue: {1 1f 20b}, {3 0f 40b}, {4 0f 20b}, {2 1f 30b}
+ // MainQueue: {3 0f 40b}, {4 0f 20b}, {2 1f 30b}, {1 0f 20b}
+ // MainQueue: {4 0f 20b}, {2 1f 30b}, {1 0f 20b}
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {4 0f 20b}, {2 1f 30b}, {1 0f 20b}" << Endl
+ << "GhostQueue: "));
+ }
+
+ Y_UNIT_TEST(EvictNext) {
+ TS3FIFOCache<TPage, TPageTraits> cache(100);
+
+ TPage page1{1, 20};
+ TPage page2{2, 30};
+ TPage page3{3, 40};
+ TPage page4{4, 10};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{1});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{2});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{3});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: {1 20b}, {2 30b}, {3 40b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {2 0f 30b}, {1 1f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: "));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {4 0f 10b}" << Endl
+ << "MainQueue: {2 0f 30b}, {1 1f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: "));
+
+ UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), 4);
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {2 0f 30b}, {1 1f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: {4 10b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), 2);
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {1 1f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: {4 10b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), 3);
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {1 0f 20b}" << Endl
+ << "GhostQueue: {4 10b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), 1);
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: {4 10b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(EvictNext(cache), std::optional<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: {4 10b}"));
+ }
+
+ Y_UNIT_TEST(UpdateLimit) {
+ TS3FIFOCache<TPage, TPageTraits> cache(100);
+
+ TPage page1{1, 20};
+ TPage page2{2, 30};
+ TPage page3{3, 40};
+ TPage page4{4, 10};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{1});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{2});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{3});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: {1 20b}, {2 30b}, {3 40b}"));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page2), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page1), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page3), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: {2 0f 30b}, {1 0f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: "));
+
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page4), TVector<ui32>{});
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {4 0f 10b}" << Endl
+ << "MainQueue: {2 0f 30b}, {1 0f 20b}, {3 0f 40b}" << Endl
+ << "GhostQueue: "));
+
+ cache.UpdateLimit(45);
+ TPage page5{5, 1};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page5), (TVector<ui32>{4, 2, 1}));
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: {5 0f 1b}" << Endl
+ << "MainQueue: {3 0f 40b}" << Endl
+ << "GhostQueue: {4 10b}"));
+
+ cache.UpdateLimit(0);
+ TPage page6{6, 1};
+ UNIT_ASSERT_VALUES_EQUAL(Touch(cache, page6), (TVector<ui32>{5, 6, 3}));
+ UNIT_ASSERT_VALUES_EQUAL(cache.Dump(), (TString)(TStringBuilder()
+ << "SmallQueue: " << Endl
+ << "MainQueue: " << Endl
+ << "GhostQueue: "));
+ }
+
+}
+
+}
diff --git a/ydb/core/tablet_flat/shared_sausagecache.cpp b/ydb/core/tablet_flat/shared_sausagecache.cpp
index cd74754bdfe..8b200181d81 100644
--- a/ydb/core/tablet_flat/shared_sausagecache.cpp
+++ b/ydb/core/tablet_flat/shared_sausagecache.cpp
@@ -3,6 +3,7 @@
#include "flat_bio_events.h"
#include "flat_bio_actor.h"
#include "util_fmt_logger.h"
+#include <ydb/core/tablet_flat/shared_cache_s3fifo.h>
#include <ydb/core/util/cache_cache.h>
#include <ydb/core/util/page_map.h>
#include <ydb/core/base/blobstorage.h>
@@ -52,24 +53,6 @@ static bool Satisfies(NLog::EPriority priority = NLog::PRI_DEBUG) {
return false;
}
-struct TEvPrivate {
- enum EEv {
- EvReplacementPolicySwitch = EventSpaceBegin(TKikimrEvents::ES_PRIVATE),
-
- EvEnd
- };
-
- static_assert(EvEnd < EventSpaceEnd(TKikimrEvents::ES_PRIVATE));
-
- struct TEvReplacementPolicySwitch : public TEventLocal<TEvReplacementPolicySwitch, EvReplacementPolicySwitch> {
- TSharedPageCacheConfig::TReplacementPolicy ReplacementPolicy;
-
- TEvReplacementPolicySwitch(TSharedPageCacheConfig::TReplacementPolicy replacementPolicy)
- : ReplacementPolicy(replacementPolicy)
- {}
- };
-};
-
class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
using ELnLev = NUtil::ELnLev;
using TBlocks = TVector<NSharedCache::TEvResult::TLoaded>;
@@ -92,7 +75,8 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
, public TIntrusiveListItem<TPage>
{
ui32 State : 4 = PageStateNo;
- ui32 CacheFlags : 16 = 0;
+ ui32 CacheFlags1 : 4 = 0;
+ ui32 CacheFlags2 : 4 = 0;
const ui32 PageId;
const size_t Size;
@@ -125,24 +109,75 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
}
void EnsureNoCacheFlags() {
- Y_VERIFY_S(CacheFlags == 0, "Unexpected " << CacheFlags << " page cache flags");
+ Y_VERIFY_S(CacheFlags1 == 0, "Unexpected page " << CacheFlags1 << " cache flags 1");
+ Y_VERIFY_S(CacheFlags2 == 0, "Unexpected page " << CacheFlags2 << " cache flags 2");
+ }
+ };
+
+ struct TCacheCachePageTraits {
+ static ui64 GetWeight(const TPage* page) {
+ return sizeof(TPage) + page->Size;
}
- struct TWeight {
- static ui64 Get(TPage *x) {
- return sizeof(TPage) + (x->State == PageStateLoaded ? x->Size : 0);
- }
- };
+ static ECacheCacheGeneration GetGeneration(const TPage *page) {
+ return static_cast<ECacheCacheGeneration>(page->CacheFlags1);
+ }
- struct TCacheFlags {
- static ui32 Get(TPage *x) {
- return x->CacheFlags;
- }
- static void Set(TPage *x, ui32 flags) {
- Y_ABORT_UNLESS(flags < Max<ui16>());
- x->CacheFlags = flags;
- }
+ static void SetGeneration(TPage *page, ECacheCacheGeneration generation) {
+ ui32 generation_ = static_cast<ui32>(generation);
+ Y_ABORT_UNLESS(generation_ < (1 << 4));
+ page->CacheFlags1 = generation_;
+ }
+ };
+
+ struct TS3FIFOPageTraits {
+ struct TPageKey {
+ TLogoBlobID LogoBlobID;
+ ui32 PageId;
};
+
+ static ui64 GetSize(const TPage* page) {
+ return sizeof(TPage) + page->Size;
+ }
+
+ static TPageKey GetKey(const TPage* page) {
+ return {page->Collection->MetaId, page->PageId};
+ }
+
+ static size_t GetHash(const TPageKey& key) {
+ return MultiHash(key.LogoBlobID.Hash(), key.PageId);
+ }
+
+ static bool Equals(const TPageKey& left, const TPageKey& right) {
+ return left.PageId == right.PageId && left.LogoBlobID == right.LogoBlobID;
+ }
+
+ static TString ToString(const TPageKey& key) {
+ return TStringBuilder() << "LogoBlobID: " << key.LogoBlobID.ToString() << " PageId: " << key.PageId;
+ }
+
+ static TString GetKeyToString(const TPage* page) {
+ return ToString(GetKey(page));
+ }
+
+ static ES3FIFOPageLocation GetLocation(const TPage* page) {
+ return static_cast<ES3FIFOPageLocation>(page->CacheFlags1);
+ }
+
+ static void SetLocation(TPage* page, ES3FIFOPageLocation location) {
+ ui32 location_ = static_cast<ui32>(location);
+ Y_ABORT_UNLESS(location_ < (1 << 4));
+ page->CacheFlags1 = location_;
+ }
+
+ static ui32 GetFrequency(const TPage* page) {
+ return page->CacheFlags2;
+ }
+
+ static void SetFrequency(TPage* page, ui32 frequency) {
+ Y_ABORT_UNLESS(frequency < (1 << 4));
+ page->CacheFlags2 = frequency;
+ }
};
struct TRequest : public TSimpleRefCount<TRequest> {
@@ -233,10 +268,12 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
// now it will be fixed by ActualizeCacheSizeLimit call
switch (Config->ReplacementPolicy) {
+ case NKikimrSharedCache::S3FIFO:
+ return MakeHolder<TS3FIFOCache<TPage, TS3FIFOPageTraits>>(1);
case NKikimrSharedCache::ThreeLeveledLRU:
default: {
TCacheCacheConfig cacheCacheConfig(1, Config->Counters->FreshBytes, Config->Counters->StagingBytes, Config->Counters->WarmBytes);
- return MakeHolder<TCacheCache<TPage, TPage::TWeight, TPage::TCacheFlags>>(std::move(cacheCacheConfig));
+ return MakeHolder<TCacheCache<TPage, TCacheCachePageTraits>>(std::move(cacheCacheConfig));
}
}
}
@@ -247,7 +284,7 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
// 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->UpdateCacheSize(limitBytes);
+ Cache->UpdateLimit(limitBytes);
if (Config->Counters) {
Config->Counters->ConfigLimitBytes->Set(Config->LimitBytes.value_or(0));
@@ -299,7 +336,7 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
DoGC();
}
- void Handle(TEvPrivate::TEvReplacementPolicySwitch::TPtr &ev) {
+ void Handle(NSharedCache::TEvReplacementPolicySwitch::TPtr &ev) {
auto *msg = ev->Get();
if (msg->ReplacementPolicy == Config->ReplacementPolicy) {
@@ -322,7 +359,7 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
page->EnsureNoCacheFlags();
// touch each page multiple times to make it warm
- for (ui32 touchTimes = 0; touchTimes < 2; touchTimes++) {
+ for (ui32 touchTimes = 0; touchTimes < 3; touchTimes++) {
Evict(Cache->Touch(page));
}
}
@@ -1123,7 +1160,7 @@ class TSharedPageCache : public TActorBootstrapped<TSharedPageCache> {
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 TEvPrivate::TEvReplacementPolicySwitch(msg->Record.GetReplacementPolicy()));
+ Schedule(TDuration::Seconds(delaySeconds), new NSharedCache::TEvReplacementPolicySwitch(msg->Record.GetReplacementPolicy()));
}
}
@@ -1219,7 +1256,7 @@ public:
hFunc(NMemory::TEvConsumerRegistered, Handle);
hFunc(NMemory::TEvConsumerLimit, Handle);
- hFunc(TEvPrivate::TEvReplacementPolicySwitch, Handle);
+ hFunc(NSharedCache::TEvReplacementPolicySwitch, Handle);
}
}
diff --git a/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp b/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp
index f18c08fe932..af7a405c300 100644
--- a/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp
+++ b/ydb/core/tablet_flat/ut/ut_shared_sausagecache.cpp
@@ -13,16 +13,30 @@ enum : ui32 {
ValueColumnId = 2,
};
+using TRetriedCounters = TVector<ui32>;
+using namespace NSharedCache;
+
+void Increment(TRetriedCounters& retried, ui32 attempts) {
+ if (attempts >= retried.size()) {
+ retried.resize(attempts + 1);
+ }
+ retried.at(attempts)++;
+}
+
struct TTxInitSchema : public ITransaction {
bool Execute(TTransactionContext& txc, const TActorContext&) override {
if (txc.DB.GetScheme().GetTableInfo(TableId))
return true;
+ TCompactionPolicy policy;
+ policy.MinBTreeIndexNodeSize = 128;
+
txc.DB.Alter()
.AddTable("test" + ToString(ui32(TableId)), TableId)
.AddColumn(TableId, "key", KeyColumnId, NScheme::TInt64::TypeId, false)
.AddColumn(TableId, "value", ValueColumnId, NScheme::TString::TypeId, false)
- .AddColumnToKey(TableId, KeyColumnId);
+ .AddColumnToKey(TableId, KeyColumnId)
+ .SetCompactionPolicy(TableId, policy);
return true;
}
@@ -59,12 +73,18 @@ struct TTxWriteRow : public ITransaction {
struct TTxReadRow : public ITransaction {
i64 Key;
+ TRetriedCounters& Retried;
+ ui32 Attempts = 0;
- explicit TTxReadRow(i64 key)
+ explicit TTxReadRow(i64 key, TRetriedCounters& retried)
: Key(key)
+ , Retried(retried)
{ }
bool Execute(TTransactionContext& txc, const TActorContext&) override {
+ Increment(Retried, Attempts);
+ Attempts++;
+
TVector<TRawTypeValue> rawKey;
rawKey.emplace_back(&Key, sizeof(Key), NScheme::TTypeInfo(NScheme::TInt64::TypeId));
@@ -99,6 +119,15 @@ void WaitEvent(TMyEnvBase& env, ui32 eventType, ui32 requiredCount = 1) {
env->DispatchEvents(options);
}
+void RestartAndClearCache(TMyEnvBase& env) {
+ env.SendSync(new TEvents::TEvPoison, false, true);
+ env->Send(MakeSharedPageCacheId(), TActorId{}, new NMemory::TEvConsumerLimit(0_MB));
+ WaitEvent(env, NMemory::EvConsumerLimit);
+ env->Send(MakeSharedPageCacheId(), TActorId{}, new NMemory::TEvConsumerLimit(8_MB));
+ WaitEvent(env, NMemory::EvConsumerLimit);
+ env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp));
+}
+
Y_UNIT_TEST(Limits) {
TMyEnvBase env;
auto counters = MakeIntrusive<TSharedPageCacheCounters>(env->GetDynamicCounters());
@@ -109,7 +138,7 @@ Y_UNIT_TEST(Limits) {
env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp));
env.SendSync(new NFake::TEvExecute{ new TTxInitSchema() });
- // write 300 rows, each ~100KB
+ // write 300 rows, each ~100KB (~30MB)
for (i64 key = 0; key < 300; ++key) {
TString value(size_t(100 * 1024), char('a' + key % 26));
env.SendSync(new NFake::TEvExecute{ new TTxWriteRow(key, std::move(value)) });
@@ -120,8 +149,9 @@ Y_UNIT_TEST(Limits) {
Cerr << "...waiting until compacted" << Endl;
env.WaitFor<NFake::TEvCompacted>();
+ TRetriedCounters retried;
for (i64 key = 0; key < 100; ++key) {
- env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key) });
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) });
}
LogCounters(counters);
UNIT_ASSERT_VALUES_EQUAL(counters->LoadInFlyBytes->Val(), 0);
@@ -161,6 +191,237 @@ Y_UNIT_TEST(Limits) {
UNIT_ASSERT_VALUES_EQUAL(counters->MemLimitBytes->Val(), counters->ActiveLimitBytes->Val());
}
+Y_UNIT_TEST(ThreeLeveledLRU) {
+ TMyEnvBase env;
+ auto counters = MakeIntrusive<TSharedPageCacheCounters>(env->GetDynamicCounters());
+
+ env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp));
+ env.SendSync(new NFake::TEvExecute{ new TTxInitSchema() });
+
+ // write 100 rows, each ~100KB (~10MB)
+ for (i64 key = 0; key < 100; ++key) {
+ TString value(size_t(100 * 1024), char('a' + key % 26));
+ env.SendSync(new NFake::TEvExecute{ new TTxWriteRow(key, std::move(value)) });
+ }
+
+ Cerr << "...compacting" << Endl;
+ env.SendSync(new NFake::TEvCompact(TableId));
+ Cerr << "...waiting until compacted" << Endl;
+ env.WaitFor<NFake::TEvCompacted>();
+
+ TRetriedCounters retried;
+ for (i64 key = 99; key >= 0; --key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) });
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 45, 5}));
+
+ RestartAndClearCache(env);
+
+ retried = {};
+ for (i64 key = 0; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB / 3 * 2), static_cast<i64>(1_MB / 3)); // 2 full layers (fresh & staging)
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 100, 14, 2}));
+
+ retried = {};
+ for (i64 key = 99; key >= 0; --key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 44, 6}));
+
+ retried = {};
+ for (i64 key = 99; key >= 0; --key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 73, 10, 1}));
+
+ RestartAndClearCache(env);
+
+ // read some key twice
+ retried = {};
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(0, retried) }, true);
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{1, 1, 1, 1}));
+ retried = {};
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(0, retried) }, true);
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{1}));
+
+ // simulate scan
+ retried = {};
+ for (i64 key = 1; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB / 3 * 2), static_cast<i64>(1_MB / 3)); // 2 full layers (fresh & staging)
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{99, 99, 13, 1}));
+
+ // read the key again
+ retried = {};
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(0, retried) }, true);
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{1, 1, 1}));
+
+ RestartAndClearCache(env);
+
+ retried = {};
+ for (i64 key = 0; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB / 3 * 2), static_cast<i64>(1_MB / 3)); // 2 full layers (fresh & staging)
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{200, 100, 14, 2}));
+
+ retried = {};
+ for (i64 key = 0; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB / 3 * 2), static_cast<i64>(1_MB / 3)); // 2 full layers (fresh & staging)
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 100, 14}));
+}
+
+Y_UNIT_TEST(S3FIFO) {
+ TMyEnvBase env;
+ auto counters = MakeIntrusive<TSharedPageCacheCounters>(env->GetDynamicCounters());
+
+ 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);
+
+ // write 100 rows, each ~100KB (~10MB)
+ for (i64 key = 0; key < 100; ++key) {
+ TString value(size_t(100 * 1024), char('a' + key % 26));
+ env.SendSync(new NFake::TEvExecute{ new TTxWriteRow(key, std::move(value)) });
+ }
+
+ Cerr << "...compacting" << Endl;
+ env.SendSync(new NFake::TEvCompact(TableId));
+ Cerr << "...waiting until compacted" << Endl;
+ env.WaitFor<NFake::TEvCompacted>();
+
+ TRetriedCounters retried;
+ for (i64 key = 99; key >= 0; --key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) });
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 92, 12}));
+
+ RestartAndClearCache(env);
+
+ retried = {};
+ for (i64 key = 0; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB / 10), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 100, 14, 2}));
+
+ retried = {};
+ for (i64 key = 99; key >= 0; --key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 92}));
+
+ retried = {};
+ for (i64 key = 99; key >= 0; --key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 28}));
+
+ RestartAndClearCache(env);
+
+ // read some key twice
+ retried = {};
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(0, retried) }, true);
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{1, 1, 1, 1}));
+ retried = {};
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(0, retried) }, true);
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{1}));
+
+ // simulate scan
+ retried = {};
+ for (i64 key = 1; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB / 10), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{99, 99, 13, 1}));
+
+ // read the key again
+ retried = {};
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(0, retried) }, true);
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{1}));
+
+ RestartAndClearCache(env);
+
+ retried = {};
+ for (i64 key = 0; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{200, 100, 14, 2}));
+
+ retried = {};
+ for (i64 key = 0; key < 100; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ LogCounters(counters);
+ UNIT_ASSERT_DOUBLES_EQUAL(counters->ActiveBytes->Val(), static_cast<i64>(8_MB), static_cast<i64>(1_MB / 3));
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{100, 28}));
+}
+
+Y_UNIT_TEST(ReplacementPolicySwitch) {
+ TMyEnvBase env;
+ auto counters = MakeIntrusive<TSharedPageCacheCounters>(env->GetDynamicCounters());
+
+ env.FireDummyTablet(ui32(NFake::TDummy::EFlg::Comp));
+ env.SendSync(new NFake::TEvExecute{ new TTxInitSchema() });
+
+ // write 100 rows, each ~100KB (~10MB)
+ for (i64 key = 0; key < 100; ++key) {
+ TString value(size_t(100 * 1024), char('a' + key % 26));
+ env.SendSync(new NFake::TEvExecute{ new TTxWriteRow(key, std::move(value)) });
+ }
+
+ Cerr << "...compacting" << Endl;
+ env.SendSync(new NFake::TEvCompact(TableId));
+ Cerr << "...waiting until compacted" << Endl;
+ env.WaitFor<NFake::TEvCompacted>();
+
+ RestartAndClearCache(env);
+
+ TRetriedCounters retried = {};
+ for (i64 key = 0; key < 3; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{3, 3, 1, 1}));
+
+ env->Send(MakeSharedPageCacheId(), TActorId{}, new TEvReplacementPolicySwitch(NKikimrSharedCache::S3FIFO));
+ WaitEvent(env, NSharedCache::EvReplacementPolicySwitch);
+
+ retried = {};
+ for (i64 key = 0; key < 3; ++key) {
+ env.SendSync(new NFake::TEvExecute{ new TTxReadRow(key, retried) }, true);
+ }
+ UNIT_ASSERT_VALUES_EQUAL(retried, (TVector<ui32>{3}));
+}
+
} // Y_UNIT_TEST_SUITE(TSharedPageCache)
} // namespace NTabletFlatExecutor
diff --git a/ydb/core/tablet_flat/ut/ya.make b/ydb/core/tablet_flat/ut/ya.make
index 39017baf695..a547bb52793 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.h
flat_test_db.cpp
flat_test_db_helpers.h
+ shared_cache_s3fifo_ut.cpp
shared_handle_ut.cpp
ut_btree_index_nodes.cpp
ut_btree_index_iter_charge.cpp
diff --git a/ydb/core/util/cache_cache.h b/ydb/core/util/cache_cache.h
index 325d80f252c..f5493abfdc6 100644
--- a/ydb/core/util/cache_cache.h
+++ b/ydb/core/util/cache_cache.h
@@ -9,16 +9,16 @@
namespace NKikimr::NCache {
+enum class ECacheCacheGeneration {
+ None,
+ Fresh,
+ Staging,
+ Warm,
+};
+
struct TCacheCacheConfig : public TAtomicRefCount<TCacheCacheConfig> {
using TCounterPtr = ::NMonitoring::TDynamicCounters::TCounterPtr;
- enum ECacheGeneration {
- CacheGenNone,
- CacheGenFresh,
- CacheGenStaging,
- CacheGenWarm,
- };
-
ui64 Limit;
ui64 FreshLimit;
@@ -50,10 +50,7 @@ struct TCacheCacheConfig : public TAtomicRefCount<TCacheCacheConfig> {
}
};
-template <typename TItem
- , typename TWeight
- , typename TCacheFlags
- >
+template <typename TItem, typename TItemTraits>
class TCacheCache : public ICacheCache<TItem> {
public:
TCacheCache(const TCacheCacheConfig &config)
@@ -88,19 +85,19 @@ public:
TIntrusiveList<TItem> evictedList;
TIntrusiveListItem<TItem> *xitem = item;
- const TCacheCacheConfig::ECacheGeneration cacheGen = GetGeneration(item);
+ const ECacheCacheGeneration cacheGen = TItemTraits::GetGeneration(item);
switch (cacheGen) {
- case TCacheCacheConfig::CacheGenNone: // place in fresh
+ case ECacheCacheGeneration::None: // place in fresh
AddToFresh(item, evictedList);
[[fallthrough]];
- case TCacheCacheConfig::CacheGenFresh: // just update inside fresh
+ case ECacheCacheGeneration::Fresh: // just update inside fresh
xitem->Unlink();
FreshList.PushFront(xitem);
break;
- case TCacheCacheConfig::CacheGenStaging: // move to warm
+ case ECacheCacheGeneration::Staging: // move to warm
MoveToWarm(item, evictedList);
break;
- case TCacheCacheConfig::CacheGenWarm: // just update inside warm
+ case ECacheCacheGeneration::Warm: // just update inside warm
xitem->Unlink();
WarmList.PushFront(xitem);
break;
@@ -113,21 +110,21 @@ public:
}
void Erase(TItem *item) override {
- const TCacheCacheConfig::ECacheGeneration cacheGen = GetGeneration(item);
+ const ECacheCacheGeneration cacheGen = TItemTraits::GetGeneration(item);
switch (cacheGen) {
- case TCacheCacheConfig::CacheGenNone:
+ case ECacheCacheGeneration::None:
break;
- case TCacheCacheConfig::CacheGenFresh:
+ case ECacheCacheGeneration::Fresh:
Unlink(item, FreshWeight);
if (Config.ReportedFresh)
*Config.ReportedFresh = FreshWeight;
break;
- case TCacheCacheConfig::CacheGenStaging:
+ case ECacheCacheGeneration::Staging:
Unlink(item, StagingWeight);
if (Config.ReportedStaging)
*Config.ReportedStaging = StagingWeight;
break;
- case TCacheCacheConfig::CacheGenWarm:
+ case ECacheCacheGeneration::Warm:
Unlink(item, WarmWeight);
if (Config.ReportedWarm)
*Config.ReportedWarm = WarmWeight;
@@ -135,18 +132,18 @@ public:
default:
Y_DEBUG_ABORT("unknown cache generation");
}
- SetGeneration(item, TCacheCacheConfig::CacheGenNone);
+ TItemTraits::SetGeneration(item, ECacheCacheGeneration::None);
}
- void UpdateCacheSize(ui64 cacheSize) override {
- Config.SetLimit(cacheSize);
+ void UpdateLimit(ui64 limit) override {
+ Config.SetLimit(limit);
}
private:
void Unlink(TItem *item, ui64 &weight) {
item->Unlink();
- const ui64 elementWeight = WeightOp.Get(item);
+ const ui64 elementWeight = TItemTraits::GetWeight(item);
Y_DEBUG_ABORT_UNLESS(elementWeight <= weight);
weight -= elementWeight;
}
@@ -154,9 +151,9 @@ private:
void AddToFresh(TItem *item, TIntrusiveList<TItem>& evictedList) {
LimitFresh(evictedList);
item->Unlink();
- FreshWeight += WeightOp.Get(item);
+ FreshWeight += TItemTraits::GetWeight(item);
FreshList.PushFront(item);
- SetGeneration(item, TCacheCacheConfig::CacheGenFresh);
+ TItemTraits::SetGeneration(item, ECacheCacheGeneration::Fresh);
if (Config.ReportedStaging)
*Config.ReportedStaging = StagingWeight;
@@ -168,9 +165,9 @@ private:
// Note: unlink first, so item is not evicted by LimitWarm call below
Unlink(item, StagingWeight);
LimitWarm(evictedList);
- WarmWeight += WeightOp.Get(item);
+ WarmWeight += TItemTraits::GetWeight(item);
WarmList.PushFront(item);
- SetGeneration(item, TCacheCacheConfig::CacheGenWarm);
+ TItemTraits::SetGeneration(item, ECacheCacheGeneration::Warm);
if (Config.ReportedStaging)
*Config.ReportedStaging = StagingWeight;
@@ -180,16 +177,16 @@ private:
void AddToStaging(TItem *item, TIntrusiveList<TItem>& evictedList) {
LimitStaging(evictedList);
- StagingWeight += WeightOp.Get(item);
+ StagingWeight += TItemTraits::GetWeight(item);
StagingList.PushFront(item);
- SetGeneration(item, TCacheCacheConfig::CacheGenStaging);
+ TItemTraits::SetGeneration(item, ECacheCacheGeneration::Staging);
}
void LimitFresh(TIntrusiveList<TItem>& evictedList) {
while (FreshWeight > Config.FreshLimit) {
Y_DEBUG_ABORT_UNLESS(!FreshList.Empty());
TItem *x = FreshList.PopBack();
- Y_ABORT_UNLESS(GetGeneration(x) == TCacheCacheConfig::CacheGenFresh, "malformed entry in fresh cache. %" PRIu32, (ui32)GetGeneration(x));
+ Y_ABORT_UNLESS(TItemTraits::GetGeneration(x) == ECacheCacheGeneration::Fresh, "malformed entry in fresh cache. %" PRIu32, (ui32)TItemTraits::GetGeneration(x));
Unlink(x, FreshWeight);
AddToStaging(x, evictedList);
}
@@ -199,7 +196,7 @@ private:
while (WarmWeight > Config.WarmLimit) {
Y_DEBUG_ABORT_UNLESS(!WarmList.Empty());
TItem *x = WarmList.PopBack();
- Y_ABORT_UNLESS(GetGeneration(x) == TCacheCacheConfig::CacheGenWarm, "malformed entry in warm cache. %" PRIu32, (ui32)GetGeneration(x));
+ Y_ABORT_UNLESS(TItemTraits::GetGeneration(x) == ECacheCacheGeneration::Warm, "malformed entry in warm cache. %" PRIu32, (ui32)TItemTraits::GetGeneration(x));
Unlink(x, WarmWeight);
AddToStaging(x, evictedList);
}
@@ -209,9 +206,9 @@ private:
while (StagingWeight > Config.StagingLimit) {
Y_DEBUG_ABORT_UNLESS(!StagingList.Empty());
TItem *evicted = StagingList.PopBack();
- Y_ABORT_UNLESS(GetGeneration(evicted) == TCacheCacheConfig::CacheGenStaging, "malformed entry in staging cache %" PRIu32, (ui32)GetGeneration(evicted));
+ Y_ABORT_UNLESS(TItemTraits::GetGeneration(evicted) == ECacheCacheGeneration::Staging, "malformed entry in staging cache %" PRIu32, (ui32)TItemTraits::GetGeneration(evicted));
Unlink(evicted, StagingWeight);
- SetGeneration(evicted, TCacheCacheConfig::CacheGenNone);
+ TItemTraits::SetGeneration(evicted, ECacheCacheGeneration::None);
evictedList.PushBack(evicted);
}
}
@@ -221,19 +218,11 @@ private:
TItem *evicted = list.PopBack();
Unlink(evicted, weight);
- SetGeneration(evicted, TCacheCacheConfig::CacheGenNone);
+ TItemTraits::SetGeneration(evicted, ECacheCacheGeneration::None);
return evicted;
}
- void SetGeneration(TItem *item, TCacheCacheConfig::ECacheGeneration gen) {
- GenerationOp.Set(item, static_cast<ui32>(gen));
- }
-
- TCacheCacheConfig::ECacheGeneration GetGeneration(TItem *item) {
- return static_cast<TCacheCacheConfig::ECacheGeneration>(GenerationOp.Get(item));
- }
-
private:
TCacheCacheConfig Config;
@@ -244,9 +233,6 @@ private:
ui64 FreshWeight;
ui64 StagingWeight;
ui64 WarmWeight;
-
- TWeight WeightOp;
- TCacheFlags GenerationOp;
};
}
diff --git a/ydb/core/util/cache_cache_iface.h b/ydb/core/util/cache_cache_iface.h
index 49a996bac45..89d5798ef6c 100644
--- a/ydb/core/util/cache_cache_iface.h
+++ b/ydb/core/util/cache_cache_iface.h
@@ -13,7 +13,7 @@ struct ICacheCache {
virtual void Erase(TItem *item) = 0;
// WARN: do not evict items
- virtual void UpdateCacheSize(ui64 cacheSize) = 0;
+ virtual void UpdateLimit(ui64 limit) = 0;
virtual ~ICacheCache() = default;
};
diff --git a/ydb/core/util/cache_cache_ut.cpp b/ydb/core/util/cache_cache_ut.cpp
index abfb5eb2517..03e83a2f10a 100644
--- a/ydb/core/util/cache_cache_ut.cpp
+++ b/ydb/core/util/cache_cache_ut.cpp
@@ -6,21 +6,22 @@ namespace NKikimr::NCache {
Y_UNIT_TEST_SUITE(TCacheCacheTest) {
struct TPage : public TIntrusiveListItem<TPage> {
- ui32 CacheGeneration : 16 = 0;
+ ECacheCacheGeneration CacheGeneration;
};
- struct TWeight {
- static ui64 Get(TPage *) {
+ struct TCacheCachePageTraits {
+ static ui64 GetWeight(const TPage*) {
return 1;
}
- };
- struct TGeneration {
- static ui32 Get(TPage *x) {
- return x->CacheGeneration;
+ static ECacheCacheGeneration GetGeneration(const TPage *page) {
+ return static_cast<ECacheCacheGeneration>(page->CacheGeneration);
}
- static void Set(TPage *x, ui32 gen) {
- x->CacheGeneration = gen;
+
+ static void SetGeneration(TPage *page, ECacheCacheGeneration generation) {
+ ui32 generation_ = static_cast<ui32>(generation);
+ Y_ABORT_UNLESS(generation_ < (1 << 4));
+ page->CacheGeneration = generation;
}
};
@@ -32,14 +33,14 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
// this should mean nothing is cacheable, but currently we will
// place 1 page on a level until it is inspected again.
TCacheCacheConfig config(1, fresh, staging, warm);
- TCacheCache<TPage, TWeight, TGeneration> cache(config);
+ TCacheCache<TPage, TCacheCachePageTraits> cache(config);
TVector<TPage> pages(3);
TIntrusiveList<TPage> evicted;
// page 0 added to fresh
evicted = cache.Touch(&pages[0]);
- UNIT_ASSERT(pages[0].CacheGeneration == TCacheCacheConfig::CacheGenFresh);
+ UNIT_ASSERT(pages[0].CacheGeneration == ECacheCacheGeneration::Fresh);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 0ULL);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 0ULL);
@@ -47,8 +48,8 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
// page 1 added to fresh first bumps page 0 to staging
evicted = cache.Touch(&pages[1]);
- UNIT_ASSERT(pages[1].CacheGeneration == TCacheCacheConfig::CacheGenFresh);
- UNIT_ASSERT(pages[0].CacheGeneration == TCacheCacheConfig::CacheGenStaging);
+ UNIT_ASSERT(pages[1].CacheGeneration == ECacheCacheGeneration::Fresh);
+ UNIT_ASSERT(pages[0].CacheGeneration == ECacheCacheGeneration::Staging);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 0ULL);
@@ -56,7 +57,7 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
// page 0 is moved to warm from staging
evicted = cache.Touch(&pages[0]);
- UNIT_ASSERT(pages[0].CacheGeneration == TCacheCacheConfig::CacheGenWarm);
+ UNIT_ASSERT(pages[0].CacheGeneration == ECacheCacheGeneration::Warm);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 0ULL);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 1ULL);
@@ -64,8 +65,8 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
// page 2 added to fresh first bumps page 1 to staging
evicted = cache.Touch(&pages[2]);
- UNIT_ASSERT(pages[2].CacheGeneration == TCacheCacheConfig::CacheGenFresh);
- UNIT_ASSERT(pages[1].CacheGeneration == TCacheCacheConfig::CacheGenStaging);
+ UNIT_ASSERT(pages[2].CacheGeneration == ECacheCacheGeneration::Fresh);
+ UNIT_ASSERT(pages[1].CacheGeneration == ECacheCacheGeneration::Staging);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 1ULL);
@@ -73,8 +74,8 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
// page 1 moves to warm, but first it bumps page 0 to staging
evicted = cache.Touch(&pages[1]);
- UNIT_ASSERT(pages[1].CacheGeneration == TCacheCacheConfig::CacheGenWarm);
- UNIT_ASSERT(pages[0].CacheGeneration == TCacheCacheConfig::CacheGenStaging);
+ UNIT_ASSERT(pages[1].CacheGeneration == ECacheCacheGeneration::Warm);
+ UNIT_ASSERT(pages[0].CacheGeneration == ECacheCacheGeneration::Staging);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 1ULL);
@@ -88,7 +89,7 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
// 2 pages per layer
TCacheCacheConfig config(3, fresh, staging, warm);
- TCacheCache<TPage, TWeight, TGeneration> cache(config);
+ TCacheCache<TPage, TCacheCachePageTraits> cache(config);
TVector<TPage> pages(6);
@@ -100,38 +101,38 @@ Y_UNIT_TEST_SUITE(TCacheCacheTest) {
cache.Touch(&pages[1]);
cache.Touch(&pages[4]);
cache.Touch(&pages[5]);
- UNIT_ASSERT(pages[0].CacheGeneration == TCacheCacheConfig::CacheGenWarm);
- UNIT_ASSERT(pages[1].CacheGeneration == TCacheCacheConfig::CacheGenWarm);
- UNIT_ASSERT(pages[2].CacheGeneration == TCacheCacheConfig::CacheGenStaging);
- UNIT_ASSERT(pages[3].CacheGeneration == TCacheCacheConfig::CacheGenStaging);
- UNIT_ASSERT(pages[4].CacheGeneration == TCacheCacheConfig::CacheGenFresh);
- UNIT_ASSERT(pages[5].CacheGeneration == TCacheCacheConfig::CacheGenFresh);
+ UNIT_ASSERT(pages[0].CacheGeneration == ECacheCacheGeneration::Warm);
+ UNIT_ASSERT(pages[1].CacheGeneration == ECacheCacheGeneration::Warm);
+ UNIT_ASSERT(pages[2].CacheGeneration == ECacheCacheGeneration::Staging);
+ UNIT_ASSERT(pages[3].CacheGeneration == ECacheCacheGeneration::Staging);
+ UNIT_ASSERT(pages[4].CacheGeneration == ECacheCacheGeneration::Fresh);
+ UNIT_ASSERT(pages[5].CacheGeneration == ECacheCacheGeneration::Fresh);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 2ULL);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 2ULL);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 2ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), &pages[2]);
- UNIT_ASSERT(pages[2].CacheGeneration == 0);
+ UNIT_ASSERT(pages[2].CacheGeneration == ECacheCacheGeneration::None);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), &pages[3]);
- UNIT_ASSERT(pages[3].CacheGeneration == 0);
+ UNIT_ASSERT(pages[3].CacheGeneration == ECacheCacheGeneration::None);
UNIT_ASSERT_VALUES_EQUAL(staging->Val(), 0ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), &pages[4]);
- UNIT_ASSERT(pages[4].CacheGeneration == 0);
+ UNIT_ASSERT(pages[4].CacheGeneration == ECacheCacheGeneration::None);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), &pages[5]);
- UNIT_ASSERT(pages[5].CacheGeneration == 0);
+ UNIT_ASSERT(pages[5].CacheGeneration == ECacheCacheGeneration::None);
UNIT_ASSERT_VALUES_EQUAL(fresh->Val(), 0ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), &pages[0]);
- UNIT_ASSERT(pages[0].CacheGeneration == 0);
+ UNIT_ASSERT(pages[0].CacheGeneration == ECacheCacheGeneration::None);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 1ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), &pages[1]);
- UNIT_ASSERT(pages[1].CacheGeneration == 0);
+ UNIT_ASSERT(pages[1].CacheGeneration == ECacheCacheGeneration::None);
UNIT_ASSERT_VALUES_EQUAL(warm->Val(), 0ULL);
UNIT_ASSERT_VALUES_EQUAL(cache.EvictNext(), nullptr);