638d239507
Summary: For our default block cache, each additional entry has extra memory overhead. It include LRUHandle (72 bytes currently) and the cache key (two varint64, file id and offset). The usage is not negligible. For example for block_size=4k, the overhead accounts for an extra 2% memory usage for the cache. The patch charging the cache for the extra usage, reducing untracked memory usage outside block cache. The feature is enabled by default and can be disabled by passing kDontChargeCacheMetadata to the cache constructor. This PR builds up on https://github.com/facebook/rocksdb/issues/4258 Pull Request resolved: https://github.com/facebook/rocksdb/pull/5797 Test Plan: - Existing tests are updated to either disable the feature when the test has too much dependency on the old way of accounting the usage or increasing the cache capacity to account for the additional charge of metadata. - The Usage tests in cache_test.cc are augmented to test the cache usage under kFullChargeCacheMetadata. Differential Revision: D17396833 Pulled By: maysamyabandeh fbshipit-source-id: 7684ccb9f8a40ca595e4f5efcdb03623afea0c6f
226 lines
7.4 KiB
C++
226 lines
7.4 KiB
C++
// 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 "rocksdb/utilities/sim_cache.h"
|
|
#include <cstdlib>
|
|
#include "db/db_test_util.h"
|
|
#include "port/stack_trace.h"
|
|
|
|
namespace rocksdb {
|
|
|
|
class SimCacheTest : public DBTestBase {
|
|
private:
|
|
size_t miss_count_ = 0;
|
|
size_t hit_count_ = 0;
|
|
size_t insert_count_ = 0;
|
|
size_t failure_count_ = 0;
|
|
|
|
public:
|
|
const size_t kNumBlocks = 5;
|
|
const size_t kValueSize = 1000;
|
|
|
|
SimCacheTest() : DBTestBase("/sim_cache_test") {}
|
|
|
|
BlockBasedTableOptions GetTableOptions() {
|
|
BlockBasedTableOptions table_options;
|
|
// Set a small enough block size so that each key-value get its own block.
|
|
table_options.block_size = 1;
|
|
return table_options;
|
|
}
|
|
|
|
Options GetOptions(const BlockBasedTableOptions& table_options) {
|
|
Options options = CurrentOptions();
|
|
options.create_if_missing = true;
|
|
// options.compression = kNoCompression;
|
|
options.statistics = rocksdb::CreateDBStatistics();
|
|
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
|
return options;
|
|
}
|
|
|
|
void InitTable(const Options& /*options*/) {
|
|
std::string value(kValueSize, 'a');
|
|
for (size_t i = 0; i < kNumBlocks * 2; i++) {
|
|
ASSERT_OK(Put(ToString(i), value.c_str()));
|
|
}
|
|
}
|
|
|
|
void RecordCacheCounters(const Options& options) {
|
|
miss_count_ = TestGetTickerCount(options, BLOCK_CACHE_MISS);
|
|
hit_count_ = TestGetTickerCount(options, BLOCK_CACHE_HIT);
|
|
insert_count_ = TestGetTickerCount(options, BLOCK_CACHE_ADD);
|
|
failure_count_ = TestGetTickerCount(options, BLOCK_CACHE_ADD_FAILURES);
|
|
}
|
|
|
|
void CheckCacheCounters(const Options& options, size_t expected_misses,
|
|
size_t expected_hits, size_t expected_inserts,
|
|
size_t expected_failures) {
|
|
size_t new_miss_count = TestGetTickerCount(options, BLOCK_CACHE_MISS);
|
|
size_t new_hit_count = TestGetTickerCount(options, BLOCK_CACHE_HIT);
|
|
size_t new_insert_count = TestGetTickerCount(options, BLOCK_CACHE_ADD);
|
|
size_t new_failure_count =
|
|
TestGetTickerCount(options, BLOCK_CACHE_ADD_FAILURES);
|
|
ASSERT_EQ(miss_count_ + expected_misses, new_miss_count);
|
|
ASSERT_EQ(hit_count_ + expected_hits, new_hit_count);
|
|
ASSERT_EQ(insert_count_ + expected_inserts, new_insert_count);
|
|
ASSERT_EQ(failure_count_ + expected_failures, new_failure_count);
|
|
miss_count_ = new_miss_count;
|
|
hit_count_ = new_hit_count;
|
|
insert_count_ = new_insert_count;
|
|
failure_count_ = new_failure_count;
|
|
}
|
|
};
|
|
|
|
TEST_F(SimCacheTest, SimCache) {
|
|
ReadOptions read_options;
|
|
auto table_options = GetTableOptions();
|
|
auto options = GetOptions(table_options);
|
|
InitTable(options);
|
|
LRUCacheOptions co;
|
|
co.capacity = 0;
|
|
co.num_shard_bits = 0;
|
|
co.strict_capacity_limit = false;
|
|
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
|
std::shared_ptr<SimCache> simCache = NewSimCache(NewLRUCache(co), 20000, 0);
|
|
table_options.block_cache = simCache;
|
|
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
|
Reopen(options);
|
|
RecordCacheCounters(options);
|
|
|
|
std::vector<std::unique_ptr<Iterator>> iterators(kNumBlocks);
|
|
Iterator* iter = nullptr;
|
|
|
|
// Load blocks into cache.
|
|
for (size_t i = 0; i < kNumBlocks; i++) {
|
|
iter = db_->NewIterator(read_options);
|
|
iter->Seek(ToString(i));
|
|
ASSERT_OK(iter->status());
|
|
CheckCacheCounters(options, 1, 0, 1, 0);
|
|
iterators[i].reset(iter);
|
|
}
|
|
ASSERT_EQ(kNumBlocks,
|
|
simCache->get_hit_counter() + simCache->get_miss_counter());
|
|
ASSERT_EQ(0, simCache->get_hit_counter());
|
|
size_t usage = simCache->GetUsage();
|
|
ASSERT_LT(0, usage);
|
|
ASSERT_EQ(usage, simCache->GetSimUsage());
|
|
simCache->SetCapacity(usage);
|
|
ASSERT_EQ(usage, simCache->GetPinnedUsage());
|
|
|
|
// Test with strict capacity limit.
|
|
simCache->SetStrictCapacityLimit(true);
|
|
iter = db_->NewIterator(read_options);
|
|
iter->Seek(ToString(kNumBlocks * 2 - 1));
|
|
ASSERT_TRUE(iter->status().IsIncomplete());
|
|
CheckCacheCounters(options, 1, 0, 0, 1);
|
|
delete iter;
|
|
iter = nullptr;
|
|
|
|
// Release iterators and access cache again.
|
|
for (size_t i = 0; i < kNumBlocks; i++) {
|
|
iterators[i].reset();
|
|
CheckCacheCounters(options, 0, 0, 0, 0);
|
|
}
|
|
// Add kNumBlocks again
|
|
for (size_t i = 0; i < kNumBlocks; i++) {
|
|
std::unique_ptr<Iterator> it(db_->NewIterator(read_options));
|
|
it->Seek(ToString(i));
|
|
ASSERT_OK(it->status());
|
|
CheckCacheCounters(options, 0, 1, 0, 0);
|
|
}
|
|
ASSERT_EQ(5, simCache->get_hit_counter());
|
|
for (size_t i = kNumBlocks; i < kNumBlocks * 2; i++) {
|
|
std::unique_ptr<Iterator> it(db_->NewIterator(read_options));
|
|
it->Seek(ToString(i));
|
|
ASSERT_OK(it->status());
|
|
CheckCacheCounters(options, 1, 0, 1, 0);
|
|
}
|
|
ASSERT_EQ(0, simCache->GetPinnedUsage());
|
|
ASSERT_EQ(3 * kNumBlocks + 1,
|
|
simCache->get_hit_counter() + simCache->get_miss_counter());
|
|
ASSERT_EQ(6, simCache->get_hit_counter());
|
|
}
|
|
|
|
TEST_F(SimCacheTest, SimCacheLogging) {
|
|
auto table_options = GetTableOptions();
|
|
auto options = GetOptions(table_options);
|
|
options.disable_auto_compactions = true;
|
|
LRUCacheOptions co;
|
|
co.capacity = 1024 * 1024;
|
|
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
|
std::shared_ptr<SimCache> sim_cache = NewSimCache(NewLRUCache(co), 20000, 0);
|
|
table_options.block_cache = sim_cache;
|
|
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
|
Reopen(options);
|
|
|
|
int num_block_entries = 20;
|
|
for (int i = 0; i < num_block_entries; i++) {
|
|
Put(Key(i), "val");
|
|
Flush();
|
|
}
|
|
|
|
std::string log_file = test::PerThreadDBPath(env_, "cache_log.txt");
|
|
ASSERT_OK(sim_cache->StartActivityLogging(log_file, env_));
|
|
for (int i = 0; i < num_block_entries; i++) {
|
|
ASSERT_EQ(Get(Key(i)), "val");
|
|
}
|
|
for (int i = 0; i < num_block_entries; i++) {
|
|
ASSERT_EQ(Get(Key(i)), "val");
|
|
}
|
|
sim_cache->StopActivityLogging();
|
|
ASSERT_OK(sim_cache->GetActivityLoggingStatus());
|
|
|
|
std::string file_contents = "";
|
|
ReadFileToString(env_, log_file, &file_contents);
|
|
|
|
int lookup_num = 0;
|
|
int add_num = 0;
|
|
std::string::size_type pos;
|
|
|
|
// count number of lookups
|
|
pos = 0;
|
|
while ((pos = file_contents.find("LOOKUP -", pos)) != std::string::npos) {
|
|
++lookup_num;
|
|
pos += 1;
|
|
}
|
|
|
|
// count number of additions
|
|
pos = 0;
|
|
while ((pos = file_contents.find("ADD -", pos)) != std::string::npos) {
|
|
++add_num;
|
|
pos += 1;
|
|
}
|
|
|
|
// We asked for every block twice
|
|
ASSERT_EQ(lookup_num, num_block_entries * 2);
|
|
|
|
// We added every block only once, since the cache can hold all blocks
|
|
ASSERT_EQ(add_num, num_block_entries);
|
|
|
|
// Log things again but stop logging automatically after reaching 512 bytes
|
|
// @lint-ignore TXT2 T25377293 Grandfathered in
|
|
int max_size = 512;
|
|
ASSERT_OK(sim_cache->StartActivityLogging(log_file, env_, max_size));
|
|
for (int it = 0; it < 10; it++) {
|
|
for (int i = 0; i < num_block_entries; i++) {
|
|
ASSERT_EQ(Get(Key(i)), "val");
|
|
}
|
|
}
|
|
ASSERT_OK(sim_cache->GetActivityLoggingStatus());
|
|
|
|
uint64_t fsize = 0;
|
|
ASSERT_OK(env_->GetFileSize(log_file, &fsize));
|
|
// error margin of 100 bytes
|
|
ASSERT_LT(fsize, max_size + 100);
|
|
ASSERT_GT(fsize, max_size - 100);
|
|
}
|
|
|
|
} // namespace rocksdb
|
|
|
|
int main(int argc, char** argv) {
|
|
rocksdb::port::InstallStackTraceHandler();
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
return RUN_ALL_TESTS();
|
|
}
|