Skip to content

Commit

Permalink
Do not block all requests on Shared Cache policy switch (#10619)
Browse files Browse the repository at this point in the history
  • Loading branch information
kunga authored Oct 23, 2024
1 parent 8a7394e commit f940b8c
Show file tree
Hide file tree
Showing 13 changed files with 856 additions and 78 deletions.
2 changes: 1 addition & 1 deletion ydb/core/protos/shared_cache.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
8 changes: 6 additions & 2 deletions ydb/core/tablet_flat/shared_cache_clock_pro.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class TClockProCache : public ICacheCache<TPage> {
{}

TIntrusiveList<TPage> EvictNext() override {
if (SizeHot + SizeCold == 0) {
if (GetSize() == 0) {
return {};
}

Expand Down Expand Up @@ -118,7 +118,11 @@ class TClockProCache : public ICacheCache<TPage> {
}
}

TString Dump() const {
ui64 GetSize() const override {
return SizeHot + SizeCold;
}

TString Dump() const override {
TStringBuilder result;

size_t count = 0;
Expand Down
11 changes: 0 additions & 11 deletions ydb/core/tablet_flat/shared_cache_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ namespace NSharedCache {
EvRequest,
EvResult,
EvUpdated,
EvReplacementPolicySwitch,

EvEnd

Expand Down Expand Up @@ -129,16 +128,6 @@ namespace NSharedCache {

THashMap<TLogoBlobID, TActions> Actions;
};

struct TEvReplacementPolicySwitch : public TEventLocal<TEvReplacementPolicySwitch, EvReplacementPolicySwitch> {
using TReplacementPolicy = NKikimrSharedCache::TReplacementPolicy;

TReplacementPolicy ReplacementPolicy;

TEvReplacementPolicySwitch(TReplacementPolicy replacementPolicy)
: ReplacementPolicy(replacementPolicy)
{}
};
}
}

Expand Down
6 changes: 5 additions & 1 deletion ydb/core/tablet_flat/shared_cache_s3fifo.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,11 @@ class TS3FIFOCache : public ICacheCache<TPage> {
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) {
Expand Down
228 changes: 228 additions & 0 deletions ydb/core/tablet_flat/shared_cache_switchable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
#pragma once
#include "defs.h"
#include <ydb/core/util/cache_cache_iface.h>
#include <library/cpp/monlib/counters/counters.h>
#include <library/cpp/monlib/dynamic_counters/counters.h>

namespace NKikimr::NCache {

template <typename TPage, typename TPageTraits>
class TSwitchableCache : public ICacheCache<TPage> {
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<ICacheCache<TPage>>&& cache, TCounterPtr& sizeCounter)
: Id(id)
, Cache(std::move(cache))
, SizeCounter(sizeCounter)
{
Y_ABORT_UNLESS(GetSize() == 0);
}

TIntrusiveList<TPage> EvictNext() {
return ProcessEvictedList(Cache->EvictNext());
}

TIntrusiveList<TPage> 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<TPage> ProcessEvictedList(TIntrusiveList<TPage>&& 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<ICacheCache<TPage>> Cache;
const TCounterPtr SizeCounter;
};

public:
TSwitchableCache(ui64 limit, THolder<ICacheCache<TPage>>&& cache, TCounterPtr sizeCounter) {
Caches.emplace_back(1, std::move(cache), sizeCounter);
UpdateLimit(limit);
}

TIntrusiveList<TPage> Switch(THolder<ICacheCache<TPage>>&& 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<TPage> evictedList;

while (Caches.size() > 1 && Caches.front().Id == cacheId) { // MaxCachesCount is exceeded
RotatePages(evictedList);
}

return evictedList;
}

TIntrusiveList<TPage> 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<TPage> 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<TPage> 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<TPage>& 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<TCacheHolder> Caches;
};

}
Loading

0 comments on commit f940b8c

Please sign in to comment.