Skip to content

Commit f853a42

Browse files
Jiayue Baofacebook-github-bot
authored andcommitted
Get or update object TTL via object shared_ptr
Summary: Add the following TTL-related APIs: - getConfiguredTtlSec - getExpiryTimeSec - extendTtlSec - updateExpiryTimeSec Usage: ``` auto ptr = objcache->find<T>("key"); auto configuredTtl = objcache->getConfiguredTtl(ptr); auto expiryTime = objcache->getExpiryTimeSec(ptr); objcache->extendTtl(ptr, std::chrono::seconds(3)); objcache->updateExpiryTimeSec(ptr, newExpiryTimeSecs); ``` Reviewed By: therealgymmy, jaesoo-fb Differential Revision: D43167879 fbshipit-source-id: 3b11fb0a2b9a3b5c38fcfd856ade100e6ae27470
1 parent 293118b commit f853a42

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

cachelib/experimental/objcache2/ObjectCache.h

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,63 @@ class ObjectCache : public ObjectCacheBase<AllocatorT> {
267267
: sizeController_->getCurrentEntriesLimit();
268268
}
269269

270+
// Get the expiry timestamp of the object
271+
// @param object object shared pointer returned from ObjectCache APIs
272+
//
273+
// @return the expiry timestamp in seconds of the object
274+
// 0 if object is nullptr
275+
template <typename T>
276+
uint32_t getExpiryTimeSec(const std::shared_ptr<T>& object) const {
277+
if (object == nullptr) {
278+
return 0;
279+
}
280+
return getReadHandleRefInternal<T>(object)->getExpiryTime();
281+
}
282+
283+
// Get the configured TTL of the object
284+
// @param object object shared pointer returned from ObjectCache APIs
285+
//
286+
// @return the configured TTL in seconds of the object
287+
// 0 if object is nullptr
288+
template <typename T>
289+
std::chrono::seconds getConfiguredTtl(
290+
const std::shared_ptr<T>& object) const {
291+
if (object == nullptr) {
292+
return std::chrono::seconds{0};
293+
}
294+
return getReadHandleRefInternal<T>(object)->getConfiguredTTL();
295+
}
296+
297+
// Update the expiry timestamp of an object
298+
//
299+
// @param object object shared pointer returned from ObjectCache APIs
300+
// @param expiryTimeSecs the expiryTime in seconds to update
301+
//
302+
// @return boolean indicating whether expiry time was successfully updated
303+
template <typename T>
304+
bool updateExpiryTimeSec(std::shared_ptr<T>& object,
305+
uint32_t expiryTimeSecs) {
306+
if (object == nullptr) {
307+
return false;
308+
}
309+
return getWriteHandleRefInternal<T>(object)->updateExpiryTime(
310+
expiryTimeSecs);
311+
}
312+
313+
// Update expiry time to @ttl seconds from now.
314+
//
315+
// @param object object shared pointer returned from ObjectCache APIs
316+
// @param ttl TTL in seconds (from now)
317+
//
318+
// @return boolean indicating whether TTL was successfully extended
319+
template <typename T>
320+
bool extendTtl(std::shared_ptr<T>& object, std::chrono::seconds ttl) {
321+
if (object == nullptr) {
322+
return false;
323+
}
324+
return getWriteHandleRefInternal<T>(object)->extendTTL(ttl);
325+
}
326+
270327
protected:
271328
// Serialize cache allocator config for exporting to Scuba
272329
std::map<std::string, std::string> serializeConfigParams() const override;
@@ -307,6 +364,28 @@ class ObjectCache : public ObjectCacheBase<AllocatorT> {
307364
bool stopSizeController(std::chrono::seconds timeout = std::chrono::seconds{
308365
0});
309366

367+
// Get a ReadHandle reference from the object shared_ptr
368+
template <typename T>
369+
typename AllocatorT::ReadHandle& getReadHandleRefInternal(
370+
const std::shared_ptr<T>& object) const {
371+
auto* deleter = std::get_deleter<Deleter<T>>(object);
372+
XDCHECK(deleter != nullptr);
373+
auto& hdl = deleter->getReadHandleRef();
374+
XDCHECK(hdl != nullptr);
375+
return hdl;
376+
}
377+
378+
// Get a WriteHandle reference from the object shared_ptr
379+
template <typename T>
380+
typename AllocatorT::WriteHandle& getWriteHandleRefInternal(
381+
std::shared_ptr<T>& object) {
382+
auto* deleter = std::get_deleter<Deleter<T>>(object);
383+
XDCHECK(deleter != nullptr);
384+
auto& hdl = deleter->getWriteHandleRef();
385+
XDCHECK(hdl != nullptr);
386+
return hdl;
387+
}
388+
310389
// Config passed to the cache.
311390
Config config_{};
312391

cachelib/experimental/objcache2/tests/ObjectCacheTest.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,69 @@ class ObjectCacheTest : public ::testing::Test {
886886
}
887887
}
888888

889+
void testGetTtl() {
890+
const uint32_t ttlSecs = 600;
891+
892+
ObjectCacheConfig config;
893+
config.setCacheName("test").setCacheCapacity(10'000).setItemDestructor(
894+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
895+
auto objcache = ObjectCache::create(config);
896+
897+
auto before = util::getCurrentTimeSec();
898+
std::this_thread::sleep_for(std::chrono::seconds{3});
899+
objcache->insertOrReplace("Foo", std::make_unique<Foo>(), 0 /*object size*/,
900+
ttlSecs);
901+
902+
// lookup via find API
903+
auto found1 = objcache->template find<Foo>("Foo");
904+
ASSERT_NE(nullptr, found1);
905+
906+
// get TTL info
907+
EXPECT_EQ(ttlSecs, objcache->getConfiguredTtl(found1).count());
908+
EXPECT_LE(before + ttlSecs, objcache->getExpiryTimeSec(found1));
909+
910+
// lookup via findToWrite API
911+
auto found2 = objcache->template findToWrite<Foo>("Foo");
912+
ASSERT_NE(nullptr, found2);
913+
914+
// get TTL info
915+
EXPECT_EQ(ttlSecs, objcache->getConfiguredTtl(found2).count());
916+
EXPECT_LE(before + ttlSecs, objcache->getExpiryTimeSec(found2));
917+
}
918+
919+
void testUpdateTtl() {
920+
const uint32_t ttlSecs = 600;
921+
922+
ObjectCacheConfig config;
923+
config.setCacheName("test").setCacheCapacity(10'000).setItemDestructor(
924+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
925+
auto objcache = ObjectCache::create(config);
926+
927+
auto insertionTime = util::getCurrentTimeSec();
928+
objcache->insertOrReplace("Foo", std::make_unique<Foo>(), 0 /*object size*/,
929+
ttlSecs);
930+
931+
auto found = objcache->template find<Foo>("Foo");
932+
ASSERT_NE(nullptr, found);
933+
934+
// get TTL info
935+
EXPECT_EQ(ttlSecs, objcache->getConfiguredTtl(found).count());
936+
EXPECT_LE(insertionTime + ttlSecs, objcache->getExpiryTimeSec(found));
937+
938+
// update expiry time
939+
auto currExpTime = objcache->getExpiryTimeSec(found);
940+
EXPECT_TRUE(objcache->updateExpiryTimeSec(found, currExpTime + ttlSecs));
941+
EXPECT_EQ(2 * ttlSecs, objcache->getConfiguredTtl(found).count());
942+
EXPECT_EQ(currExpTime + ttlSecs, objcache->getExpiryTimeSec(found));
943+
944+
// extend TTL
945+
auto now = util::getCurrentTimeSec();
946+
std::this_thread::sleep_for(std::chrono::seconds{3});
947+
EXPECT_TRUE(objcache->extendTtl(found, std::chrono::seconds(3 * ttlSecs)));
948+
EXPECT_LE(now + ttlSecs, objcache->getExpiryTimeSec(found));
949+
EXPECT_LE(3 * ttlSecs, objcache->getConfiguredTtl(found).count());
950+
}
951+
889952
void testMultithreadReplace() {
890953
// Sanity test to see if insertOrReplace across multiple
891954
// threads are safe.
@@ -1079,6 +1142,32 @@ class ObjectCacheTest : public ::testing::Test {
10791142
fs[i].join();
10801143
}
10811144
}
1145+
1146+
void testMultithreadUpdateTtl() {
1147+
// Sanity test to see if update TTL across multiple
1148+
// threads is safe.
1149+
ObjectCacheConfig config;
1150+
config.setCacheName("test").setCacheCapacity(10'000).setItemDestructor(
1151+
[&](ObjectCacheDestructorData data) { data.deleteObject<Foo>(); });
1152+
auto objcache = ObjectCache::create(config);
1153+
objcache->insertOrReplace("key", std::make_unique<Foo>(), 0, 60);
1154+
1155+
auto runUpdateTtlOps = [&] {
1156+
for (int i = 0; i < 2000; i++) {
1157+
auto found = objcache->template find<Foo>("key");
1158+
auto configuredTtlSecs = objcache->getConfiguredTtl(found).count();
1159+
objcache->extendTtl(found, std::chrono::seconds{configuredTtlSecs});
1160+
}
1161+
};
1162+
1163+
std::vector<std::thread> ts;
1164+
for (int i = 0; i < 10; i++) {
1165+
ts.push_back(std::thread{runUpdateTtlOps});
1166+
}
1167+
for (int i = 0; i < 10; i++) {
1168+
ts[i].join();
1169+
}
1170+
}
10821171
};
10831172

10841173
using AllocatorTypes = ::testing::Types<LruAllocator,
@@ -1117,6 +1206,9 @@ TYPED_TEST(ObjectCacheTest, PersistenceHighLoad) {
11171206
this->testPersistenceHighLoad();
11181207
}
11191208

1209+
TYPED_TEST(ObjectCacheTest, GetTtl) { this->testGetTtl(); }
1210+
TYPED_TEST(ObjectCacheTest, UpdateTtl) { this->testUpdateTtl(); }
1211+
11201212
TYPED_TEST(ObjectCacheTest, MultithreadReplace) {
11211213
this->testMultithreadReplace();
11221214
}
@@ -1135,6 +1227,9 @@ TYPED_TEST(ObjectCacheTest, MultithreadFindAndEviction) {
11351227
TYPED_TEST(ObjectCacheTest, MultithreadFindAndReplaceWith10Shards) {
11361228
this->testMultithreadFindAndReplaceWith10Shards();
11371229
}
1230+
TYPED_TEST(ObjectCacheTest, MultithreadUpdateTtl) {
1231+
this->testMultithreadUpdateTtl();
1232+
}
11381233

11391234
using ObjectCache = ObjectCache<LruAllocator>;
11401235
TEST(ObjectCacheTest, LruEviction) {

0 commit comments

Comments
 (0)