Skip to content

Commit 64033e7

Browse files
authored
Merge c753bab into e562a67
2 parents e562a67 + c753bab commit 64033e7

13 files changed

+861
-78
lines changed

ydb/core/protos/shared_cache.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ message TSharedCacheConfig {
1414
optional uint32 ActivePagesReservationPercent = 4 [default = 50];
1515
reserved 5;
1616
optional TReplacementPolicy ReplacementPolicy = 6 [default = ThreeLeveledLRU];
17-
optional uint32 ReplacementPolicySwitchUniformDelaySeconds = 7 [default = 3600];
17+
reserved 7;
1818
}

ydb/core/tablet_flat/shared_cache_clock_pro.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class TClockProCache : public ICacheCache<TPage> {
6464
{}
6565

6666
TIntrusiveList<TPage> EvictNext() override {
67-
if (SizeHot + SizeCold == 0) {
67+
if (GetSize() == 0) {
6868
return {};
6969
}
7070

@@ -118,7 +118,11 @@ class TClockProCache : public ICacheCache<TPage> {
118118
}
119119
}
120120

121-
TString Dump() const {
121+
ui64 GetSize() const override {
122+
return SizeHot + SizeCold;
123+
}
124+
125+
TString Dump() const override {
122126
TStringBuilder result;
123127

124128
size_t count = 0;
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
#pragma once
2+
#include "defs.h"
3+
#include <ydb/core/util/cache_cache_iface.h>
4+
#include <library/cpp/monlib/counters/counters.h>
5+
#include <library/cpp/monlib/dynamic_counters/counters.h>
6+
7+
namespace NKikimr::NCache {
8+
9+
template <typename TPage, typename TPageTraits>
10+
class TCompositeCache : public ICacheCache<TPage> {
11+
using TCounterPtr = ::NMonitoring::TDynamicCounters::TCounterPtr;
12+
13+
static const ui32 MaxCachesCount = 3;
14+
static const ui32 RotatePagesPerCallCount = 10;
15+
static_assert(MaxCachesCount < (1 << 4));
16+
17+
class TCacheHolder {
18+
public:
19+
TCacheHolder(ui32 id, THolder<ICacheCache<TPage>>&& cache, TCounterPtr& sizeCounter)
20+
: Id(id)
21+
, Cache(std::move(cache))
22+
, SizeCounter(sizeCounter)
23+
{
24+
Y_ABORT_UNLESS(GetSize() == 0);
25+
}
26+
27+
TIntrusiveList<TPage> EvictNext() {
28+
return ProcessEvictedList(Cache->EvictNext());
29+
}
30+
31+
TIntrusiveList<TPage> Touch(TPage* page) {
32+
ui32 cacheId = TPageTraits::GetCacheId(page);
33+
if (cacheId == 0) {
34+
TPageTraits::SetCacheId(page, Id);
35+
SizeCounter->Add(TPageTraits::GetSize(page));
36+
} else {
37+
Y_ABORT_UNLESS(cacheId == Id);
38+
}
39+
40+
return ProcessEvictedList(Cache->Touch(page));
41+
}
42+
43+
void Erase(TPage* page) {
44+
ui32 cacheId = TPageTraits::GetCacheId(page);
45+
if (cacheId != 0) {
46+
Y_ABORT_UNLESS(cacheId == Id);
47+
SizeCounter->Sub(TPageTraits::GetSize(page));
48+
TPageTraits::SetCacheId(page, 0);
49+
}
50+
51+
Cache->Erase(page);
52+
}
53+
54+
void UpdateLimit(ui64 limit) {
55+
Cache->UpdateLimit(limit);
56+
}
57+
58+
ui64 GetSize() const {
59+
return Cache->GetSize();
60+
}
61+
62+
TString Dump() const {
63+
return Cache->Dump();
64+
}
65+
66+
private:
67+
TIntrusiveList<TPage> ProcessEvictedList(TIntrusiveList<TPage>&& evictedList) {
68+
ui64 evictedSize = 0;
69+
70+
for (auto& page_ : evictedList) {
71+
TPage* page = &page_;
72+
Y_ABORT_UNLESS(TPageTraits::GetCacheId(page) == Id);
73+
TPageTraits::SetCacheId(page, 0);
74+
evictedSize += TPageTraits::GetSize(page);
75+
}
76+
77+
SizeCounter->Sub(evictedSize);
78+
79+
return evictedList;
80+
}
81+
82+
public:
83+
const ui32 Id; // in [1 .. MaxCachesCount] range
84+
private:
85+
const THolder<ICacheCache<TPage>> Cache;
86+
const TCounterPtr SizeCounter;
87+
};
88+
89+
public:
90+
TCompositeCache(ui64 limit, THolder<ICacheCache<TPage>>&& cache, TCounterPtr sizeCounter) {
91+
Caches.emplace_back(1, std::move(cache), sizeCounter);
92+
UpdateLimit(limit);
93+
}
94+
95+
TIntrusiveList<TPage> Switch(THolder<ICacheCache<TPage>>&& cache, TCounterPtr sizeCounter) Y_WARN_UNUSED_RESULT {
96+
ui32 cacheId = Caches.back().Id + 1;
97+
if (cacheId > MaxCachesCount) {
98+
cacheId -= MaxCachesCount;
99+
}
100+
101+
Caches.emplace_back(cacheId, std::move(cache), sizeCounter)
102+
.UpdateLimit(Limit);
103+
104+
TIntrusiveList<TPage> evictedList;
105+
106+
while (Caches.size() > 1 && Caches.front().Id == cacheId) { // MaxCachesCount is exceeded
107+
RotatePages(evictedList);
108+
}
109+
110+
return evictedList;
111+
}
112+
113+
TIntrusiveList<TPage> EvictNext() override {
114+
while (Y_UNLIKELY(Caches.size() > 1)) {
115+
auto result = Caches.front().EvictNext();
116+
if (!result) {
117+
Y_ABORT_UNLESS(Caches.front().GetSize() == 0);
118+
Caches.pop_front();
119+
} else {
120+
return result;
121+
}
122+
}
123+
124+
return Caches.back().EvictNext();
125+
}
126+
127+
TIntrusiveList<TPage> Touch(TPage* page) override {
128+
if (Y_LIKELY(Caches.size() == 1)) {
129+
return Caches.back().Touch(page);
130+
}
131+
132+
TIntrusiveList<TPage> evictedList = GetCache(TPageTraits::GetCacheId(page)).Touch(page);
133+
134+
RotatePages(evictedList);
135+
136+
while (GetSize() > Limit && Caches.size() > 1) {
137+
Append(evictedList, EvictNext());
138+
}
139+
140+
return evictedList;
141+
}
142+
143+
void Erase(TPage* page) override {
144+
if (Y_LIKELY(Caches.size() == 1)) {
145+
Caches.back().Erase(page);
146+
return;
147+
}
148+
149+
GetCache(TPageTraits::GetCacheId(page))
150+
.Erase(page);
151+
}
152+
153+
void UpdateLimit(ui64 limit) override {
154+
Limit = limit;
155+
for (auto& cache : Caches) {
156+
cache.UpdateLimit(limit);
157+
}
158+
}
159+
160+
ui64 GetSize() const override {
161+
ui64 result = 0;
162+
for (const auto& cache : Caches) {
163+
result += cache.GetSize();
164+
}
165+
return result;
166+
}
167+
168+
TString Dump() const override {
169+
TStringBuilder result;
170+
size_t count = 0;
171+
172+
for (const auto& cache : Caches) {
173+
if (count) result << "; ";
174+
result << cache.Dump();
175+
count++;
176+
}
177+
178+
return result;
179+
}
180+
181+
private:
182+
TCacheHolder& GetCache(ui32 cacheId) {
183+
if (cacheId == 0) {
184+
// use the most-recent cache by default
185+
return Caches.back();
186+
} else {
187+
// Note: this loop might be replaced with formula
188+
// but it seems useless and error-prone
189+
for (auto& cache : Caches) {
190+
if (cache.Id == cacheId) {
191+
return cache;
192+
}
193+
}
194+
Y_ABORT("Failed to locate page cache");
195+
}
196+
}
197+
198+
void RotatePages(TIntrusiveList<TPage>& evictedList) {
199+
ui32 rotatedPagesCount = 0;
200+
while (Caches.size() > 1 && rotatedPagesCount < RotatePagesPerCallCount) {
201+
auto rotatedList = Caches.front().EvictNext();
202+
if (!rotatedList) {
203+
Y_ABORT_UNLESS(Caches.front().GetSize() == 0);
204+
Caches.pop_front();
205+
continue;
206+
}
207+
208+
while (!rotatedList.Empty()) {
209+
TPage* page = rotatedList.PopFront();
210+
211+
// touch each page multiple times to make it warm
212+
for (ui32 touchTimes = 0; touchTimes < 3; touchTimes++) {
213+
Append(evictedList, Caches.back().Touch(page));
214+
}
215+
216+
rotatedPagesCount++;
217+
}
218+
}
219+
}
220+
221+
void Append(TIntrusiveList<TPage>& left, TIntrusiveList<TPage>&& right) {
222+
while (!right.Empty()) {
223+
TPage* page = right.PopFront();
224+
left.PushBack(page);
225+
}
226+
}
227+
228+
private:
229+
ui64 Limit;
230+
TDeque<TCacheHolder> Caches;
231+
};
232+
233+
}

0 commit comments

Comments
 (0)