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
main
Andrew Kryczka 3 years ago committed by Facebook GitHub Bot
parent fefacd33e3
commit d6e016be6d
  1. 3
      HISTORY.md
  2. 60
      cache/cache_entry_roles.cc
  3. 38
      cache/cache_entry_roles.h
  4. 26
      db/db_block_cache_test.cc
  5. 31
      db/db_properties_test.cc
  6. 22
      db/internal_stats.cc
  7. 54
      include/rocksdb/cache.h
  8. 4
      include/rocksdb/db.h

@ -24,6 +24,9 @@
### Behavior changes ### 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). * 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) ## 7.1.0 (03/23/2022)
### New Features ### New Features
* Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp. * Allow WriteBatchWithIndex to index a WriteBatch that includes keys with user-defined timestamps. The index itself does not have timestamp.

@ -11,7 +11,7 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{ std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
"DataBlock", "DataBlock",
"FilterBlock", "FilterBlock",
"FilterMetaBlock", "FilterMetaBlock",
@ -25,7 +25,7 @@ std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
"Misc", "Misc",
}}; }};
std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{ std::array<std::string, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
"data-block", "data-block",
"filter-block", "filter-block",
"filter-meta-block", "filter-meta-block",
@ -39,6 +39,62 @@ std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
"misc", "misc",
}}; }};
const std::string& GetCacheEntryRoleName(CacheEntryRole role) {
return kCacheEntryRoleToHyphenString[static_cast<size_t>(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 { namespace {
struct Registry { struct Registry {

@ -15,43 +15,9 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
// Classifications of block cache entries, for reporting statistics extern std::array<std::string, kNumCacheEntryRoles>
// 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<uint32_t>(CacheEntryRole::kMisc) + 1;
extern std::array<const char*, kNumCacheEntryRoles>
kCacheEntryRoleToCamelString; kCacheEntryRoleToCamelString;
extern std::array<const char*, kNumCacheEntryRoles> extern std::array<std::string, kNumCacheEntryRoles>
kCacheEntryRoleToHyphenString; kCacheEntryRoleToHyphenString;
// To associate cache entries with their role, we use a hack on the // To associate cache entries with their role, we use a hack on the

@ -1404,21 +1404,11 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
ASSERT_TRUE( ASSERT_TRUE(
db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats, &values)); db_->GetMapProperty(DB::Properties::kBlockCacheEntryStats, &values));
EXPECT_EQ( for (size_t i = 0; i < kNumCacheEntryRoles; ++i) {
ToString(expected[static_cast<size_t>(CacheEntryRole::kIndexBlock)]), auto role = static_cast<CacheEntryRole>(i);
values["count.index-block"]); EXPECT_EQ(ToString(expected[i]),
EXPECT_EQ( values[BlockCacheEntryStatsMapKeys::EntryCount(role)]);
ToString(expected[static_cast<size_t>(CacheEntryRole::kDataBlock)]), }
values["count.data-block"]);
EXPECT_EQ(
ToString(expected[static_cast<size_t>(CacheEntryRole::kFilterBlock)]),
values["count.filter-block"]);
EXPECT_EQ(
ToString(
prev_expected[static_cast<size_t>(CacheEntryRole::kWriteBuffer)]),
values["count.write-buffer"]);
EXPECT_EQ(ToString(expected[static_cast<size_t>(CacheEntryRole::kMisc)]),
values["count.misc"]);
// Add one for kWriteBuffer // Add one for kWriteBuffer
{ {
@ -1431,7 +1421,8 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
env_->MockSleepForSeconds(1); env_->MockSleepForSeconds(1);
EXPECT_EQ(ToString(prev_expected[static_cast<size_t>( EXPECT_EQ(ToString(prev_expected[static_cast<size_t>(
CacheEntryRole::kWriteBuffer)]), CacheEntryRole::kWriteBuffer)]),
values["count.write-buffer"]); values[BlockCacheEntryStatsMapKeys::EntryCount(
CacheEntryRole::kWriteBuffer)]);
// Not enough for a "background" miss but enough for a "foreground" miss // Not enough for a "background" miss but enough for a "foreground" miss
env_->MockSleepForSeconds(45); env_->MockSleepForSeconds(45);
@ -1440,7 +1431,8 @@ TEST_F(DBBlockCacheTest, CacheEntryRoleStats) {
EXPECT_EQ( EXPECT_EQ(
ToString( ToString(
expected[static_cast<size_t>(CacheEntryRole::kWriteBuffer)]), expected[static_cast<size_t>(CacheEntryRole::kWriteBuffer)]),
values["count.write-buffer"]); values[BlockCacheEntryStatsMapKeys::EntryCount(
CacheEntryRole::kWriteBuffer)]);
} }
prev_expected = expected; prev_expected = expected;

@ -1997,6 +1997,37 @@ TEST_F(DBPropertiesTest, GetMapPropertyDbStats) {
Close(); Close();
} }
TEST_F(DBPropertiesTest, GetMapPropertyBlockCacheEntryStats) {
// Currently only verifies the expected properties are present
std::map<std::string, std::string> 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<CacheEntryRole>(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 { namespace {
std::string PopMetaIndexKey(InternalIterator* meta_iter) { std::string PopMetaIndexKey(InternalIterator* meta_iter) {
Status s = meta_iter->status(); Status s = meta_iter->status();

@ -702,17 +702,21 @@ void InternalStats::CacheEntryRoleStats::ToMap(
std::map<std::string, std::string>* values, SystemClock* clock) const { std::map<std::string, std::string>* values, SystemClock* clock) const {
values->clear(); values->clear();
auto& v = *values; auto& v = *values;
v["id"] = cache_id; v[BlockCacheEntryStatsMapKeys::CacheId()] = cache_id;
v["capacity"] = ROCKSDB_NAMESPACE::ToString(cache_capacity); v[BlockCacheEntryStatsMapKeys::CacheCapacityBytes()] =
v["secs_for_last_collection"] = ROCKSDB_NAMESPACE::ToString(cache_capacity);
v[BlockCacheEntryStatsMapKeys::LastCollectionDurationSeconds()] =
ROCKSDB_NAMESPACE::ToString(GetLastDurationMicros() / 1000000.0); ROCKSDB_NAMESPACE::ToString(GetLastDurationMicros() / 1000000.0);
v["secs_since_last_collection"] = ROCKSDB_NAMESPACE::ToString( v[BlockCacheEntryStatsMapKeys::LastCollectionAgeSeconds()] =
(clock->NowMicros() - last_end_time_micros_) / 1000000U); ROCKSDB_NAMESPACE::ToString((clock->NowMicros() - last_end_time_micros_) /
1000000U);
for (size_t i = 0; i < kNumCacheEntryRoles; ++i) { for (size_t i = 0; i < kNumCacheEntryRoles; ++i) {
std::string role = kCacheEntryRoleToHyphenString[i]; auto role = static_cast<CacheEntryRole>(i);
v["count." + role] = ROCKSDB_NAMESPACE::ToString(entry_counts[i]); v[BlockCacheEntryStatsMapKeys::EntryCount(role)] =
v["bytes." + role] = ROCKSDB_NAMESPACE::ToString(total_charges[i]); ROCKSDB_NAMESPACE::ToString(entry_counts[i]);
v["percent." + role] = v[BlockCacheEntryStatsMapKeys::UsedBytes(role)] =
ROCKSDB_NAMESPACE::ToString(total_charges[i]);
v[BlockCacheEntryStatsMapKeys::UsedPercent(role)] =
ROCKSDB_NAMESPACE::ToString(100.0 * total_charges[i] / cache_capacity); ROCKSDB_NAMESPACE::ToString(100.0 * total_charges[i] / cache_capacity);
} }
} }

@ -540,4 +540,58 @@ class Cache {
std::shared_ptr<MemoryAllocator> memory_allocator_; std::shared_ptr<MemoryAllocator> 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<uint32_t>(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 } // namespace ROCKSDB_NAMESPACE

@ -876,7 +876,9 @@ class DB {
static const std::string kLevelStats; static const std::string kLevelStats;
// "rocksdb.block-cache-entry-stats" - returns a multi-line string or // "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; static const std::string kBlockCacheEntryStats;
// "rocksdb.num-immutable-mem-table" - returns number of immutable // "rocksdb.num-immutable-mem-table" - returns number of immutable

Loading…
Cancel
Save