From 6d93b87588ecf34ad281dc881cdf27edcde09c9b Mon Sep 17 00:00:00 2001 From: Zhichao Cao Date: Tue, 19 Oct 2021 15:53:16 -0700 Subject: [PATCH] Add lowest_used_cache_tier to ImmutableDBOptions to enable or disable Secondary Cache (#9050) Summary: Currently, if Secondary Cache is provided to the lru cache, it is used by default. We add CacheTier to advanced_options.h to describe the cache tier we used. Add a `lowest_used_cache_tier` option to `DBOptions` (immutable) and pass it to BlockBasedTableReader to decide if secondary cache will be used or not. By default it is `CacheTier::kNonVolatileTier`, which means, we always use both block cache (kVolatileTier) and secondary cache (kNonVolatileTier). By set it to `CacheTier::kVolatileTier`, the DB will not use the secondary cache. Pull Request resolved: https://github.com/facebook/rocksdb/pull/9050 Test Plan: added new tests Reviewed By: anand1976 Differential Revision: D31744769 Pulled By: zhichao-cao fbshipit-source-id: a0575ebd23e1c6dfcfc2b4c8578764e73b15bce6 --- HISTORY.md | 1 + cache/lru_cache_test.cc | 309 +++++++++++++++++- include/rocksdb/advanced_options.h | 8 + include/rocksdb/options.h | 12 + options/db_options.cc | 9 + options/db_options.h | 1 + options/options_helper.cc | 1 + options/options_settable_test.cc | 1 + table/block_based/block_based_table_reader.cc | 89 +++-- table/block_based/block_based_table_reader.h | 11 +- 10 files changed, 410 insertions(+), 32 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 3da75ae1ad..eba52ad3ec 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -26,6 +26,7 @@ * Some fields of SstFileMetaData are deprecated for compatibility with new base class FileStorageInfo. * Add `file_temperature` to `IngestExternalFileArg` such that when ingesting SST files, we are able to indicate the temperature of the this batch of files. * If `DB::Close()` failed with a non aborted status, calling `DB::Close()` again will return the original status instead of Status::OK. +* Add CacheTier to advanced_options.h to describe the cache tier we used. Add a `lowest_used_cache_tier` option to `DBOptions` (immutable) and pass it to BlockBasedTableReader. By default it is `CacheTier::kNonVolatileBlockTier`, which means, we always use both block cache (kVolatileTier) and secondary cache (kNonVolatileBlockTier). By set it to `CacheTier::kVolatileTier`, the DB will not use the secondary cache. ### Performance Improvements * Improved CPU efficiency of building block-based table (SST) files (#9039 and #9040). diff --git a/cache/lru_cache_test.cc b/cache/lru_cache_test.cc index b4eede205f..3a4549f004 100644 --- a/cache/lru_cache_test.cc +++ b/cache/lru_cache_test.cc @@ -695,7 +695,7 @@ TEST_F(DBSecondaryCacheTest, TestSecondaryCacheCorrectness1) { } ASSERT_OK(Flush()); - // After Flush is successful, RocksDB do the paranoid check for the new + // After Flush is successful, RocksDB will do the paranoid check for the new // SST file. Meta blocks are always cached in the block cache and they // will not be evicted. When block_2 is cache miss and read out, it is // inserted to the block cache. Note that, block_1 is never successfully @@ -789,7 +789,7 @@ TEST_F(DBSecondaryCacheTest, TestSecondaryCacheCorrectness2) { } ASSERT_OK(Flush()); - // After Flush is successful, RocksDB do the paranoid check for the new + // After Flush is successful, RocksDB will do the paranoid check for the new // SST file. Meta blocks are always cached in the block cache and they // will not be evicted. When block_2 is cache miss and read out, it is // inserted to the block cache. Thefore, block_1 is evicted from block @@ -883,7 +883,7 @@ TEST_F(DBSecondaryCacheTest, NoSecondaryCacheInsertion) { } ASSERT_OK(Flush()); - // After Flush is successful, RocksDB do the paranoid check for the new + // After Flush is successful, RocksDB will do the paranoid check for the new // SST file. Meta blocks are always cached in the block cache and they // will not be evicted. Now, block cache is large enough, it cache // both block_1 and block_2. When first time read block_1 and block_2 @@ -985,7 +985,7 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheFailureTest) { } ASSERT_OK(Flush()); - // After Flush is successful, RocksDB do the paranoid check for the new + // After Flush is successful, RocksDB will do the paranoid check for the new // SST file. Meta blocks are always cached in the block cache and they // will not be evicted. When block_2 is cache miss and read out, it is // inserted to the block cache. Note that, block_1 is never successfully @@ -1543,6 +1543,307 @@ TEST_F(DBSecondaryCacheTest, LRUCacheDumpLoadWithFilter) { ASSERT_OK(DestroyDB(dbname2, options)); } +// Test the option not to use the secondary cache in a certain DB. +TEST_F(DBSecondaryCacheTest, TestSecondaryCacheOptionBasic) { + LRUCacheOptions opts(4 * 1024, 0, false, 0.5, nullptr, + kDefaultToAdaptiveMutex, kDontChargeCacheMetadata); + std::shared_ptr secondary_cache( + new TestSecondaryCache(2048 * 1024)); + opts.secondary_cache = secondary_cache; + std::shared_ptr cache = NewLRUCache(opts); + BlockBasedTableOptions table_options; + table_options.block_cache = cache; + table_options.block_size = 4 * 1024; + Options options = GetDefaultOptions(); + options.create_if_missing = true; + options.table_factory.reset(NewBlockBasedTableFactory(table_options)); + options.env = fault_env_.get(); + fault_fs_->SetFailGetUniqueId(true); + options.lowest_used_cache_tier = CacheTier::kVolatileTier; + + // Set the file paranoid check, so after flush, the file will be read + // all the blocks will be accessed. + options.paranoid_file_checks = true; + DestroyAndReopen(options); + std::string session_id; + ASSERT_OK(db_->GetDbSessionId(session_id)); + secondary_cache->SetDbSessionId(session_id); + Random rnd(301); + const int N = 6; + for (int i = 0; i < N; i++) { + std::string p_v = rnd.RandomString(1007); + ASSERT_OK(Put(Key(i), p_v)); + } + + ASSERT_OK(Flush()); + + for (int i = 0; i < N; i++) { + std::string p_v = rnd.RandomString(1007); + ASSERT_OK(Put(Key(i + 70), p_v)); + } + + ASSERT_OK(Flush()); + + // Flush will trigger the paranoid check and read blocks. But only block cache + // will be read. No operations for secondary cache. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + Compact("a", "z"); + + // Compaction will also insert and evict blocks, no operations to the block + // cache. No operations for secondary cache. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + std::string v = Get(Key(0)); + ASSERT_EQ(1007, v.size()); + + // Check the data in first block. Cache miss, direclty read from SST file. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + v = Get(Key(5)); + ASSERT_EQ(1007, v.size()); + + // Check the second block. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + v = Get(Key(5)); + ASSERT_EQ(1007, v.size()); + + // block cache hit + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + v = Get(Key(70)); + ASSERT_EQ(1007, v.size()); + + // Check the first block in the second SST file. Cache miss and trigger SST + // file read. No operations for secondary cache. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + v = Get(Key(75)); + ASSERT_EQ(1007, v.size()); + + // Check the second block in the second SST file. Cache miss and trigger SST + // file read. No operations for secondary cache. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + Destroy(options); +} + +// We disable the secondary cache in DBOptions at first. Close and reopen the DB +// with new options, which set the lowest_used_cache_tier to +// kNonVolatileBlockTier. So secondary cache will be used. +TEST_F(DBSecondaryCacheTest, TestSecondaryCacheOptionChange) { + LRUCacheOptions opts(4 * 1024, 0, false, 0.5, nullptr, + kDefaultToAdaptiveMutex, kDontChargeCacheMetadata); + std::shared_ptr secondary_cache( + new TestSecondaryCache(2048 * 1024)); + opts.secondary_cache = secondary_cache; + std::shared_ptr cache = NewLRUCache(opts); + BlockBasedTableOptions table_options; + table_options.block_cache = cache; + table_options.block_size = 4 * 1024; + Options options = GetDefaultOptions(); + options.create_if_missing = true; + options.table_factory.reset(NewBlockBasedTableFactory(table_options)); + options.env = fault_env_.get(); + fault_fs_->SetFailGetUniqueId(true); + options.lowest_used_cache_tier = CacheTier::kVolatileTier; + + // Set the file paranoid check, so after flush, the file will be read + // all the blocks will be accessed. + options.paranoid_file_checks = true; + DestroyAndReopen(options); + std::string session_id; + ASSERT_OK(db_->GetDbSessionId(session_id)); + secondary_cache->SetDbSessionId(session_id); + Random rnd(301); + const int N = 6; + for (int i = 0; i < N; i++) { + std::string p_v = rnd.RandomString(1007); + ASSERT_OK(Put(Key(i), p_v)); + } + + ASSERT_OK(Flush()); + + for (int i = 0; i < N; i++) { + std::string p_v = rnd.RandomString(1007); + ASSERT_OK(Put(Key(i + 70), p_v)); + } + + ASSERT_OK(Flush()); + + // Flush will trigger the paranoid check and read blocks. But only block cache + // will be read. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + Compact("a", "z"); + + // Compaction will also insert and evict blocks, no operations to the block + // cache. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + std::string v = Get(Key(0)); + ASSERT_EQ(1007, v.size()); + + // Check the data in first block. Cache miss, direclty read from SST file. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + v = Get(Key(5)); + ASSERT_EQ(1007, v.size()); + + // Check the second block. + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + v = Get(Key(5)); + ASSERT_EQ(1007, v.size()); + + // block cache hit + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + + // Change the option to enable secondary cache after we Reopen the DB + options.lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier; + Reopen(options); + + v = Get(Key(70)); + ASSERT_EQ(1007, v.size()); + + // Enable the secondary cache, trigger lookup of the first block in second SST + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 1u); + + v = Get(Key(75)); + ASSERT_EQ(1007, v.size()); + + // trigger lookup of the second block in second SST + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 2u); + Destroy(options); +} + +// Two DB test. We create 2 DBs sharing the same block cache and secondary +// cache. We diable the secondary cache option for DB2. +TEST_F(DBSecondaryCacheTest, TestSecondaryCacheOptionTwoDB) { + LRUCacheOptions opts(4 * 1024, 0, false, 0.5, nullptr, + kDefaultToAdaptiveMutex, kDontChargeCacheMetadata); + std::shared_ptr secondary_cache( + new TestSecondaryCache(2048 * 1024)); + opts.secondary_cache = secondary_cache; + std::shared_ptr cache = NewLRUCache(opts); + BlockBasedTableOptions table_options; + table_options.block_cache = cache; + table_options.block_size = 4 * 1024; + Options options = GetDefaultOptions(); + options.create_if_missing = true; + options.table_factory.reset(NewBlockBasedTableFactory(table_options)); + options.env = fault_env_.get(); + options.paranoid_file_checks = true; + std::string dbname1 = test::PerThreadDBPath("db_t_1"); + ASSERT_OK(DestroyDB(dbname1, options)); + DB* db1 = nullptr; + ASSERT_OK(DB::Open(options, dbname1, &db1)); + std::string dbname2 = test::PerThreadDBPath("db_t_2"); + ASSERT_OK(DestroyDB(dbname2, options)); + DB* db2 = nullptr; + Options options2 = options; + options2.lowest_used_cache_tier = CacheTier::kVolatileTier; + ASSERT_OK(DB::Open(options2, dbname2, &db2)); + fault_fs_->SetFailGetUniqueId(true); + + // Set the file paranoid check, so after flush, the file will be read + // all the blocks will be accessed. + std::string session_id; + ASSERT_OK(db1->GetDbSessionId(session_id)); + secondary_cache->SetDbSessionId(session_id); + + WriteOptions wo; + Random rnd(301); + const int N = 6; + for (int i = 0; i < N; i++) { + std::string p_v = rnd.RandomString(1007); + ASSERT_OK(db1->Put(wo, Key(i), p_v)); + } + + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 0u); + ASSERT_OK(db1->Flush(FlushOptions())); + + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 2u); + + for (int i = 0; i < N; i++) { + std::string p_v = rnd.RandomString(1007); + ASSERT_OK(db2->Put(wo, Key(i), p_v)); + } + + // No change in the secondary cache, since it is disabled in DB2 + ASSERT_EQ(secondary_cache->num_inserts(), 0u); + ASSERT_EQ(secondary_cache->num_lookups(), 2u); + ASSERT_OK(db2->Flush(FlushOptions())); + ASSERT_EQ(secondary_cache->num_inserts(), 1u); + ASSERT_EQ(secondary_cache->num_lookups(), 2u); + + Slice bg("a"); + Slice ed("b"); + ASSERT_OK(db1->CompactRange(CompactRangeOptions(), &bg, &ed)); + ASSERT_OK(db2->CompactRange(CompactRangeOptions(), &bg, &ed)); + + ASSERT_EQ(secondary_cache->num_inserts(), 1u); + ASSERT_EQ(secondary_cache->num_lookups(), 2u); + + ReadOptions ro; + std::string v; + ASSERT_OK(db1->Get(ro, Key(0), &v)); + ASSERT_EQ(1007, v.size()); + + // DB 1 has lookup block 1 and it is miss in block cache, trigger secondary + // cache lookup + ASSERT_EQ(secondary_cache->num_inserts(), 1u); + ASSERT_EQ(secondary_cache->num_lookups(), 3u); + + ASSERT_OK(db1->Get(ro, Key(5), &v)); + ASSERT_EQ(1007, v.size()); + + // DB 1 lookup the second block and it is miss in block cache, trigger + // secondary cache lookup + ASSERT_EQ(secondary_cache->num_inserts(), 1u); + ASSERT_EQ(secondary_cache->num_lookups(), 4u); + + ASSERT_OK(db2->Get(ro, Key(0), &v)); + ASSERT_EQ(1007, v.size()); + + // For db2, it is not enabled with secondary cache, so no search in the + // secondary cache + ASSERT_EQ(secondary_cache->num_inserts(), 1u); + ASSERT_EQ(secondary_cache->num_lookups(), 4u); + + ASSERT_OK(db2->Get(ro, Key(5), &v)); + ASSERT_EQ(1007, v.size()); + + // For db2, it is not enabled with secondary cache, so no search in the + // secondary cache + ASSERT_EQ(secondary_cache->num_inserts(), 1u); + ASSERT_EQ(secondary_cache->num_lookups(), 4u); + + fault_fs_->SetFailGetUniqueId(false); + fault_fs_->SetFilesystemActive(true); + delete db1; + delete db2; + ASSERT_OK(DestroyDB(dbname1, options)); + ASSERT_OK(DestroyDB(dbname2, options)); +} + #endif // ROCKSDB_LITE } // namespace ROCKSDB_NAMESPACE diff --git a/include/rocksdb/advanced_options.h b/include/rocksdb/advanced_options.h index ab14bff47c..5abda96884 100644 --- a/include/rocksdb/advanced_options.h +++ b/include/rocksdb/advanced_options.h @@ -202,6 +202,14 @@ enum class Temperature : uint8_t { kCold = 0x0C, }; +// The control option of how the cache tiers will be used. Currently rocksdb +// support block cahe (volatile tier), secondary cache (non-volatile tier). +// In the future, we may add more caching layers. +enum class CacheTier : uint8_t { + kVolatileTier = 0, + kNonVolatileBlockTier = 0x01, +}; + enum UpdateStatus { // Return status For inplace update callback UPDATE_FAILED = 0, // Nothing to update UPDATED_INPLACE = 1, // Value updated inplace diff --git a/include/rocksdb/options.h b/include/rocksdb/options.h index a0e420df91..884e80ebb5 100644 --- a/include/rocksdb/options.h +++ b/include/rocksdb/options.h @@ -1336,6 +1336,18 @@ struct DBOptions { // backward/forward compatibility support for now. Some known issues are still // under development. std::shared_ptr compaction_service = nullptr; + + // It indicates, which lowest cache tier we want to + // use for a certain DB. Currently we support volatile_tier and + // non_volatile_tier. They are layered. By setting it to kVolatileTier, only + // the block cache (current implemented volatile_tier) is used. So + // cache entries will not spill to secondary cache (current + // implemented non_volatile_tier), and block cache lookup misses will not + // lookup in the secondary cache. When kNonVolatileBlockTier is used, we use + // both block cache and secondary cache. + // + // Default: kNonVolatileBlockTier + CacheTier lowest_used_cache_tier = CacheTier::kNonVolatileBlockTier; }; // Options to control the behavior of a database (passed to DB::Open) diff --git a/options/db_options.cc b/options/db_options.cc index d99df242cf..bd1cbda88c 100644 --- a/options/db_options.cc +++ b/options/db_options.cc @@ -41,6 +41,10 @@ static std::unordered_map {"SEQUENTIAL", DBOptions::AccessHint::SEQUENTIAL}, {"WILLNEED", DBOptions::AccessHint::WILLNEED}}; +static std::unordered_map cache_tier_string_map = { + {"kVolatileTier", CacheTier::kVolatileTier}, + {"kNonVolatileBlockTier", CacheTier::kNonVolatileBlockTier}}; + static std::unordered_map info_log_level_string_map = {{"DEBUG_LEVEL", InfoLogLevel::DEBUG_LEVEL}, {"INFO_LEVEL", InfoLogLevel::INFO_LEVEL}, @@ -524,6 +528,10 @@ static std::unordered_map return Status::OK(); }, nullptr}}, + {"lowest_used_cache_tier", + OptionTypeInfo::Enum( + offsetof(struct ImmutableDBOptions, lowest_used_cache_tier), + &cache_tier_string_map, OptionTypeFlags::kNone)}, }; const std::string OptionsHelper::kDBOptionsName = "DBOptions"; @@ -723,6 +731,7 @@ ImmutableDBOptions::ImmutableDBOptions(const DBOptions& options) allow_data_in_errors(options.allow_data_in_errors), db_host_id(options.db_host_id), checksum_handoff_file_types(options.checksum_handoff_file_types), + lowest_used_cache_tier(options.lowest_used_cache_tier), compaction_service(options.compaction_service) { stats = statistics.get(); fs = env->GetFileSystem(); diff --git a/options/db_options.h b/options/db_options.h index d2b0568026..5244460e89 100644 --- a/options/db_options.h +++ b/options/db_options.h @@ -99,6 +99,7 @@ struct ImmutableDBOptions { bool allow_data_in_errors; std::string db_host_id; FileTypeSet checksum_handoff_file_types; + CacheTier lowest_used_cache_tier; // Convenience/Helper objects that are not part of the base DBOptions std::shared_ptr fs; SystemClock* clock; diff --git a/options/options_helper.cc b/options/options_helper.cc index 02d0171a59..9a59b08103 100644 --- a/options/options_helper.cc +++ b/options/options_helper.cc @@ -186,6 +186,7 @@ DBOptions BuildDBOptions(const ImmutableDBOptions& immutable_db_options, options.allow_data_in_errors = immutable_db_options.allow_data_in_errors; options.checksum_handoff_file_types = immutable_db_options.checksum_handoff_file_types; + options.lowest_used_cache_tier = immutable_db_options.lowest_used_cache_tier; return options; } diff --git a/options/options_settable_test.cc b/options/options_settable_test.cc index 613fd84007..4168c0b96d 100644 --- a/options/options_settable_test.cc +++ b/options/options_settable_test.cc @@ -342,6 +342,7 @@ TEST_F(OptionsSettableTest, DBOptionsAllFieldsSettable) { "max_bgerror_resume_count=2;" "bgerror_resume_retry_interval=1000000" "db_host_id=hostname;" + "lowest_used_cache_tier=kNonVolatileBlockTier;" "allow_data_in_errors=false", new_options)); diff --git a/table/block_based/block_based_table_reader.cc b/table/block_based/block_based_table_reader.cc index ef6d8f0580..ad8ee53f97 100644 --- a/table/block_based/block_based_table_reader.cc +++ b/table/block_based/block_based_table_reader.cc @@ -353,12 +353,17 @@ void BlockBasedTable::UpdateCacheInsertionMetrics( } Cache::Handle* BlockBasedTable::GetEntryFromCache( - Cache* block_cache, const Slice& key, BlockType block_type, const bool wait, - GetContext* get_context, const Cache::CacheItemHelper* cache_helper, + const CacheTier& cache_tier, Cache* block_cache, const Slice& key, + BlockType block_type, const bool wait, GetContext* get_context, + const Cache::CacheItemHelper* cache_helper, const Cache::CreateCallback& create_cb, Cache::Priority priority) const { - auto cache_handle = - block_cache->Lookup(key, cache_helper, create_cb, priority, wait, - rep_->ioptions.statistics.get()); + Cache::Handle* cache_handle = nullptr; + if (cache_tier == CacheTier::kNonVolatileBlockTier) { + cache_handle = block_cache->Lookup(key, cache_helper, create_cb, priority, + wait, rep_->ioptions.statistics.get()); + } else { + cache_handle = block_cache->Lookup(key, rep_->ioptions.statistics.get()); + } if (cache_handle != nullptr) { UpdateCacheHitMetrics(block_type, get_context, @@ -370,6 +375,23 @@ Cache::Handle* BlockBasedTable::GetEntryFromCache( return cache_handle; } +template +Status BlockBasedTable::InsertEntryToCache( + const CacheTier& cache_tier, Cache* block_cache, const Slice& key, + const Cache::CacheItemHelper* cache_helper, + std::unique_ptr& block_holder, size_t charge, + Cache::Handle** cache_handle, Cache::Priority priority) const { + Status s = Status::OK(); + if (cache_tier == CacheTier::kNonVolatileBlockTier) { + s = block_cache->Insert(key, block_holder.get(), cache_helper, charge, + cache_handle, priority); + } else { + s = block_cache->Insert(key, block_holder.get(), charge, + cache_helper->del_cb, cache_handle, priority); + } + return s; +} + // Helper function to setup the cache key's prefix for the Table. void BlockBasedTable::SetupCacheKeyPrefix(Rep* rep, const std::string& db_session_id, @@ -1174,8 +1196,10 @@ Status BlockBasedTable::GetDataBlockFromCache( // Lookup uncompressed cache first if (block_cache != nullptr) { - auto cache_handle = GetEntryFromCache( - block_cache, block_cache_key, block_type, wait, get_context, + Cache::Handle* cache_handle = nullptr; + cache_handle = GetEntryFromCache( + rep_->ioptions.lowest_used_cache_tier, block_cache, block_cache_key, + block_type, wait, get_context, BlocklikeTraits::GetCacheItemHelper(block_type), create_cb, priority); if (cache_handle != nullptr) { @@ -1195,12 +1219,18 @@ Status BlockBasedTable::GetDataBlockFromCache( assert(!compressed_block_cache_key.empty()); BlockContents contents; - Cache::CreateCallback create_cb_special = GetCreateCallback( - read_amp_bytes_per_bit, statistics, using_zstd, filter_policy); - block_cache_compressed_handle = block_cache_compressed->Lookup( - compressed_block_cache_key, - BlocklikeTraits::GetCacheItemHelper(block_type), - create_cb_special, priority, true); + if (rep_->ioptions.lowest_used_cache_tier == + CacheTier::kNonVolatileBlockTier) { + Cache::CreateCallback create_cb_special = GetCreateCallback( + read_amp_bytes_per_bit, statistics, using_zstd, filter_policy); + block_cache_compressed_handle = block_cache_compressed->Lookup( + compressed_block_cache_key, + BlocklikeTraits::GetCacheItemHelper(block_type), + create_cb_special, priority, true); + } else { + block_cache_compressed_handle = + block_cache_compressed->Lookup(compressed_block_cache_key, statistics); + } // if we found in the compressed cache, then uncompress and insert into // uncompressed cache @@ -1237,10 +1267,10 @@ Status BlockBasedTable::GetDataBlockFromCache( read_options.fill_cache) { size_t charge = block_holder->ApproximateMemoryUsage(); Cache::Handle* cache_handle = nullptr; - s = block_cache->Insert( - block_cache_key, block_holder.get(), - BlocklikeTraits::GetCacheItemHelper(block_type), charge, - &cache_handle, priority); + s = InsertEntryToCache( + rep_->ioptions.lowest_used_cache_tier, block_cache, block_cache_key, + BlocklikeTraits::GetCacheItemHelper(block_type), + block_holder, charge, &cache_handle, priority); if (s.ok()) { assert(cache_handle != nullptr); block->SetCachedValue(block_holder.release(), block_cache, @@ -1325,18 +1355,23 @@ Status BlockBasedTable::PutDataBlockToCache( // We cannot directly put raw_block_contents because this could point to // an object in the stack. - BlockContents* block_cont_for_comp_cache = - new BlockContents(std::move(*raw_block_contents)); - s = block_cache_compressed->Insert( - compressed_block_cache_key, block_cont_for_comp_cache, + std::unique_ptr block_cont_for_comp_cache( + new BlockContents(std::move(*raw_block_contents))); + s = InsertEntryToCache( + rep_->ioptions.lowest_used_cache_tier, block_cache_compressed, + compressed_block_cache_key, BlocklikeTraits::GetCacheItemHelper(block_type), - block_cont_for_comp_cache->ApproximateMemoryUsage()); + block_cont_for_comp_cache, + block_cont_for_comp_cache->ApproximateMemoryUsage(), nullptr, + Cache::Priority::LOW); + + BlockContents* block_cont_raw_ptr = block_cont_for_comp_cache.release(); if (s.ok()) { // Avoid the following code to delete this cached block. RecordTick(statistics, BLOCK_CACHE_COMPRESSED_ADD); } else { RecordTick(statistics, BLOCK_CACHE_COMPRESSED_ADD_FAILURES); - delete block_cont_for_comp_cache; + delete block_cont_raw_ptr; } } @@ -1344,10 +1379,10 @@ Status BlockBasedTable::PutDataBlockToCache( if (block_cache != nullptr && block_holder->own_bytes()) { size_t charge = block_holder->ApproximateMemoryUsage(); Cache::Handle* cache_handle = nullptr; - s = block_cache->Insert( - block_cache_key, block_holder.get(), - BlocklikeTraits::GetCacheItemHelper(block_type), charge, - &cache_handle, priority); + s = InsertEntryToCache( + rep_->ioptions.lowest_used_cache_tier, block_cache, block_cache_key, + BlocklikeTraits::GetCacheItemHelper(block_type), + block_holder, charge, &cache_handle, priority); if (s.ok()) { assert(cache_handle != nullptr); cached_block->SetCachedValue(block_holder.release(), block_cache, diff --git a/table/block_based/block_based_table_reader.h b/table/block_based/block_based_table_reader.h index 0c81f79951..31c7b946bd 100644 --- a/table/block_based/block_based_table_reader.h +++ b/table/block_based/block_based_table_reader.h @@ -275,13 +275,22 @@ class BlockBasedTable : public TableReader { void UpdateCacheMissMetrics(BlockType block_type, GetContext* get_context) const; - Cache::Handle* GetEntryFromCache(Cache* block_cache, const Slice& key, + Cache::Handle* GetEntryFromCache(const CacheTier& cache_tier, + Cache* block_cache, const Slice& key, BlockType block_type, const bool wait, GetContext* get_context, const Cache::CacheItemHelper* cache_helper, const Cache::CreateCallback& create_cb, Cache::Priority priority) const; + template + Status InsertEntryToCache(const CacheTier& cache_tier, Cache* block_cache, + const Slice& key, + const Cache::CacheItemHelper* cache_helper, + std::unique_ptr& block_holder, + size_t charge, Cache::Handle** cache_handle, + Cache::Priority priority) const; + // Either Block::NewDataIterator() or Block::NewIndexIterator(). template static TBlockIter* InitBlockIterator(const Rep* rep, Block* block,