From 4b65cfc723e054cc5f3cf83afc497110edc8c240 Mon Sep 17 00:00:00 2001 From: Phani Shekhar Mantripragada Date: Tue, 28 Nov 2017 10:35:17 -0800 Subject: [PATCH] Support for block_cache num_shards and other config via option string. Summary: Problem: Option string accepts only cache_size as parameter for block_cache which is specified as "block_cache=1M". It doesn't accept other parameters like num_shards etc. Changes : 1) ParseBlockBasedTableOption in block_based_table_factory is edited to accept cache options in the format "block_cache=:::". Options other than cache_size are optional to maintain backward compatibility. The changes are valid for block_cache_compressed as well. For example, "block_cache=1M:6:true:0.5", "block_cache=1M:6:true", "block_cache=1M:6" and "block_cache=1M" are all valid option strings. 2) Corresponding unit tests are added. Closes https://github.com/facebook/rocksdb/pull/3108 Differential Revision: D6420997 Pulled By: sagar0 fbshipit-source-id: cdea8b785688d2802907974af27225ccc1c0cd43 --- cache/lru_cache.cc | 19 ++++++ cache/lru_cache.h | 5 ++ include/rocksdb/cache.h | 27 +++++++++ options/options_helper.cc | 28 +++++++++ options/options_helper.h | 6 ++ options/options_test.cc | 97 ++++++++++++++++++++++++++++++ table/block_based_table_factory.cc | 30 +++++++-- 7 files changed, 207 insertions(+), 5 deletions(-) diff --git a/cache/lru_cache.cc b/cache/lru_cache.cc index e4ab4065e..b655b335f 100644 --- a/cache/lru_cache.cc +++ b/cache/lru_cache.cc @@ -169,6 +169,11 @@ size_t LRUCacheShard::TEST_GetLRUSize() { return lru_size; } +double LRUCacheShard::GetHighPriPoolRatio() { + MutexLock l(&mutex_); + return high_pri_pool_ratio_; +} + void LRUCacheShard::LRU_Remove(LRUHandle* e) { assert(e->next != nullptr); assert(e->prev != nullptr); @@ -513,6 +518,20 @@ size_t LRUCache::TEST_GetLRUSize() { return lru_size_of_all_shards; } +double LRUCache::GetHighPriPoolRatio() { + double result = 0.0; + if (num_shards_ > 0) { + result = shards_[0].GetHighPriPoolRatio(); + } + return result; +} + +std::shared_ptr NewLRUCache(const LRUCacheOptions& cache_opts) { + return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits, + cache_opts.strict_capacity_limit, + cache_opts.high_pri_pool_ratio); +} + std::shared_ptr NewLRUCache(size_t capacity, int num_shard_bits, bool strict_capacity_limit, double high_pri_pool_ratio) { diff --git a/cache/lru_cache.h b/cache/lru_cache.h index abe78fd0c..38491ee52 100644 --- a/cache/lru_cache.h +++ b/cache/lru_cache.h @@ -202,6 +202,9 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard { // not threadsafe size_t TEST_GetLRUSize(); + // Retrives high pri pool ratio + double GetHighPriPoolRatio(); + // Overloading to aligned it to cache line size void* operator new(size_t); @@ -293,6 +296,8 @@ class LRUCache : public ShardedCache { // Retrieves number of elements in LRU, for unit test purpose only size_t TEST_GetLRUSize(); + // Retrives high pri pool ratio + double GetHighPriPoolRatio(); private: LRUCacheShard* shards_; diff --git a/include/rocksdb/cache.h b/include/rocksdb/cache.h index 5ebd66bde..c9efd5fcf 100644 --- a/include/rocksdb/cache.h +++ b/include/rocksdb/cache.h @@ -33,6 +33,31 @@ namespace rocksdb { class Cache; +struct LRUCacheOptions { + // Capacity of the cache. + size_t capacity = 0; + + // Cache is sharded into 2^num_shard_bits shards, + // by hash of key. Refer to NewLRUCache for further + // information. + int num_shard_bits = -1; + + // If strict_capacity_limit is set, + // insert to the cache will fail when cache is full. + bool strict_capacity_limit = false; + + // Percentage of cache reserved for high priority entries. + double high_pri_pool_ratio = 0.0; + + LRUCacheOptions() {} + LRUCacheOptions(size_t _capacity, int _num_shard_bits, + bool _strict_capacity_limit, double _high_pri_pool_ratio) + : capacity(_capacity), + num_shard_bits(_num_shard_bits), + strict_capacity_limit(_strict_capacity_limit), + high_pri_pool_ratio(_high_pri_pool_ratio) {} +}; + // Create a new cache with a fixed size capacity. The cache is sharded // to 2^num_shard_bits shards, by hash of the key. The total capacity // is divided and evenly assigned to each shard. If strict_capacity_limit @@ -46,6 +71,8 @@ extern std::shared_ptr NewLRUCache(size_t capacity, bool strict_capacity_limit = false, double high_pri_pool_ratio = 0.0); +extern std::shared_ptr NewLRUCache(const LRUCacheOptions& cache_opts); + // Similar to NewLRUCache, but create a cache based on CLOCK algorithm with // better concurrent performance in some cases. See util/clock_cache.cc for // more detail. diff --git a/options/options_helper.cc b/options/options_helper.cc index 173660030..39141d9a1 100644 --- a/options/options_helper.cc +++ b/options/options_helper.cc @@ -491,6 +491,11 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type, } return true; } + case OptionType::kLRUCacheOptions: { + return ParseStructOptions(value, + reinterpret_cast(opt_address), + lru_cache_options_type_info); + } default: return false; } @@ -1519,6 +1524,7 @@ std::unordered_map ColumnFamilyOptions OptionsHelper::dummy_cf_options; CompactionOptionsFIFO OptionsHelper::dummy_comp_options; +LRUCacheOptions OptionsHelper::dummy_lru_cache_options; // offset_of is used to get the offset of a class data member // ex: offset_of(&ColumnFamilyOptions::num_levels) @@ -1544,6 +1550,11 @@ int offset_of(T1 CompactionOptionsFIFO::*member) { return int(size_t(&(OptionsHelper::dummy_comp_options.*member)) - size_t(&OptionsHelper::dummy_comp_options)); } +template +int offset_of(T1 LRUCacheOptions::*member) { + return int(size_t(&(OptionsHelper::dummy_lru_cache_options.*member)) - + size_t(&OptionsHelper::dummy_lru_cache_options)); +} std::unordered_map OptionsHelper::cf_options_type_info = { @@ -1788,6 +1799,23 @@ std::unordered_map OptionType::kBoolean, OptionVerificationType::kNormal, true, offsetof(struct CompactionOptionsFIFO, allow_compaction)}}}; +std::unordered_map + OptionsHelper::lru_cache_options_type_info = { + {"capacity", {offset_of(&LRUCacheOptions::capacity), + OptionType::kSizeT, OptionVerificationType::kNormal, true, + offsetof(struct LRUCacheOptions, capacity)}}, + {"num_shard_bits", {offset_of(&LRUCacheOptions::num_shard_bits), + OptionType::kInt, OptionVerificationType::kNormal, true, + offsetof(struct LRUCacheOptions, num_shard_bits)}}, + {"strict_capacity_limit", + {offset_of(&LRUCacheOptions::strict_capacity_limit), + OptionType::kBoolean, OptionVerificationType::kNormal, true, + offsetof(struct LRUCacheOptions, strict_capacity_limit)}}, + {"high_pri_pool_ratio", + {offset_of(&LRUCacheOptions::high_pri_pool_ratio), + OptionType::kDouble, OptionVerificationType::kNormal, true, + offsetof(struct LRUCacheOptions, high_pri_pool_ratio)}}}; + #endif // !ROCKSDB_LITE } // namespace rocksdb diff --git a/options/options_helper.h b/options/options_helper.h index 44c77e6c5..e7aff37c5 100644 --- a/options/options_helper.h +++ b/options/options_helper.h @@ -73,6 +73,7 @@ enum class OptionType { kWALRecoveryMode, kAccessHint, kInfoLogLevel, + kLRUCacheOptions, kUnknown }; @@ -145,6 +146,8 @@ struct OptionsHelper { static std::unordered_map fifo_compaction_options_type_info; static std::unordered_map db_options_type_info; + static std::unordered_map + lru_cache_options_type_info; static std::unordered_map compression_type_string_map; static std::unordered_map @@ -162,6 +165,7 @@ struct OptionsHelper { info_log_level_string_map; static ColumnFamilyOptions dummy_cf_options; static CompactionOptionsFIFO dummy_comp_options; + static LRUCacheOptions dummy_lru_cache_options; #endif // !ROCKSDB_LITE }; @@ -177,6 +181,8 @@ static auto& cf_options_type_info = OptionsHelper::cf_options_type_info; static auto& fifo_compaction_options_type_info = OptionsHelper::fifo_compaction_options_type_info; static auto& db_options_type_info = OptionsHelper::db_options_type_info; +static auto& lru_cache_options_type_info = + OptionsHelper::lru_cache_options_type_info; static auto& compression_type_string_map = OptionsHelper::compression_type_string_map; static auto& block_base_table_index_type_string_map = diff --git a/options/options_test.cc b/options/options_test.cc index abbdbb017..3d9d37de9 100644 --- a/options/options_test.cc +++ b/options/options_test.cc @@ -16,6 +16,8 @@ #include #include +#include "cache/lru_cache.h" +#include "cache/sharded_cache.h" #include "options/options_helper.h" #include "options/options_parser.h" #include "options/options_sanity_check.h" @@ -529,6 +531,101 @@ TEST_F(OptionsTest, GetBlockBasedTableOptionsFromString) { ASSERT_EQ(table_opt.cache_index_and_filter_blocks, new_opt.cache_index_and_filter_blocks); ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy); + + // Check block cache options are overwritten when specified + // in new format as a struct. + ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt, + "block_cache={capacity=1M;num_shard_bits=4;" + "strict_capacity_limit=true;high_pri_pool_ratio=0.5;};" + "block_cache_compressed={capacity=1M;num_shard_bits=4;" + "strict_capacity_limit=true;high_pri_pool_ratio=0.5;}", + &new_opt)); + ASSERT_TRUE(new_opt.block_cache != nullptr); + ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetNumShardBits(), 4); + ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetHighPriPoolRatio(), 0.5); + ASSERT_TRUE(new_opt.block_cache_compressed != nullptr); + ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetNumShardBits(), 4); + ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetHighPriPoolRatio(), + 0.5); + + // Set only block cache capacity. Check other values are + // reset to default values. + ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt, + "block_cache={capacity=2M};" + "block_cache_compressed={capacity=2M}", + &new_opt)); + ASSERT_TRUE(new_opt.block_cache != nullptr); + ASSERT_EQ(new_opt.block_cache->GetCapacity(), 2*1024UL*1024UL); + // Default values + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetNumShardBits(), + GetDefaultCacheShardBits(new_opt.block_cache->GetCapacity())); + ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetHighPriPoolRatio(), 0.0); + ASSERT_TRUE(new_opt.block_cache_compressed != nullptr); + ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 2*1024UL*1024UL); + // Default values + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetNumShardBits(), + GetDefaultCacheShardBits( + new_opt.block_cache_compressed->GetCapacity())); + ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetHighPriPoolRatio(), + 0.0); + + // Set couple of block cache options. + ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt, + "block_cache={num_shard_bits=5;high_pri_pool_ratio=0.5;};" + "block_cache_compressed={num_shard_bits=5;" + "high_pri_pool_ratio=0.5;}", + &new_opt)); + ASSERT_EQ(new_opt.block_cache->GetCapacity(), 0); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetNumShardBits(), 5); + ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), false); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetHighPriPoolRatio(), 0.5); + ASSERT_TRUE(new_opt.block_cache_compressed != nullptr); + ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 0); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetNumShardBits(), 5); + ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), false); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetHighPriPoolRatio(), + 0.5); + + // Set couple of block cache options. + ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt, + "block_cache={capacity=1M;num_shard_bits=4;" + "strict_capacity_limit=true;};" + "block_cache_compressed={capacity=1M;num_shard_bits=4;" + "strict_capacity_limit=true;}", + &new_opt)); + ASSERT_TRUE(new_opt.block_cache != nullptr); + ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetNumShardBits(), 4); + ASSERT_EQ(new_opt.block_cache->HasStrictCapacityLimit(), true); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache)->GetHighPriPoolRatio(), 0.0); + ASSERT_TRUE(new_opt.block_cache_compressed != nullptr); + ASSERT_EQ(new_opt.block_cache_compressed->GetCapacity(), 1024UL*1024UL); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetNumShardBits(), 4); + ASSERT_EQ(new_opt.block_cache_compressed->HasStrictCapacityLimit(), true); + ASSERT_EQ(std::dynamic_pointer_cast( + new_opt.block_cache_compressed)->GetHighPriPoolRatio(), + 0.0); } #endif // !ROCKSDB_LITE diff --git a/table/block_based_table_factory.cc b/table/block_based_table_factory.cc index 7ee7f9fd5..2f3b6cd84 100644 --- a/table/block_based_table_factory.cc +++ b/table/block_based_table_factory.cc @@ -291,11 +291,31 @@ std::string ParseBlockBasedTableOption(const std::string& name, if (!input_strings_escaped) { // if the input string is not escaped, it means this function is // invoked from SetOptions, which takes the old format. - if (name == "block_cache") { - new_options->block_cache = NewLRUCache(ParseSizeT(value)); - return ""; - } else if (name == "block_cache_compressed") { - new_options->block_cache_compressed = NewLRUCache(ParseSizeT(value)); + if (name == "block_cache" || name == "block_cache_compressed") { + // cache options can be specified in the following format + // "block_cache={capacity=1M;num_shard_bits=4; + // strict_capacity_limit=true;high_pri_pool_ratio=0.5;}" + // To support backward compatibility, the following format + // is also supported. + // "block_cache=1M" + std::shared_ptr cache; + // block_cache is specified in format block_cache=. + if (value.find('=') == std::string::npos) { + cache = NewLRUCache(ParseSizeT(value)); + } else { + LRUCacheOptions cache_opts; + if(!ParseOptionHelper(reinterpret_cast(&cache_opts), + OptionType::kLRUCacheOptions, value)) { + return "Invalid cache options"; + } + cache = NewLRUCache(cache_opts); + } + + if (name == "block_cache") { + new_options->block_cache = cache; + } else { + new_options->block_cache_compressed = cache; + } return ""; } else if (name == "filter_policy") { // Expect the following format