From d6e016be6dceae99adb2c068331c90d468661873 Mon Sep 17 00:00:00 2001 From: Andrew Kryczka Date: Thu, 14 Apr 2022 09:38:55 -0700 Subject: [PATCH] Expose `CacheEntryRole` and map keys for block cache stat collections (#9838) Summary: This gives users the ability to examine the map populated by `GetMapProperty()` with property `kBlockCacheEntryStats`. It also sets us up for a possible future where cache reservations are configured according to `CacheEntryRole`s rather than flags coupled to roles. Pull Request resolved: https://github.com/facebook/rocksdb/pull/9838 Test Plan: - migrated test DBBlockCacheTest.CacheEntryRoleStats to use this API. That test verifies some of the contents are as expected - added a DBPropertiesTest to verify the public map keys are present, and nothing else Reviewed By: hx235 Differential Revision: D35629493 Pulled By: ajkr fbshipit-source-id: 5c4356b8560e85d1f881fd32c44c15960b02fc68 --- HISTORY.md | 3 ++ cache/cache_entry_roles.cc | 60 ++++++++++++++++++++++++++++++++++++-- cache/cache_entry_roles.h | 38 ++---------------------- db/db_block_cache_test.cc | 26 ++++++----------- db/db_properties_test.cc | 31 ++++++++++++++++++++ db/internal_stats.cc | 22 ++++++++------ include/rocksdb/cache.h | 54 ++++++++++++++++++++++++++++++++++ include/rocksdb/db.h | 4 ++- 8 files changed, 173 insertions(+), 65 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7d19e52d1..c6d7a5da4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -24,6 +24,9 @@ ### Behavior changes * Disallow usage of commit-time-write-batch for write-prepared/write-unprepared transactions if TransactionOptions::use_only_the_last_commit_time_batch_for_recovery is false to prevent two (or more) uncommitted versions of the same key in the database. Otherwise, bottommost compaction may violate the internal key uniqueness invariant of SSTs if the sequence numbers of both internal keys are zeroed out (#9794). +### Public API changes +* Exposed APIs to examine results of block cache stats collections in a structured way. In particular, users of `GetMapProperty()` with property `kBlockCacheEntryStats` can now use the functions in `BlockCacheEntryStatsMapKeys` to find stats in the map. + ## 7.1.0 (03/23/2022) ### New Features * Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp. diff --git a/cache/cache_entry_roles.cc b/cache/cache_entry_roles.cc index 19af4996c..5b49df457 100644 --- a/cache/cache_entry_roles.cc +++ b/cache/cache_entry_roles.cc @@ -11,7 +11,7 @@ namespace ROCKSDB_NAMESPACE { -std::array kCacheEntryRoleToCamelString{{ +std::array kCacheEntryRoleToCamelString{{ "DataBlock", "FilterBlock", "FilterMetaBlock", @@ -25,7 +25,7 @@ std::array kCacheEntryRoleToCamelString{{ "Misc", }}; -std::array kCacheEntryRoleToHyphenString{{ +std::array kCacheEntryRoleToHyphenString{{ "data-block", "filter-block", "filter-meta-block", @@ -39,6 +39,62 @@ std::array kCacheEntryRoleToHyphenString{{ "misc", }}; +const std::string& GetCacheEntryRoleName(CacheEntryRole role) { + return kCacheEntryRoleToHyphenString[static_cast(role)]; +} + +const std::string& BlockCacheEntryStatsMapKeys::CacheId() { + static const std::string kCacheId = "id"; + return kCacheId; +} + +const std::string& BlockCacheEntryStatsMapKeys::CacheCapacityBytes() { + static const std::string kCacheCapacityBytes = "capacity"; + return kCacheCapacityBytes; +} + +const std::string& +BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds() { + static const std::string kLastCollectionDurationSeconds = + "secs_for_last_collection"; + return kLastCollectionDurationSeconds; +} + +const std::string& BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds() { + static const std::string kLastCollectionAgeSeconds = + "secs_since_last_collection"; + return kLastCollectionAgeSeconds; +} + +namespace { + +std::string GetPrefixedCacheEntryRoleName(const std::string& prefix, + CacheEntryRole role) { + const std::string& role_name = GetCacheEntryRoleName(role); + std::string prefixed_role_name; + prefixed_role_name.reserve(prefix.size() + role_name.size()); + prefixed_role_name.append(prefix); + prefixed_role_name.append(role_name); + return prefixed_role_name; +} + +} // namespace + +std::string BlockCacheEntryStatsMapKeys::EntryCount(CacheEntryRole role) { + const static std::string kPrefix = "count."; + return GetPrefixedCacheEntryRoleName(kPrefix, role); +} + +std::string BlockCacheEntryStatsMapKeys::UsedBytes(CacheEntryRole role) { + const static std::string kPrefix = "bytes."; + return GetPrefixedCacheEntryRoleName(kPrefix, role); +} + +std::string BlockCacheEntryStatsMapKeys::UsedPercent(CacheEntryRole role) { + const static std::string kPrefix = "percent."; + return GetPrefixedCacheEntryRoleName(kPrefix, role); +} + namespace { struct Registry { diff --git a/cache/cache_entry_roles.h b/cache/cache_entry_roles.h index d46abf207..5a49fdfd4 100644 --- a/cache/cache_entry_roles.h +++ b/cache/cache_entry_roles.h @@ -15,43 +15,9 @@ namespace ROCKSDB_NAMESPACE { -// Classifications of block cache entries, for reporting statistics -// Adding new enum to this class requires corresponding updates to -// kCacheEntryRoleToCamelString and kCacheEntryRoleToHyphenString -enum class CacheEntryRole { - // Block-based table data block - kDataBlock, - // Block-based table filter block (full or partitioned) - kFilterBlock, - // Block-based table metadata block for partitioned filter - kFilterMetaBlock, - // Block-based table deprecated filter block (old "block-based" filter) - kDeprecatedFilterBlock, - // Block-based table index block - kIndexBlock, - // Other kinds of block-based table block - kOtherBlock, - // WriteBufferManager reservations to account for memtable usage - kWriteBuffer, - // BlockBasedTableBuilder reservations to account for - // compression dictionary building buffer's memory usage - kCompressionDictionaryBuildingBuffer, - // Filter reservations to account for - // (new) bloom and ribbon filter construction's memory usage - kFilterConstruction, - // BlockBasedTableReader reservations to account for - // its memory usage - kBlockBasedTableReader, - // Default bucket, for miscellaneous cache entries. Do not use for - // entries that could potentially add up to large usage. - kMisc, -}; -constexpr uint32_t kNumCacheEntryRoles = - static_cast(CacheEntryRole::kMisc) + 1; - -extern std::array +extern std::array kCacheEntryRoleToCamelString; -extern std::array +extern std::array kCacheEntryRoleToHyphenString; // To associate cache entries with their role, we use a hack on the diff --git a/db/db_block_cache_test.cc b/db/db_block_cache_test.cc index fc99adaaf..b6afb8003 100644 --- a/db/db_block_cache_test.cc +++ b/db/db_block_cache_test.cc @@ -1404,21 +1404,11 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) { ASSERT_TRUE( db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats, &values)); - EXPECT_EQ( - ToString(expected[static_cast(CacheEntryRole::kIndexBlock)]), - values["count.index-block"]); - EXPECT_EQ( - ToString(expected[static_cast(CacheEntryRole::kDataBlock)]), - values["count.data-block"]); - EXPECT_EQ( - ToString(expected[static_cast(CacheEntryRole::kFilterBlock)]), - values["count.filter-block"]); - EXPECT_EQ( - ToString( - prev_expected[static_cast(CacheEntryRole::kWriteBuffer)]), - values["count.write-buffer"]); - EXPECT_EQ(ToString(expected[static_cast(CacheEntryRole::kMisc)]), - values["count.misc"]); + for (size_t i = 0; i < kNumCacheEntryRoles; ++i) { + auto role = static_cast(i); + EXPECT_EQ(ToString(expected[i]), + values[BlockCacheEntryStatsMapKeys::EntryCount(role)]); + } // Add one for kWriteBuffer { @@ -1431,7 +1421,8 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) { env_->MockSleepForSeconds(1); EXPECT_EQ(ToString(prev_expected[static_cast( CacheEntryRole::kWriteBuffer)]), - values["count.write-buffer"]); + values[BlockCacheEntryStatsMapKeys::EntryCount( + CacheEntryRole::kWriteBuffer)]); // Not enough for a "background" miss but enough for a "foreground" miss env_->MockSleepForSeconds(45); @@ -1440,7 +1431,8 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) { EXPECT_EQ( ToString( expected[static_cast(CacheEntryRole::kWriteBuffer)]), - values["count.write-buffer"]); + values[BlockCacheEntryStatsMapKeys::EntryCount( + CacheEntryRole::kWriteBuffer)]); } prev_expected = expected; diff --git a/db/db_properties_test.cc b/db/db_properties_test.cc index fac877dcf..a568bc117 100644 --- a/db/db_properties_test.cc +++ b/db/db_properties_test.cc @@ -1997,6 +1997,37 @@ TEST_F(DBPropertiesTest, GetMapPropertyDbStats) { Close(); } +TEST_F(DBPropertiesTest, GetMapPropertyBlockCacheEntryStats) { + // Currently only verifies the expected properties are present + std::map values; + ASSERT_TRUE( + db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats, &values)); + + ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::CacheId()) != + values.end()); + ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::CacheCapacityBytes()) != + values.end()); + ASSERT_TRUE( + values.find( + BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds()) != + values.end()); + ASSERT_TRUE( + values.find(BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds()) != + values.end()); + for (size_t i = 0; i < kNumCacheEntryRoles; ++i) { + CacheEntryRole role = static_cast(i); + ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::EntryCount(role)) != + values.end()); + ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::UsedBytes(role)) != + values.end()); + ASSERT_TRUE(values.find(BlockCacheEntryStatsMapKeys::UsedPercent(role)) != + values.end()); + } + + // There should be no extra values in the map. + ASSERT_EQ(3 * kNumCacheEntryRoles + 4, values.size()); +} + namespace { std::string PopMetaIndexKey(InternalIterator* meta_iter) { Status s = meta_iter->status(); diff --git a/db/internal_stats.cc b/db/internal_stats.cc index 8a2a6f93f..929b6f82d 100644 --- a/db/internal_stats.cc +++ b/db/internal_stats.cc @@ -702,17 +702,21 @@ void InternalStats::CacheEntryRoleStats::ToMap( std::map* values, SystemClock* clock) const { values->clear(); auto& v = *values; - v["id"] = cache_id; - v["capacity"] = ROCKSDB_NAMESPACE::ToString(cache_capacity); - v["secs_for_last_collection"] = + v[BlockCacheEntryStatsMapKeys::CacheId()] = cache_id; + v[BlockCacheEntryStatsMapKeys::CacheCapacityBytes()] = + ROCKSDB_NAMESPACE::ToString(cache_capacity); + v[BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds()] = ROCKSDB_NAMESPACE::ToString(GetLastDurationMicros() / 1000000.0); - v["secs_since_last_collection"] = ROCKSDB_NAMESPACE::ToString( - (clock->NowMicros() - last_end_time_micros_) / 1000000U); + v[BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds()] = + ROCKSDB_NAMESPACE::ToString((clock->NowMicros() - last_end_time_micros_) / + 1000000U); for (size_t i = 0; i < kNumCacheEntryRoles; ++i) { - std::string role = kCacheEntryRoleToHyphenString[i]; - v["count." + role] = ROCKSDB_NAMESPACE::ToString(entry_counts[i]); - v["bytes." + role] = ROCKSDB_NAMESPACE::ToString(total_charges[i]); - v["percent." + role] = + auto role = static_cast(i); + v[BlockCacheEntryStatsMapKeys::EntryCount(role)] = + ROCKSDB_NAMESPACE::ToString(entry_counts[i]); + v[BlockCacheEntryStatsMapKeys::UsedBytes(role)] = + ROCKSDB_NAMESPACE::ToString(total_charges[i]); + v[BlockCacheEntryStatsMapKeys::UsedPercent(role)] = ROCKSDB_NAMESPACE::ToString(100.0 * total_charges[i] / cache_capacity); } } diff --git a/include/rocksdb/cache.h b/include/rocksdb/cache.h index 4a3ae8793..6b393026d 100644 --- a/include/rocksdb/cache.h +++ b/include/rocksdb/cache.h @@ -540,4 +540,58 @@ class Cache { std::shared_ptr memory_allocator_; }; +// Classifications of block cache entries. +// +// Developer notes: Adding a new enum to this class requires corresponding +// updates to `kCacheEntryRoleToCamelString` and +// `kCacheEntryRoleToHyphenString`. Do not add to this enum after `kMisc` since +// `kNumCacheEntryRoles` assumes `kMisc` comes last. +enum class CacheEntryRole { + // Block-based table data block + kDataBlock, + // Block-based table filter block (full or partitioned) + kFilterBlock, + // Block-based table metadata block for partitioned filter + kFilterMetaBlock, + // Block-based table deprecated filter block (old "block-based" filter) + kDeprecatedFilterBlock, + // Block-based table index block + kIndexBlock, + // Other kinds of block-based table block + kOtherBlock, + // WriteBufferManager reservations to account for memtable usage + kWriteBuffer, + // BlockBasedTableBuilder reservations to account for + // compression dictionary building buffer's memory usage + kCompressionDictionaryBuildingBuffer, + // Filter reservations to account for + // (new) bloom and ribbon filter construction's memory usage + kFilterConstruction, + // BlockBasedTableReader reservations to account for + // its memory usage + kBlockBasedTableReader, + // Default bucket, for miscellaneous cache entries. Do not use for + // entries that could potentially add up to large usage. + kMisc, +}; +constexpr uint32_t kNumCacheEntryRoles = + static_cast(CacheEntryRole::kMisc) + 1; + +// Obtain a hyphen-separated, lowercase name of a `CacheEntryRole`. +const std::string& GetCacheEntryRoleName(CacheEntryRole); + +// For use with `GetMapProperty()` for property +// `DB::Properties::kBlockCacheEntryStats`. On success, the map will +// be populated with all keys that can be obtained from these functions. +struct BlockCacheEntryStatsMapKeys { + static const std::string& CacheId(); + static const std::string& CacheCapacityBytes(); + static const std::string& LastCollectionDurationSeconds(); + static const std::string& LastCollectionAgeSeconds(); + + static std::string EntryCount(CacheEntryRole); + static std::string UsedBytes(CacheEntryRole); + static std::string UsedPercent(CacheEntryRole); +}; + } // namespace ROCKSDB_NAMESPACE diff --git a/include/rocksdb/db.h b/include/rocksdb/db.h index f7c0e3be9..4dd9fd1c8 100644 --- a/include/rocksdb/db.h +++ b/include/rocksdb/db.h @@ -876,7 +876,9 @@ class DB { static const std::string kLevelStats; // "rocksdb.block-cache-entry-stats" - returns a multi-line string or - // map with statistics on block cache usage. + // map with statistics on block cache usage. See + // `BlockCacheEntryStatsMapKeys` for structured representation of keys + // available in the map form. static const std::string kBlockCacheEntryStats; // "rocksdb.num-immutable-mem-table" - returns number of immutable