Skip to content

Commit 0100830

Browse files
archang19meta-codesync[bot]
authored andcommitted
Add runtime ObjectCache size limit resizing
Summary: Add an ObjectCache API for updating the total-object-size target used by the periodic size controller at runtime. The setter updates the controller target atomically, rejects zero as an invalid runtime limit, and leaves placeholder/entry-limit mutation on the existing periodic controller path. Serialization and counters now report the current runtime total-object-size target rather than only the startup config value. Reviewed By: pbhandar2 Differential Revision: D103556127 fbshipit-source-id: 50edc0bba38d5aaa88719acf50d41fd7e3d23840
1 parent d18631d commit 0100830

3 files changed

Lines changed: 152 additions & 12 deletions

File tree

cachelib/object_cache/ObjectCache.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,27 @@ class ObjectCache : public ObjectCacheBase<AllocatorT> {
426426
: sizeController_->getCurrentEntriesLimit();
427427
}
428428

429+
// Get the current total object size limit used by the size controller.
430+
size_t getTotalObjectSizeLimit() const {
431+
return sizeController_ == nullptr
432+
? config_.totalObjectSizeLimit
433+
: sizeController_->getTotalObjectSizeLimit();
434+
}
435+
436+
// Update the total object size limit used by the size controller. The limit
437+
// must be positive and takes effect when the size controller next runs.
438+
//
439+
// Returns false if no size controller is active for this cache (i.e.,
440+
// objectSizeTrackingEnabled is false or sizeControllerIntervalMs == 0),
441+
// or if totalObjectSizeLimit is zero.
442+
bool setTotalObjectSizeLimit(size_t totalObjectSizeLimit) {
443+
if (totalObjectSizeLimit == 0 || sizeController_ == nullptr) {
444+
return false;
445+
}
446+
sizeController_->setTotalObjectSizeLimit(totalObjectSizeLimit);
447+
return true;
448+
}
449+
429450
// Get the expiry timestamp of the object
430451
// @param object object shared pointer returned from ObjectCache APIs
431452
//
@@ -1156,8 +1177,7 @@ ObjectCache<AllocatorT>::serializeConfigParams() const {
11561177
config["aggregatePoolStats"] = config_.aggregatePoolStats ? "true" : "false";
11571178
if (config_.objectSizeTrackingEnabled &&
11581179
config_.sizeControllerIntervalMs > 0) {
1159-
config["totalObjectSizeLimit"] =
1160-
std::to_string(config_.totalObjectSizeLimit);
1180+
config["totalObjectSizeLimit"] = std::to_string(getTotalObjectSizeLimit());
11611181
config["sizeControllerIntervalMs"] =
11621182
std::to_string(config_.sizeControllerIntervalMs);
11631183
}

cachelib/object_cache/ObjectCacheSizeController.h

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ class ObjectCacheSizeController : public PeriodicWorker {
4747
size_t getCurrentEntriesLimit() const {
4848
return currentEntriesLimit_.load(std::memory_order_relaxed);
4949
}
50+
size_t getTotalObjectSizeLimit() const {
51+
return totalObjectSizeLimit_.load(std::memory_order_relaxed);
52+
}
53+
54+
void setTotalObjectSizeLimit(size_t totalObjectSizeLimit);
5055

5156
void getCounters(const util::CounterVisitor& visitor) const;
5257
size_t getTotalObjSizeBytes() const;
@@ -85,6 +90,7 @@ class ObjectCacheSizeController : public PeriodicWorker {
8590

8691
// will be adjusted to control the cache size limit
8792
std::atomic<size_t> currentEntriesLimit_;
93+
std::atomic<size_t> totalObjectSizeLimit_;
8894

8995
// callback to get free memory in bytes
9096
GetFreeMemCb getFreeMem_;
@@ -141,9 +147,9 @@ void ObjectCacheSizeController<AllocatorT>::work() {
141147
}
142148
auto minEntriesWarmCacheThreshold =
143149
kSizeControllerThresholdPct * objCache_.config_.l1EntriesLimit / 100;
150+
auto totalObjectSizeLimit = getTotalObjectSizeLimit();
144151
auto totalObjectSizeWarmCacheThreshold =
145-
kSizeControllerThresholdPct * objCache_.config_.totalObjectSizeLimit /
146-
100;
152+
kSizeControllerThresholdPct * totalObjectSizeLimit / 100;
147153

148154
// Check if the cache is warming up.
149155
// A cache is considered to be warming up if:
@@ -173,8 +179,7 @@ void ObjectCacheSizeController<AllocatorT>::work() {
173179
if (averageObjSize == 0) {
174180
return;
175181
}
176-
auto newEntriesLimit =
177-
objCache_.config_.totalObjectSizeLimit / averageObjSize;
182+
auto newEntriesLimit = totalObjectSizeLimit / averageObjSize;
178183

179184
if (newEntriesLimit > objCache_.config_.l1EntriesLimit) {
180185
XLOGF_EVERY_MS(INFO, 60'000,
@@ -184,11 +189,12 @@ void ObjectCacheSizeController<AllocatorT>::work() {
184189
}
185190

186191
XLOGF_EVERY_MS(INFO, 60'000,
187-
"CacheLib size-controller: total object size = {}, current "
188-
"entries = {}, average object size = "
189-
"{}, new entries limit = {}, current entries limit = {}",
190-
totalObjSize, currentNumEntries, averageObjSize,
191-
newEntriesLimit, currentEntriesLimit_);
192+
"CacheLib size-controller: total object size = {}, total "
193+
"object size limit = {}, current entries = {}, average "
194+
"object size = {}, new entries limit = {}, current entries "
195+
"limit = {}",
196+
totalObjSize, totalObjectSizeLimit, currentNumEntries,
197+
averageObjSize, newEntriesLimit, currentEntriesLimit_);
192198

193199
// We have computed the newEntriesLimit to satisfy the numEntries and
194200
// totalObjSize limits. If user has specified additional control
@@ -256,6 +262,12 @@ void ObjectCacheSizeController<AllocatorT>::work() {
256262
}
257263
}
258264

265+
template <typename AllocatorT>
266+
void ObjectCacheSizeController<AllocatorT>::setTotalObjectSizeLimit(
267+
size_t totalObjectSizeLimit) {
268+
totalObjectSizeLimit_.store(totalObjectSizeLimit, std::memory_order_relaxed);
269+
}
270+
259271
template <typename AllocatorT>
260272
void ObjectCacheSizeController<AllocatorT>::shrinkCacheByEntriesNum(
261273
size_t entries) {
@@ -332,6 +344,8 @@ void ObjectCacheSizeController<AllocatorT>::getCounters(
332344
visitor("objcache.rss_mem_bytes", objCache_.config_.getRSSMemBytes());
333345
visitor("objcache.current_entries_limit",
334346
currentEntriesLimit_.load(std::memory_order_relaxed));
347+
visitor("objcache.total_object_size_limit",
348+
totalObjectSizeLimit_.load(std::memory_order_relaxed));
335349
visitor("objcache.size_controller.shrink_cache_calls",
336350
shrinkCacheCalls_.get(), util::CounterVisitor::CounterType::RATE);
337351
visitor("objcache.size_controller.expand_cache_calls",
@@ -362,5 +376,6 @@ ObjectCacheSizeController<AllocatorT>::ObjectCacheSizeController(
362376
: throttlerConfig_(throttlerConfig),
363377
objCache_(objCache),
364378
mode_(objCache_.config_.memoryMode),
365-
currentEntriesLimit_(objCache_.config_.l1EntriesLimit) {}
379+
currentEntriesLimit_(objCache_.config_.l1EntriesLimit),
380+
totalObjectSizeLimit_(objCache_.config_.totalObjectSizeLimit) {}
366381
} // namespace facebook::cachelib::objcache2

cachelib/object_cache/tests/ObjectCacheTest.cpp

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,26 @@ class ObjectCacheTest : public ::testing::Test {
855855
EXPECT_GT(stats.getCounts().at("objcache.key_padding_bytes"), 0);
856856
}
857857

858+
void testRuntimeTotalObjectSizeLimitVisibility() {
859+
ObjectCacheConfig config;
860+
config.setCacheName("runtime_resize_visibility_test");
861+
config.setItemDestructor(
862+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
863+
config.setCacheCapacity(10 /* l1EntriesLimit */,
864+
200 /* totalObjectSizeLimit */,
865+
60'000 /* sizeControllerIntervalMs */);
866+
867+
auto objcache = ObjectCache::create(config);
868+
ASSERT_TRUE(objcache->setTotalObjectSizeLimit(100));
869+
870+
EXPECT_EQ(objcache->serializeConfigParams().at("totalObjectSizeLimit"),
871+
"100");
872+
873+
util::StatsMap stats;
874+
objcache->getObjectCacheCounters(stats.createCountVisitor());
875+
EXPECT_EQ(stats.getCounts().at("objcache.total_object_size_limit"), 100);
876+
}
877+
858878
void testMultithreadObjectSizeTrackingWithMutation() {
859879
if (!folly::usingJEMalloc()) {
860880
return;
@@ -2008,6 +2028,9 @@ TYPED_TEST(ObjectCacheTest, ObjectSizeTrackingWithMutation) {
20082028
TYPED_TEST(ObjectCacheTest, ObjectSizeTrackingWithSizeUpdate) {
20092029
this->testObjectSizeTrackingWithSizeUpdate();
20102030
}
2031+
TYPED_TEST(ObjectCacheTest, RuntimeTotalObjectSizeLimitVisibility) {
2032+
this->testRuntimeTotalObjectSizeLimitVisibility();
2033+
}
20112034
TYPED_TEST(ObjectCacheTest, MultithreadObjectSizeTrackingWithMutation) {
20122035
this->testMultithreadObjectSizeTrackingWithMutation();
20132036
}
@@ -2176,6 +2199,88 @@ TEST(ObjectCacheTest, LruEvictionWithSizeControl) {
21762199
}
21772200
}
21782201

2202+
TEST(ObjectCacheTest, RuntimeTotalObjectSizeLimitResize) {
2203+
constexpr size_t kEntriesLimit = 20;
2204+
constexpr int kSizeControllerIntervalMs = 10;
2205+
2206+
ObjectCache::Config config;
2207+
config.setCacheName("runtime_resize_test");
2208+
config.setItemDestructor(
2209+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
2210+
config.setCacheCapacity(kEntriesLimit, 200 /* totalObjectSizeLimit */,
2211+
kSizeControllerIntervalMs);
2212+
2213+
auto objcache = ObjectCache::create(config);
2214+
for (size_t i = 0; i < 8; i++) {
2215+
objcache->insertOrReplace(folly::sformat("key_{}", i),
2216+
std::make_unique<Foo>(), 25);
2217+
}
2218+
2219+
ASSERT_EQ(objcache->getTotalObjectSizeLimit(), 200);
2220+
ASSERT_EQ(objcache->getTotalObjectSize(), 200);
2221+
2222+
ASSERT_TRUE(objcache->setTotalObjectSizeLimit(100));
2223+
EXPECT_EQ(objcache->getTotalObjectSizeLimit(), 100);
2224+
ASSERT_TRUE(test_util::eventuallyTrue(
2225+
[&]() {
2226+
return objcache->getTotalObjectSize() <= 100 &&
2227+
objcache->getCurrentEntriesLimit() == 4;
2228+
},
2229+
3 /* timeoutSecs */));
2230+
EXPECT_LE(objcache->getTotalObjectSize(), 100);
2231+
EXPECT_EQ(objcache->getCurrentEntriesLimit(), 4);
2232+
2233+
ASSERT_TRUE(objcache->setTotalObjectSizeLimit(200));
2234+
EXPECT_EQ(objcache->getTotalObjectSizeLimit(), 200);
2235+
ASSERT_TRUE(test_util::eventuallyTrue(
2236+
[&]() { return objcache->getCurrentEntriesLimit() > 4; },
2237+
3 /* timeoutSecs */));
2238+
EXPECT_GT(objcache->getCurrentEntriesLimit(), 4);
2239+
EXPECT_LE(objcache->getCurrentEntriesLimit(), kEntriesLimit);
2240+
2241+
for (size_t i = 8; i < 12; i++) {
2242+
objcache->insertOrReplace(folly::sformat("key_{}", i),
2243+
std::make_unique<Foo>(), 25);
2244+
}
2245+
EXPECT_LE(objcache->getTotalObjectSize(), 200);
2246+
}
2247+
2248+
TEST(ObjectCacheTest, RuntimeTotalObjectSizeLimitRejectsZero) {
2249+
ObjectCache::Config config;
2250+
config.setCacheName("runtime_resize_zero_test");
2251+
config.setItemDestructor(
2252+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
2253+
config.setCacheCapacity(10 /* l1EntriesLimit */,
2254+
100 /* totalObjectSizeLimit */,
2255+
60'000 /* sizeControllerIntervalMs */);
2256+
2257+
auto objcache = ObjectCache::create(config);
2258+
for (size_t i = 0; i < 4; i++) {
2259+
objcache->insertOrReplace(folly::sformat("key_{}", i),
2260+
std::make_unique<Foo>(), 25);
2261+
}
2262+
2263+
ASSERT_EQ(objcache->getTotalObjectSize(), 100);
2264+
EXPECT_FALSE(objcache->setTotalObjectSizeLimit(0));
2265+
EXPECT_EQ(objcache->getTotalObjectSizeLimit(), 100);
2266+
EXPECT_EQ(objcache->getTotalObjectSize(), 100);
2267+
EXPECT_EQ(objcache->getNumEntries(), 4);
2268+
}
2269+
2270+
TEST(ObjectCacheTest, RuntimeTotalObjectSizeLimitUnsupported) {
2271+
ObjectCache::Config config;
2272+
config.setCacheName("runtime_resize_unsupported_test");
2273+
config.setItemDestructor(
2274+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
2275+
config.setCacheCapacity(10);
2276+
2277+
auto objcache = ObjectCache::create(config);
2278+
// Entry-count-only ObjectCache instances do not have a size controller, so
2279+
// runtime object-size-limit updates are unsupported and must fail cleanly.
2280+
EXPECT_EQ(objcache->getTotalObjectSizeLimit(), 0);
2281+
EXPECT_FALSE(objcache->setTotalObjectSizeLimit(100));
2282+
}
2283+
21792284
TEST(ObjectCacheTest, ExportStats) {
21802285
std::string cacheName = "service_data_exporter_test";
21812286
ObjectCache::Config config;

0 commit comments

Comments
 (0)