| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058 |
- // Copyright (c) Meta Platforms, Inc. and affiliates.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- #include "cache/compressed_secondary_cache.h"
- #include "cache/secondary_cache_adapter.h"
- #include "db/db_test_util.h"
- #include "rocksdb/cache.h"
- #include "rocksdb/secondary_cache.h"
- #include "typed_cache.h"
- #include "util/random.h"
- namespace ROCKSDB_NAMESPACE {
- class TestSecondaryCache : public SecondaryCache {
- public:
- explicit TestSecondaryCache(size_t capacity, bool ready_before_wait)
- : cache_(NewLRUCache(capacity, 0, false, 0.5 /* high_pri_pool_ratio */,
- nullptr, kDefaultToAdaptiveMutex,
- kDontChargeCacheMetadata)),
- ready_before_wait_(ready_before_wait),
- num_insert_saved_(0),
- num_hits_(0),
- num_misses_(0) {}
- const char* Name() const override { return "TestSecondaryCache"; }
- Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*value*/,
- const Cache::CacheItemHelper* /*helper*/,
- bool /*force_insert*/) override {
- assert(false);
- return Status::NotSupported();
- }
- Status InsertSaved(const Slice& key, const Slice& saved,
- CompressionType type = kNoCompression,
- CacheTier source = CacheTier::kVolatileTier) override {
- CheckCacheKeyCommonPrefix(key);
- size_t size;
- char* buf;
- Status s;
- num_insert_saved_++;
- size = saved.size();
- buf = new char[size + sizeof(uint64_t) + 2 * sizeof(uint16_t)];
- EncodeFixed64(buf, size);
- buf += sizeof(uint64_t);
- EncodeFixed16(buf, type);
- buf += sizeof(uint16_t);
- EncodeFixed16(buf, (uint16_t)source);
- buf += sizeof(uint16_t);
- memcpy(buf, saved.data(), size);
- buf -= sizeof(uint64_t) + 2 * sizeof(uint16_t);
- if (!s.ok()) {
- delete[] buf;
- return s;
- }
- return cache_.Insert(key, buf, size);
- }
- std::unique_ptr<SecondaryCacheResultHandle> Lookup(
- const Slice& key, const Cache::CacheItemHelper* helper,
- Cache::CreateContext* create_context, bool wait, bool /*advise_erase*/,
- Statistics* /*stats*/, bool& kept_in_sec_cache) override {
- std::string key_str = key.ToString();
- TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
- std::unique_ptr<SecondaryCacheResultHandle> secondary_handle;
- kept_in_sec_cache = false;
- TypedHandle* handle = cache_.Lookup(key);
- if (handle) {
- num_hits_++;
- Cache::ObjectPtr value = nullptr;
- size_t charge = 0;
- Status s;
- char* ptr = cache_.Value(handle);
- CompressionType type;
- CacheTier source;
- size_t size = DecodeFixed64(ptr);
- ptr += sizeof(uint64_t);
- type = static_cast<CompressionType>(DecodeFixed16(ptr));
- ptr += sizeof(uint16_t);
- source = static_cast<CacheTier>(DecodeFixed16(ptr));
- assert(source == CacheTier::kVolatileTier);
- ptr += sizeof(uint16_t);
- s = helper->create_cb(Slice(ptr, size), type, source, create_context,
- /*alloc*/ nullptr, &value, &charge);
- if (s.ok()) {
- secondary_handle.reset(new TestSecondaryCacheResultHandle(
- cache_.get(), handle, value, charge,
- /*ready=*/wait || ready_before_wait_));
- kept_in_sec_cache = true;
- } else {
- cache_.Release(handle);
- }
- } else {
- num_misses_++;
- }
- return secondary_handle;
- }
- bool SupportForceErase() const override { return false; }
- void Erase(const Slice& /*key*/) override {}
- void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override {
- for (SecondaryCacheResultHandle* handle : handles) {
- TestSecondaryCacheResultHandle* sec_handle =
- static_cast<TestSecondaryCacheResultHandle*>(handle);
- EXPECT_FALSE(sec_handle->IsReady());
- sec_handle->SetReady();
- }
- }
- std::string GetPrintableOptions() const override { return ""; }
- uint32_t num_insert_saved() { return num_insert_saved_; }
- uint32_t num_hits() { return num_hits_; }
- uint32_t num_misses() { return num_misses_; }
- void CheckCacheKeyCommonPrefix(const Slice& key) {
- Slice current_prefix(key.data(), OffsetableCacheKey::kCommonPrefixSize);
- if (ckey_prefix_.empty()) {
- ckey_prefix_ = current_prefix.ToString();
- } else {
- EXPECT_EQ(ckey_prefix_, current_prefix.ToString());
- }
- }
- private:
- class TestSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
- public:
- TestSecondaryCacheResultHandle(Cache* cache, Cache::Handle* handle,
- Cache::ObjectPtr value, size_t size,
- bool ready)
- : cache_(cache),
- handle_(handle),
- value_(value),
- size_(size),
- is_ready_(ready) {}
- ~TestSecondaryCacheResultHandle() override { cache_->Release(handle_); }
- bool IsReady() override { return is_ready_; }
- void Wait() override {}
- Cache::ObjectPtr Value() override {
- assert(is_ready_);
- return value_;
- }
- size_t Size() override { return Value() ? size_ : 0; }
- void SetReady() { is_ready_ = true; }
- private:
- Cache* cache_;
- Cache::Handle* handle_;
- Cache::ObjectPtr value_;
- size_t size_;
- bool is_ready_;
- };
- using SharedCache =
- BasicTypedSharedCacheInterface<char[], CacheEntryRole::kMisc>;
- using TypedHandle = SharedCache::TypedHandle;
- SharedCache cache_;
- bool ready_before_wait_;
- uint32_t num_insert_saved_;
- uint32_t num_hits_;
- uint32_t num_misses_;
- std::string ckey_prefix_;
- };
- class DBTieredSecondaryCacheTest : public DBTestBase {
- public:
- DBTieredSecondaryCacheTest()
- : DBTestBase("db_tiered_secondary_cache_test", /*env_do_fsync=*/true) {}
- std::shared_ptr<Cache> NewCache(
- size_t pri_capacity, size_t compressed_capacity, size_t nvm_capacity,
- TieredAdmissionPolicy adm_policy = TieredAdmissionPolicy::kAdmPolicyAuto,
- bool ready_before_wait = false) {
- LRUCacheOptions lru_opts;
- TieredCacheOptions opts;
- lru_opts.capacity = 0;
- lru_opts.num_shard_bits = 0;
- lru_opts.high_pri_pool_ratio = 0;
- opts.cache_opts = &lru_opts;
- opts.cache_type = PrimaryCacheType::kCacheTypeLRU;
- opts.comp_cache_opts.capacity = 0;
- opts.comp_cache_opts.num_shard_bits = 0;
- opts.total_capacity = pri_capacity + compressed_capacity;
- opts.compressed_secondary_ratio = compressed_secondary_ratio_ =
- (double)compressed_capacity / opts.total_capacity;
- if (nvm_capacity > 0) {
- nvm_sec_cache_.reset(
- new TestSecondaryCache(nvm_capacity, ready_before_wait));
- opts.nvm_sec_cache = nvm_sec_cache_;
- }
- opts.adm_policy = adm_policy;
- cache_ = NewTieredCache(opts);
- assert(cache_ != nullptr);
- return cache_;
- }
- void ClearPrimaryCache() {
- ASSERT_EQ(UpdateTieredCache(cache_, -1, 1.0), Status::OK());
- ASSERT_EQ(UpdateTieredCache(cache_, -1, compressed_secondary_ratio_),
- Status::OK());
- }
- TestSecondaryCache* nvm_sec_cache() { return nvm_sec_cache_.get(); }
- CompressedSecondaryCache* compressed_secondary_cache() {
- return static_cast<CompressedSecondaryCache*>(
- static_cast<CacheWithSecondaryAdapter*>(cache_.get())
- ->TEST_GetSecondaryCache());
- }
- private:
- std::shared_ptr<Cache> cache_;
- std::shared_ptr<TestSecondaryCache> nvm_sec_cache_;
- double compressed_secondary_ratio_;
- };
- // In this test, the block size is set to 4096. Each value is 1007 bytes, so
- // each data block contains exactly 4 KV pairs. Metadata blocks are not
- // cached, so we can accurately estimate the cache usage.
- TEST_F(DBTieredSecondaryCacheTest, BasicTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- // We want a block cache of size 5KB, and a compressed secondary cache of
- // size 5KB. However, we specify a block cache size of 256KB here in order
- // to take into account the cache reservation in the block cache on
- // behalf of the compressed cache. The unit of cache reservation is 256KB.
- // The effective block cache capacity will be calculated as 256 + 5 = 261KB,
- // and 256KB will be reserved for the compressed cache, leaving 5KB for
- // the primary block cache. We only have to worry about this here because
- // the cache size is so small.
- table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 256 * 1024);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- // Disable paranoid_file_checks so that flush will not read back the newly
- // written file
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- // The first 2 Gets, for keys 0 and 5, will load the corresponding data
- // blocks as they will be cache misses. The nvm secondary cache will be
- // warmed up with the compressed blocks
- std::string v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 1u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 1u);
- v = Get(Key(5));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- // At this point, the nvm cache is warmed up with the data blocks for 0
- // and 5. The next Get will lookup the block in nvm and will be a hit.
- // It will be created as a standalone entry in memory, and a placeholder
- // will be inserted in the primary and compressed caches.
- v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 1u);
- // For this Get, the primary and compressed only have placeholders for
- // the required data block. So we will lookup the nvm cache and find the
- // block there. This time, the block will be promoted to the primary
- // block cache. No promotion to the compressed secondary cache happens,
- // and it will retain the placeholder.
- v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 2u);
- // This Get will find the data block in the primary cache.
- v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 2u);
- // We repeat the sequence for key 5. This will end up evicting the block
- // for 0 from the in-memory cache.
- v = Get(Key(5));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
- v = Get(Key(5));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
- v = Get(Key(5));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
- // This Get for key 0 will find the data block in nvm. Since the compressed
- // cache still has the placeholder, the block (compressed) will be
- // admitted. It is theh inserted into the primary as a standalone entry.
- v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 5u);
- // This Get for key 0 will find the data block in the compressed secondary
- // cache.
- v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 2u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 5u);
- Destroy(options);
- }
- // This test is very similar to BasicTest, except it calls MultiGet rather
- // than Get, in order to exercise the async lookup and WaitAll path.
- TEST_F(DBTieredSecondaryCacheTest, BasicMultiGetTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- table_options.block_cache = NewCache(260 * 1024, 10 * 1024, 256 * 1024);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- std::vector<std::string> keys;
- std::vector<std::string> values;
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- keys.clear();
- values.clear();
- keys.push_back(Key(12));
- keys.push_back(Key(16));
- keys.push_back(Key(20));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 6u);
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 6u);
- keys.clear();
- values.clear();
- keys.push_back(Key(12));
- keys.push_back(Key(16));
- keys.push_back(Key(20));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 9u);
- keys.clear();
- values.clear();
- keys.push_back(Key(12));
- keys.push_back(Key(16));
- keys.push_back(Key(20));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 12u);
- keys.clear();
- values.clear();
- keys.push_back(Key(12));
- keys.push_back(Key(16));
- keys.push_back(Key(20));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 12u);
- Destroy(options);
- }
- TEST_F(DBTieredSecondaryCacheTest, WaitAllTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- std::vector<std::string> keys;
- std::vector<std::string> values;
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- keys.clear();
- values.clear();
- keys.push_back(Key(12));
- keys.push_back(Key(16));
- keys.push_back(Key(20));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- // Insert placeholders for 4 in primary and compressed
- std::string val = Get(Key(4));
- // Force placeholder 4 out of primary
- keys.clear();
- values.clear();
- keys.push_back(Key(24));
- keys.push_back(Key(28));
- keys.push_back(Key(32));
- keys.push_back(Key(36));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 10u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 1u);
- // Now read 4 again. This will create a placeholder in primary, and insert
- // in compressed secondary since it already has a placeholder
- val = Get(Key(4));
- // Now read 0, 4 and 8. While 4 is already in the compressed secondary
- // cache, 0 and 8 will be read asynchronously from the nvm tier. The
- // WaitAll will be called for all 3 blocks.
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 10u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 10u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
- Destroy(options);
- }
- TEST_F(DBTieredSecondaryCacheTest, ReadyBeforeWaitAllTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024,
- TieredAdmissionPolicy::kAdmPolicyAuto,
- /*ready_before_wait=*/true);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.statistics = CreateDBStatistics();
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- std::vector<std::string> keys;
- std::vector<std::string> values;
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 3u);
- keys.clear();
- values.clear();
- keys.push_back(Key(12));
- keys.push_back(Key(16));
- keys.push_back(Key(20));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 6u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 3u);
- ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 6u);
- ClearPrimaryCache();
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(32));
- keys.push_back(Key(36));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
- ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
- keys.clear();
- values.clear();
- keys.push_back(Key(0));
- keys.push_back(Key(32));
- keys.push_back(Key(36));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 4u);
- ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 8u);
- Destroy(options);
- }
- // This test is for iteration. It iterates through a set of keys in two
- // passes. First pass loads the compressed blocks into the nvm tier, and
- // the second pass should hit all of those blocks.
- TEST_F(DBTieredSecondaryCacheTest, IterateTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- table_options.block_cache = NewCache(250 * 1024, 10 * 1024, 256 * 1024);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- ReadOptions ro;
- ro.readahead_size = 256 * 1024;
- auto iter = dbfull()->NewIterator(ro);
- iter->SeekToFirst();
- for (int i = 0; i < 31; ++i) {
- ASSERT_EQ(Key(i), iter->key().ToString());
- ASSERT_EQ(1007, iter->value().size());
- iter->Next();
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- delete iter;
- iter = dbfull()->NewIterator(ro);
- iter->SeekToFirst();
- for (int i = 0; i < 31; ++i) {
- ASSERT_EQ(Key(i), iter->key().ToString());
- ASSERT_EQ(1007, iter->value().size());
- iter->Next();
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 8u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 8u);
- delete iter;
- Destroy(options);
- }
- TEST_F(DBTieredSecondaryCacheTest, VolatileTierTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- // We want a block cache of size 5KB, and a compressed secondary cache of
- // size 5KB. However, we specify a block cache size of 256KB here in order
- // to take into account the cache reservation in the block cache on
- // behalf of the compressed cache. The unit of cache reservation is 256KB.
- // The effective block cache capacity will be calculated as 256 + 5 = 261KB,
- // and 256KB will be reserved for the compressed cache, leaving 5KB for
- // the primary block cache. We only have to worry about this here because
- // the cache size is so small.
- table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 256 * 1024);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- // Disable paranoid_file_checks so that flush will not read back the newly
- // written file
- options.paranoid_file_checks = false;
- options.lowest_used_cache_tier = CacheTier::kVolatileTier;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- // Since lowest_used_cache_tier is the volatile tier, nothing should be
- // inserted in the secondary cache.
- std::string v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 0u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 0u);
- Destroy(options);
- }
- class DBTieredAdmPolicyTest
- : public DBTieredSecondaryCacheTest,
- public testing::WithParamInterface<TieredAdmissionPolicy> {};
- TEST_P(DBTieredAdmPolicyTest, CompressedOnlyTest) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- // We want a block cache of size 10KB, and a compressed secondary cache of
- // size 10KB. However, we specify a block cache size of 256KB here in order
- // to take into account the cache reservation in the block cache on
- // behalf of the compressed cache. The unit of cache reservation is 256KB.
- // The effective block cache capacity will be calculated as 256 + 10 = 266KB,
- // and 256KB will be reserved for the compressed cache, leaving 10KB for
- // the primary block cache. We only have to worry about this here because
- // the cache size is so small.
- table_options.block_cache = NewCache(256 * 1024, 10 * 1024, 0, GetParam());
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
- // Disable paranoid_file_checks so that flush will not read back the newly
- // written file
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- // The first 2 Gets, for keys 0 and 5, will load the corresponding data
- // blocks as they will be cache misses. Since this is a 2-tier cache (
- // primary and compressed), no warm-up should happen with the compressed
- // blocks.
- std::string v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- v = Get(Key(5));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(compressed_secondary_cache()->TEST_GetUsage(), comp_cache_usage);
- Destroy(options);
- }
- TEST_P(DBTieredAdmPolicyTest, CompressedCacheAdmission) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- BlockBasedTableOptions table_options;
- // We want a block cache of size 5KB, and a compressed secondary cache of
- // size 5KB. However, we specify a block cache size of 256KB here in order
- // to take into account the cache reservation in the block cache on
- // behalf of the compressed cache. The unit of cache reservation is 256KB.
- // The effective block cache capacity will be calculated as 256 + 5 = 261KB,
- // and 256KB will be reserved for the compressed cache, leaving 10KB for
- // the primary block cache. We only have to worry about this here because
- // the cache size is so small.
- table_options.block_cache = NewCache(256 * 1024, 5 * 1024, 0, GetParam());
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- size_t comp_cache_usage = compressed_secondary_cache()->TEST_GetUsage();
- // Disable paranoid_file_checks so that flush will not read back the newly
- // written file
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- // The second Get (for 5) will evict the data block loaded by the first
- // Get, which will be admitted into the compressed secondary cache only
- // for the kAdmPolicyAllowAll policy
- std::string v = Get(Key(0));
- ASSERT_EQ(1007, v.size());
- v = Get(Key(5));
- ASSERT_EQ(1007, v.size());
- if (GetParam() == TieredAdmissionPolicy::kAdmPolicyAllowAll) {
- ASSERT_GT(compressed_secondary_cache()->TEST_GetUsage(),
- comp_cache_usage + 128);
- } else {
- ASSERT_LT(compressed_secondary_cache()->TEST_GetUsage(),
- comp_cache_usage + 128);
- }
- Destroy(options);
- }
- TEST_F(DBTieredSecondaryCacheTest, FSBufferTest) {
- class WrapFS : public FileSystemWrapper {
- public:
- explicit WrapFS(const std::shared_ptr<FileSystem>& _target)
- : FileSystemWrapper(_target) {}
- ~WrapFS() override {}
- const char* Name() const override { return "WrapFS"; }
- IOStatus NewRandomAccessFile(const std::string& fname,
- const FileOptions& opts,
- std::unique_ptr<FSRandomAccessFile>* result,
- IODebugContext* dbg) override {
- class WrappedRandomAccessFile : public FSRandomAccessFileOwnerWrapper {
- public:
- explicit WrappedRandomAccessFile(
- std::unique_ptr<FSRandomAccessFile>& file)
- : FSRandomAccessFileOwnerWrapper(std::move(file)) {}
- IOStatus MultiRead(FSReadRequest* reqs, size_t num_reqs,
- const IOOptions& options,
- IODebugContext* dbg) override {
- for (size_t i = 0; i < num_reqs; ++i) {
- FSReadRequest& req = reqs[i];
- // See https://github.com/facebook/rocksdb/pull/13195 for why we
- // want to set up our test implementation for FSAllocationPtr this
- // way.
- char* internalData = new char[req.len];
- req.status = Read(req.offset, req.len, options, &req.result,
- internalData, dbg);
- Slice* internalSlice = new Slice(internalData, req.len);
- FSAllocationPtr internalPtr(internalSlice, [](void* ptr) {
- delete[] static_cast<const char*>(
- static_cast<Slice*>(ptr)->data_);
- delete static_cast<Slice*>(ptr);
- });
- req.fs_scratch = std::move(internalPtr);
- }
- return IOStatus::OK();
- }
- };
- std::unique_ptr<FSRandomAccessFile> file;
- IOStatus s = target()->NewRandomAccessFile(fname, opts, &file, dbg);
- EXPECT_OK(s);
- result->reset(new WrappedRandomAccessFile(file));
- return s;
- }
- void SupportedOps(int64_t& supported_ops) override {
- supported_ops = 1 << FSSupportedOps::kAsyncIO;
- supported_ops |= 1 << FSSupportedOps::kFSBuffer;
- }
- };
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- std::shared_ptr<WrapFS> wrap_fs =
- std::make_shared<WrapFS>(env_->GetFileSystem());
- std::unique_ptr<Env> wrap_env(new CompositeEnvWrapper(env_, wrap_fs));
- BlockBasedTableOptions table_options;
- table_options.block_cache = NewCache(250 * 1024, 20 * 1024, 256 * 1024,
- TieredAdmissionPolicy::kAdmPolicyAuto,
- /*ready_before_wait=*/true);
- table_options.block_size = 4 * 1024;
- table_options.cache_index_and_filter_blocks = false;
- Options options = GetDefaultOptions();
- options.create_if_missing = true;
- options.compression = kLZ4Compression;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- options.statistics = CreateDBStatistics();
- options.env = wrap_env.get();
- options.paranoid_file_checks = false;
- DestroyAndReopen(options);
- Random rnd(301);
- const int N = 256;
- for (int i = 0; i < N; i++) {
- std::string p_v;
- test::CompressibleString(&rnd, 0.5, 1007, &p_v);
- ASSERT_OK(Put(Key(i), p_v));
- }
- ASSERT_OK(Flush());
- std::vector<std::string> keys;
- std::vector<std::string> values;
- keys.push_back(Key(0));
- keys.push_back(Key(4));
- keys.push_back(Key(8));
- values = MultiGet(keys, /*snapshot=*/nullptr, /*async=*/true);
- ASSERT_EQ(values.size(), keys.size());
- for (const auto& value : values) {
- ASSERT_EQ(1007, value.size());
- }
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 3u);
- ASSERT_EQ(nvm_sec_cache()->num_hits(), 0u);
- std::string v = Get(Key(12));
- ASSERT_EQ(1007, v.size());
- ASSERT_EQ(nvm_sec_cache()->num_insert_saved(), 4u);
- ASSERT_EQ(nvm_sec_cache()->num_misses(), 4u);
- ASSERT_EQ(options.statistics->getTickerCount(BLOCK_CACHE_MISS), 4u);
- Close();
- Destroy(options);
- }
- INSTANTIATE_TEST_CASE_P(
- DBTieredAdmPolicyTest, DBTieredAdmPolicyTest,
- ::testing::Values(TieredAdmissionPolicy::kAdmPolicyAuto,
- TieredAdmissionPolicy::kAdmPolicyPlaceholder,
- TieredAdmissionPolicy::kAdmPolicyAllowCacheHits,
- TieredAdmissionPolicy::kAdmPolicyAllowAll));
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|