blob: 9da714ca2bee9221ac2181bf191414d2612d4e8b [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/cfile/block_cache.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <ostream>
#include <string>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "kudu/gutil/macros.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/block_cache_metrics.h"
#include "kudu/util/cache.h"
#include "kudu/util/cache_metrics.h"
#include "kudu/util/flag_tags.h"
#include "kudu/util/flag_validators.h"
#include "kudu/util/process_memory.h"
#include "kudu/util/slice.h"
#include "kudu/util/string_case.h"
DEFINE_int64(block_cache_capacity_mb, 512, "block cache capacity in MB");
TAG_FLAG(block_cache_capacity_mb, stable);
// Yes, it's strange: the default value is 'true' but that's intentional.
// The idea is to avoid the corresponding group flag validator striking
// while running anything but master and tserver. As for the master and tserver,
// those have the default value for this flag set to 'false'.
DEFINE_bool(force_block_cache_capacity, true,
"Force Kudu to accept the block cache size, even if it is unsafe.");
TAG_FLAG(force_block_cache_capacity, unsafe);
TAG_FLAG(force_block_cache_capacity, hidden);
DEFINE_string(block_cache_type, "DRAM",
"Which type of block cache to use for caching data. "
"Valid choices are 'DRAM' or 'NVM'. DRAM, the default, "
"caches data in regular memory. 'NVM' caches data "
"in a memory-mapped file using the memkind library. To use 'NVM', "
"libmemkind 1.8.0 or newer must be available on the system; "
"otherwise Kudu will crash.");
using strings::Substitute;
template <class T> class scoped_refptr;
namespace kudu {
class MetricEntity;
namespace cfile {
namespace {
Cache* CreateCache(int64_t capacity) {
const auto mem_type = BlockCache::GetConfiguredCacheMemoryTypeOrDie();
switch (mem_type) {
case Cache::MemoryType::DRAM:
return NewCache<Cache::EvictionPolicy::LRU, Cache::MemoryType::DRAM>(
capacity, "block_cache");
case Cache::MemoryType::NVM:
return NewCache<Cache::EvictionPolicy::LRU, Cache::MemoryType::NVM>(
capacity, "block_cache");
default:
LOG(FATAL) << "unsupported LRU cache memory type: " << mem_type;
return nullptr;
}
}
} // anonymous namespace
bool ValidateBlockCacheCapacity() {
if (FLAGS_force_block_cache_capacity) {
return true;
}
if (FLAGS_block_cache_type != "DRAM") {
return true;
}
int64_t capacity = FLAGS_block_cache_capacity_mb * 1024 * 1024;
int64_t mpt = process_memory::MemoryPressureThreshold();
if (capacity > mpt) {
LOG(ERROR) << Substitute("Block cache capacity exceeds the memory pressure "
"threshold ($0 bytes vs. $1 bytes). This will "
"cause instability and harmful flushing behavior. "
"Lower --block_cache_capacity_mb or raise "
"--memory_limit_hard_bytes.",
capacity, mpt);
return false;
}
if (capacity > mpt / 2) {
LOG(WARNING) << Substitute("Block cache capacity exceeds 50% of the memory "
"pressure threshold ($0 bytes vs. 50% of $1 bytes). "
"This may cause performance problems. Consider "
"lowering --block_cache_capacity_mb or raising "
"--memory_limit_hard_bytes.",
capacity, mpt);
}
return true;
}
GROUP_FLAG_VALIDATOR(block_cache_capacity_mb, ValidateBlockCacheCapacity);
Cache::MemoryType BlockCache::GetConfiguredCacheMemoryTypeOrDie() {
ToUpperCase(FLAGS_block_cache_type, &FLAGS_block_cache_type);
if (FLAGS_block_cache_type == "NVM") {
return Cache::MemoryType::NVM;
}
if (FLAGS_block_cache_type == "DRAM") {
return Cache::MemoryType::DRAM;
}
LOG(FATAL) << "Unknown block cache type: '" << FLAGS_block_cache_type
<< "' (expected 'DRAM' or 'NVM')";
__builtin_unreachable();
}
BlockCache::BlockCache()
: BlockCache(FLAGS_block_cache_capacity_mb * 1024 * 1024) {
}
BlockCache::BlockCache(size_t capacity)
: cache_(CreateCache(capacity)) {
}
BlockCache::PendingEntry BlockCache::Allocate(const CacheKey& key, size_t block_size) {
Slice key_slice(reinterpret_cast<const uint8_t*>(&key), sizeof(key));
return PendingEntry(cache_->Allocate(key_slice, block_size));
}
bool BlockCache::Lookup(const CacheKey& key, Cache::CacheBehavior behavior,
BlockCacheHandle* handle) {
auto h(cache_->Lookup(
Slice(reinterpret_cast<const uint8_t*>(&key), sizeof(key)), behavior));
if (h) {
handle->SetHandle(std::move(h));
return true;
}
return false;
}
void BlockCache::Insert(BlockCache::PendingEntry* entry, BlockCacheHandle* inserted) {
auto h(cache_->Insert(std::move(entry->handle_),
/* eviction_callback= */ nullptr));
inserted->SetHandle(std::move(h));
}
void BlockCache::StartInstrumentation(const scoped_refptr<MetricEntity>& metric_entity,
Cache::ExistingMetricsPolicy metrics_policy) {
std::unique_ptr<BlockCacheMetrics> metrics(new BlockCacheMetrics(metric_entity));
cache_->SetMetrics(std::move(metrics), metrics_policy);
}
} // namespace cfile
} // namespace kudu