| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // 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 <array>
- #include <iterator>
- #include <memory>
- #include <tuple>
- #include "cache/secondary_cache_adapter.h"
- #include "memory/jemalloc_nodump_allocator.h"
- #include "rocksdb/cache.h"
- #include "rocksdb/convenience.h"
- #include "test_util/secondary_cache_test_util.h"
- #include "test_util/testharness.h"
- #include "test_util/testutil.h"
- #include "util/cast_util.h"
- namespace ROCKSDB_NAMESPACE {
- using secondary_cache_test_util::GetTestingCacheTypes;
- using secondary_cache_test_util::WithCacheType;
- // Read and reset a statistic
- template <typename T>
- T Pop(T& var) {
- T ret = var;
- var = T();
- return ret;
- }
- // 16 bytes for HCC compatibility
- const std::string key0 = "____ ____key0";
- const std::string key1 = "____ ____key1";
- const std::string key2 = "____ ____key2";
- const std::string key3 = "____ ____key3";
- class CompressedSecondaryCacheTestBase : public testing::Test,
- public WithCacheType {
- public:
- CompressedSecondaryCacheTestBase() = default;
- ~CompressedSecondaryCacheTestBase() override = default;
- protected:
- void BasicTestHelper(std::shared_ptr<SecondaryCache> sec_cache,
- bool sec_cache_is_compressed) {
- CompressedSecondaryCache* comp_sec_cache =
- static_cast<CompressedSecondaryCache*>(sec_cache.get());
- get_perf_context()->Reset();
- bool kept_in_sec_cache{true};
- // Lookup an non-existent key.
- std::unique_ptr<SecondaryCacheResultHandle> handle0 =
- sec_cache->Lookup(key0, GetHelper(), this, true, /*advise_erase=*/true,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle0, nullptr);
- Random rnd(301);
- // Insert and Lookup the item k1 for the first time.
- std::string str1 = test::CompressibleString(&rnd, 0.5, 1000);
- TestItem item1(str1.data(), str1.length());
- // A dummy handle is inserted if the item is inserted for the first time.
- ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 1);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
- sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle1_1, nullptr);
- // Insert and Lookup the item k1 for the second time and advise erasing it.
- ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
- if (sec_cache_is_compressed) {
- ASSERT_GT(comp_sec_cache->TEST_GetCharge(key1), str1.length() / 4);
- ASSERT_LT(comp_sec_cache->TEST_GetCharge(key1), str1.length() * 3 / 4);
- } else {
- ASSERT_GE(comp_sec_cache->TEST_GetCharge(key1), str1.length());
- // NOTE: split-merge is worse (1048 vs. 1024)
- ASSERT_LE(comp_sec_cache->TEST_GetCharge(key1), 1048U);
- }
- std::unique_ptr<SecondaryCacheResultHandle> handle1_2 =
- sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_NE(handle1_2, nullptr);
- ASSERT_FALSE(kept_in_sec_cache);
- if (sec_cache_is_compressed) {
- ASSERT_EQ(
- Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
- str1.length());
- ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
- str1.length() * 3 / 4);
- ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
- str1.length() / 4);
- } else {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- }
- std::unique_ptr<TestItem> val1 =
- std::unique_ptr<TestItem>(static_cast<TestItem*>(handle1_2->Value()));
- ASSERT_NE(val1, nullptr);
- ASSERT_EQ(memcmp(val1->Buf(), item1.Buf(), item1.Size()), 0);
- // Lookup the item k1 again.
- std::unique_ptr<SecondaryCacheResultHandle> handle1_3 =
- sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/true,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle1_3, nullptr);
- // Insert and Lookup the item k2.
- std::string str2 = test::CompressibleString(&rnd, 0.5, 1017);
- TestItem item2(str2.data(), str2.length());
- ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 2);
- std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
- sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle2_1, nullptr);
- ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 2);
- if (sec_cache_is_compressed) {
- ASSERT_EQ(
- Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
- str2.length());
- ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
- str2.length() * 3 / 4);
- ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
- str2.length() / 4);
- } else {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- }
- std::unique_ptr<SecondaryCacheResultHandle> handle2_2 =
- sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_NE(handle2_2, nullptr);
- std::unique_ptr<TestItem> val2 =
- std::unique_ptr<TestItem>(static_cast<TestItem*>(handle2_2->Value()));
- ASSERT_NE(val2, nullptr);
- ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
- // Release handles
- std::vector<SecondaryCacheResultHandle*> handles = {handle1_2.get(),
- handle2_2.get()};
- sec_cache->WaitAll(handles);
- handle1_2.reset();
- handle2_2.reset();
- // Insert and Lookup a non-compressible item k3.
- std::string str3 = rnd.RandomBinaryString(480);
- TestItem item3(str3.data(), str3.length());
- ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelper(), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 3);
- std::unique_ptr<SecondaryCacheResultHandle> handle3_1 =
- sec_cache->Lookup(key3, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle3_1, nullptr);
- ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelper(), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 3);
- if (sec_cache_is_compressed) {
- // TODO: consider a compression rejected stat?
- ASSERT_EQ(
- Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
- str3.length());
- ASSERT_EQ(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
- str3.length());
- } else {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- }
- std::unique_ptr<SecondaryCacheResultHandle> handle3_2 =
- sec_cache->Lookup(key3, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_NE(handle3_2, nullptr);
- std::unique_ptr<TestItem> val3 =
- std::unique_ptr<TestItem>(static_cast<TestItem*>(handle3_2->Value()));
- ASSERT_NE(val3, nullptr);
- ASSERT_EQ(memcmp(val3->Buf(), item3.Buf(), item3.Size()), 0);
- EXPECT_GE(comp_sec_cache->TEST_GetCharge(key3), str3.length());
- EXPECT_LE(comp_sec_cache->TEST_GetCharge(key3), 512);
- sec_cache.reset();
- }
- void BasicTest(bool sec_cache_is_compressed, bool use_jemalloc) {
- CompressedSecondaryCacheOptions opts;
- opts.capacity = 2048;
- opts.num_shard_bits = 0;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- opts.compression_type = CompressionType::kNoCompression;
- sec_cache_is_compressed = false;
- }
- } else {
- opts.compression_type = CompressionType::kNoCompression;
- }
- if (use_jemalloc) {
- JemallocAllocatorOptions jopts;
- std::shared_ptr<MemoryAllocator> allocator;
- std::string msg;
- if (JemallocNodumpAllocator::IsSupported(&msg)) {
- Status s = NewJemallocNodumpAllocator(jopts, &allocator);
- if (s.ok()) {
- opts.memory_allocator = allocator;
- }
- } else {
- ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
- }
- }
- std::shared_ptr<SecondaryCache> sec_cache =
- NewCompressedSecondaryCache(opts);
- BasicTestHelper(sec_cache, sec_cache_is_compressed);
- }
- void FailsTest(bool sec_cache_is_compressed) {
- CompressedSecondaryCacheOptions secondary_cache_opts;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- } else {
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- secondary_cache_opts.capacity = 1400;
- secondary_cache_opts.num_shard_bits = 0;
- secondary_cache_opts.strict_capacity_limit = true;
- std::shared_ptr<SecondaryCache> sec_cache =
- NewCompressedSecondaryCache(secondary_cache_opts);
- // Insert and Lookup the first item.
- Random rnd(301);
- std::string str1(rnd.RandomString(1000));
- TestItem item1(str1.data(), str1.length());
- // Insert a dummy handle.
- ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
- // Insert k1.
- ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
- // Insert and Lookup the second item.
- std::string str2(rnd.RandomString(500));
- TestItem item2(str2.data(), str2.length());
- // Insert a dummy handle, k1 is not evicted.
- ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
- bool kept_in_sec_cache{false};
- std::unique_ptr<SecondaryCacheResultHandle> handle1 =
- sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_NE(handle1, nullptr);
- std::unique_ptr<TestItem> val1{static_cast<TestItem*>(handle1->Value())};
- ASSERT_NE(val1, nullptr);
- ASSERT_EQ(val1->ToString(), str1);
- handle1.reset();
- // Insert k2 and k1 is evicted.
- ASSERT_OK(sec_cache->Insert(key2, &item2, GetHelper(), false));
- handle1 =
- sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle1, nullptr);
- std::unique_ptr<SecondaryCacheResultHandle> handle2 =
- sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_NE(handle2, nullptr);
- std::unique_ptr<TestItem> val2{static_cast<TestItem*>(handle2->Value())};
- ASSERT_NE(val2, nullptr);
- ASSERT_EQ(memcmp(val2->Buf(), item2.Buf(), item2.Size()), 0);
- // Insert k1 again and a dummy handle is inserted.
- ASSERT_OK(sec_cache->Insert(key1, &item1, GetHelper(), false));
- std::unique_ptr<SecondaryCacheResultHandle> handle1_1 =
- sec_cache->Lookup(key1, GetHelper(), this, true, /*advise_erase=*/false,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle1_1, nullptr);
- // Create Fails.
- SetFailCreate(true);
- std::unique_ptr<SecondaryCacheResultHandle> handle2_1 =
- sec_cache->Lookup(key2, GetHelper(), this, true, /*advise_erase=*/true,
- /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_EQ(handle2_1, nullptr);
- // Save Fails.
- std::string str3 = rnd.RandomString(10);
- TestItem item3(str3.data(), str3.length());
- // The first Status is OK because a dummy handle is inserted.
- ASSERT_OK(sec_cache->Insert(key3, &item3, GetHelperFail(), false));
- ASSERT_NOK(sec_cache->Insert(key3, &item3, GetHelperFail(), false));
- sec_cache.reset();
- }
- void BasicIntegrationTest(bool sec_cache_is_compressed,
- bool enable_custom_split_merge) {
- CompressedSecondaryCacheOptions secondary_cache_opts;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- sec_cache_is_compressed = false;
- }
- } else {
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- secondary_cache_opts.capacity = 6000;
- secondary_cache_opts.num_shard_bits = 0;
- secondary_cache_opts.enable_custom_split_merge = enable_custom_split_merge;
- std::shared_ptr<SecondaryCache> secondary_cache =
- NewCompressedSecondaryCache(secondary_cache_opts);
- std::shared_ptr<Cache> cache = NewCache(
- /*_capacity =*/1300, /*_num_shard_bits =*/0,
- /*_strict_capacity_limit =*/true, secondary_cache);
- std::shared_ptr<Statistics> stats = CreateDBStatistics();
- get_perf_context()->Reset();
- Random rnd(301);
- std::string str1 = test::CompressibleString(&rnd, 0.5, 1001);
- auto item1_1 = new TestItem(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1_1, GetHelper(), str1.length()));
- std::string str2 = test::CompressibleString(&rnd, 0.5, 1012);
- auto item2_1 = new TestItem(str2.data(), str2.length());
- // After this Insert, primary cache contains k2 and secondary cache contains
- // k1's dummy item.
- ASSERT_OK(cache->Insert(key2, item2_1, GetHelper(), str2.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 1);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- std::string str3 = test::CompressibleString(&rnd, 0.5, 1024);
- auto item3_1 = new TestItem(str3.data(), str3.length());
- // After this Insert, primary cache contains k3 and secondary cache contains
- // k1's dummy item and k2's dummy item.
- ASSERT_OK(cache->Insert(key3, item3_1, GetHelper(), str3.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 2);
- // After this Insert, primary cache contains k1 and secondary cache contains
- // k1's dummy item, k2's dummy item, and k3's dummy item.
- auto item1_2 = new TestItem(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1_2, GetHelper(), str1.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 3);
- // After this Insert, primary cache contains k2 and secondary cache contains
- // k1's item, k2's dummy item, and k3's dummy item.
- auto item2_2 = new TestItem(str2.data(), str2.length());
- ASSERT_OK(cache->Insert(key2, item2_2, GetHelper(), str2.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1);
- if (sec_cache_is_compressed) {
- ASSERT_EQ(
- Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
- str1.length());
- ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
- str1.length());
- ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
- str1.length() / 10);
- } else {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- }
- // After this Insert, primary cache contains k3 and secondary cache contains
- // k1's item and k2's item.
- auto item3_2 = new TestItem(str3.data(), str3.length());
- ASSERT_OK(cache->Insert(key3, item3_2, GetHelper(), str3.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 2);
- if (sec_cache_is_compressed) {
- ASSERT_EQ(
- Pop(get_perf_context()->compressed_sec_cache_uncompressed_bytes),
- str2.length());
- ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
- str2.length());
- ASSERT_GT(Pop(get_perf_context()->compressed_sec_cache_compressed_bytes),
- str2.length() / 10);
- } else {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- }
- Cache::Handle* handle;
- handle = cache->Lookup(key3, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_NE(handle, nullptr);
- auto val3 = static_cast<TestItem*>(cache->Value(handle));
- ASSERT_NE(val3, nullptr);
- ASSERT_EQ(memcmp(val3->Buf(), item3_2->Buf(), item3_2->Size()), 0);
- cache->Release(handle);
- // Lookup an non-existent key.
- handle = cache->Lookup(key0, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_EQ(handle, nullptr);
- // This Lookup should just insert a dummy handle in the primary cache
- // and the k1 is still in the secondary cache.
- handle = cache->Lookup(key1, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_NE(handle, nullptr);
- ASSERT_EQ(get_perf_context()->block_cache_standalone_handle_count, 1);
- auto val1_1 = static_cast<TestItem*>(cache->Value(handle));
- ASSERT_NE(val1_1, nullptr);
- ASSERT_EQ(memcmp(val1_1->Buf(), str1.data(), str1.size()), 0);
- cache->Release(handle);
- // This Lookup should erase k1 from the secondary cache and insert
- // it into primary cache; then k3 is demoted.
- // k2 and k3 are in secondary cache.
- handle = cache->Lookup(key1, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_NE(handle, nullptr);
- ASSERT_EQ(get_perf_context()->block_cache_standalone_handle_count, 1);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 3);
- cache->Release(handle);
- // k2 is still in secondary cache.
- handle = cache->Lookup(key2, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_NE(handle, nullptr);
- ASSERT_EQ(get_perf_context()->block_cache_standalone_handle_count, 2);
- cache->Release(handle);
- // Testing SetCapacity().
- ASSERT_OK(secondary_cache->SetCapacity(0));
- handle = cache->Lookup(key3, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_EQ(handle, nullptr);
- ASSERT_OK(secondary_cache->SetCapacity(7000));
- size_t capacity;
- ASSERT_OK(secondary_cache->GetCapacity(capacity));
- ASSERT_EQ(capacity, 7000);
- auto item1_3 = new TestItem(str1.data(), str1.length());
- // After this Insert, primary cache contains k1.
- ASSERT_OK(cache->Insert(key1, item1_3, GetHelper(), str2.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 3);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 4);
- auto item2_3 = new TestItem(str2.data(), str2.length());
- // After this Insert, primary cache contains k2 and secondary cache contains
- // k1's dummy item.
- ASSERT_OK(cache->Insert(key2, item2_3, GetHelper(), str1.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 4);
- auto item1_4 = new TestItem(str1.data(), str1.length());
- // After this Insert, primary cache contains k1 and secondary cache contains
- // k1's dummy item and k2's dummy item.
- ASSERT_OK(cache->Insert(key1, item1_4, GetHelper(), str2.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 5);
- auto item2_4 = new TestItem(str2.data(), str2.length());
- // After this Insert, primary cache contains k2 and secondary cache contains
- // k1's real item and k2's dummy item.
- ASSERT_OK(cache->Insert(key2, item2_4, GetHelper(), str2.length()));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 5);
- // This Lookup should just insert a dummy handle in the primary cache
- // and the k1 is still in the secondary cache.
- handle = cache->Lookup(key1, GetHelper(), this, Cache::Priority::LOW,
- stats.get());
- ASSERT_NE(handle, nullptr);
- cache->Release(handle);
- ASSERT_EQ(get_perf_context()->block_cache_standalone_handle_count, 3);
- cache.reset();
- secondary_cache.reset();
- }
- void BasicIntegrationFailTest(bool sec_cache_is_compressed) {
- CompressedSecondaryCacheOptions secondary_cache_opts;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- } else {
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- secondary_cache_opts.capacity = 6000;
- secondary_cache_opts.num_shard_bits = 0;
- std::shared_ptr<SecondaryCache> secondary_cache =
- NewCompressedSecondaryCache(secondary_cache_opts);
- std::shared_ptr<Cache> cache = NewCache(
- /*_capacity=*/1300, /*_num_shard_bits=*/0,
- /*_strict_capacity_limit=*/false, secondary_cache);
- Random rnd(301);
- std::string str1 = rnd.RandomString(1001);
- auto item1 = std::make_unique<TestItem>(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1.get(), GetHelper(), str1.length()));
- item1.release(); // Appease clang-analyze "potential memory leak"
- Cache::Handle* handle;
- handle = cache->Lookup(key2, nullptr, this, Cache::Priority::LOW);
- ASSERT_EQ(handle, nullptr);
- handle = cache->Lookup(key2, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_EQ(handle, nullptr);
- Cache::AsyncLookupHandle ah;
- ah.key = key2;
- ah.helper = GetHelper();
- ah.create_context = this;
- ah.priority = Cache::Priority::LOW;
- cache->StartAsyncLookup(ah);
- cache->Wait(ah);
- ASSERT_EQ(ah.Result(), nullptr);
- cache.reset();
- secondary_cache.reset();
- }
- void IntegrationSaveFailTest(bool sec_cache_is_compressed) {
- CompressedSecondaryCacheOptions secondary_cache_opts;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- } else {
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- secondary_cache_opts.capacity = 6000;
- secondary_cache_opts.num_shard_bits = 0;
- std::shared_ptr<SecondaryCache> secondary_cache =
- NewCompressedSecondaryCache(secondary_cache_opts);
- std::shared_ptr<Cache> cache = NewCache(
- /*_capacity=*/1300, /*_num_shard_bits=*/0,
- /*_strict_capacity_limit=*/true, secondary_cache);
- Random rnd(301);
- std::string str1 = rnd.RandomString(1001);
- auto item1 = new TestItem(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1, GetHelperFail(), str1.length()));
- std::string str2 = rnd.RandomString(1002);
- auto item2 = new TestItem(str2.data(), str2.length());
- // k1 should be demoted to the secondary cache.
- ASSERT_OK(cache->Insert(key2, item2, GetHelperFail(), str2.length()));
- Cache::Handle* handle;
- handle = cache->Lookup(key2, GetHelperFail(), this, Cache::Priority::LOW);
- ASSERT_NE(handle, nullptr);
- cache->Release(handle);
- // This lookup should fail, since k1 demotion would have failed.
- handle = cache->Lookup(key1, GetHelperFail(), this, Cache::Priority::LOW);
- ASSERT_EQ(handle, nullptr);
- // Since k1 was not promoted, k2 should still be in cache.
- handle = cache->Lookup(key2, GetHelperFail(), this, Cache::Priority::LOW);
- ASSERT_NE(handle, nullptr);
- cache->Release(handle);
- cache.reset();
- secondary_cache.reset();
- }
- void IntegrationCreateFailTest(bool sec_cache_is_compressed) {
- CompressedSecondaryCacheOptions secondary_cache_opts;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- } else {
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- secondary_cache_opts.capacity = 6000;
- secondary_cache_opts.num_shard_bits = 0;
- std::shared_ptr<SecondaryCache> secondary_cache =
- NewCompressedSecondaryCache(secondary_cache_opts);
- std::shared_ptr<Cache> cache = NewCache(
- /*_capacity=*/1300, /*_num_shard_bits=*/0,
- /*_strict_capacity_limit=*/true, secondary_cache);
- Random rnd(301);
- std::string str1 = rnd.RandomString(1001);
- auto item1 = new TestItem(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1, GetHelper(), str1.length()));
- std::string str2 = rnd.RandomString(1002);
- auto item2 = new TestItem(str2.data(), str2.length());
- // k1 should be demoted to the secondary cache.
- ASSERT_OK(cache->Insert(key2, item2, GetHelper(), str2.length()));
- Cache::Handle* handle;
- SetFailCreate(true);
- handle = cache->Lookup(key2, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_NE(handle, nullptr);
- cache->Release(handle);
- // This lookup should fail, since k1 creation would have failed
- handle = cache->Lookup(key1, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_EQ(handle, nullptr);
- // Since k1 didn't get promoted, k2 should still be in cache
- handle = cache->Lookup(key2, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_NE(handle, nullptr);
- cache->Release(handle);
- cache.reset();
- secondary_cache.reset();
- }
- void IntegrationFullCapacityTest(bool sec_cache_is_compressed) {
- CompressedSecondaryCacheOptions secondary_cache_opts;
- if (sec_cache_is_compressed) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- } else {
- secondary_cache_opts.compression_type = CompressionType::kNoCompression;
- }
- secondary_cache_opts.capacity = 6000;
- secondary_cache_opts.num_shard_bits = 0;
- std::shared_ptr<SecondaryCache> secondary_cache =
- NewCompressedSecondaryCache(secondary_cache_opts);
- std::shared_ptr<Cache> cache = NewCache(
- /*_capacity=*/1300, /*_num_shard_bits=*/0,
- /*_strict_capacity_limit=*/false, secondary_cache);
- Random rnd(301);
- std::string str1 = rnd.RandomString(1001);
- auto item1_1 = new TestItem(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1_1, GetHelper(), str1.length()));
- std::string str2 = rnd.RandomString(1002);
- std::string str2_clone{str2};
- auto item2 = new TestItem(str2.data(), str2.length());
- // After this Insert, primary cache contains k2 and secondary cache contains
- // k1's dummy item.
- ASSERT_OK(cache->Insert(key2, item2, GetHelper(), str2.length()));
- // After this Insert, primary cache contains k1 and secondary cache contains
- // k1's dummy item and k2's dummy item.
- auto item1_2 = new TestItem(str1.data(), str1.length());
- ASSERT_OK(cache->Insert(key1, item1_2, GetHelper(), str1.length()));
- auto item2_2 = new TestItem(str2.data(), str2.length());
- // After this Insert, primary cache contains k2 and secondary cache contains
- // k1's item and k2's dummy item.
- ASSERT_OK(cache->Insert(key2, item2_2, GetHelper(), str2.length()));
- Cache::Handle* handle2;
- handle2 = cache->Lookup(key2, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_NE(handle2, nullptr);
- cache->Release(handle2);
- // k1 promotion should fail because cache is at capacity and
- // strict_capacity_limit is true, but the lookup should still succeed.
- // A k1's dummy item is inserted into primary cache.
- Cache::Handle* handle1;
- handle1 = cache->Lookup(key1, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_NE(handle1, nullptr);
- cache->Release(handle1);
- // Since k1 didn't get inserted, k2 should still be in cache
- handle2 = cache->Lookup(key2, GetHelper(), this, Cache::Priority::LOW);
- ASSERT_NE(handle2, nullptr);
- cache->Release(handle2);
- cache.reset();
- secondary_cache.reset();
- }
- void SplitValueIntoChunksTest() {
- JemallocAllocatorOptions jopts;
- std::shared_ptr<MemoryAllocator> allocator;
- std::string msg;
- if (JemallocNodumpAllocator::IsSupported(&msg)) {
- Status s = NewJemallocNodumpAllocator(jopts, &allocator);
- if (!s.ok()) {
- ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
- }
- } else {
- ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
- }
- using CacheValueChunk = CompressedSecondaryCache::CacheValueChunk;
- std::unique_ptr<CompressedSecondaryCache> sec_cache =
- std::make_unique<CompressedSecondaryCache>(
- CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0,
- allocator));
- Random rnd(301);
- // 8500 = 8169 + 233 + 98, so there should be 3 chunks after split.
- size_t str_size{8500};
- std::string str = rnd.RandomString(static_cast<int>(str_size));
- size_t charge{0};
- CacheValueChunk* chunks_head = sec_cache->SplitValueIntoChunks(str, charge);
- ASSERT_EQ(charge, str_size + 3 * (sizeof(CacheValueChunk) - 1));
- CacheValueChunk* current_chunk = chunks_head;
- ASSERT_EQ(current_chunk->size, 8192 - sizeof(CacheValueChunk) + 1);
- current_chunk = current_chunk->next;
- ASSERT_EQ(current_chunk->size, 256 - sizeof(CacheValueChunk) + 1);
- current_chunk = current_chunk->next;
- ASSERT_EQ(current_chunk->size, 98);
- sec_cache->GetHelper(true)->del_cb(chunks_head, /*alloc*/ nullptr);
- }
- void MergeChunksIntoValueTest() {
- using CacheValueChunk = CompressedSecondaryCache::CacheValueChunk;
- Random rnd(301);
- size_t size1{2048};
- std::string str1 = rnd.RandomString(static_cast<int>(size1));
- CacheValueChunk* current_chunk = reinterpret_cast<CacheValueChunk*>(
- new char[sizeof(CacheValueChunk) - 1 + size1]);
- CacheValueChunk* chunks_head = current_chunk;
- memcpy(current_chunk->data, str1.data(), size1);
- current_chunk->size = size1;
- size_t size2{256};
- std::string str2 = rnd.RandomString(static_cast<int>(size2));
- current_chunk->next = reinterpret_cast<CacheValueChunk*>(
- new char[sizeof(CacheValueChunk) - 1 + size2]);
- current_chunk = current_chunk->next;
- memcpy(current_chunk->data, str2.data(), size2);
- current_chunk->size = size2;
- size_t size3{31};
- std::string str3 = rnd.RandomString(static_cast<int>(size3));
- current_chunk->next = reinterpret_cast<CacheValueChunk*>(
- new char[sizeof(CacheValueChunk) - 1 + size3]);
- current_chunk = current_chunk->next;
- memcpy(current_chunk->data, str3.data(), size3);
- current_chunk->size = size3;
- current_chunk->next = nullptr;
- std::string str = str1 + str2 + str3;
- std::unique_ptr<CompressedSecondaryCache> sec_cache =
- std::make_unique<CompressedSecondaryCache>(
- CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0));
- std::string value_str = sec_cache->MergeChunksIntoValue(chunks_head);
- ASSERT_EQ(value_str.size(), size1 + size2 + size3);
- ASSERT_EQ(value_str, str);
- while (chunks_head != nullptr) {
- CacheValueChunk* tmp_chunk = chunks_head;
- chunks_head = chunks_head->next;
- tmp_chunk->Free();
- }
- }
- void SplictValueAndMergeChunksTest() {
- JemallocAllocatorOptions jopts;
- std::shared_ptr<MemoryAllocator> allocator;
- std::string msg;
- if (JemallocNodumpAllocator::IsSupported(&msg)) {
- Status s = NewJemallocNodumpAllocator(jopts, &allocator);
- if (!s.ok()) {
- ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
- }
- } else {
- ROCKSDB_GTEST_BYPASS("JEMALLOC not supported");
- }
- using CacheValueChunk = CompressedSecondaryCache::CacheValueChunk;
- std::unique_ptr<CompressedSecondaryCache> sec_cache =
- std::make_unique<CompressedSecondaryCache>(
- CompressedSecondaryCacheOptions(1000, 0, true, 0.5, 0.0,
- allocator));
- Random rnd(301);
- // 8500 = 8169 + 233 + 98, so there should be 3 chunks after split.
- size_t str_size{8500};
- std::string str = rnd.RandomString(static_cast<int>(str_size));
- size_t charge{0};
- CacheValueChunk* chunks_head = sec_cache->SplitValueIntoChunks(str, charge);
- ASSERT_EQ(charge, str_size + 3 * (sizeof(CacheValueChunk) - 1));
- std::string value_str = sec_cache->MergeChunksIntoValue(chunks_head);
- ASSERT_EQ(value_str.size(), str_size);
- ASSERT_EQ(value_str, str);
- sec_cache->GetHelper(true)->del_cb(chunks_head, /*alloc*/ nullptr);
- }
- };
- class CompressedSecondaryCacheTest
- : public CompressedSecondaryCacheTestBase,
- public testing::WithParamInterface<std::string> {
- const std::string& Type() const override { return GetParam(); }
- };
- INSTANTIATE_TEST_CASE_P(CompressedSecondaryCacheTest,
- CompressedSecondaryCacheTest, GetTestingCacheTypes());
- class CompressedSecCacheTestWithCompressAndAllocatorParam
- : public CompressedSecondaryCacheTestBase,
- public ::testing::WithParamInterface<
- std::tuple<bool, bool, std::string>> {
- public:
- CompressedSecCacheTestWithCompressAndAllocatorParam() {
- sec_cache_is_compressed_ = std::get<0>(GetParam());
- use_jemalloc_ = std::get<1>(GetParam());
- }
- const std::string& Type() const override { return std::get<2>(GetParam()); }
- bool sec_cache_is_compressed_;
- bool use_jemalloc_;
- };
- TEST_P(CompressedSecCacheTestWithCompressAndAllocatorParam, BasicTes) {
- BasicTest(sec_cache_is_compressed_, use_jemalloc_);
- }
- INSTANTIATE_TEST_CASE_P(CompressedSecCacheTests,
- CompressedSecCacheTestWithCompressAndAllocatorParam,
- ::testing::Combine(testing::Bool(), testing::Bool(),
- GetTestingCacheTypes()));
- class CompressedSecondaryCacheTestWithCompressionParam
- : public CompressedSecondaryCacheTestBase,
- public ::testing::WithParamInterface<std::tuple<bool, std::string>> {
- public:
- CompressedSecondaryCacheTestWithCompressionParam() {
- sec_cache_is_compressed_ = std::get<0>(GetParam());
- }
- const std::string& Type() const override { return std::get<1>(GetParam()); }
- bool sec_cache_is_compressed_;
- };
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam, BasicTestFromString) {
- std::shared_ptr<SecondaryCache> sec_cache{nullptr};
- std::string sec_cache_uri;
- if (sec_cache_is_compressed_) {
- if (LZ4_Supported()) {
- sec_cache_uri =
- "compressed_secondary_cache://"
- "capacity=2048;num_shard_bits=0;compression_type=kLZ4Compression;"
- "compress_format_version=2";
- } else {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- sec_cache_uri =
- "compressed_secondary_cache://"
- "capacity=2048;num_shard_bits=0;compression_type=kNoCompression";
- sec_cache_is_compressed_ = false;
- }
- Status s = SecondaryCache::CreateFromString(ConfigOptions(), sec_cache_uri,
- &sec_cache);
- EXPECT_OK(s);
- } else {
- sec_cache_uri =
- "compressed_secondary_cache://"
- "capacity=2048;num_shard_bits=0;compression_type=kNoCompression";
- Status s = SecondaryCache::CreateFromString(ConfigOptions(), sec_cache_uri,
- &sec_cache);
- EXPECT_OK(s);
- }
- BasicTestHelper(sec_cache, sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
- BasicTestFromStringWithSplit) {
- std::shared_ptr<SecondaryCache> sec_cache{nullptr};
- std::string sec_cache_uri;
- if (sec_cache_is_compressed_) {
- if (LZ4_Supported()) {
- sec_cache_uri =
- "compressed_secondary_cache://"
- "capacity=2048;num_shard_bits=0;compression_type=kLZ4Compression;"
- "compress_format_version=2;enable_custom_split_merge=true";
- } else {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- sec_cache_uri =
- "compressed_secondary_cache://"
- "capacity=2048;num_shard_bits=0;compression_type=kNoCompression;"
- "enable_custom_split_merge=true";
- sec_cache_is_compressed_ = false;
- }
- Status s = SecondaryCache::CreateFromString(ConfigOptions(), sec_cache_uri,
- &sec_cache);
- EXPECT_OK(s);
- } else {
- sec_cache_uri =
- "compressed_secondary_cache://"
- "capacity=2048;num_shard_bits=0;compression_type=kNoCompression;"
- "enable_custom_split_merge=true";
- Status s = SecondaryCache::CreateFromString(ConfigOptions(), sec_cache_uri,
- &sec_cache);
- EXPECT_OK(s);
- }
- BasicTestHelper(sec_cache, sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam, FailsTest) {
- FailsTest(sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
- BasicIntegrationFailTest) {
- BasicIntegrationFailTest(sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
- IntegrationSaveFailTest) {
- IntegrationSaveFailTest(sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
- IntegrationCreateFailTest) {
- IntegrationCreateFailTest(sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
- IntegrationFullCapacityTest) {
- IntegrationFullCapacityTest(sec_cache_is_compressed_);
- }
- TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
- CompressedSecondaryCacheOptions opts;
- opts.capacity = 2048;
- opts.num_shard_bits = 0;
- if (sec_cache_is_compressed_) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
- return;
- }
- } else {
- opts.compression_type = CompressionType::kNoCompression;
- }
- // Select a random subset to include, for fast test
- Random& r = *Random::GetTLSInstance();
- CacheEntryRoleSet do_not_compress;
- for (uint32_t i = 0; i < kNumCacheEntryRoles; ++i) {
- // A few included on average, but decent chance of zero
- if (r.OneIn(5)) {
- do_not_compress.Add(static_cast<CacheEntryRole>(i));
- }
- }
- opts.do_not_compress_roles = do_not_compress;
- std::shared_ptr<SecondaryCache> sec_cache = NewCompressedSecondaryCache(opts);
- Random rnd(301);
- std::string junk = test::CompressibleString(&rnd, 0.5, 1000);
- for (uint32_t i = 0; i < kNumCacheEntryRoles; ++i) {
- CacheEntryRole role = static_cast<CacheEntryRole>(i);
- // Uniquify `junk`
- junk[0] = static_cast<char>(i);
- TestItem item{junk.data(), junk.length()};
- Slice ith_key = Slice(junk.data(), 16);
- get_perf_context()->Reset();
- ASSERT_OK(sec_cache->Insert(ith_key, &item, GetHelper(role), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 1U);
- ASSERT_OK(sec_cache->Insert(ith_key, &item, GetHelper(role), false));
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1U);
- bool kept_in_sec_cache{true};
- std::unique_ptr<SecondaryCacheResultHandle> handle = sec_cache->Lookup(
- ith_key, GetHelper(role), this, true,
- /*advise_erase=*/true, /*stats=*/nullptr, kept_in_sec_cache);
- ASSERT_NE(handle, nullptr);
- // Lookup returns the right data
- std::unique_ptr<TestItem> val =
- std::unique_ptr<TestItem>(static_cast<TestItem*>(handle->Value()));
- ASSERT_NE(val, nullptr);
- ASSERT_EQ(memcmp(val->Buf(), item.Buf(), item.Size()), 0);
- bool compressed =
- sec_cache_is_compressed_ && !do_not_compress.Contains(role);
- if (compressed) {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
- junk.length());
- ASSERT_LT(get_perf_context()->compressed_sec_cache_compressed_bytes,
- junk.length() * 3 / 4);
- ASSERT_GT(get_perf_context()->compressed_sec_cache_compressed_bytes,
- junk.length() / 4);
- } else {
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
- ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
- }
- }
- }
- INSTANTIATE_TEST_CASE_P(CompressedSecCacheTests,
- CompressedSecondaryCacheTestWithCompressionParam,
- testing::Combine(testing::Bool(),
- GetTestingCacheTypes()));
- class CompressedSecCacheTestWithCompressAndSplitParam
- : public CompressedSecondaryCacheTestBase,
- public ::testing::WithParamInterface<
- std::tuple<bool, bool, std::string>> {
- public:
- CompressedSecCacheTestWithCompressAndSplitParam() {
- sec_cache_is_compressed_ = std::get<0>(GetParam());
- enable_custom_split_merge_ = std::get<1>(GetParam());
- }
- const std::string& Type() const override { return std::get<2>(GetParam()); }
- bool sec_cache_is_compressed_;
- bool enable_custom_split_merge_;
- };
- TEST_P(CompressedSecCacheTestWithCompressAndSplitParam, BasicIntegrationTest) {
- BasicIntegrationTest(sec_cache_is_compressed_, enable_custom_split_merge_);
- }
- INSTANTIATE_TEST_CASE_P(CompressedSecCacheTests,
- CompressedSecCacheTestWithCompressAndSplitParam,
- ::testing::Combine(testing::Bool(), testing::Bool(),
- GetTestingCacheTypes()));
- TEST_P(CompressedSecondaryCacheTest, SplitValueIntoChunksTest) {
- SplitValueIntoChunksTest();
- }
- TEST_P(CompressedSecondaryCacheTest, MergeChunksIntoValueTest) {
- MergeChunksIntoValueTest();
- }
- TEST_P(CompressedSecondaryCacheTest, SplictValueAndMergeChunksTest) {
- SplictValueAndMergeChunksTest();
- }
- using secondary_cache_test_util::WithCacheType;
- class CompressedSecCacheTestWithTiered
- : public testing::Test,
- public WithCacheType,
- public testing::WithParamInterface<
- std::tuple<PrimaryCacheType, TieredAdmissionPolicy>> {
- public:
- using secondary_cache_test_util::WithCacheType::TestItem;
- CompressedSecCacheTestWithTiered() {
- LRUCacheOptions lru_opts;
- HyperClockCacheOptions hcc_opts(
- /*_capacity=*/0,
- /*_estimated_entry_charge=*/256 << 10,
- /*_num_shard_bits=*/0);
- // eviction_effort_cap setting simply to avoid churn in existing test
- hcc_opts.eviction_effort_cap = 100;
- TieredCacheOptions opts;
- lru_opts.capacity = 0;
- lru_opts.num_shard_bits = 0;
- lru_opts.high_pri_pool_ratio = 0;
- opts.cache_type = std::get<0>(GetParam());
- if (opts.cache_type == PrimaryCacheType::kCacheTypeLRU) {
- opts.cache_opts = &lru_opts;
- } else {
- opts.cache_opts = &hcc_opts;
- }
- opts.adm_policy = std::get<1>(GetParam());
- ;
- opts.comp_cache_opts.capacity = 0;
- opts.comp_cache_opts.num_shard_bits = 0;
- opts.total_capacity = 100 << 20;
- opts.compressed_secondary_ratio = 0.3;
- cache_ = NewTieredCache(opts);
- cache_res_mgr_ =
- std::make_shared<CacheReservationManagerImpl<CacheEntryRole::kMisc>>(
- cache_);
- }
- const std::string& Type() const override {
- if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
- return lru_str;
- } else {
- return hcc_str;
- }
- }
- protected:
- CacheReservationManager* cache_res_mgr() { return cache_res_mgr_.get(); }
- std::shared_ptr<Cache> GetTieredCache() { return cache_; }
- Cache* GetCache() {
- return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
- cache_.get())
- ->TEST_GetCache();
- }
- SecondaryCache* GetSecondaryCache() {
- return static_cast_with_check<CacheWithSecondaryAdapter, Cache>(
- cache_.get())
- ->TEST_GetSecondaryCache();
- }
- size_t GetPercent(size_t val, unsigned int percent) {
- return static_cast<size_t>(val * percent / 100);
- }
- private:
- std::shared_ptr<Cache> cache_;
- std::shared_ptr<CacheReservationManager> cache_res_mgr_;
- static std::string lru_str;
- static std::string hcc_str;
- };
- std::string CompressedSecCacheTestWithTiered::lru_str(WithCacheType::kLRU);
- std::string CompressedSecCacheTestWithTiered::hcc_str(
- WithCacheType::kFixedHyperClock);
- bool CacheUsageWithinBounds(size_t val1, size_t val2, size_t error) {
- return ((val1 < (val2 + error)) && (val1 > (val2 - error)));
- }
- TEST_P(CompressedSecCacheTestWithTiered, CacheReservationManager) {
- CompressedSecondaryCache* sec_cache =
- static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
- // Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
- // double explicit casts
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
- GetPercent(37 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
- }
- TEST_P(CompressedSecCacheTestWithTiered,
- CacheReservationManagerMultipleUpdate) {
- CompressedSecondaryCache* sec_cache =
- static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
- int i;
- for (i = 0; i < 10; ++i) {
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation((1 + i) << 20));
- }
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
- GetPercent(37 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- for (i = 10; i > 0; --i) {
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(((i - 1) << 20)));
- }
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- EXPECT_EQ(sec_cache->TEST_GetUsage(), 0);
- }
- TEST_P(CompressedSecCacheTestWithTiered, AdmissionPolicy) {
- if (!LZ4_Supported()) {
- ROCKSDB_GTEST_BYPASS("This test requires LZ4 support\n");
- return;
- }
- Cache* tiered_cache = GetTieredCache().get();
- Cache* cache = GetCache();
- std::vector<CacheKey> keys;
- std::vector<std::string> vals;
- // Make the item size slightly less than 10MB to ensure we can fit the
- // expected number of items in the cache
- int item_size = (10 << 20) - (1 << 18);
- int i;
- Random rnd(301);
- for (i = 0; i < 14; ++i) {
- keys.emplace_back(CacheKey::CreateUniqueForCacheLifetime(cache));
- vals.emplace_back(rnd.RandomString(item_size));
- }
- for (i = 0; i < 7; ++i) {
- TestItem* item = new TestItem(vals[i].data(), vals[i].length());
- ASSERT_OK(tiered_cache->Insert(keys[i].AsSlice(), item, GetHelper(),
- vals[i].length()));
- }
- Cache::Handle* handle1;
- handle1 = tiered_cache->Lookup(keys[0].AsSlice(), GetHelper(),
- /*context*/ this, Cache::Priority::LOW);
- ASSERT_NE(handle1, nullptr);
- Cache::Handle* handle2;
- handle2 = tiered_cache->Lookup(keys[1].AsSlice(), GetHelper(),
- /*context*/ this, Cache::Priority::LOW);
- ASSERT_NE(handle2, nullptr);
- tiered_cache->Release(handle1);
- tiered_cache->Release(handle2);
- // Flush all previous entries out of the primary cache
- for (i = 7; i < 14; ++i) {
- TestItem* item = new TestItem(vals[i].data(), vals[i].length());
- ASSERT_OK(tiered_cache->Insert(keys[i].AsSlice(), item, GetHelper(),
- vals[i].length()));
- }
- // keys 0 and 1 should be found as they had the hit bit set
- handle1 = tiered_cache->Lookup(keys[0].AsSlice(), GetHelper(),
- /*context*/ this, Cache::Priority::LOW);
- ASSERT_NE(handle1, nullptr);
- handle2 = tiered_cache->Lookup(keys[1].AsSlice(), GetHelper(),
- /*context*/ this, Cache::Priority::LOW);
- ASSERT_NE(handle2, nullptr);
- tiered_cache->Release(handle1);
- tiered_cache->Release(handle2);
- handle1 = tiered_cache->Lookup(keys[2].AsSlice(), GetHelper(),
- /*context*/ this, Cache::Priority::LOW);
- ASSERT_EQ(handle1, nullptr);
- handle1 = tiered_cache->Lookup(keys[3].AsSlice(), GetHelper(),
- /*context*/ this, Cache::Priority::LOW);
- ASSERT_EQ(handle1, nullptr);
- }
- TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdate) {
- CompressedSecondaryCache* sec_cache =
- static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
- std::shared_ptr<Cache> tiered_cache = GetTieredCache();
- // Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
- // double explicit casts
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- size_t sec_capacity;
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (30 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, 130 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (39 << 20),
- GetPercent(39 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (39 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, 70 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (21 << 20),
- GetPercent(21 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (21 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, 100 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (30 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.4));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (40 << 20),
- GetPercent(40 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (40 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.2));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (20 << 20),
- GetPercent(20 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (20 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 1.0));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (100 << 20),
- GetPercent(100 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, 100 << 20);
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.0));
- // Only check usage for LRU cache. HCC shows a 64KB usage for some reason
- if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
- ASSERT_EQ(GetCache()->GetUsage(), 0);
- }
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, 0);
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.3));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (30 << 20));
- }
- TEST_P(CompressedSecCacheTestWithTiered, DynamicUpdateWithReservation) {
- CompressedSecondaryCache* sec_cache =
- static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
- std::shared_ptr<Cache> tiered_cache = GetTieredCache();
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(10 << 20));
- // Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
- // double explicit casts
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
- GetPercent(37 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- size_t sec_capacity;
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (30 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, 70 << 20));
- // Only check usage for LRU cache. HCC is slightly off for some reason
- if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (28 << 20),
- GetPercent(28 << 20, 1));
- }
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (21 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, 130 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (46 << 20),
- GetPercent(46 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (39 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, 100 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
- GetPercent(37 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (30 << 20));
- ASSERT_OK(tiered_cache->GetSecondaryCacheCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, 30 << 20);
- size_t sec_usage;
- ASSERT_OK(tiered_cache->GetSecondaryCachePinnedUsage(sec_usage));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_usage, 3 << 20,
- GetPercent(3 << 20, 1));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.39));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (45 << 20),
- GetPercent(45 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (4 << 20),
- GetPercent(4 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (39 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.2));
- // Only check usage for LRU cache. HCC is slightly off for some reason
- if (std::get<0>(GetParam()) == PrimaryCacheType::kCacheTypeLRU) {
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (28 << 20),
- GetPercent(28 << 20, 1));
- }
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (2 << 20),
- GetPercent(2 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (20 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 1.0));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (100 << 20),
- GetPercent(100 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (10 << 20),
- GetPercent(10 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, 100 << 20);
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.0));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (10 << 20),
- GetPercent(10 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, 0);
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.3));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (37 << 20),
- GetPercent(37 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (3 << 20),
- GetPercent(3 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, 30 << 20);
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
- }
- TEST_P(CompressedSecCacheTestWithTiered, ReservationOverCapacity) {
- CompressedSecondaryCache* sec_cache =
- static_cast<CompressedSecondaryCache*>(GetSecondaryCache());
- std::shared_ptr<Cache> tiered_cache = GetTieredCache();
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(110 << 20));
- // Use EXPECT_PRED3 instead of EXPECT_NEAR to void too many size_t to
- // double explicit casts
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (110 << 20),
- GetPercent(110 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (30 << 20),
- GetPercent(30 << 20, 1));
- size_t sec_capacity;
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (30 << 20));
- ASSERT_OK(UpdateTieredCache(tiered_cache, -1, 0.39));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (110 << 20),
- GetPercent(110 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (39 << 20),
- GetPercent(39 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (39 << 20));
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(90 << 20));
- EXPECT_PRED3(CacheUsageWithinBounds, GetCache()->GetUsage(), (94 << 20),
- GetPercent(94 << 20, 1));
- EXPECT_PRED3(CacheUsageWithinBounds, sec_cache->TEST_GetUsage(), (35 << 20),
- GetPercent(35 << 20, 1));
- ASSERT_OK(sec_cache->GetCapacity(sec_capacity));
- ASSERT_EQ(sec_capacity, (39 << 20));
- ASSERT_OK(cache_res_mgr()->UpdateCacheReservation(0));
- }
- INSTANTIATE_TEST_CASE_P(
- CompressedSecCacheTests, CompressedSecCacheTestWithTiered,
- ::testing::Values(
- std::make_tuple(PrimaryCacheType::kCacheTypeLRU,
- TieredAdmissionPolicy::kAdmPolicyAllowCacheHits),
- std::make_tuple(PrimaryCacheType::kCacheTypeHCC,
- TieredAdmissionPolicy::kAdmPolicyAllowCacheHits)));
- } // namespace ROCKSDB_NAMESPACE
- int main(int argc, char** argv) {
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- }
|