Skip to content

Commit 456cb9a

Browse files
rlyerlymeta-codesync[bot]
authored andcommitted
Add persistence tests for CacheComponents
Summary: Add tests for persist/recover functionality for all CacheComponents. Reviewed By: pbhandar2 Differential Revision: D97173718 fbshipit-source-id: f50da3b17702f40277c53851f70596a920445ec4
1 parent f217480 commit 456cb9a

4 files changed

Lines changed: 197 additions & 21 deletions

File tree

cachelib/interface/components/FlashCacheComponent.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ namespace facebook::cachelib::interface {
2525
class FlashCacheItem;
2626
class ConsistentFlashCacheItem;
2727

28+
namespace test {
29+
class FlashCacheFactory;
30+
}
31+
2832
/**
2933
* A cache component that uses Cachelib's BlockCache flash cache without RAM
3034
* cache.
@@ -144,6 +148,7 @@ class FlashCacheComponent : public CacheComponentWithStats {
144148

145149
friend class FlashCacheItem;
146150
friend class ConsistentFlashCacheItem;
151+
friend class test::FlashCacheFactory;
147152
};
148153

149154
/**

cachelib/interface/components/tests/CacheComponentFactory.cpp

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,34 @@
2323
namespace facebook::cachelib::interface::test {
2424

2525
std::unique_ptr<CacheComponent> RAMCacheFactory::create() {
26-
auto config = createConfig();
27-
auto poolConfig = createPoolConfig();
28-
auto ramCache = ASSERT_OK(
29-
RAMCacheComponent::create(std::move(config), std::move(poolConfig)));
26+
auto ramCache = ASSERT_OK(createWithPersistence(
27+
RAMCacheComponent::PersistenceConfig::noPersistenceOrRecovery()));
3028
return std::make_unique<RAMCacheComponent>(std::move(ramCache));
3129
}
3230

31+
std::unique_ptr<CacheComponent> RAMCacheFactory::createPersistent() {
32+
auto ramCache = ASSERT_OK(createWithPersistence(
33+
RAMCacheComponent::PersistenceConfig::persistenceButNoRecovery(
34+
tmpDir_.path().string())));
35+
return std::make_unique<RAMCacheComponent>(std::move(ramCache));
36+
}
37+
38+
Result<std::unique_ptr<CacheComponent>> RAMCacheFactory::recover() {
39+
auto result = createWithPersistence(
40+
RAMCacheComponent::PersistenceConfig::persistenceAndRecovery(
41+
tmpDir_.path().string()));
42+
if (result.hasError()) {
43+
return folly::makeUnexpected(std::move(result).error());
44+
}
45+
return std::make_unique<RAMCacheComponent>(std::move(result).value());
46+
}
47+
48+
Result<RAMCacheComponent> RAMCacheFactory::createWithPersistence(
49+
RAMCacheComponent::PersistenceConfig pc) {
50+
return RAMCacheComponent::create(createConfig(), createPoolConfig(),
51+
std::move(pc));
52+
}
53+
3354
/* static */ LruAllocatorConfig RAMCacheFactory::createConfig() {
3455
LruAllocatorConfig config;
3556
config.setCacheName("CacheComponentTest");
@@ -54,31 +75,72 @@ FlashCacheFactory::FlashCacheFactory()
5475
: hits_(/* count */ kDeviceSize / kRegionSize, /* value */ 0) {}
5576

5677
std::unique_ptr<CacheComponent> FlashCacheFactory::create() {
57-
auto flashCache = ASSERT_OK(FlashCacheComponent::create(
58-
"CacheComponentTest", makeConfig(), makeDevice()));
59-
return std::make_unique<FlashCacheComponent>(std::move(flashCache));
78+
return ASSERT_OK(createWithPersistence(
79+
makeDevice(kCacheSize), // don't need space for metadata
80+
FlashCacheComponent::PersistenceConfig::noPersistenceOrRecovery()));
81+
}
82+
83+
std::unique_ptr<CacheComponent> FlashCacheFactory::createPersistent() {
84+
return ASSERT_OK(createWithPersistence(
85+
makeDevice(kDeviceSize),
86+
FlashCacheComponent::PersistenceConfig::persistenceButNoRecovery(
87+
kMetadataSize)));
88+
}
89+
90+
void FlashCacheFactory::onShutdown(CacheComponent& component) {
91+
savedDevice_ =
92+
std::move(static_cast<FlashCacheComponent&>(component).device_);
93+
}
94+
95+
Result<std::unique_ptr<CacheComponent>> FlashCacheFactory::recover() {
96+
auto device =
97+
savedDevice_ ? std::move(savedDevice_) : makeDevice(kDeviceSize);
98+
return createWithPersistence(
99+
std::move(device),
100+
FlashCacheComponent::PersistenceConfig::persistenceAndRecovery(
101+
kMetadataSize));
102+
}
103+
104+
Result<std::unique_ptr<CacheComponent>>
105+
FlashCacheFactory::createWithPersistence(
106+
std::unique_ptr<navy::Device> device,
107+
FlashCacheComponent::PersistenceConfig pc) {
108+
auto result = FlashCacheComponent::create("CacheComponentTest", makeConfig(),
109+
std::move(device), std::move(pc));
110+
if (result.hasError()) {
111+
return folly::makeUnexpected(std::move(result).error());
112+
}
113+
return std::make_unique<FlashCacheComponent>(std::move(result).value());
60114
}
61115

62-
/* static */ std::unique_ptr<navy::Device> FlashCacheFactory::makeDevice() {
63-
return navy::createMemoryDevice(kDeviceSize, /* encryptor */ nullptr);
116+
/* static */ std::unique_ptr<navy::Device> FlashCacheFactory::makeDevice(
117+
size_t size) {
118+
return navy::createMemoryDevice(size, /* encryptor */ nullptr, kIOAlignSize);
64119
}
65120

66121
navy::BlockCache::Config FlashCacheFactory::makeConfig() {
67122
navy::BlockCache::Config config;
68123
config.regionSize = kRegionSize;
69-
config.cacheSize = kDeviceSize;
124+
config.cacheSize = kCacheSize;
125+
config.indexConfig.setNumSparseMapBuckets(64);
70126
config.evictionPolicy =
71127
std::make_unique<::testing::NiceMock<navy::MockPolicy>>(&hits_);
72128
config.reinsertionConfig.enablePctBased(100);
73129
return config;
74130
}
75131

76-
std::unique_ptr<CacheComponent> ConsistentFlashCacheFactory::create() {
77-
auto consistentFlashCache = ASSERT_OK(ConsistentFlashCacheComponent::create(
78-
"CacheComponentTest", makeConfig(), makeDevice(),
79-
std::make_unique<MurmurHash2>(), kShardsPower));
132+
Result<std::unique_ptr<CacheComponent>>
133+
ConsistentFlashCacheFactory::createWithPersistence(
134+
std::unique_ptr<navy::Device> device,
135+
FlashCacheComponent::PersistenceConfig pc) {
136+
auto result = ConsistentFlashCacheComponent::create(
137+
"CacheComponentTest", makeConfig(), std::move(device),
138+
std::make_unique<MurmurHash2>(), kShardsPower, std::move(pc));
139+
if (result.hasError()) {
140+
return folly::makeUnexpected(std::move(result).error());
141+
}
80142
return std::make_unique<ConsistentFlashCacheComponent>(
81-
std::move(consistentFlashCache));
143+
std::move(result).value());
82144
}
83145

84146
} // namespace facebook::cachelib::interface::test

cachelib/interface/components/tests/CacheComponentFactory.h

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
#pragma once
1818

19+
#include <folly/testing/TestUtil.h>
20+
1921
#include "cachelib/interface/CacheComponent.h"
2022
#include "cachelib/interface/components/FlashCacheComponent.h"
2123
#include "cachelib/interface/components/RAMCacheComponent.h"
@@ -26,13 +28,22 @@ class CacheFactory {
2628
public:
2729
virtual ~CacheFactory() = default;
2830
virtual std::unique_ptr<CacheComponent> create() = 0;
31+
virtual std::unique_ptr<CacheComponent> createPersistent() = 0;
32+
virtual void onShutdown(CacheComponent& /* component */) {}
33+
virtual Result<std::unique_ptr<CacheComponent>> recover() = 0;
2934
};
3035

3136
class RAMCacheFactory : public CacheFactory {
3237
public:
3338
std::unique_ptr<CacheComponent> create() override;
39+
std::unique_ptr<CacheComponent> createPersistent() override;
40+
Result<std::unique_ptr<CacheComponent>> recover() override;
3441

3542
private:
43+
folly::test::TemporaryDirectory tmpDir_;
44+
45+
Result<RAMCacheComponent> createWithPersistence(
46+
RAMCacheComponent::PersistenceConfig pc);
3647
static LruAllocatorConfig createConfig();
3748
static RAMCacheComponent::PoolConfig createPoolConfig();
3849
};
@@ -41,20 +52,33 @@ class FlashCacheFactory : public CacheFactory {
4152
public:
4253
FlashCacheFactory();
4354
std::unique_ptr<CacheComponent> create() override;
55+
std::unique_ptr<CacheComponent> createPersistent() override;
56+
void onShutdown(CacheComponent& /* component */) override;
57+
Result<std::unique_ptr<CacheComponent>> recover() override;
4458

4559
protected:
46-
static std::unique_ptr<navy::Device> makeDevice();
60+
static constexpr size_t kMetadataSize{/* 32KB */ 32 * 1024};
61+
static constexpr size_t kCacheSize{/* 256KB */ 256 * 1024};
62+
static constexpr size_t kDeviceSize{kMetadataSize + kCacheSize};
63+
std::unique_ptr<navy::Device> savedDevice_;
64+
65+
virtual Result<std::unique_ptr<CacheComponent>> createWithPersistence(
66+
std::unique_ptr<navy::Device> device,
67+
FlashCacheComponent::PersistenceConfig pc);
68+
static std::unique_ptr<navy::Device> makeDevice(size_t size);
4769
navy::BlockCache::Config makeConfig();
4870

4971
private:
5072
static constexpr size_t kRegionSize{/* 16KB */ 16 * 1024};
51-
static constexpr size_t kDeviceSize{/* 256KB */ 256 * 1024};
73+
static constexpr uint32_t kIOAlignSize{4096};
5274
std::vector<uint32_t> hits_;
5375
};
5476

5577
class ConsistentFlashCacheFactory : public FlashCacheFactory {
56-
public:
57-
std::unique_ptr<CacheComponent> create() override;
78+
protected:
79+
Result<std::unique_ptr<CacheComponent>> createWithPersistence(
80+
std::unique_ptr<navy::Device> device,
81+
FlashCacheComponent::PersistenceConfig pc) override;
5882

5983
private:
6084
static constexpr size_t kShardsPower{4};

cachelib/interface/components/tests/CacheComponentTest.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <folly/Random.h>
1818
#include <folly/coro/BlockingWait.h>
1919
#include <folly/coro/GtestHelpers.h>
20+
#include <folly/testing/TestUtil.h>
2021
#include <gtest/gtest.h>
2122

2223
#include <atomic>
@@ -43,9 +44,9 @@ class CacheComponentTest : public ::testing::Test {
4344
ASSERT_NE(cache_, nullptr) << "Failed to create cache";
4445
}
4546

46-
std::unique_ptr<CacheComponent> cache_;
47+
void TearDown() override { EXPECT_OK(cache_->shutdown()); }
4748

48-
private:
49+
std::unique_ptr<CacheComponent> cache_;
4950
std::unique_ptr<FactoryType> factory_;
5051
};
5152

@@ -661,4 +662,88 @@ TYPED_TEST(CacheComponentTest, MultiThreadedOperations) {
661662
EXPECT_EQ(dataCorruptions.load(), 0);
662663
}
663664

665+
// ============================================================================
666+
// Persist and Recover Tests
667+
// ============================================================================
668+
669+
CO_TYPED_TEST(CacheComponentTest, PersistAndRecover) {
670+
const std::string key = "persist_key";
671+
const std::string data = "persist_data";
672+
673+
{
674+
auto cache = this->factory_->createPersistent();
675+
676+
auto handle = ASSERT_OK(co_await cache->allocate(
677+
key, data.size(), util::getCurrentTimeSec(), 3600));
678+
std::memcpy(handle->getMemory(), data.c_str(), data.size());
679+
EXPECT_OK(co_await cache->insert(std::move(handle)));
680+
681+
EXPECT_OK(cache->shutdown());
682+
this->factory_->onShutdown(*cache);
683+
}
684+
685+
auto result = this->factory_->recover();
686+
CO_ASSERT_TRUE(result.hasValue());
687+
auto recovered = std::move(result).value();
688+
689+
auto findResult = ASSERT_OK(co_await recovered->find(key));
690+
CO_ASSERT_TRUE(findResult.has_value());
691+
std::string retrievedData(
692+
findResult.value()->template getMemoryAs<const char>(), data.size());
693+
EXPECT_EQ(retrievedData, data);
694+
}
695+
696+
CO_TYPED_TEST(CacheComponentTest, PersistAndRecoverMultipleItems) {
697+
constexpr int kNumItems = 10;
698+
699+
{
700+
auto cache = this->factory_->createPersistent();
701+
702+
for (int i = 0; i < kNumItems; ++i) {
703+
auto key = "key_" + std::to_string(i);
704+
auto val = "data_" + std::to_string(i);
705+
auto handle = ASSERT_OK(co_await cache->allocate(
706+
key, val.size(), util::getCurrentTimeSec(), 3600));
707+
std::memcpy(handle->getMemory(), val.c_str(), val.size());
708+
EXPECT_OK(co_await cache->insert(std::move(handle)));
709+
}
710+
711+
EXPECT_OK(cache->shutdown());
712+
this->factory_->onShutdown(*cache);
713+
}
714+
715+
auto result = this->factory_->recover();
716+
CO_ASSERT_TRUE(result.hasValue());
717+
auto recovered = std::move(result).value();
718+
719+
for (int i = 0; i < kNumItems; ++i) {
720+
auto key = "key_" + std::to_string(i);
721+
auto expectedVal = "data_" + std::to_string(i);
722+
auto findResult = ASSERT_OK(co_await recovered->find(key));
723+
CO_ASSERT_TRUE(findResult.has_value());
724+
std::string retrievedData(
725+
findResult.value()->template getMemoryAs<const char>(),
726+
expectedVal.size());
727+
EXPECT_EQ(retrievedData, expectedVal);
728+
}
729+
EXPECT_OK(recovered->shutdown());
730+
}
731+
732+
CO_TYPED_TEST(CacheComponentTest, RecoverWithoutPriorPersist) {
733+
// Cache components recover gracefully with an empty cache
734+
auto recovered = ASSERT_OK(this->factory_->recover());
735+
auto findResult = ASSERT_OK(co_await recovered->find("nonexistent_key"));
736+
EXPECT_FALSE(findResult.has_value());
737+
}
738+
739+
CO_TYPED_TEST(CacheComponentTest, PersistShutdownWithPersistence) {
740+
auto cache = this->factory_->createPersistent();
741+
742+
auto handle = ASSERT_OK(
743+
co_await cache->allocate("key", 100, util::getCurrentTimeSec(), 3600));
744+
EXPECT_OK(co_await cache->insert(std::move(handle)));
745+
746+
EXPECT_OK(cache->shutdown());
747+
}
748+
664749
} // namespace

0 commit comments

Comments
 (0)