mirror of
https://github.com/facebook/rocksdb.git
synced 2024-12-01 07:15:51 +00:00
351d2fd2b6
Summary: In theory, there should be no danger in mutability, as table builders and readers work from copies of BlockBasedTableOptions. However, there is currently an unresolved read-write race that affecting SetOptions on BBTO fields. This should be generally acceptable for non-pointer options of 64 bits or less, but a fix is needed to make it mutability general here. See https://github.com/facebook/rocksdb/issues/10079 This change systematically sets all of those "simple" options (and future such options) as mutable. (Resurrecting this PR perhaps preferable to proposed https://github.com/facebook/rocksdb/issues/13063) Pull Request resolved: https://github.com/facebook/rocksdb/pull/10021 Test Plan: Some unit test updates. XXX comment added to stress test code Reviewed By: cbi42 Differential Revision: D64360967 Pulled By: pdillinger fbshipit-source-id: ff220fa778331852fe331b42b76ac4adfcd2d760
200 lines
7.2 KiB
C++
200 lines
7.2 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).
|
|
//
|
|
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
|
|
|
#include "rocksdb/cache.h"
|
|
|
|
#include "cache/lru_cache.h"
|
|
#include "rocksdb/secondary_cache.h"
|
|
#include "rocksdb/utilities/customizable_util.h"
|
|
#include "rocksdb/utilities/options_type.h"
|
|
#include "util/string_util.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
const Cache::CacheItemHelper kNoopCacheItemHelper{};
|
|
|
|
static std::unordered_map<std::string, OptionTypeInfo>
|
|
lru_cache_options_type_info = {
|
|
{"capacity",
|
|
{offsetof(struct LRUCacheOptions, capacity), OptionType::kSizeT,
|
|
OptionVerificationType::kNormal, OptionTypeFlags::kMutable}},
|
|
{"num_shard_bits",
|
|
{offsetof(struct LRUCacheOptions, num_shard_bits), OptionType::kInt,
|
|
OptionVerificationType::kNormal, OptionTypeFlags::kMutable}},
|
|
{"strict_capacity_limit",
|
|
{offsetof(struct LRUCacheOptions, strict_capacity_limit),
|
|
OptionType::kBoolean, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
{"high_pri_pool_ratio",
|
|
{offsetof(struct LRUCacheOptions, high_pri_pool_ratio),
|
|
OptionType::kDouble, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
{"low_pri_pool_ratio",
|
|
{offsetof(struct LRUCacheOptions, low_pri_pool_ratio),
|
|
OptionType::kDouble, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
};
|
|
|
|
static std::unordered_map<std::string, OptionTypeInfo>
|
|
comp_sec_cache_options_type_info = {
|
|
{"capacity",
|
|
{offsetof(struct CompressedSecondaryCacheOptions, capacity),
|
|
OptionType::kSizeT, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
{"num_shard_bits",
|
|
{offsetof(struct CompressedSecondaryCacheOptions, num_shard_bits),
|
|
OptionType::kInt, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
{"compression_type",
|
|
{offsetof(struct CompressedSecondaryCacheOptions, compression_type),
|
|
OptionType::kCompressionType, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
{"compress_format_version",
|
|
{offsetof(struct CompressedSecondaryCacheOptions,
|
|
compress_format_version),
|
|
OptionType::kUInt32T, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
{"enable_custom_split_merge",
|
|
{offsetof(struct CompressedSecondaryCacheOptions,
|
|
enable_custom_split_merge),
|
|
OptionType::kBoolean, OptionVerificationType::kNormal,
|
|
OptionTypeFlags::kMutable}},
|
|
};
|
|
|
|
namespace {
|
|
static void NoopDelete(Cache::ObjectPtr /*obj*/,
|
|
MemoryAllocator* /*allocator*/) {
|
|
assert(false);
|
|
}
|
|
|
|
static size_t SliceSize(Cache::ObjectPtr obj) {
|
|
return static_cast<Slice*>(obj)->size();
|
|
}
|
|
|
|
static Status SliceSaveTo(Cache::ObjectPtr from_obj, size_t from_offset,
|
|
size_t length, char* out) {
|
|
const Slice& slice = *static_cast<Slice*>(from_obj);
|
|
std::memcpy(out, slice.data() + from_offset, length);
|
|
return Status::OK();
|
|
}
|
|
|
|
static Status NoopCreate(const Slice& /*data*/, CompressionType /*type*/,
|
|
CacheTier /*source*/, Cache::CreateContext* /*ctx*/,
|
|
MemoryAllocator* /*allocator*/,
|
|
Cache::ObjectPtr* /*out_obj*/,
|
|
size_t* /*out_charge*/) {
|
|
assert(false);
|
|
return Status::NotSupported();
|
|
}
|
|
|
|
static Cache::CacheItemHelper kBasicCacheItemHelper(CacheEntryRole::kMisc,
|
|
&NoopDelete);
|
|
} // namespace
|
|
|
|
const Cache::CacheItemHelper kSliceCacheItemHelper{
|
|
CacheEntryRole::kMisc, &NoopDelete, &SliceSize,
|
|
&SliceSaveTo, &NoopCreate, &kBasicCacheItemHelper,
|
|
};
|
|
|
|
Status SecondaryCache::CreateFromString(
|
|
const ConfigOptions& config_options, const std::string& value,
|
|
std::shared_ptr<SecondaryCache>* result) {
|
|
if (value.find("compressed_secondary_cache://") == 0) {
|
|
std::string args = value;
|
|
args.erase(0, std::strlen("compressed_secondary_cache://"));
|
|
Status status;
|
|
std::shared_ptr<SecondaryCache> sec_cache;
|
|
|
|
CompressedSecondaryCacheOptions sec_cache_opts;
|
|
status = OptionTypeInfo::ParseStruct(config_options, "",
|
|
&comp_sec_cache_options_type_info, "",
|
|
args, &sec_cache_opts);
|
|
if (status.ok()) {
|
|
sec_cache = NewCompressedSecondaryCache(sec_cache_opts);
|
|
}
|
|
|
|
|
|
if (status.ok()) {
|
|
result->swap(sec_cache);
|
|
}
|
|
return status;
|
|
} else {
|
|
return LoadSharedObject<SecondaryCache>(config_options, value, result);
|
|
}
|
|
}
|
|
|
|
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
|
const std::string& value,
|
|
std::shared_ptr<Cache>* result) {
|
|
Status status;
|
|
std::shared_ptr<Cache> cache;
|
|
if (StartsWith(value, "null")) {
|
|
cache = nullptr;
|
|
} else if (value.find("://") == std::string::npos) {
|
|
if (value.find('=') == std::string::npos) {
|
|
cache = NewLRUCache(ParseSizeT(value));
|
|
} else {
|
|
LRUCacheOptions cache_opts;
|
|
status = OptionTypeInfo::ParseStruct(config_options, "",
|
|
&lru_cache_options_type_info, "",
|
|
value, &cache_opts);
|
|
if (status.ok()) {
|
|
cache = NewLRUCache(cache_opts);
|
|
}
|
|
}
|
|
if (status.ok()) {
|
|
result->swap(cache);
|
|
}
|
|
} else {
|
|
status = LoadSharedObject<Cache>(config_options, value, result);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
bool Cache::AsyncLookupHandle::IsReady() {
|
|
return pending_handle == nullptr || pending_handle->IsReady();
|
|
}
|
|
|
|
bool Cache::AsyncLookupHandle::IsPending() { return pending_handle != nullptr; }
|
|
|
|
Cache::Handle* Cache::AsyncLookupHandle::Result() {
|
|
assert(!IsPending());
|
|
return result_handle;
|
|
}
|
|
|
|
void Cache::StartAsyncLookup(AsyncLookupHandle& async_handle) {
|
|
async_handle.found_dummy_entry = false; // in case re-used
|
|
assert(!async_handle.IsPending());
|
|
async_handle.result_handle =
|
|
Lookup(async_handle.key, async_handle.helper, async_handle.create_context,
|
|
async_handle.priority, async_handle.stats);
|
|
}
|
|
|
|
Cache::Handle* Cache::Wait(AsyncLookupHandle& async_handle) {
|
|
WaitAll(&async_handle, 1);
|
|
return async_handle.Result();
|
|
}
|
|
|
|
void Cache::WaitAll(AsyncLookupHandle* async_handles, size_t count) {
|
|
for (size_t i = 0; i < count; ++i) {
|
|
if (async_handles[i].IsPending()) {
|
|
// If a pending handle gets here, it should be marked at "to be handled
|
|
// by a caller" by that caller erasing the pending_cache on it.
|
|
assert(async_handles[i].pending_cache == nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Cache::SetEvictionCallback(EvictionCallback&& fn) {
|
|
// Overwriting non-empty with non-empty could indicate a bug
|
|
assert(!eviction_callback_ || !fn);
|
|
eviction_callback_ = std::move(fn);
|
|
}
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|