Skip to content

Commit ba60050

Browse files
committed
Do not block all requests on Shared Cache policy switch (#10619)
1 parent face7da commit ba60050

12 files changed

+854
-64
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;

ydb/core/tablet_flat/shared_cache_s3fifo.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,11 @@ class TS3FIFOCache : public ICacheCache<TPage> {
225225
GhostQueue.UpdateLimit(limit);
226226
}
227227

228-
TString Dump() const {
228+
ui64 GetSize() const override {
229+
return SmallQueue.Size + MainQueue.Size;
230+
}
231+
232+
TString Dump() const override {
229233
TStringBuilder result;
230234

231235
auto dump = [&](const TQueue& queue) {
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
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 TSwitchableCache : 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+
85+
private:
86+
const THolder<ICacheCache<TPage>> Cache;
87+
const TCounterPtr SizeCounter;
88+
};
89+
90+
public:
91+
TSwitchableCache(ui64 limit, THolder<ICacheCache<TPage>>&& cache, TCounterPtr sizeCounter) {
92+
Caches.emplace_back(1, std::move(cache), sizeCounter);
93+
UpdateLimit(limit);
94+
}
95+
96+
TIntrusiveList<TPage> Switch(THolder<ICacheCache<TPage>>&& cache, TCounterPtr sizeCounter) Y_WARN_UNUSED_RESULT {
97+
ui32 cacheId = Caches.back().Id + 1;
98+
if (cacheId > MaxCachesCount) {
99+
cacheId -= MaxCachesCount;
100+
}
101+
102+
Caches.emplace_back(cacheId, std::move(cache), sizeCounter)
103+
.UpdateLimit(Limit);
104+
105+
TIntrusiveList<TPage> evictedList;
106+
107+
while (Caches.size() > 1 && Caches.front().Id == cacheId) { // MaxCachesCount is exceeded
108+
RotatePages(evictedList);
109+
}
110+
111+
return evictedList;
112+
}
113+
114+
TIntrusiveList<TPage> EvictNext() override {
115+
while (Y_UNLIKELY(Caches.size() > 1)) {
116+
auto result = Caches.front().EvictNext();
117+
if (!result) {
118+
Y_ABORT_UNLESS(Caches.front().GetSize() == 0);
119+
Caches.pop_front();
120+
} else {
121+
return result;
122+
}
123+
}
124+
125+
return Caches.back().EvictNext();
126+
}
127+
128+
TIntrusiveList<TPage> Touch(TPage* page) override {
129+
if (Y_LIKELY(Caches.size() == 1)) {
130+
return Caches.back().Touch(page);
131+
}
132+
133+
ui32 cacheId = TPageTraits::GetCacheId(page);
134+
if (cacheId > 0 && cacheId != Caches.back().Id) {
135+
// rotate the current page first:
136+
GetCache(cacheId).Erase(page);
137+
}
138+
139+
TIntrusiveList<TPage> evictedList = Caches.back().Touch(page);
140+
141+
RotatePages(evictedList);
142+
143+
while (GetSize() > Limit && Caches.size() > 1) {
144+
evictedList.Append(EvictNext());
145+
}
146+
147+
return evictedList;
148+
}
149+
150+
void Erase(TPage* page) override {
151+
if (Y_LIKELY(Caches.size() == 1)) {
152+
Caches.back().Erase(page);
153+
return;
154+
}
155+
156+
GetCache(TPageTraits::GetCacheId(page))
157+
.Erase(page);
158+
}
159+
160+
void UpdateLimit(ui64 limit) override {
161+
Limit = limit;
162+
for (auto& cache : Caches) {
163+
cache.UpdateLimit(limit);
164+
}
165+
}
166+
167+
ui64 GetSize() const override {
168+
ui64 result = 0;
169+
for (const auto& cache : Caches) {
170+
result += cache.GetSize();
171+
}
172+
return result;
173+
}
174+
175+
TString Dump() const override {
176+
TStringBuilder result;
177+
size_t count = 0;
178+
179+
for (const auto& cache : Caches) {
180+
if (count) result << "; ";
181+
result << cache.Dump();
182+
count++;
183+
}
184+
185+
return result;
186+
}
187+
188+
private:
189+
TCacheHolder& GetCache(ui32 cacheId) {
190+
if (cacheId == 0) {
191+
// use the most-recent cache by default
192+
return Caches.back();
193+
} else {
194+
// Note: this loop might be replaced with formula
195+
// but it seems useless and error-prone
196+
for (auto& cache : Caches) {
197+
if (cache.Id == cacheId) {
198+
return cache;
199+
}
200+
}
201+
Y_ABORT("Failed to locate page cache");
202+
}
203+
}
204+
205+
void RotatePages(TIntrusiveList<TPage>& evictedList) {
206+
ui32 rotatedPagesCount = 0;
207+
while (Caches.size() > 1 && rotatedPagesCount < RotatePagesPerCallCount) {
208+
auto rotatedList = Caches.front().EvictNext();
209+
if (!rotatedList) {
210+
Y_ABORT_UNLESS(Caches.front().GetSize() == 0);
211+
Caches.pop_front();
212+
continue;
213+
}
214+
215+
while (!rotatedList.Empty()) {
216+
TPage* page = rotatedList.PopFront();
217+
evictedList.Append(Caches.back().Touch(page));
218+
rotatedPagesCount++;
219+
}
220+
}
221+
}
222+
223+
private:
224+
ui64 Limit;
225+
TDeque<TCacheHolder> Caches;
226+
};
227+
228+
}

0 commit comments

Comments
 (0)