This is an automated email from the ASF dual-hosted git repository.
granthenke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new f1a3242 [util] renaming entities in cache-related files
f1a3242 is described below
commit f1a3242e650848f9dd36d00189a475a26c4d4f10
Author: Alexey Serbin <[email protected]>
AuthorDate: Tue Mar 19 12:34:02 2019 -0700
[util] renaming entities in cache-related files
This is a preparatory changelist to introduce FIFO-based
cache in one of follow-up changelists.
Change-Id: I963ef41a8097d96fd9530107495dc22066222de4
Reviewed-on: http://gerrit.cloudera.org:8080/12796
Reviewed-by: Adar Dembo <[email protected]>
Tested-by: Kudu Jenkins
---
src/kudu/cfile/block_cache-test.cc | 2 +-
src/kudu/cfile/block_cache.cc | 10 +-
src/kudu/cfile/block_cache.h | 2 +-
src/kudu/cfile/cfile-test.cc | 90 ++++++-------
src/kudu/codegen/code_cache.cc | 2 +-
src/kudu/util/cache-bench.cc | 2 +-
src/kudu/util/cache-test.cc | 228 +++++++++++++++++++++-----------
src/kudu/util/cache.cc | 257 ++++++++++++++++++++-----------------
src/kudu/util/cache.h | 26 ++--
src/kudu/util/file_cache.cc | 2 +-
10 files changed, 368 insertions(+), 253 deletions(-)
diff --git a/src/kudu/cfile/block_cache-test.cc
b/src/kudu/cfile/block_cache-test.cc
index 5caebce..3430180 100644
--- a/src/kudu/cfile/block_cache-test.cc
+++ b/src/kudu/cfile/block_cache-test.cc
@@ -47,7 +47,7 @@ TEST(TestBlockCache, TestBasics) {
BlockCache::CacheKey key(id, 1);
std::shared_ptr<MemTracker> mem_tracker;
- if (BlockCache::GetConfiguredCacheTypeOrDie() == DRAM_CACHE) {
+ if (BlockCache::GetConfiguredCacheMemoryTypeOrDie() ==
Cache::MemoryType::DRAM) {
ASSERT_TRUE(MemTracker::FindTracker("block_cache-sharded_lru_cache",
&mem_tracker));
}
diff --git a/src/kudu/cfile/block_cache.cc b/src/kudu/cfile/block_cache.cc
index 1df5c19..ce11c2f 100644
--- a/src/kudu/cfile/block_cache.cc
+++ b/src/kudu/cfile/block_cache.cc
@@ -70,8 +70,8 @@ namespace cfile {
namespace {
Cache* CreateCache(int64_t capacity) {
- CacheType t = BlockCache::GetConfiguredCacheTypeOrDie();
- return NewLRUCache(t, capacity, "block_cache");
+ auto mem_type = BlockCache::GetConfiguredCacheMemoryTypeOrDie();
+ return NewLRUCache(mem_type, capacity, "block_cache");
}
// Validates the block cache capacity won't permit the cache to grow large
enough
@@ -106,13 +106,13 @@ bool ValidateBlockCacheCapacity() {
GROUP_FLAG_VALIDATOR(block_cache_capacity_mb, ValidateBlockCacheCapacity);
-CacheType BlockCache::GetConfiguredCacheTypeOrDie() {
+Cache::MemoryType BlockCache::GetConfiguredCacheMemoryTypeOrDie() {
ToUpperCase(FLAGS_block_cache_type, &FLAGS_block_cache_type);
if (FLAGS_block_cache_type == "NVM") {
- return NVM_CACHE;
+ return Cache::MemoryType::NVM;
}
if (FLAGS_block_cache_type == "DRAM") {
- return DRAM_CACHE;
+ return Cache::MemoryType::DRAM;
}
LOG(FATAL) << "Unknown block cache type: '" << FLAGS_block_cache_type
diff --git a/src/kudu/cfile/block_cache.h b/src/kudu/cfile/block_cache.h
index 2122522..381758b 100644
--- a/src/kudu/cfile/block_cache.h
+++ b/src/kudu/cfile/block_cache.h
@@ -50,7 +50,7 @@ class BlockCache {
public:
// Parse the gflag which configures the block cache. FATALs if the flag is
// invalid.
- static CacheType GetConfiguredCacheTypeOrDie();
+ static Cache::MemoryType GetConfiguredCacheMemoryTypeOrDie();
// BlockId refers to the unique identifier for a Kudu block, that is, for an
// entire CFile. This is different than the block cache's notion of a block,
diff --git a/src/kudu/cfile/cfile-test.cc b/src/kudu/cfile/cfile-test.cc
index 78bdefd..82535fe 100644
--- a/src/kudu/cfile/cfile-test.cc
+++ b/src/kudu/cfile/cfile-test.cc
@@ -394,10 +394,11 @@ class TestCFile : public CFileTestBase {
};
// Subclass of TestCFile which is parameterized on the block cache type.
-// Tests that use TEST_P(TestCFileBothCacheTypes, ...) will run twice --
-// once for each cache type (DRAM, NVM).
-class TestCFileBothCacheTypes : public TestCFile,
- public
::testing::WithParamInterface<CacheType> {
+// Tests that use TEST_P(TestCFileBothCacheMemoryTypes, ...) will run twice --
+// once for each cache memory type (DRAM, NVM).
+class TestCFileBothCacheMemoryTypes :
+ public TestCFile,
+ public ::testing::WithParamInterface<Cache::MemoryType> {
public:
void SetUp() OVERRIDE {
#if defined(HAVE_LIB_VMEM)
@@ -410,16 +411,17 @@ class TestCFileBothCacheTypes : public TestCFile,
}
#endif
switch (GetParam()) {
- case DRAM_CACHE:
+ case Cache::MemoryType::DRAM:
FLAGS_block_cache_type = "DRAM";
break;
#if defined(HAVE_LIB_VMEM)
- case NVM_CACHE:
+ case Cache::MemoryType::NVM:
FLAGS_block_cache_type = "NVM";
break;
#endif
default:
- LOG(FATAL) << "Unknown block cache type: '" << GetParam();
+ LOG(FATAL) << "Unknown block cache type: '"
+ << static_cast<int16_t>(GetParam());
}
CFileTestBase::SetUp();
}
@@ -430,10 +432,12 @@ class TestCFileBothCacheTypes : public TestCFile,
};
#if defined(__linux__)
-INSTANTIATE_TEST_CASE_P(CacheTypes, TestCFileBothCacheTypes,
- ::testing::Values(DRAM_CACHE, NVM_CACHE));
+INSTANTIATE_TEST_CASE_P(CacheMemoryTypes, TestCFileBothCacheMemoryTypes,
+ ::testing::Values(Cache::MemoryType::DRAM,
+ Cache::MemoryType::NVM));
#else
-INSTANTIATE_TEST_CASE_P(CacheTypes, TestCFileBothCacheTypes,
::testing::Values(DRAM_CACHE));
+INSTANTIATE_TEST_CASE_P(CacheMemoryTypes, TestCFileBothCacheMemoryTypes,
+ ::testing::Values(Cache::MemoryType::DRAM));
#endif
template<DataType type>
@@ -453,7 +457,7 @@ void CopyOne(CFileIterator *it,
// Only run the 100M entry tests in non-debug mode.
// They take way too long with debugging enabled.
-TEST_P(TestCFileBothCacheTypes, TestWrite100MFileInts) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestWrite100MFileInts) {
BlockId block_id;
LOG_TIMING(INFO, "writing 100m ints") {
LOG(INFO) << "Starting writefile";
@@ -471,7 +475,7 @@ TEST_P(TestCFileBothCacheTypes, TestWrite100MFileInts) {
}
}
-TEST_P(TestCFileBothCacheTypes, TestWrite100MFileNullableInts) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestWrite100MFileNullableInts) {
BlockId block_id;
LOG_TIMING(INFO, "writing 100m nullable ints") {
LOG(INFO) << "Starting writefile";
@@ -489,26 +493,26 @@ TEST_P(TestCFileBothCacheTypes,
TestWrite100MFileNullableInts) {
}
}
-TEST_P(TestCFileBothCacheTypes, TestWrite100MFileStringsPrefixEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestWrite100MFileStringsPrefixEncoding) {
TestWrite100MFileStrings(PREFIX_ENCODING);
}
-TEST_P(TestCFileBothCacheTypes, TestWrite100MUniqueStringsDictEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestWrite100MUniqueStringsDictEncoding) {
TestWrite100MFileStrings(DICT_ENCODING);
}
-TEST_P(TestCFileBothCacheTypes,
TestWrite100MLowCardinalityStringsDictEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes,
TestWrite100MLowCardinalityStringsDictEncoding) {
TestWriteDictEncodingLowCardinalityStrings(100 * 1e6);
}
-TEST_P(TestCFileBothCacheTypes, TestWrite100MFileStringsPlainEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestWrite100MFileStringsPlainEncoding) {
TestWrite100MFileStrings(PLAIN_ENCODING);
}
#endif
// Write and Read 1 million unique strings with dictionary encoding
-TEST_P(TestCFileBothCacheTypes, TestWrite1MUniqueFileStringsDictEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes,
TestWrite1MUniqueFileStringsDictEncoding) {
BlockId block_id;
LOG_TIMING(INFO, "writing 1M unique strings") {
LOG(INFO) << "Starting writefile";
@@ -527,42 +531,42 @@ TEST_P(TestCFileBothCacheTypes,
TestWrite1MUniqueFileStringsDictEncoding) {
}
// Write and Read 1 million strings, which contains duplicates with dictionary
encoding
-TEST_P(TestCFileBothCacheTypes, TestWrite1MLowCardinalityStringsDictEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes,
TestWrite1MLowCardinalityStringsDictEncoding) {
TestWriteDictEncodingLowCardinalityStrings(1000000);
}
-TEST_P(TestCFileBothCacheTypes, TestReadWriteUInt32) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteUInt32) {
for (auto enc : { PLAIN_ENCODING, RLE }) {
TestReadWriteFixedSizeTypes<UInt32DataGenerator<false>>(enc);
}
}
-TEST_P(TestCFileBothCacheTypes, TestReadWriteInt32) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteInt32) {
for (auto enc : { PLAIN_ENCODING, RLE }) {
TestReadWriteFixedSizeTypes<Int32DataGenerator<false>>(enc);
}
}
-TEST_P(TestCFileBothCacheTypes, TestReadWriteUInt64) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteUInt64) {
for (auto enc : { PLAIN_ENCODING, RLE, BIT_SHUFFLE }) {
TestReadWriteFixedSizeTypes<UInt64DataGenerator<false>>(enc);
}
}
-TEST_P(TestCFileBothCacheTypes, TestReadWriteInt64) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteInt64) {
for (auto enc : { PLAIN_ENCODING, RLE, BIT_SHUFFLE }) {
TestReadWriteFixedSizeTypes<Int64DataGenerator<false>>(enc);
}
}
-TEST_P(TestCFileBothCacheTypes, TestReadWriteInt128) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteInt128) {
TestReadWriteFixedSizeTypes<Int128DataGenerator<false>>(PLAIN_ENCODING);
}
-TEST_P(TestCFileBothCacheTypes, TestFixedSizeReadWritePlainEncodingFloat) {
+TEST_P(TestCFileBothCacheMemoryTypes,
TestFixedSizeReadWritePlainEncodingFloat) {
TestReadWriteFixedSizeTypes<FPDataGenerator<FLOAT, false>>(PLAIN_ENCODING);
}
-TEST_P(TestCFileBothCacheTypes, TestFixedSizeReadWritePlainEncodingDouble) {
+TEST_P(TestCFileBothCacheMemoryTypes,
TestFixedSizeReadWritePlainEncodingDouble) {
TestReadWriteFixedSizeTypes<FPDataGenerator<DOUBLE, false>>(PLAIN_ENCODING);
}
@@ -717,12 +721,12 @@ void TestCFile::TestReadWriteStrings(EncodingType
encoding,
}
-TEST_P(TestCFileBothCacheTypes, TestReadWriteStringsPrefixEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteStringsPrefixEncoding) {
TestReadWriteStrings(PREFIX_ENCODING);
}
// Read/Write test for dictionary encoded blocks
-TEST_P(TestCFileBothCacheTypes, TestReadWriteStringsDictEncoding) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteStringsDictEncoding) {
TestReadWriteStrings(DICT_ENCODING);
}
@@ -732,7 +736,7 @@ TEST_P(TestCFileBothCacheTypes,
TestReadWriteStringsDictEncoding) {
// This test is disabled in TSAN because it's single-threaded anyway
// and runs extremely slowly with TSAN enabled.
#ifndef THREAD_SANITIZER
-TEST_P(TestCFileBothCacheTypes, TestReadWriteLargeStrings) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReadWriteLargeStrings) {
// Pad the values out to a length of ~65KB.
// We use this method instead of just a longer sprintf format since
// this is much more CPU-efficient (speeds up the test).
@@ -750,7 +754,7 @@ TEST_P(TestCFileBothCacheTypes, TestReadWriteLargeStrings) {
#endif
// Test that metadata entries stored in the cfile are persisted.
-TEST_P(TestCFileBothCacheTypes, TestMetadata) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestMetadata) {
BlockId block_id;
// Write the file.
@@ -791,7 +795,7 @@ TEST_P(TestCFileBothCacheTypes, TestMetadata) {
}
}
-TEST_P(TestCFileBothCacheTypes, TestDefaultColumnIter) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestDefaultColumnIter) {
const int kNumItems = 64;
uint8_t null_bitmap[BitmapSize(kNumItems)];
uint32_t data[kNumItems];
@@ -840,14 +844,14 @@ TEST_P(TestCFileBothCacheTypes, TestDefaultColumnIter) {
}
}
-TEST_P(TestCFileBothCacheTypes, TestAppendRaw) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestAppendRaw) {
TestReadWriteRawBlocks(NO_COMPRESSION, 1000);
TestReadWriteRawBlocks(SNAPPY, 1000);
TestReadWriteRawBlocks(LZ4, 1000);
TestReadWriteRawBlocks(ZLIB, 1000);
}
-TEST_P(TestCFileBothCacheTypes, TestChecksumFlags) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestChecksumFlags) {
for (bool write_checksums : {false, true}) {
for (bool verify_checksums : {false, true}) {
FLAGS_cfile_write_checksums = write_checksums;
@@ -858,7 +862,7 @@ TEST_P(TestCFileBothCacheTypes, TestChecksumFlags) {
}
}
-TEST_P(TestCFileBothCacheTypes, TestDataCorruption) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestDataCorruption) {
FLAGS_cfile_write_checksums = true;
FLAGS_cfile_verify_checksums = true;
@@ -895,7 +899,7 @@ TEST_P(TestCFileBothCacheTypes, TestDataCorruption) {
}
}
-TEST_P(TestCFileBothCacheTypes, TestNullInts) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestNullInts) {
UInt32DataGenerator<true> generator;
TestNullTypes(&generator, PLAIN_ENCODING, NO_COMPRESSION);
TestNullTypes(&generator, PLAIN_ENCODING, LZ4);
@@ -905,32 +909,32 @@ TEST_P(TestCFileBothCacheTypes, TestNullInts) {
TestNullTypes(&generator, RLE, LZ4);
}
-TEST_P(TestCFileBothCacheTypes, TestNullFloats) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestNullFloats) {
FPDataGenerator<FLOAT, true> generator;
TestNullTypes(&generator, PLAIN_ENCODING, NO_COMPRESSION);
TestNullTypes(&generator, BIT_SHUFFLE, NO_COMPRESSION);
}
-TEST_P(TestCFileBothCacheTypes, TestNullPrefixStrings) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestNullPrefixStrings) {
StringDataGenerator<true> generator("hello %zu");
TestNullTypes(&generator, PLAIN_ENCODING, NO_COMPRESSION);
TestNullTypes(&generator, PLAIN_ENCODING, LZ4);
}
-TEST_P(TestCFileBothCacheTypes, TestNullPlainStrings) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestNullPlainStrings) {
StringDataGenerator<true> generator("hello %zu");
TestNullTypes(&generator, PREFIX_ENCODING, NO_COMPRESSION);
TestNullTypes(&generator, PREFIX_ENCODING, LZ4);
}
// Test for dictionary encoding
-TEST_P(TestCFileBothCacheTypes, TestNullDictStrings) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestNullDictStrings) {
StringDataGenerator<true> generator("hello %zu");
TestNullTypes(&generator, DICT_ENCODING, NO_COMPRESSION);
TestNullTypes(&generator, DICT_ENCODING, LZ4);
}
-TEST_P(TestCFileBothCacheTypes, TestReleaseBlock) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestReleaseBlock) {
unique_ptr<WritableBlock> sink;
ASSERT_OK(fs_manager_->CreateNewBlock({}, &sink));
ASSERT_EQ(WritableBlock::CLEAN, sink->state());
@@ -943,7 +947,7 @@ TEST_P(TestCFileBothCacheTypes, TestReleaseBlock) {
ASSERT_OK(transaction->CommitCreatedBlocks());
}
-TEST_P(TestCFileBothCacheTypes, TestLazyInit) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestLazyInit) {
// Create a small test file.
BlockId block_id;
{
@@ -994,7 +998,7 @@ TEST_P(TestCFileBothCacheTypes, TestLazyInit) {
// Tests that the block cache keys used by CFileReaders are stable. That is,
// different reader instances operating on the same block should use the same
// block cache keys.
-TEST_P(TestCFileBothCacheTypes, TestCacheKeysAreStable) {
+TEST_P(TestCFileBothCacheMemoryTypes, TestCacheKeysAreStable) {
// Set up block cache instrumentation.
MetricRegistry registry;
scoped_refptr<MetricEntity>
entity(METRIC_ENTITY_server.Instantiate(®istry, "test_entity"));
@@ -1036,8 +1040,8 @@ TEST_P(TestCFileBothCacheTypes, TestCacheKeysAreStable) {
#if defined(HAVE_LIB_VMEM)
// Inject failures in nvm allocation and ensure that we can still read a file.
-TEST_P(TestCFileBothCacheTypes, TestNvmAllocationFailure) {
- if (GetParam() != NVM_CACHE) return;
+TEST_P(TestCFileBothCacheMemoryTypes, TestNvmAllocationFailure) {
+ if (GetParam() != Cache::MemoryType::NVM) return;
FLAGS_nvm_cache_simulate_allocation_failure = true;
TestReadWriteFixedSizeTypes<UInt32DataGenerator<false> >(PLAIN_ENCODING);
}
diff --git a/src/kudu/codegen/code_cache.cc b/src/kudu/codegen/code_cache.cc
index bd3b17d..8dafebe 100644
--- a/src/kudu/codegen/code_cache.cc
+++ b/src/kudu/codegen/code_cache.cc
@@ -60,7 +60,7 @@ class CodeCache::EvictionCallback : public
Cache::EvictionCallback {
};
CodeCache::CodeCache(size_t capacity)
- : cache_(NewLRUCache(DRAM_CACHE, capacity, "code_cache")) {
+ : cache_(NewLRUCache(Cache::MemoryType::DRAM, capacity, "code_cache")) {
eviction_callback_.reset(new EvictionCallback());
}
diff --git a/src/kudu/util/cache-bench.cc b/src/kudu/util/cache-bench.cc
index 1e705be..a73800d 100644
--- a/src/kudu/util/cache-bench.cc
+++ b/src/kudu/util/cache-bench.cc
@@ -97,7 +97,7 @@ class CacheBench : public KuduTest,
void SetUp() override {
KuduTest::SetUp();
- cache_.reset(NewLRUCache(DRAM_CACHE, kCacheCapacity, "test-cache"));
+ cache_.reset(NewLRUCache(Cache::MemoryType::DRAM, kCacheCapacity,
"test-cache"));
}
// Run queries against the cache until '*done' becomes true.
diff --git a/src/kudu/util/cache-test.cc b/src/kudu/util/cache-test.cc
index 38d4f03..c9d40e4 100644
--- a/src/kudu/util/cache-test.cc
+++ b/src/kudu/util/cache-test.cc
@@ -4,7 +4,6 @@
#include "kudu/util/cache.h"
-#include <cassert>
#include <cstring>
#include <memory>
#include <string>
@@ -16,8 +15,6 @@
#include <glog/logging.h>
#include <gtest/gtest.h>
-#include "kudu/gutil/gscoped_ptr.h"
-#include "kudu/gutil/port.h"
#include "kudu/gutil/ref_counted.h"
#include "kudu/util/block_cache_metrics.h"
#include "kudu/util/cache_metrics.h"
@@ -30,12 +27,18 @@
#include "kudu/util/test_macros.h"
#include "kudu/util/test_util.h"
-#if defined(__linux__)
+DECLARE_bool(cache_force_single_shard);
+#if defined(HAVE_LIB_VMEM)
DECLARE_string(nvm_cache_path);
-#endif // defined(__linux__)
+#endif // #if defined(HAVE_LIB_VMEM)
DECLARE_double(cache_memtracker_approximation_ratio);
+using std::tuple;
+using std::shared_ptr;
+using std::unique_ptr;
+using std::vector;
+
namespace kudu {
// Conversions between numeric keys/values and the types expected by Cache.
@@ -45,84 +48,129 @@ static std::string EncodeInt(int k) {
return result.ToString();
}
static int DecodeInt(const Slice& k) {
- assert(k.size() == 4);
+ CHECK_EQ(4, k.size());
return DecodeFixed32(k.data());
}
-class CacheTest : public KuduTest,
- public ::testing::WithParamInterface<CacheType>,
- public Cache::EvictionCallback {
+// Cache composition type: some test scenarios assume cache is single-sharded
+// to keep the logic simpler.
+enum class CacheComposition {
+ MultiShard,
+ SingleShard,
+};
+
+class CacheBaseTest : public KuduTest,
+ public Cache::EvictionCallback {
public:
+ explicit CacheBaseTest(size_t cache_size)
+ : cache_size_(cache_size) {
+ }
+
+ size_t cache_size() const {
+ return cache_size_;
+ }
- // Implementation of the EvictionCallback interface
+ // Implementation of the EvictionCallback interface.
void EvictedEntry(Slice key, Slice val) override {
evicted_keys_.push_back(DecodeInt(key));
evicted_values_.push_back(DecodeInt(val));
}
- std::vector<int> evicted_keys_;
- std::vector<int> evicted_values_;
- std::shared_ptr<MemTracker> mem_tracker_;
- gscoped_ptr<Cache> cache_;
- MetricRegistry metric_registry_;
- static const int kCacheSize = 14*1024*1024;
+ int Lookup(int key) {
+ Cache::Handle* handle = cache_->Lookup(EncodeInt(key),
Cache::EXPECT_IN_CACHE);
+ const int r = (handle == nullptr) ? -1 : DecodeInt(cache_->Value(handle));
+ if (handle != nullptr) {
+ cache_->Release(handle);
+ }
+ return r;
+ }
- virtual void SetUp() OVERRIDE {
+ void Insert(int key, int value, int charge = 1) {
+ std::string key_str = EncodeInt(key);
+ std::string val_str = EncodeInt(value);
+ Cache::PendingHandle* handle = CHECK_NOTNULL(
+ cache_->Allocate(key_str, val_str.size(), charge));
+ memcpy(cache_->MutableValue(handle), val_str.data(), val_str.size());
-#if defined(HAVE_LIB_VMEM)
- if (google::GetCommandLineFlagInfoOrDie("nvm_cache_path").is_default) {
- FLAGS_nvm_cache_path = GetTestPath("nvm-cache");
- ASSERT_OK(Env::Default()->CreateDir(FLAGS_nvm_cache_path));
- }
-#endif // defined(HAVE_LIB_VMEM)
+ cache_->Release(cache_->Insert(handle, this));
+ }
+
+ void Erase(int key) {
+ cache_->Erase(EncodeInt(key));
+ }
+ protected:
+ void SetupWithParameters(Cache::MemoryType mem_type,
+ CacheComposition cache_composition) {
// Disable approximate tracking of cache memory since we make specific
// assertions on the MemTracker in this test.
FLAGS_cache_memtracker_approximation_ratio = 0;
- cache_.reset(NewLRUCache(GetParam(), kCacheSize, "cache_test"));
+ // Using single shard makes the logic of scenarios simple for capacity-
+ // and eviction-related behavior.
+ FLAGS_cache_force_single_shard =
+ (cache_composition == CacheComposition::SingleShard);
+
+#if defined(HAVE_LIB_VMEM)
+ if (google::GetCommandLineFlagInfoOrDie("nvm_cache_path").is_default) {
+ FLAGS_nvm_cache_path = GetTestPath("nvm-cache");
+ ASSERT_OK(Env::Default()->CreateDir(FLAGS_nvm_cache_path));
+ }
+#endif // #if defined(HAVE_LIB_VMEM)
+ cache_.reset(NewLRUCache(mem_type, cache_size(), "cache_test"));
MemTracker::FindTracker("cache_test-sharded_lru_cache", &mem_tracker_);
+
// Since nvm cache does not have memtracker due to the use of
// tcmalloc for this we only check for it in the DRAM case.
- if (GetParam() == DRAM_CACHE) {
+ if (mem_type == Cache::MemoryType::DRAM) {
ASSERT_TRUE(mem_tracker_.get());
}
scoped_refptr<MetricEntity> entity = METRIC_ENTITY_server.Instantiate(
&metric_registry_, "test");
- std::unique_ptr<BlockCacheMetrics> metrics(new BlockCacheMetrics(entity));
+ unique_ptr<BlockCacheMetrics> metrics(new BlockCacheMetrics(entity));
cache_->SetMetrics(std::move(metrics));
}
- int Lookup(int key) {
- Cache::Handle* handle = cache_->Lookup(EncodeInt(key),
Cache::EXPECT_IN_CACHE);
- const int r = (handle == nullptr) ? -1 : DecodeInt(cache_->Value(handle));
- if (handle != nullptr) {
- cache_->Release(handle);
- }
- return r;
- }
-
- void Insert(int key, int value, int charge = 1) {
- std::string key_str = EncodeInt(key);
- std::string val_str = EncodeInt(value);
- Cache::PendingHandle* handle = CHECK_NOTNULL(cache_->Allocate(key_str,
val_str.size(), charge));
- memcpy(cache_->MutableValue(handle), val_str.data(), val_str.size());
+ const size_t cache_size_;
+ vector<int> evicted_keys_;
+ vector<int> evicted_values_;
+ shared_ptr<MemTracker> mem_tracker_;
+ unique_ptr<Cache> cache_;
+ MetricRegistry metric_registry_;
+};
- cache_->Release(cache_->Insert(handle, this));
+class CacheTest :
+ public CacheBaseTest,
+ public ::testing::WithParamInterface<tuple<Cache::MemoryType,
+ CacheComposition>> {
+ public:
+ CacheTest()
+ : CacheBaseTest(14 * 1024 * 1024) {
}
- void Erase(int key) {
- cache_->Erase(EncodeInt(key));
+ void SetUp() override {
+ const auto& param = GetParam();
+ SetupWithParameters(std::get<0>(param),
+ std::get<1>(param));
}
};
-#if defined(__linux__)
-INSTANTIATE_TEST_CASE_P(CacheTypes, CacheTest, ::testing::Values(DRAM_CACHE,
NVM_CACHE));
+#if defined(HAVE_LIB_VMEM)
+INSTANTIATE_TEST_CASE_P(
+ CacheTypes, CacheTest,
+ ::testing::Combine(::testing::Values(Cache::MemoryType::DRAM,
+ Cache::MemoryType::NVM),
+ ::testing::Values(CacheComposition::MultiShard,
+ CacheComposition::SingleShard)));
#else
-INSTANTIATE_TEST_CASE_P(CacheTypes, CacheTest, ::testing::Values(DRAM_CACHE));
-#endif // defined(__linux__)
+INSTANTIATE_TEST_CASE_P(
+ CacheTypes, CacheTest,
+ ::testing::Combine(::testing::Values(Cache::MemoryType::DRAM),
+ ::testing::Values(CacheComposition::MultiShard,
+ CacheComposition::SingleShard)));
+#endif // #if defined(HAVE_LIB_VMEM) ... #else ...
TEST_P(CacheTest, TrackMemory) {
if (mem_tracker_) {
@@ -201,35 +249,15 @@ TEST_P(CacheTest, EntriesArePinned) {
ASSERT_EQ(102, evicted_values_[1]);
}
-TEST_P(CacheTest, EvictionPolicy) {
- Insert(100, 101);
- Insert(200, 201);
-
- const int kNumElems = 1000;
- const int kSizePerElem = kCacheSize / kNumElems;
-
- // Loop adding and looking up new entries, but repeatedly accessing key 101.
This
- // frequently-used entry should not be evicted.
- for (int i = 0; i < kNumElems + 1000; i++) {
- Insert(1000+i, 2000+i, kSizePerElem);
- ASSERT_EQ(2000+i, Lookup(1000+i));
- ASSERT_EQ(101, Lookup(100));
- }
- ASSERT_EQ(101, Lookup(100));
- // Since '200' wasn't accessed in the loop above, it should have
- // been evicted.
- ASSERT_EQ(-1, Lookup(200));
-}
-
+// Add a bunch of light and heavy entries and then count the combined
+// size of items still in the cache, which must be approximately the
+// same as the total capacity.
TEST_P(CacheTest, HeavyEntries) {
- // Add a bunch of light and heavy entries and then count the combined
- // size of items still in the cache, which must be approximately the
- // same as the total capacity.
- const int kLight = kCacheSize/1000;
- const int kHeavy = kCacheSize/100;
+ const int kLight = cache_size() / 1000;
+ const int kHeavy = cache_size() / 100;
int added = 0;
int index = 0;
- while (added < 2*kCacheSize) {
+ while (added < 2 * cache_size()) {
const int weight = (index & 1) ? kLight : kHeavy;
Insert(index, 1000+index, weight);
added += weight;
@@ -245,7 +273,59 @@ TEST_P(CacheTest, HeavyEntries) {
ASSERT_EQ(1000+i, r);
}
}
- ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10);
+ ASSERT_LE(cached_weight, cache_size() + cache_size() / 10);
+}
+
+// This class is dedicated for scenarios specific for LRUCache.
+class LRUCacheTest :
+ public CacheBaseTest,
+ public ::testing::WithParamInterface<tuple<Cache::MemoryType,
+ CacheComposition>> {
+ public:
+ LRUCacheTest()
+ : CacheBaseTest(14 * 1024 * 1024) {
+ }
+
+ void SetUp() override {
+ const auto& param = GetParam();
+ SetupWithParameters(std::get<0>(param),
+ std::get<1>(param));
+ }
+};
+
+#if defined(HAVE_LIB_VMEM)
+INSTANTIATE_TEST_CASE_P(
+ CacheTypes, LRUCacheTest,
+ ::testing::Combine(::testing::Values(Cache::MemoryType::DRAM,
+ Cache::MemoryType::NVM),
+ ::testing::Values(CacheComposition::MultiShard,
+ CacheComposition::SingleShard)));
+#else
+INSTANTIATE_TEST_CASE_P(
+ CacheTypes, LRUCacheTest,
+ ::testing::Combine(::testing::Values(Cache::MemoryType::DRAM),
+ ::testing::Values(CacheComposition::MultiShard,
+ CacheComposition::SingleShard)));
+#endif // #if defined(HAVE_LIB_VMEM) ... #else ...
+
+TEST_P(LRUCacheTest, EvictionPolicy) {
+ static constexpr int kNumElems = 1000;
+ const int size_per_elem = cache_size() / kNumElems;
+
+ Insert(100, 101);
+ Insert(200, 201);
+
+ // Loop adding and looking up new entries, but repeatedly accessing key 101.
+ // This frequently-used entry should not be evicted.
+ for (int i = 0; i < kNumElems + 1000; i++) {
+ Insert(1000+i, 2000+i, size_per_elem);
+ ASSERT_EQ(2000+i, Lookup(1000+i));
+ ASSERT_EQ(101, Lookup(100));
+ }
+ ASSERT_EQ(101, Lookup(100));
+ // Since '200' wasn't accessed in the loop above, it should have
+ // been evicted.
+ ASSERT_EQ(-1, Lookup(200));
}
} // namespace kudu
diff --git a/src/kudu/util/cache.cc b/src/kudu/util/cache.cc
index 5c10702..ef8a9d7 100644
--- a/src/kudu/util/cache.cc
+++ b/src/kudu/util/cache.cc
@@ -18,7 +18,6 @@
#include <glog/logging.h>
#include "kudu/gutil/bits.h"
-#include "kudu/gutil/gscoped_ptr.h"
#include "kudu/gutil/hash/city.h"
#include "kudu/gutil/macros.h"
#include "kudu/gutil/port.h"
@@ -65,15 +64,16 @@ namespace {
typedef simple_spinlock MutexType;
-// LRU cache implementation
+// Recency list cache implementation (LRU, etc.)
-// An entry is a variable length heap-allocated structure. Entries
-// are kept in a circular doubly linked list ordered by access time.
-struct LRUHandle {
+// Recency list handle. An entry is a variable length heap-allocated structure.
+// Entries are kept in a circular doubly linked list ordered by some recency
+// criterion (e.g., access time for LRU policy).
+struct RLHandle {
Cache::EvictionCallback* eviction_callback;
- LRUHandle* next_hash;
- LRUHandle* next;
- LRUHandle* prev;
+ RLHandle* next_hash;
+ RLHandle* next;
+ RLHandle* prev;
size_t charge; // TODO(opt): Only allow uint32_t?
uint32_t key_length;
uint32_t val_length;
@@ -94,7 +94,7 @@ struct LRUHandle {
}
const uint8_t* val_ptr() const {
- return const_cast<LRUHandle*>(this)->mutable_val_ptr();
+ return const_cast<RLHandle*>(this)->mutable_val_ptr();
}
Slice value() const {
@@ -112,13 +112,13 @@ class HandleTable {
HandleTable() : length_(0), elems_(0), list_(nullptr) { Resize(); }
~HandleTable() { delete[] list_; }
- LRUHandle* Lookup(const Slice& key, uint32_t hash) {
+ RLHandle* Lookup(const Slice& key, uint32_t hash) {
return *FindPointer(key, hash);
}
- LRUHandle* Insert(LRUHandle* h) {
- LRUHandle** ptr = FindPointer(h->key(), h->hash);
- LRUHandle* old = *ptr;
+ RLHandle* Insert(RLHandle* h) {
+ RLHandle** ptr = FindPointer(h->key(), h->hash);
+ RLHandle* old = *ptr;
h->next_hash = (old == nullptr ? nullptr : old->next_hash);
*ptr = h;
if (old == nullptr) {
@@ -132,9 +132,9 @@ class HandleTable {
return old;
}
- LRUHandle* Remove(const Slice& key, uint32_t hash) {
- LRUHandle** ptr = FindPointer(key, hash);
- LRUHandle* result = *ptr;
+ RLHandle* Remove(const Slice& key, uint32_t hash) {
+ RLHandle** ptr = FindPointer(key, hash);
+ RLHandle* result = *ptr;
if (result != nullptr) {
*ptr = result->next_hash;
--elems_;
@@ -147,13 +147,13 @@ class HandleTable {
// a linked list of cache entries that hash into the bucket.
uint32_t length_;
uint32_t elems_;
- LRUHandle** list_;
+ RLHandle** list_;
// Return a pointer to slot that points to a cache entry that
// matches key/hash. If there is no such cache entry, return a
// pointer to the trailing slot in the corresponding linked list.
- LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
- LRUHandle** ptr = &list_[hash & (length_ - 1)];
+ RLHandle** FindPointer(const Slice& key, uint32_t hash) {
+ RLHandle** ptr = &list_[hash & (length_ - 1)];
while (*ptr != nullptr &&
((*ptr)->hash != hash || key != (*ptr)->key())) {
ptr = &(*ptr)->next_hash;
@@ -166,15 +166,15 @@ class HandleTable {
while (new_length < elems_ * 1.5) {
new_length *= 2;
}
- auto new_list = new LRUHandle*[new_length];
+ auto new_list = new RLHandle*[new_length];
memset(new_list, 0, sizeof(new_list[0]) * new_length);
uint32_t count = 0;
for (uint32_t i = 0; i < length_; i++) {
- LRUHandle* h = list_[i];
+ RLHandle* h = list_[i];
while (h != nullptr) {
- LRUHandle* next = h->next_hash;
+ RLHandle* next = h->next_hash;
uint32_t hash = h->hash;
- LRUHandle** ptr = &new_list[hash & (new_length - 1)];
+ RLHandle** ptr = &new_list[hash & (new_length - 1)];
h->next_hash = *ptr;
*ptr = h;
h = next;
@@ -189,12 +189,12 @@ class HandleTable {
};
// A single shard of sharded cache.
-class LRUCache {
+class CacheShard {
public:
- explicit LRUCache(MemTracker* tracker);
- ~LRUCache();
+ explicit CacheShard(MemTracker* tracker);
+ ~CacheShard();
- // Separate from constructor so caller can easily make an array of LRUCache
+ // Separate from constructor so caller can easily make an array of CacheShard
void SetCapacity(size_t capacity) {
capacity_ = capacity;
max_deferred_consumption_ = capacity *
FLAGS_cache_memtracker_approximation_ratio;
@@ -202,20 +202,21 @@ class LRUCache {
void SetMetrics(CacheMetrics* metrics) { metrics_ = metrics; }
- Cache::Handle* Insert(LRUHandle* handle, Cache::EvictionCallback*
eviction_callback);
+ Cache::Handle* Insert(RLHandle* handle, Cache::EvictionCallback*
eviction_callback);
// Like Cache::Lookup, but with an extra "hash" parameter.
Cache::Handle* Lookup(const Slice& key, uint32_t hash, bool caching);
void Release(Cache::Handle* handle);
void Erase(const Slice& key, uint32_t hash);
private:
- void LRU_Remove(LRUHandle* e);
- void LRU_Append(LRUHandle* e);
+ void RL_Remove(RLHandle* e);
+ void RL_Append(RLHandle* e);
// Just reduce the reference count by 1.
// Return true if last reference
- bool Unref(LRUHandle* e);
+ bool Unref(RLHandle* e);
// Call the user's eviction callback, if it exists, and free the entry.
- void FreeEntry(LRUHandle* e);
+ void FreeEntry(RLHandle* e);
+
// Update the memtracker's consumption by the given amount.
//
@@ -237,6 +238,9 @@ class LRUCache {
// Positive delta indicates an increased memory consumption.
void UpdateMemTracker(int64_t delta);
+ // Update the metrics for a lookup operation in the cache.
+ void UpdateMetricsLookup(bool was_hit, bool caching);
+
// Initialized before use.
size_t capacity_;
@@ -244,9 +248,9 @@ class LRUCache {
MutexType mutex_;
size_t usage_;
- // Dummy head of LRU list.
- // lru.prev is newest entry, lru.next is oldest entry.
- LRUHandle lru_;
+ // Dummy head of recency list.
+ // rl.prev is newest entry, rl.next is oldest entry.
+ RLHandle rl_;
HandleTable table_;
@@ -260,18 +264,18 @@ class LRUCache {
CacheMetrics* metrics_;
};
-LRUCache::LRUCache(MemTracker* tracker)
- : usage_(0),
- mem_tracker_(tracker),
- metrics_(nullptr) {
- // Make empty circular linked list
- lru_.next = &lru_;
- lru_.prev = &lru_;
+CacheShard::CacheShard(MemTracker* tracker)
+ : usage_(0),
+ mem_tracker_(tracker),
+ metrics_(nullptr) {
+ // Make empty circular linked list.
+ rl_.next = &rl_;
+ rl_.prev = &rl_;
}
-LRUCache::~LRUCache() {
- for (LRUHandle* e = lru_.next; e != &lru_; ) {
- LRUHandle* next = e->next;
+CacheShard::~CacheShard() {
+ for (RLHandle* e = rl_.next; e != &rl_; ) {
+ RLHandle* next = e->next;
DCHECK_EQ(e->refs.load(std::memory_order_relaxed), 1)
<< "caller has an unreleased handle";
if (Unref(e)) {
@@ -282,12 +286,12 @@ LRUCache::~LRUCache() {
mem_tracker_->Consume(deferred_consumption_);
}
-bool LRUCache::Unref(LRUHandle* e) {
+bool CacheShard::Unref(RLHandle* e) {
DCHECK_GT(e->refs.load(std::memory_order_relaxed), 0);
return e->refs.fetch_sub(1) == 1;
}
-void LRUCache::FreeEntry(LRUHandle* e) {
+void CacheShard::FreeEntry(RLHandle* e) {
DCHECK_EQ(e->refs.load(std::memory_order_relaxed), 0);
if (e->eviction_callback) {
e->eviction_callback->EvictedEntry(e->key(), e->value());
@@ -300,7 +304,7 @@ void LRUCache::FreeEntry(LRUHandle* e) {
delete [] e;
}
-void LRUCache::UpdateMemTracker(int64_t delta) {
+void CacheShard::UpdateMemTracker(int64_t delta) {
int64_t old_deferred = deferred_consumption_.fetch_add(delta);
int64_t new_deferred = old_deferred + delta;
@@ -311,93 +315,100 @@ void LRUCache::UpdateMemTracker(int64_t delta) {
}
}
-void LRUCache::LRU_Remove(LRUHandle* e) {
+void CacheShard::UpdateMetricsLookup(bool was_hit, bool caching) {
+ if (PREDICT_TRUE(metrics_)) {
+ metrics_->lookups->Increment();
+ if (was_hit) {
+ if (caching) {
+ metrics_->cache_hits_caching->Increment();
+ } else {
+ metrics_->cache_hits->Increment();
+ }
+ } else {
+ if (caching) {
+ metrics_->cache_misses_caching->Increment();
+ } else {
+ metrics_->cache_misses->Increment();
+ }
+ }
+ }
+}
+
+void CacheShard::RL_Remove(RLHandle* e) {
e->next->prev = e->prev;
e->prev->next = e->next;
usage_ -= e->charge;
}
-void LRUCache::LRU_Append(LRUHandle* e) {
- // Make "e" newest entry by inserting just before lru_
- e->next = &lru_;
- e->prev = lru_.prev;
+void CacheShard::RL_Append(RLHandle* e) {
+ // Make "e" newest entry by inserting just before rl_.
+ e->next = &rl_;
+ e->prev = rl_.prev;
e->prev->next = e;
e->next->prev = e;
usage_ += e->charge;
}
-Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash, bool caching)
{
- LRUHandle* e;
+Cache::Handle* CacheShard::Lookup(const Slice& key,
+ uint32_t hash,
+ bool caching) {
+ RLHandle* e;
{
std::lock_guard<MutexType> l(mutex_);
e = table_.Lookup(key, hash);
if (e != nullptr) {
e->refs.fetch_add(1, std::memory_order_relaxed);
- LRU_Remove(e);
- LRU_Append(e);
+ RL_Remove(e);
+ RL_Append(e);
}
}
// Do the metrics outside of the lock.
- if (metrics_) {
- metrics_->lookups->Increment();
- bool was_hit = (e != nullptr);
- if (was_hit) {
- if (caching) {
- metrics_->cache_hits_caching->Increment();
- } else {
- metrics_->cache_hits->Increment();
- }
- } else {
- if (caching) {
- metrics_->cache_misses_caching->Increment();
- } else {
- metrics_->cache_misses->Increment();
- }
- }
- }
+ UpdateMetricsLookup(e != nullptr, caching);
return reinterpret_cast<Cache::Handle*>(e);
}
-void LRUCache::Release(Cache::Handle* handle) {
- LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
+void CacheShard::Release(Cache::Handle* handle) {
+ RLHandle* e = reinterpret_cast<RLHandle*>(handle);
bool last_reference = Unref(e);
if (last_reference) {
FreeEntry(e);
}
}
-Cache::Handle* LRUCache::Insert(LRUHandle* e, Cache::EvictionCallback
*eviction_callback) {
-
- // Set the remaining LRUHandle members which were not already allocated
during
+Cache::Handle* CacheShard::Insert(
+ RLHandle* handle,
+ Cache::EvictionCallback* eviction_callback) {
+ // Set the remaining RLHandle members which were not already allocated during
// Allocate().
- e->eviction_callback = eviction_callback;
- e->refs.store(2, std::memory_order_relaxed); // One from LRUCache, one for
the returned handle
- UpdateMemTracker(e->charge);
+ handle->eviction_callback = eviction_callback;
+ // Two refs for the handle: one from CacheShard, one for the returned handle.
+ handle->refs.store(2, std::memory_order_relaxed);
+ UpdateMemTracker(handle->charge);
if (PREDICT_TRUE(metrics_)) {
- metrics_->cache_usage->IncrementBy(e->charge);
+ metrics_->cache_usage->IncrementBy(handle->charge);
metrics_->inserts->Increment();
}
- LRUHandle* to_remove_head = nullptr;
+ RLHandle* to_remove_head = nullptr;
{
std::lock_guard<MutexType> l(mutex_);
- LRU_Append(e);
+ RL_Append(handle);
- LRUHandle* old = table_.Insert(e);
+ RLHandle* old = table_.Insert(handle);
if (old != nullptr) {
- LRU_Remove(old);
+ RL_Remove(old);
if (Unref(old)) {
old->next = to_remove_head;
to_remove_head = old;
}
}
- while (usage_ > capacity_ && lru_.next != &lru_) {
- LRUHandle* old = lru_.next;
- LRU_Remove(old);
+ while (usage_ > capacity_ && rl_.next != &rl_) {
+ RLHandle* old = rl_.next;
+ RL_Remove(old);
table_.Remove(old->key(), old->hash);
if (Unref(old)) {
old->next = to_remove_head;
@@ -409,22 +420,22 @@ Cache::Handle* LRUCache::Insert(LRUHandle* e,
Cache::EvictionCallback *eviction_
// we free the entries here outside of mutex for
// performance reasons
while (to_remove_head != nullptr) {
- LRUHandle* next = to_remove_head->next;
+ RLHandle* next = to_remove_head->next;
FreeEntry(to_remove_head);
to_remove_head = next;
}
- return reinterpret_cast<Cache::Handle*>(e);
+ return reinterpret_cast<Cache::Handle*>(handle);
}
-void LRUCache::Erase(const Slice& key, uint32_t hash) {
- LRUHandle* e;
+void CacheShard::Erase(const Slice& key, uint32_t hash) {
+ RLHandle* e;
bool last_reference = false;
{
std::lock_guard<MutexType> l(mutex_);
e = table_.Remove(key, hash);
if (e != nullptr) {
- LRU_Remove(e);
+ RL_Remove(e);
last_reference = Unref(e);
}
}
@@ -440,15 +451,15 @@ void LRUCache::Erase(const Slice& key, uint32_t hash) {
int DetermineShardBits() {
int bits = PREDICT_FALSE(FLAGS_cache_force_single_shard) ?
0 : Bits::Log2Ceiling(base::NumCPUs());
- VLOG(1) << "Will use " << (1 << bits) << " shards for LRU cache.";
+ VLOG(1) << "Will use " << (1 << bits) << " shards for recency list cache.";
return bits;
}
-class ShardedLRUCache : public Cache {
+class ShardedCache : public Cache {
private:
shared_ptr<MemTracker> mem_tracker_;
unique_ptr<CacheMetrics> metrics_;
- vector<LRUCache*> shards_;
+ vector<CacheShard*> shards_;
// Number of bits of hash used to determine the shard.
const int shard_bits_;
@@ -469,8 +480,8 @@ class ShardedLRUCache : public Cache {
}
public:
- explicit ShardedLRUCache(size_t capacity, const string& id)
- : shard_bits_(DetermineShardBits()) {
+ explicit ShardedCache(size_t capacity, const string& id)
+ : shard_bits_(DetermineShardBits()) {
// A cache is often a singleton, so:
// 1. We reuse its MemTracker if one already exists, and
// 2. It is directly parented to the root MemTracker.
@@ -480,19 +491,19 @@ class ShardedLRUCache : public Cache {
int num_shards = 1 << shard_bits_;
const size_t per_shard = (capacity + (num_shards - 1)) / num_shards;
for (int s = 0; s < num_shards; s++) {
- gscoped_ptr<LRUCache> shard(new LRUCache(mem_tracker_.get()));
+ unique_ptr<CacheShard> shard(new CacheShard(mem_tracker_.get()));
shard->SetCapacity(per_shard);
shards_.push_back(shard.release());
}
}
- virtual ~ShardedLRUCache() {
+ virtual ~ShardedCache() {
STLDeleteElements(&shards_);
}
Handle* Insert(PendingHandle* handle,
Cache::EvictionCallback* eviction_callback) override {
- LRUHandle* h = reinterpret_cast<LRUHandle*>(DCHECK_NOTNULL(handle));
+ RLHandle* h = reinterpret_cast<RLHandle*>(DCHECK_NOTNULL(handle));
return shards_[Shard(h->hash)]->Insert(h, eviction_callback);
}
Handle* Lookup(const Slice& key, CacheBehavior caching) override {
@@ -500,7 +511,7 @@ class ShardedLRUCache : public Cache {
return shards_[Shard(hash)]->Lookup(key, hash, caching == EXPECT_IN_CACHE);
}
void Release(Handle* handle) override {
- LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
+ RLHandle* h = reinterpret_cast<RLHandle*>(handle);
shards_[Shard(h->hash)]->Release(handle);
}
void Erase(const Slice& key) override {
@@ -508,7 +519,7 @@ class ShardedLRUCache : public Cache {
shards_[Shard(hash)]->Erase(key, hash);
}
Slice Value(Handle* handle) override {
- return reinterpret_cast<LRUHandle*>(handle)->value();
+ return reinterpret_cast<RLHandle*>(handle)->value();
}
void SetMetrics(std::unique_ptr<CacheMetrics> metrics) override {
// TODO(KUDU-2165): reuse of the Cache singleton across multiple
MiniCluster servers
@@ -521,7 +532,7 @@ class ShardedLRUCache : public Cache {
return;
}
metrics_ = std::move(metrics);
- for (LRUCache* cache : shards_) {
+ for (auto* cache : shards_) {
cache->SetMetrics(metrics_.get());
}
}
@@ -531,11 +542,11 @@ class ShardedLRUCache : public Cache {
DCHECK_GE(key_len, 0);
DCHECK_GE(val_len, 0);
int key_len_padded = KUDU_ALIGN_UP(key_len, sizeof(void*));
- uint8_t* buf = new uint8_t[sizeof(LRUHandle)
+ uint8_t* buf = new uint8_t[sizeof(RLHandle)
+ key_len_padded + val_len // the kv_data VLA
data
- 1 // (the VLA has a 1-byte placeholder)
];
- LRUHandle* handle = reinterpret_cast<LRUHandle*>(buf);
+ RLHandle* handle = reinterpret_cast<RLHandle*>(buf);
handle->key_length = key_len;
handle->val_length = val_len;
handle->charge = (charge == kAutomaticCharge) ?
kudu_malloc_usable_size(buf) : charge;
@@ -551,24 +562,38 @@ class ShardedLRUCache : public Cache {
}
uint8_t* MutableValue(PendingHandle* h) override {
- return reinterpret_cast<LRUHandle*>(h)->mutable_val_ptr();
+ return reinterpret_cast<RLHandle*>(h)->mutable_val_ptr();
}
-
};
} // end anonymous namespace
-Cache* NewLRUCache(CacheType type, size_t capacity, const string& id) {
- switch (type) {
- case DRAM_CACHE:
- return new ShardedLRUCache(capacity, id);
+Cache* NewLRUCache(Cache::MemoryType mem_type, size_t capacity, const string&
id) {
+ switch (mem_type) {
+ case Cache::MemoryType::DRAM:
+ return new ShardedCache(capacity, id);
#if defined(HAVE_LIB_VMEM)
- case NVM_CACHE:
+ case Cache::MemoryType::NVM:
return NewLRUNvmCache(capacity, id);
#endif
default:
- LOG(FATAL) << "Unsupported LRU cache type: " << type;
+ LOG(FATAL) << "unsupported memory type for LRU cache: " << mem_type;
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, Cache::MemoryType mem_type) {
+ switch (mem_type) {
+ case Cache::MemoryType::DRAM:
+ os << "DRAM";
+ break;
+ case Cache::MemoryType::NVM:
+ os << "NVM";
+ break;
+ default:
+ os << "unknown (" << static_cast<int>(mem_type) << ")";
+ break;
}
+ return os;
}
} // namespace kudu
diff --git a/src/kudu/util/cache.h b/src/kudu/util/cache.h
index 4c0c7eb..83e3684 100644
--- a/src/kudu/util/cache.h
+++ b/src/kudu/util/cache.h
@@ -20,6 +20,7 @@
#include <cstddef>
#include <cstdint>
+#include <iosfwd>
#include <memory>
#include <string>
@@ -28,20 +29,17 @@
namespace kudu {
-class Cache;
struct CacheMetrics;
-enum CacheType {
- DRAM_CACHE,
- NVM_CACHE
-};
-
-// Create a new cache with a fixed size capacity. This implementation
-// of Cache uses a least-recently-used eviction policy.
-Cache* NewLRUCache(CacheType type, size_t capacity, const std::string& id);
-
class Cache {
public:
+
+ // Type of memory backing the cache's storage.
+ enum class MemoryType {
+ DRAM,
+ NVM,
+ };
+
// Callback interface which is called when an entry is evicted from the
// cache.
class EvictionCallback {
@@ -210,6 +208,14 @@ class Cache {
DISALLOW_COPY_AND_ASSIGN(Cache);
};
+// Create a new cache with a fixed size capacity. This implementation
+// of Cache uses a least-recently-used eviction policy.
+Cache* NewLRUCache(Cache::MemoryType mem_type,
+ size_t capacity,
+ const std::string& id);
+
+std::ostream& operator<<(std::ostream& os, Cache::MemoryType mem_type);
+
} // namespace kudu
#endif
diff --git a/src/kudu/util/file_cache.cc b/src/kudu/util/file_cache.cc
index d1bb92f..3ff22b0 100644
--- a/src/kudu/util/file_cache.cc
+++ b/src/kudu/util/file_cache.cc
@@ -459,7 +459,7 @@ FileCache<FileType>::FileCache(const string& cache_name,
: env_(env),
cache_name_(cache_name),
eviction_cb_(new EvictionCallback<FileType>()),
- cache_(NewLRUCache(DRAM_CACHE, max_open_files, cache_name)),
+ cache_(NewLRUCache(Cache::MemoryType::DRAM, max_open_files, cache_name)),
running_(1) {
if (entity) {
unique_ptr<FileCacheMetrics> metrics(new FileCacheMetrics(entity));