mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
41
Commits
ajkr-patch-2
...
v8.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdf403f591 | ||
|
|
4b87e21f3a | ||
|
|
d47126875b | ||
|
|
b6640c3117 | ||
|
|
25e1365227 | ||
|
|
6aef1a05d6 | ||
|
|
68e4581c67 | ||
|
|
64a1f7670f | ||
|
|
88056ea6cb | ||
|
|
ab22e79824 | ||
|
|
94ec433833 | ||
|
|
9794acb597 | ||
|
|
6d5e8604f1 | ||
|
|
1969815fa8 | ||
|
|
c19672c187 | ||
|
|
42d6652ba2 | ||
|
|
a72f591825 | ||
|
|
ab2157fa6f | ||
|
|
3cacd4b4ec | ||
|
|
b7747bbc9f | ||
|
|
34bb3ddc43 | ||
|
|
ee5305fabb | ||
|
|
77b61abc7b | ||
|
|
876d281592 | ||
|
|
6650ca244e | ||
|
|
b5827c806c | ||
|
|
68fa90ca43 | ||
|
|
54d72085b5 | ||
|
|
9b66331388 | ||
|
|
92e8874654 | ||
|
|
4a51900700 | ||
|
|
27cf09172c | ||
|
|
cf756ed916 | ||
|
|
e17f31057b | ||
|
|
0cf1008fe3 | ||
|
|
390cc0b156 | ||
|
|
63da9cfa26 | ||
|
|
701a19cc83 | ||
|
|
fec5c8deb8 | ||
|
|
6781009ee8 | ||
|
|
6af16ac7c1 |
No files matched your search
+32
-15
@@ -152,15 +152,15 @@ commands:
|
||||
steps:
|
||||
- run:
|
||||
name: "Test low-variance benchmarks"
|
||||
command: ./tools/benchmark_ci.py --db_dir /tmp/rocksdb-benchmark-datadir --output_dir /tmp/benchmark-results --num_keys 10000000
|
||||
command: ./tools/benchmark_ci.py --db_dir /tmp/rocksdb-benchmark-datadir --output_dir /tmp/benchmark-results --num_keys 20000000
|
||||
environment:
|
||||
LD_LIBRARY_PATH: /usr/local/lib
|
||||
# How long to run parts of the test(s)
|
||||
DURATION_RO: 400
|
||||
DURATION_RW: 700
|
||||
DURATION_RO: 300
|
||||
DURATION_RW: 500
|
||||
# Keep threads within physical capacity of server (much lower than default)
|
||||
NUM_THREADS: 1
|
||||
MAX_BACKGROUND_JOBS: 3
|
||||
MAX_BACKGROUND_JOBS: 4
|
||||
# Don't run a couple of "optional" initial tests
|
||||
CI_TESTS_ONLY: "true"
|
||||
# Reduce configured size of levels to ensure more levels in the leveled compaction LSM tree
|
||||
@@ -170,7 +170,11 @@ commands:
|
||||
# The benchmark host has 32GB memory
|
||||
# The following values are tailored to work with that
|
||||
# Note, tests may not exercise the targeted issues if the memory is increased on new test hosts.
|
||||
|
||||
COMPRESSION_TYPE: "none"
|
||||
CACHE_INDEX_AND_FILTER_BLOCKS: 1
|
||||
MIN_LEVEL_TO_COMPRESS: 3
|
||||
CACHE_SIZE_MB: 10240
|
||||
MB_WRITE_PER_SEC: 2
|
||||
|
||||
post-benchmarks:
|
||||
steps:
|
||||
@@ -269,12 +273,12 @@ jobs:
|
||||
./sst_dump --help | grep -E -q 'Supported compression types: kNoCompression$' # Verify no compiled in compression
|
||||
- post-steps
|
||||
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
build-linux-static_lib-alt_namespace-status_checked:
|
||||
executor: linux-docker
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=shared OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j32 check
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=static OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j24 check
|
||||
- post-steps
|
||||
|
||||
build-linux-release:
|
||||
@@ -282,11 +286,21 @@ jobs:
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so # ensure shared lib built
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a # ensure static lib built
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- run: make clean
|
||||
- run: apt-get remove -y libgflags-dev
|
||||
- run: make V=1 -j32 LIB_MODE=shared release
|
||||
- run: ls librocksdb.so # ensure shared lib built
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- run: make clean
|
||||
- run: make V=1 -j32 release
|
||||
- run: ls librocksdb.a # ensure static lib built
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- post-steps
|
||||
|
||||
@@ -406,7 +420,10 @@ jobs:
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- run: make V=1 -j8 unity_test
|
||||
- run:
|
||||
name: "Unity build"
|
||||
command: make V=1 -j8 unity_test
|
||||
no_output_timeout: 20m
|
||||
- run: make V=1 -j8 -k check-headers # could be moved to a different build
|
||||
- post-steps
|
||||
|
||||
@@ -417,7 +434,7 @@ jobs:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- build-folly
|
||||
- run: USE_FOLLY=1 CC=gcc-7 CXX=g++-7 V=1 make -j32 check
|
||||
- run: USE_FOLLY=1 LIB_MODE=static CC=gcc-7 CXX=g++-7 V=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-7-with-folly-lite-no-test:
|
||||
@@ -463,7 +480,7 @@ jobs:
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench
|
||||
- run: LIB_MODE=static CC=gcc-11 CXX=g++-11 V=1 make -j32 all microbench # TODO: LIB_MODE only to work around unresolved linker failures
|
||||
- post-steps
|
||||
|
||||
build-linux-clang-13-no_test_run:
|
||||
@@ -482,7 +499,7 @@ jobs:
|
||||
- pre-steps
|
||||
- setup-folly
|
||||
- build-folly
|
||||
- run: CC=clang-13 CXX=clang++-13 USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check
|
||||
- run: CC=clang-13 CXX=clang++-13 LIB_MODE=static USE_CLANG=1 USE_FOLLY=1 COMPILE_WITH_UBSAN=1 COMPILE_WITH_ASAN=1 make -j32 check # TODO: LIB_MODE only to work around unresolved linker failures
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
@@ -499,7 +516,7 @@ jobs:
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000' blackbox_crash_test_with_atomic_flush
|
||||
- run: ulimit -S -n `ulimit -H -n` && make V=1 -j8 CRASH_TEST_EXT_ARGS='--duration=960 --max_key=2500000 --async_io=0' blackbox_crash_test_with_atomic_flush
|
||||
- post-steps
|
||||
|
||||
build-linux-crashtest-tiered-storage-bb:
|
||||
@@ -509,7 +526,7 @@ jobs:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS=--duration=10800 blackbox_crash_test_with_tiered_storage
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --async_io=0' blackbox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
@@ -520,7 +537,7 @@ jobs:
|
||||
- pre-steps
|
||||
- run:
|
||||
name: "run crashtest"
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS=--duration=10800 whitebox_crash_test_with_tiered_storage
|
||||
command: ulimit -S -n `ulimit -H -n` && make V=1 -j32 CRASH_TEST_EXT_ARGS='--duration=10800 --async_io=0' whitebox_crash_test_with_tiered_storage
|
||||
no_output_timeout: 100m
|
||||
- post-steps
|
||||
|
||||
@@ -805,7 +822,7 @@ workflows:
|
||||
- build-linux-clang10-asan
|
||||
- build-linux-clang10-ubsan
|
||||
- build-linux-clang10-mini-tsan
|
||||
- build-linux-shared_lib-alt_namespace-status_checked
|
||||
- build-linux-static_lib-alt_namespace-status_checked
|
||||
jobs-linux-no-test-run:
|
||||
jobs:
|
||||
- build-linux-release
|
||||
|
||||
@@ -879,6 +879,7 @@ set(SOURCES
|
||||
util/compression_context_cache.cc
|
||||
util/concurrent_task_limiter_impl.cc
|
||||
util/crc32c.cc
|
||||
util/data_structure.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/hash.cc
|
||||
util/murmurhash.cc
|
||||
|
||||
+18
-2
@@ -1,8 +1,8 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
|
||||
## 8.0.0 (02/19/2023)
|
||||
### Behavior changes
|
||||
* `ReadOptions::verify_checksums=false` disables checksum verification for more reads of non-`CacheEntryRole::kDataBlock` blocks.
|
||||
* In case of scan with async_io enabled, if posix doesn't support IOUring, Status::NotSupported error will be returned to the users. Initially that error was swallowed and reads were switched to synchronous reads.
|
||||
|
||||
### Bug Fixes
|
||||
* Fixed a data race on `ColumnFamilyData::flush_reason` caused by concurrent flushes.
|
||||
@@ -10,6 +10,9 @@
|
||||
* Fixed some atypical behaviors for `LockWAL()` such as allowing concurrent/recursive use and not expecting `UnlockWAL()` after non-OK result. See API comments.
|
||||
* Fixed a feature interaction bug where for blobs `GetEntity` would expose the blob reference instead of the blob value.
|
||||
* Fixed `DisableManualCompaction()` and `CompactRangeOptions::canceled` to cancel compactions even when they are waiting on conflicting compactions to finish
|
||||
* Fixed a bug in which a successful `GetMergeOperands()` could transiently return `Status::MergeInProgress()`
|
||||
* Return the correct error (Status::NotSupported()) to MultiGet caller when ReadOptions::async_io flag is true and IO uring is not enabled. Previously, Status::Corruption() was being returned when the actual failure was lack of async IO support.
|
||||
* Fixed a bug in DB open/recovery from a compressed WAL that was caused due to incorrect handling of certain record fragments with the same offset within a WAL block.
|
||||
|
||||
### Feature Removal
|
||||
* Remove RocksDB Lite.
|
||||
@@ -17,9 +20,22 @@
|
||||
* Remove deprecated Env::LoadEnv(). Use Env::CreateFromString() instead.
|
||||
* Remove deprecated FileSystem::Load(). Use FileSystem::CreateFromString() instead.
|
||||
* Removed the deprecated version of these utility functions and the corresponding Java bindings: `LoadOptionsFromFile`, `LoadLatestOptions`, `CheckOptionsCompatibility`.
|
||||
* Remove the FactoryFunc from the LoadObject method from the Customizable helper methods.
|
||||
|
||||
### Public API Changes
|
||||
* Moved rarely-needed Cache class definition to new advanced_cache.h, and added a CacheWrapper class to advanced_cache.h. Minor changes to SimCache API definitions.
|
||||
* Completely removed the following deprecated/obsolete statistics: the tickers `BLOCK_CACHE_INDEX_BYTES_EVICT`, `BLOCK_CACHE_FILTER_BYTES_EVICT`, `BLOOM_FILTER_MICROS`, `NO_FILE_CLOSES`, `STALL_L0_SLOWDOWN_MICROS`, `STALL_MEMTABLE_COMPACTION_MICROS`, `STALL_L0_NUM_FILES_MICROS`, `RATE_LIMIT_DELAY_MILLIS`, `NO_ITERATORS`, `NUMBER_FILTERED_DELETES`, `WRITE_TIMEDOUT`, `BLOB_DB_GC_NUM_KEYS_OVERWRITTEN`, `BLOB_DB_GC_NUM_KEYS_EXPIRED`, `BLOB_DB_GC_BYTES_OVERWRITTEN`, `BLOB_DB_GC_BYTES_EXPIRED`, `BLOCK_CACHE_COMPRESSION_DICT_BYTES_EVICT` as well as the histograms `STALL_L0_SLOWDOWN_COUNT`, `STALL_MEMTABLE_COMPACTION_COUNT`, `STALL_L0_NUM_FILES_COUNT`, `HARD_RATE_LIMIT_DELAY_COUNT`, `SOFT_RATE_LIMIT_DELAY_COUNT`, `BLOB_DB_GC_MICROS`, and `NUM_DATA_BLOCKS_READ_PER_LEVEL`. Note that as a result, the C++ enum values of the still supported statistics have changed. Developers are advised to not rely on the actual numeric values.
|
||||
* Deprecated IngestExternalFileOptions::write_global_seqno and change default to false. This option only needs to be set to true to generate a DB compatible with RocksDB versions before 5.16.0.
|
||||
* Remove deprecated APIs `GetColumnFamilyOptionsFrom{Map|String}(const ColumnFamilyOptions&, ..)`, `GetDBOptionsFrom{Map|String}(const DBOptions&, ..)`, `GetBlockBasedTableOptionsFrom{Map|String}(const BlockBasedTableOptions& table_options, ..)` and ` GetPlainTableOptionsFrom{Map|String}(const PlainTableOptions& table_options,..)`.
|
||||
* Added a subcode of `Status::Corruption`, `Status::SubCode::kMergeOperatorFailed`, for users to identify corruption failures originating in the merge operator, as opposed to RocksDB's internally identified data corruptions
|
||||
|
||||
### Build Changes
|
||||
* The `make` build now builds a shared library by default instead of a static library. Use `LIB_MODE=static` to override.
|
||||
|
||||
### New Features
|
||||
* Compaction filters are now supported for wide-column entities by means of the `FilterV3` API. See the comment of the API for more details.
|
||||
* Added `do_not_compress_roles` to `CompressedSecondaryCacheOptions` to disable compression on certain kinds of block. Filter blocks are now not compressed by CompressedSecondaryCache by default.
|
||||
* Added a new `MultiGetEntity` API that enables batched wide-column point lookups. See the API comments for more details.
|
||||
|
||||
## 7.10.0 (01/23/2023)
|
||||
### Behavior changes
|
||||
|
||||
@@ -44,13 +44,6 @@ quoted_perl_command = $(subst ','\'',$(perl_command))
|
||||
# Set the default DEBUG_LEVEL to 1
|
||||
DEBUG_LEVEL?=1
|
||||
|
||||
# LIB_MODE says whether or not to use/build "shared" or "static" libraries.
|
||||
# Mode "static" means to link against static libraries (.a)
|
||||
# Mode "shared" means to link against shared libraries (.so, .sl, .dylib, etc)
|
||||
#
|
||||
# Set the default LIB_MODE to static
|
||||
LIB_MODE?=static
|
||||
|
||||
# OBJ_DIR is where the object files reside. Default to the current directory
|
||||
OBJ_DIR?=.
|
||||
|
||||
@@ -81,7 +74,19 @@ else ifneq ($(filter jtest rocksdbjava%, $(MAKECMDGOALS)),)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(info $$DEBUG_LEVEL is ${DEBUG_LEVEL})
|
||||
# LIB_MODE says whether or not to use/build "shared" or "static" libraries.
|
||||
# Mode "static" means to link against static libraries (.a)
|
||||
# Mode "shared" means to link against shared libraries (.so, .sl, .dylib, etc)
|
||||
#
|
||||
ifeq ($(DEBUG_LEVEL), 0)
|
||||
# For optimized, set the default LIB_MODE to static for code size/efficiency
|
||||
LIB_MODE?=static
|
||||
else
|
||||
# For debug, set the default LIB_MODE to shared for efficient `make check` etc.
|
||||
LIB_MODE?=shared
|
||||
endif
|
||||
|
||||
$(info $$DEBUG_LEVEL is $(DEBUG_LEVEL), $$LIB_MODE is $(LIB_MODE))
|
||||
|
||||
# Figure out optimize level.
|
||||
ifneq ($(DEBUG_LEVEL), 2)
|
||||
@@ -1217,9 +1222,9 @@ clean: clean-ext-libraries-all clean-rocks clean-rocksjava
|
||||
clean-not-downloaded: clean-ext-libraries-bin clean-rocks clean-not-downloaded-rocksjava
|
||||
|
||||
clean-rocks:
|
||||
echo shared=$(ALL_SHARED_LIBS)
|
||||
echo static=$(ALL_STATIC_LIBS)
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(ALL_STATIC_LIBS) $(ALL_SHARED_LIBS) $(MICROBENCHS)
|
||||
# Not practical to exactly match all versions/variants in naming (e.g. debug or not)
|
||||
rm -f ${LIBNAME}*.so* ${LIBNAME}*.a
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(MICROBENCHS)
|
||||
rm -rf $(CLEAN_FILES) ios-x86 ios-arm scan_build_report
|
||||
$(FIND) . -name "*.[oda]" -exec rm -f {} \;
|
||||
$(FIND) . -type f \( -name "*.gcda" -o -name "*.gcno" \) -exec rm -f {} \;
|
||||
|
||||
@@ -247,6 +247,7 @@ cpp_library_wrapper(name="rocksdb_lib", srcs=[
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/crc32c_arm64.cc",
|
||||
"util/data_structure.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/file_checksum_helper.cc",
|
||||
"util/hash.cc",
|
||||
@@ -589,6 +590,7 @@ cpp_library_wrapper(name="rocksdb_whole_archive_lib", srcs=[
|
||||
"util/concurrent_task_limiter_impl.cc",
|
||||
"util/crc32c.cc",
|
||||
"util/crc32c_arm64.cc",
|
||||
"util/data_structure.cc",
|
||||
"util/dynamic_bloom.cc",
|
||||
"util/file_checksum_helper.cc",
|
||||
"util/hash.cc",
|
||||
|
||||
Vendored
+1
-2
@@ -87,8 +87,7 @@ Status SecondaryCache::CreateFromString(
|
||||
}
|
||||
return status;
|
||||
} else {
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, nullptr,
|
||||
result);
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -16,7 +16,7 @@
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "monitoring/histogram.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/math.h"
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "cache/clock_cache.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <numeric>
|
||||
|
||||
@@ -18,6 +17,7 @@
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/lang.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/math.h"
|
||||
#include "util/random.h"
|
||||
|
||||
Vendored
+11
-7
@@ -22,12 +22,13 @@ CompressedSecondaryCache::CompressedSecondaryCache(
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version,
|
||||
bool enable_custom_split_merge)
|
||||
bool enable_custom_split_merge,
|
||||
const CacheEntryRoleSet& do_not_compress_roles)
|
||||
: cache_options_(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, low_pri_pool_ratio, memory_allocator,
|
||||
use_adaptive_mutex, metadata_charge_policy,
|
||||
compression_type, compress_format_version,
|
||||
enable_custom_split_merge) {
|
||||
enable_custom_split_merge, do_not_compress_roles) {
|
||||
cache_ =
|
||||
NewLRUCache(capacity, num_shard_bits, strict_capacity_limit,
|
||||
high_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
@@ -71,7 +72,8 @@ std::unique_ptr<SecondaryCacheResultHandle> CompressedSecondaryCache::Lookup(
|
||||
Status s;
|
||||
Cache::ObjectPtr value{nullptr};
|
||||
size_t charge{0};
|
||||
if (cache_options_.compression_type == kNoCompression) {
|
||||
if (cache_options_.compression_type == kNoCompression ||
|
||||
cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
s = helper->create_cb(Slice(ptr->get(), handle_value_charge),
|
||||
create_context, allocator, &value, &charge);
|
||||
} else {
|
||||
@@ -143,7 +145,8 @@ Status CompressedSecondaryCache::Insert(const Slice& key,
|
||||
Slice val(ptr.get(), size);
|
||||
|
||||
std::string compressed_val;
|
||||
if (cache_options_.compression_type != kNoCompression) {
|
||||
if (cache_options_.compression_type != kNoCompression &&
|
||||
!cache_options_.do_not_compress_roles.Contains(helper->role)) {
|
||||
PERF_COUNTER_ADD(compressed_sec_cache_uncompressed_bytes, size);
|
||||
CompressionOptions compression_opts;
|
||||
CompressionContext compression_context(cache_options_.compression_type);
|
||||
@@ -314,12 +317,13 @@ std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator, bool use_adaptive_mutex,
|
||||
CacheMetadataChargePolicy metadata_charge_policy,
|
||||
CompressionType compression_type, uint32_t compress_format_version,
|
||||
bool enable_custom_split_merge) {
|
||||
bool enable_custom_split_merge,
|
||||
const CacheEntryRoleSet& do_not_compress_roles) {
|
||||
return std::make_shared<CompressedSecondaryCache>(
|
||||
capacity, num_shard_bits, strict_capacity_limit, high_pri_pool_ratio,
|
||||
low_pri_pool_ratio, memory_allocator, use_adaptive_mutex,
|
||||
metadata_charge_policy, compression_type, compress_format_version,
|
||||
enable_custom_split_merge);
|
||||
enable_custom_split_merge, do_not_compress_roles);
|
||||
}
|
||||
|
||||
std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
@@ -331,7 +335,7 @@ std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
opts.high_pri_pool_ratio, opts.low_pri_pool_ratio, opts.memory_allocator,
|
||||
opts.use_adaptive_mutex, opts.metadata_charge_policy,
|
||||
opts.compression_type, opts.compress_format_version,
|
||||
opts.enable_custom_split_merge);
|
||||
opts.enable_custom_split_merge, opts.do_not_compress_roles);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+3
-1
@@ -78,7 +78,9 @@ class CompressedSecondaryCache : public SecondaryCache {
|
||||
kDefaultCacheMetadataChargePolicy,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2,
|
||||
bool enable_custom_split_merge = false);
|
||||
bool enable_custom_split_merge = false,
|
||||
const CacheEntryRoleSet& do_not_compress_roles = {
|
||||
CacheEntryRole::kFilterBlock});
|
||||
~CompressedSecondaryCache() override;
|
||||
|
||||
const char* Name() const override { return "CompressedSecondaryCache"; }
|
||||
|
||||
+94
-4
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "cache/compressed_secondary_cache.h"
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
@@ -75,12 +76,23 @@ class CompressedSecondaryCacheTest : public testing::Test,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static constexpr Cache::CacheItemHelper kHelper{
|
||||
CacheEntryRole::kMisc, &DeletionCallback, &SizeCallback, &SaveToCallback,
|
||||
&CreateCallback};
|
||||
static constexpr auto GenerateHelpersByRole() {
|
||||
std::array<Cache::CacheItemHelper, kNumCacheEntryRoles> a;
|
||||
for (uint32_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
a[i] = Cache::CacheItemHelper{static_cast<CacheEntryRole>(i),
|
||||
&DeletionCallback, &SizeCallback,
|
||||
&SaveToCallback, &CreateCallback};
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
static const std::array<Cache::CacheItemHelper, kNumCacheEntryRoles>
|
||||
kHelperByRole;
|
||||
|
||||
static const Cache::CacheItemHelper& kHelper;
|
||||
|
||||
static constexpr Cache::CacheItemHelper kHelperFail{
|
||||
CacheEntryRole::kMisc, &DeletionCallback, &SizeCallback,
|
||||
CacheEntryRole::kDataBlock, &DeletionCallback, &SizeCallback,
|
||||
&SaveToCallbackFail, &CreateCallback};
|
||||
|
||||
void SetFailCreate(bool fail) { fail_create_ = fail; }
|
||||
@@ -787,6 +799,13 @@ class CompressedSecondaryCacheTest : public testing::Test,
|
||||
bool fail_create_;
|
||||
};
|
||||
|
||||
const std::array<Cache::CacheItemHelper, kNumCacheEntryRoles>
|
||||
CompressedSecondaryCacheTest::kHelperByRole = GenerateHelpersByRole();
|
||||
|
||||
const Cache::CacheItemHelper& CompressedSecondaryCacheTest::kHelper =
|
||||
CompressedSecondaryCacheTest::kHelperByRole[static_cast<int>(
|
||||
CacheEntryRole::kDataBlock)];
|
||||
|
||||
class CompressedSecCacheTestWithCompressAndAllocatorParam
|
||||
: public CompressedSecondaryCacheTest,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
|
||||
@@ -906,6 +925,77 @@ TEST_P(CompressedSecondaryCacheTestWithCompressionParam,
|
||||
IntegrationFullCapacityTest(sec_cache_is_compressed_);
|
||||
}
|
||||
|
||||
TEST_P(CompressedSecondaryCacheTestWithCompressionParam, EntryRoles) {
|
||||
CompressedSecondaryCacheOptions opts;
|
||||
opts.capacity = 2048;
|
||||
opts.num_shard_bits = 0;
|
||||
|
||||
if (sec_cache_is_compressed_) {
|
||||
if (!LZ4_Supported()) {
|
||||
ROCKSDB_GTEST_SKIP("This test requires LZ4 support.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
opts.compression_type = CompressionType::kNoCompression;
|
||||
}
|
||||
|
||||
// Select a random subset to include, for fast test
|
||||
Random& r = *Random::GetTLSInstance();
|
||||
CacheEntryRoleSet do_not_compress;
|
||||
for (uint32_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
// A few included on average, but decent chance of zero
|
||||
if (r.OneIn(5)) {
|
||||
do_not_compress.Add(static_cast<CacheEntryRole>(i));
|
||||
}
|
||||
}
|
||||
opts.do_not_compress_roles = do_not_compress;
|
||||
|
||||
std::shared_ptr<SecondaryCache> sec_cache = NewCompressedSecondaryCache(opts);
|
||||
|
||||
// Fixed seed to ensure consistent compressibility (doesn't compress)
|
||||
std::string junk(Random(301).RandomString(1000));
|
||||
|
||||
for (uint32_t i = 0; i < kNumCacheEntryRoles; ++i) {
|
||||
CacheEntryRole role = static_cast<CacheEntryRole>(i);
|
||||
|
||||
// Uniquify `junk`
|
||||
junk[0] = static_cast<char>(i);
|
||||
TestItem item{junk.data(), junk.length()};
|
||||
Slice ith_key = Slice(junk.data(), 5);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
ASSERT_OK(sec_cache->Insert(ith_key, &item, &kHelperByRole[i]));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_dummy_count, 1U);
|
||||
|
||||
ASSERT_OK(sec_cache->Insert(ith_key, &item, &kHelperByRole[i]));
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_insert_real_count, 1U);
|
||||
|
||||
bool is_in_sec_cache{true};
|
||||
std::unique_ptr<SecondaryCacheResultHandle> handle =
|
||||
sec_cache->Lookup(ith_key, &kHelperByRole[i], this, true,
|
||||
/*advise_erase=*/true, is_in_sec_cache);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
|
||||
// Lookup returns the right data
|
||||
std::unique_ptr<TestItem> val =
|
||||
std::unique_ptr<TestItem>(static_cast<TestItem*>(handle->Value()));
|
||||
ASSERT_NE(val, nullptr);
|
||||
ASSERT_EQ(memcmp(val->Buf(), item.Buf(), item.Size()), 0);
|
||||
|
||||
bool compressed =
|
||||
sec_cache_is_compressed_ && !do_not_compress.Contains(role);
|
||||
if (compressed) {
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes,
|
||||
1000);
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes,
|
||||
1007);
|
||||
} else {
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_uncompressed_bytes, 0);
|
||||
ASSERT_EQ(get_perf_context()->compressed_sec_cache_compressed_bytes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompressedSecCacheTests,
|
||||
CompressedSecondaryCacheTestWithCompressionParam,
|
||||
testing::Bool());
|
||||
|
||||
Vendored
+1
-1
@@ -15,7 +15,7 @@
|
||||
|
||||
#include "port/lang.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -29,8 +29,8 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "memory/memory_allocator.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
@@ -1782,16 +1782,49 @@ TEST_F(DBBlobBasicTest, GetEntityBlob) {
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob_value[] = "blob_value";
|
||||
|
||||
constexpr char other_key[] = "other_key";
|
||||
constexpr char other_blob_value[] = "other_blob_value";
|
||||
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
ASSERT_OK(Put(other_key, other_blob_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(
|
||||
db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(), key, &result));
|
||||
|
||||
WideColumns expected_columns{{kDefaultWideColumnName, blob_value}};
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
WideColumns other_expected_columns{
|
||||
{kDefaultWideColumnName, other_blob_value}};
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(), key,
|
||||
&result));
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
other_key, &result));
|
||||
|
||||
ASSERT_EQ(result.columns(), other_expected_columns);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr size_t num_keys = 2;
|
||||
|
||||
std::array<Slice, num_keys> keys{{key, other_key}};
|
||||
std::array<PinnableWideColumns, num_keys> results;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
&keys[0], &results[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), expected_columns);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(results[1].columns(), other_expected_columns);
|
||||
}
|
||||
}
|
||||
|
||||
class DBBlobWithTimestampTest : public DBBasicTestWithTimestampBase {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
@@ -6397,4 +6397,3 @@ void rocksdb_enable_manual_compaction(rocksdb_t* db) {
|
||||
}
|
||||
|
||||
} // end extern "C"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/prefetch_buffer_collection.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/wide/wide_column_serialization.h"
|
||||
#include "logging/logging.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
@@ -225,8 +226,8 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: support compaction filter for wide-column entities
|
||||
if (ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex) {
|
||||
if (ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex &&
|
||||
ikey_.type != kTypeWideColumnEntity) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -234,7 +235,9 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
CompactionFilter::Decision::kUndetermined;
|
||||
CompactionFilter::ValueType value_type =
|
||||
ikey_.type == kTypeValue ? CompactionFilter::ValueType::kValue
|
||||
: CompactionFilter::ValueType::kBlobIndex;
|
||||
: ikey_.type == kTypeBlobIndex
|
||||
? CompactionFilter::ValueType::kBlobIndex
|
||||
: CompactionFilter::ValueType::kWideColumnEntity;
|
||||
|
||||
// Hack: pass internal key to BlobIndexCompactionFilter since it needs
|
||||
// to get sequence number.
|
||||
@@ -248,6 +251,8 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> new_columns;
|
||||
|
||||
{
|
||||
StopWatchNano timer(clock_, report_detailed_time_);
|
||||
|
||||
@@ -303,10 +308,36 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
value_type = CompactionFilter::ValueType::kValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (decision == CompactionFilter::Decision::kUndetermined) {
|
||||
decision = compaction_filter_->FilterV2(
|
||||
level_, filter_key, value_type,
|
||||
blob_value_.empty() ? value_ : blob_value_, &compaction_filter_value_,
|
||||
const Slice* existing_val = nullptr;
|
||||
const WideColumns* existing_col = nullptr;
|
||||
|
||||
WideColumns existing_columns;
|
||||
|
||||
if (ikey_.type != kTypeWideColumnEntity) {
|
||||
if (!blob_value_.empty()) {
|
||||
existing_val = &blob_value_;
|
||||
} else {
|
||||
existing_val = &value_;
|
||||
}
|
||||
} else {
|
||||
Slice value_copy = value_;
|
||||
const Status s =
|
||||
WideColumnSerialization::Deserialize(value_copy, existing_columns);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
validity_info_.Invalidate();
|
||||
return false;
|
||||
}
|
||||
|
||||
existing_col = &existing_columns;
|
||||
}
|
||||
|
||||
decision = compaction_filter_->FilterV3(
|
||||
level_, filter_key, value_type, existing_val, existing_col,
|
||||
&compaction_filter_value_, &new_columns,
|
||||
compaction_filter_skip_until_.rep());
|
||||
}
|
||||
|
||||
@@ -315,9 +346,10 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
}
|
||||
|
||||
if (decision == CompactionFilter::Decision::kUndetermined) {
|
||||
// Should not reach here, since FilterV2 should never return kUndetermined.
|
||||
status_ =
|
||||
Status::NotSupported("FilterV2() should never return kUndetermined");
|
||||
// Should not reach here, since FilterV2/FilterV3 should never return
|
||||
// kUndetermined.
|
||||
status_ = Status::NotSupported(
|
||||
"FilterV2/FilterV3 should never return kUndetermined");
|
||||
validity_info_.Invalidate();
|
||||
return false;
|
||||
}
|
||||
@@ -326,7 +358,7 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
cmp_->Compare(*compaction_filter_skip_until_.rep(), ikey_.user_key) <=
|
||||
0) {
|
||||
// Can't skip to a key smaller than the current one.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
// Keep the key as per FilterV2/FilterV3 documentation.
|
||||
decision = CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
|
||||
@@ -388,6 +420,35 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
status_ = Status::IOError("Failed to access blob during compaction filter");
|
||||
validity_info_.Invalidate();
|
||||
return false;
|
||||
} else if (decision == CompactionFilter::Decision::kChangeWideColumnEntity) {
|
||||
WideColumns sorted_columns;
|
||||
|
||||
sorted_columns.reserve(new_columns.size());
|
||||
for (const auto& column : new_columns) {
|
||||
sorted_columns.emplace_back(column.first, column.second);
|
||||
}
|
||||
|
||||
std::sort(sorted_columns.begin(), sorted_columns.end(),
|
||||
[](const WideColumn& lhs, const WideColumn& rhs) {
|
||||
return lhs.name().compare(rhs.name()) < 0;
|
||||
});
|
||||
|
||||
{
|
||||
const Status s = WideColumnSerialization::Serialize(
|
||||
sorted_columns, compaction_filter_value_);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
validity_info_.Invalidate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ikey_.type != kTypeWideColumnEntity) {
|
||||
ikey_.type = kTypeWideColumnEntity;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeWideColumnEntity);
|
||||
}
|
||||
|
||||
value_ = compaction_filter_value_;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -78,7 +78,6 @@ Status SstPartitionerFactory::CreateFromString(
|
||||
std::call_once(once, [&]() {
|
||||
RegisterSstPartitionerFactories(*(ObjectLibrary::Default().get()), "");
|
||||
});
|
||||
return LoadSharedObject<SstPartitionerFactory>(options, value, nullptr,
|
||||
result);
|
||||
return LoadSharedObject<SstPartitionerFactory>(options, value, result);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+88
-2
@@ -30,6 +30,9 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
static bool enable_io_uring = true;
|
||||
extern "C" bool RocksDbIOUringEnable() { return enable_io_uring; }
|
||||
|
||||
class DBBasicTest : public DBTestBase {
|
||||
public:
|
||||
DBBasicTest() : DBTestBase("db_basic_test", /*env_do_fsync=*/false) {}
|
||||
@@ -2173,6 +2176,7 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
||||
options_.disable_auto_compactions = true;
|
||||
options_.statistics = statistics_;
|
||||
options_.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options_.env = Env::Default();
|
||||
Reopen(options_);
|
||||
int num_keys = 0;
|
||||
|
||||
@@ -2239,6 +2243,20 @@ class DBMultiGetAsyncIOTest : public DBBasicTest,
|
||||
const std::shared_ptr<Statistics>& statistics() { return statistics_; }
|
||||
|
||||
protected:
|
||||
void PrepareDBForTest() {
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
Reopen(options_);
|
||||
#else // ROCKSDB_IOURING_PRESENT
|
||||
// Warm up the block cache so we don't need to use the IO uring
|
||||
Iterator* iter = dbfull()->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst(); iter->Valid() && iter->status().ok();
|
||||
iter->Next())
|
||||
;
|
||||
EXPECT_OK(iter->status());
|
||||
delete iter;
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
}
|
||||
|
||||
void ReopenDB() { Reopen(options_); }
|
||||
|
||||
private:
|
||||
@@ -2253,6 +2271,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
||||
std::vector<PinnableSlice> values(key_strs.size());
|
||||
std::vector<Status> statuses(key_strs.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2271,6 +2291,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// With async IO, lookups will happen in parallel for each key
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
if (GetParam()) {
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||
@@ -2280,6 +2301,11 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL0) {
|
||||
// L0 file
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 3);
|
||||
}
|
||||
#else // ROCKSDB_IOURING_PRESENT
|
||||
if (GetParam()) {
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
@@ -2297,6 +2323,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2310,6 +2338,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
@@ -2317,9 +2346,11 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1) {
|
||||
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
@@ -2335,9 +2366,9 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
int count = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"TableCache::GetTableReader:BeforeOpenFile", [&](void* status) {
|
||||
static int count = 0;
|
||||
count++;
|
||||
// Fail the last table reader open, which is the 6th SST file
|
||||
// since 3 overlapping L0 files + 3 L1 files containing the keys
|
||||
@@ -2360,7 +2391,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ReopenDB();
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
@@ -2382,6 +2413,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1Error) {
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
std::vector<std::string> key_strs;
|
||||
@@ -2399,6 +2431,8 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2412,6 +2446,7 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
ASSERT_EQ(values[1], "val_l1_" + std::to_string(54));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
@@ -2422,6 +2457,7 @@ TEST_P(DBMultiGetAsyncIOTest, LastKeyInFile) {
|
||||
// will lookup 2 files in parallel and issue 2 async reads
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 2);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 2);
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
@@ -2440,6 +2476,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2453,6 +2491,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
ASSERT_EQ(values[1], "val_l2_" + std::to_string(56));
|
||||
ASSERT_EQ(values[2], "val_l1_" + std::to_string(102));
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
@@ -2462,6 +2501,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2) {
|
||||
// Otherwise, the L2 lookup will happen after L1.
|
||||
ASSERT_EQ(multiget_io_batch_size.count, GetParam() ? 1 : 2);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, GetParam() ? 3 : 2);
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
||||
@@ -2478,6 +2518,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2493,6 +2535,7 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeOverlapL0L1) {
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 2);
|
||||
}
|
||||
|
||||
#ifdef ROCKSDB_IOURING_PRESENT
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
@@ -2507,6 +2550,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL2WithRangeDelInL1) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2536,6 +2581,8 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
PrepareDBForTest();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
@@ -2550,6 +2597,45 @@ TEST_P(DBMultiGetAsyncIOTest, GetFromL1AndL2WithRangeDelInL1) {
|
||||
// Bloom filters in L0/L1 will avoid the coroutine calls in those levels
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||
}
|
||||
#endif // ROCKSDB_IOURING_PRESENT
|
||||
|
||||
TEST_P(DBMultiGetAsyncIOTest, GetNoIOUring) {
|
||||
std::vector<std::string> key_strs;
|
||||
std::vector<Slice> keys;
|
||||
std::vector<PinnableSlice> values;
|
||||
std::vector<Status> statuses;
|
||||
|
||||
key_strs.push_back(Key(33));
|
||||
key_strs.push_back(Key(54));
|
||||
key_strs.push_back(Key(102));
|
||||
keys.push_back(key_strs[0]);
|
||||
keys.push_back(key_strs[1]);
|
||||
keys.push_back(key_strs[2]);
|
||||
values.resize(keys.size());
|
||||
statuses.resize(keys.size());
|
||||
|
||||
enable_io_uring = false;
|
||||
ReopenDB();
|
||||
|
||||
ReadOptions ro;
|
||||
ro.async_io = true;
|
||||
ro.optimize_multiget_for_io = GetParam();
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(statuses[0], Status::NotSupported());
|
||||
ASSERT_EQ(statuses[1], Status::NotSupported());
|
||||
ASSERT_EQ(statuses[2], Status::NotSupported());
|
||||
|
||||
HistogramData multiget_io_batch_size;
|
||||
|
||||
statistics()->histogramData(MULTIGET_IO_BATCH_SIZE, &multiget_io_batch_size);
|
||||
|
||||
// A batch of 3 async IOs is expected, one for each overlapping file in L1
|
||||
ASSERT_EQ(multiget_io_batch_size.count, 1);
|
||||
ASSERT_EQ(multiget_io_batch_size.max, 3);
|
||||
ASSERT_EQ(statistics()->getTickerCount(MULTIGET_COROUTINE_COUNT), 3);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetAsyncIOTest, DBMultiGetAsyncIOTest,
|
||||
testing::Bool());
|
||||
|
||||
@@ -287,9 +287,11 @@ class PersistentCacheFromCache : public PersistentCache {
|
||||
};
|
||||
|
||||
class ReadOnlyCacheWrapper : public CacheWrapper {
|
||||
public:
|
||||
using CacheWrapper::CacheWrapper;
|
||||
|
||||
using Cache::Insert;
|
||||
const char* Name() const override { return "ReadOnlyCacheWrapper"; }
|
||||
|
||||
Status Insert(const Slice& /*key*/, Cache::ObjectPtr /*value*/,
|
||||
const CacheItemHelper* /*helper*/, size_t /*charge*/,
|
||||
Handle** /*handle*/, Priority /*priority*/) override {
|
||||
@@ -711,7 +713,8 @@ class LookupLiarCache : public CacheWrapper {
|
||||
explicit LookupLiarCache(std::shared_ptr<Cache> target)
|
||||
: CacheWrapper(std::move(target)) {}
|
||||
|
||||
using Cache::Lookup;
|
||||
const char* Name() const override { return "LookupLiarCache"; }
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper = nullptr,
|
||||
CreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW, bool wait = true,
|
||||
|
||||
+105
-9
@@ -1539,6 +1539,14 @@ Status DBImpl::LockWAL() {
|
||||
assert(lock_wal_write_token_);
|
||||
++lock_wal_count_;
|
||||
} else {
|
||||
// NOTE: this will "unnecessarily" wait for other non-LockWAL() write
|
||||
// stalls to clear before LockWAL returns, however fixing that would
|
||||
// not be simple because if we notice the primary queue is already
|
||||
// stalled, that stall might clear while we release DB mutex in
|
||||
// EnterUnbatched() for the nonmem queue. And if we work around that in
|
||||
// the naive way, we could deadlock by locking the two queues in different
|
||||
// orders.
|
||||
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WriteThread::Writer nonmem_w;
|
||||
@@ -1571,6 +1579,8 @@ Status DBImpl::LockWAL() {
|
||||
|
||||
Status DBImpl::UnlockWAL() {
|
||||
bool signal = false;
|
||||
uint64_t maybe_stall_begun_count = 0;
|
||||
uint64_t nonmem_maybe_stall_begun_count = 0;
|
||||
{
|
||||
InstrumentedMutexLock lock(&mutex_);
|
||||
if (lock_wal_count_ == 0) {
|
||||
@@ -1580,12 +1590,28 @@ Status DBImpl::UnlockWAL() {
|
||||
if (lock_wal_count_ == 0) {
|
||||
lock_wal_write_token_.reset();
|
||||
signal = true;
|
||||
// For the last UnlockWAL, we don't want to return from UnlockWAL()
|
||||
// until the thread(s) that called BeginWriteStall() have had a chance to
|
||||
// call EndWriteStall(), so that no_slowdown writes after UnlockWAL() are
|
||||
// guaranteed to succeed if there's no other source of stall.
|
||||
maybe_stall_begun_count = write_thread_.GetBegunCountOfOutstandingStall();
|
||||
if (two_write_queues_) {
|
||||
nonmem_maybe_stall_begun_count =
|
||||
nonmem_write_thread_.GetBegunCountOfOutstandingStall();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (signal) {
|
||||
// SignalAll outside of mutex for efficiency
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
// Ensure stalls have cleared
|
||||
if (maybe_stall_begun_count) {
|
||||
write_thread_.WaitForStallEndedCount(maybe_stall_begun_count);
|
||||
}
|
||||
if (nonmem_maybe_stall_begun_count) {
|
||||
nonmem_write_thread_.WaitForStallEndedCount(nonmem_maybe_stall_begun_count);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -2579,14 +2605,25 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
return MultiGet(read_options, num_keys, column_families, keys, values,
|
||||
/*timestamps=*/nullptr, statuses, sorted_input);
|
||||
MultiGet(read_options, num_keys, column_families, keys, values,
|
||||
/* timestamps */ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses, const bool sorted_input) {
|
||||
MultiGetCommon(read_options, num_keys, column_families, keys, values,
|
||||
/* columns */ nullptr, timestamps, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGetCommon(const ReadOptions& read_options,
|
||||
const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
PinnableWideColumns* columns,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
if (num_keys == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -2632,8 +2669,20 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
values[i].Reset();
|
||||
key_context.emplace_back(column_families[i], keys[i], &values[i],
|
||||
PinnableSlice* val = nullptr;
|
||||
PinnableWideColumns* col = nullptr;
|
||||
|
||||
if (values) {
|
||||
val = &values[i];
|
||||
val->Reset();
|
||||
} else {
|
||||
assert(columns);
|
||||
|
||||
col = &columns[i];
|
||||
col->Reset();
|
||||
}
|
||||
|
||||
key_context.emplace_back(column_families[i], keys[i], val, col,
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
}
|
||||
@@ -2757,8 +2806,8 @@ void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
Status* statuses, const bool sorted_input) {
|
||||
return MultiGet(read_options, column_family, num_keys, keys, values,
|
||||
/*timestamp=*/nullptr, statuses, sorted_input);
|
||||
MultiGet(read_options, column_family, num_keys, keys, values,
|
||||
/* timestamps */ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
@@ -2766,6 +2815,16 @@ void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
MultiGetCommon(read_options, column_family, num_keys, keys, values,
|
||||
/* columns */ nullptr, timestamps, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGetCommon(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys,
|
||||
PinnableSlice* values, PinnableWideColumns* columns,
|
||||
std::string* timestamps, Status* statuses,
|
||||
bool sorted_input) {
|
||||
if (tracer_) {
|
||||
// TODO: This mutex should be removed later, to improve performance when
|
||||
// tracing is enabled.
|
||||
@@ -2779,8 +2838,20 @@ void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
values[i].Reset();
|
||||
key_context.emplace_back(column_family, keys[i], &values[i],
|
||||
PinnableSlice* val = nullptr;
|
||||
PinnableWideColumns* col = nullptr;
|
||||
|
||||
if (values) {
|
||||
val = &values[i];
|
||||
val->Reset();
|
||||
} else {
|
||||
assert(columns);
|
||||
|
||||
col = &columns[i];
|
||||
col->Reset();
|
||||
}
|
||||
|
||||
key_context.emplace_back(column_family, keys[i], val, col,
|
||||
timestamps ? ×tamps[i] : nullptr,
|
||||
&statuses[i]);
|
||||
}
|
||||
@@ -2942,8 +3013,17 @@ Status DBImpl::MultiGetImpl(
|
||||
uint64_t bytes_read = 0;
|
||||
for (size_t i = start_key; i < start_key + num_keys - keys_left; ++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
assert(key);
|
||||
assert(key->s);
|
||||
|
||||
if (key->s->ok()) {
|
||||
bytes_read += key->value->size();
|
||||
if (key->value) {
|
||||
bytes_read += key->value->size();
|
||||
} else {
|
||||
assert(key->columns);
|
||||
bytes_read += key->columns->serialized_size();
|
||||
}
|
||||
|
||||
num_found++;
|
||||
}
|
||||
}
|
||||
@@ -2967,6 +3047,22 @@ Status DBImpl::MultiGetImpl(
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::MultiGetEntity(const ReadOptions& options, size_t num_keys,
|
||||
ColumnFamilyHandle** column_families,
|
||||
const Slice* keys, PinnableWideColumns* results,
|
||||
Status* statuses, bool sorted_input) {
|
||||
MultiGetCommon(options, num_keys, column_families, keys, /* values */ nullptr,
|
||||
results, /* timestamps */ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
void DBImpl::MultiGetEntity(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, size_t num_keys,
|
||||
const Slice* keys, PinnableWideColumns* results,
|
||||
Status* statuses, bool sorted_input) {
|
||||
MultiGetCommon(options, column_family, num_keys, keys, /* values */ nullptr,
|
||||
results, /* timestamps */ nullptr, statuses, sorted_input);
|
||||
}
|
||||
|
||||
Status DBImpl::CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) {
|
||||
|
||||
+42
-21
@@ -277,33 +277,40 @@ class DBImpl : public DB {
|
||||
// The values and statuses parameters are arrays with number of elements
|
||||
// equal to keys.size(). This allows the storage for those to be alloacted
|
||||
// by the caller on the stack for small batches
|
||||
virtual void MultiGet(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
virtual void MultiGet(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys, PinnableSlice* values,
|
||||
Status* statuses, const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const size_t num_keys, const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
|
||||
virtual void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
virtual void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, Status* statuses,
|
||||
const bool sorted_input = false) override;
|
||||
void MultiGet(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, std::string* timestamps,
|
||||
Status* statuses, const bool sorted_input = false) override;
|
||||
|
||||
virtual void MultiGetWithCallback(
|
||||
void MultiGetWithCallback(
|
||||
const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
ReadCallback* callback,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys);
|
||||
|
||||
using DB::MultiGetEntity;
|
||||
|
||||
void MultiGetEntity(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, size_t num_keys,
|
||||
const Slice* keys, PinnableWideColumns* results,
|
||||
Status* statuses, bool sorted_input) override;
|
||||
|
||||
void MultiGetEntity(const ReadOptions& options, size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableWideColumns* results, Status* statuses,
|
||||
bool sorted_input) override;
|
||||
|
||||
virtual Status CreateColumnFamily(const ColumnFamilyOptions& cf_options,
|
||||
const std::string& column_family,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
@@ -1117,6 +1124,8 @@ class DBImpl : public DB {
|
||||
|
||||
void TEST_UnlockMutex();
|
||||
|
||||
void TEST_SignalAllBgCv();
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
void* TEST_BeginWrite();
|
||||
|
||||
@@ -2189,6 +2198,18 @@ class DBImpl : public DB {
|
||||
const size_t num_keys, bool sorted,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* key_ptrs);
|
||||
|
||||
void MultiGetCommon(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, const size_t num_keys,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
PinnableWideColumns* columns, std::string* timestamps,
|
||||
Status* statuses, bool sorted_input);
|
||||
|
||||
void MultiGetCommon(const ReadOptions& options, const size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableSlice* values, PinnableWideColumns* columns,
|
||||
std::string* timestamps, Status* statuses,
|
||||
bool sorted_input);
|
||||
|
||||
// A structure to hold the information required to process MultiGet of keys
|
||||
// belonging to one column family. For a multi column family MultiGet, there
|
||||
// will be a container of these objects.
|
||||
|
||||
@@ -198,6 +198,8 @@ void DBImpl::TEST_LockMutex() { mutex_.Lock(); }
|
||||
|
||||
void DBImpl::TEST_UnlockMutex() { mutex_.Unlock(); }
|
||||
|
||||
void DBImpl::TEST_SignalAllBgCv() { bg_cv_.SignalAll(); }
|
||||
|
||||
void* DBImpl::TEST_BeginWrite() {
|
||||
auto w = new WriteThread::Writer();
|
||||
write_thread_.EnterUnbatched(w, &mutex_);
|
||||
|
||||
@@ -361,8 +361,6 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl:AfterDeletion",
|
||||
&file_deletion_status);
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl:AfterDeletion2",
|
||||
const_cast<std::string*>(&fname));
|
||||
if (file_deletion_status.ok()) {
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[JOB %d] Delete %s type=%d #%" PRIu64 " -- %s\n", job_id,
|
||||
|
||||
@@ -1833,8 +1833,13 @@ Status DBImpl::DelayWrite(uint64_t num_bytes, WriteThread& write_thread,
|
||||
// Notify write_thread about the stall so it can setup a barrier and
|
||||
// fail any pending writers with no_slowdown
|
||||
write_thread.BeginWriteStall();
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Wait");
|
||||
if (&write_thread == &write_thread_) {
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:Wait");
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::DelayWrite:NonmemWait");
|
||||
}
|
||||
bg_cv_.Wait();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DelayWrite:AfterWait", &mutex_);
|
||||
write_thread.EndWriteStall();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,9 @@ TEST_F(DBMergeOperatorTest, MergeOperatorFailsWithMustMerge) {
|
||||
{
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k0", &value));
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k1", &value).IsCorruption());
|
||||
Status s = db_->Get(ReadOptions(), "k1", &value);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_EQ(Status::SubCode::kMergeOperatorFailed, s.subcode());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k2", &value));
|
||||
}
|
||||
|
||||
@@ -243,6 +245,8 @@ TEST_F(DBMergeOperatorTest, MergeOperatorFailsWithMustMerge) {
|
||||
ASSERT_EQ("k0", iter->key());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
ASSERT_EQ(Status::SubCode::kMergeOperatorFailed,
|
||||
iter->status().subcode());
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
+2
-76
@@ -909,82 +909,6 @@ class TestPutOperator : public MergeOperator {
|
||||
virtual const char* Name() const override { return "TestPutOperator"; }
|
||||
};
|
||||
|
||||
// A wrapper around Cache that can easily be extended with instrumentation,
|
||||
// etc.
|
||||
class CacheWrapper : public Cache {
|
||||
public:
|
||||
explicit CacheWrapper(std::shared_ptr<Cache> target)
|
||||
: target_(std::move(target)) {}
|
||||
|
||||
const char* Name() const override { return target_->Name(); }
|
||||
|
||||
Status Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
return target_->Insert(key, value, helper, charge, handle, priority);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority = Priority::LOW, bool wait = true,
|
||||
Statistics* stats = nullptr) override {
|
||||
return target_->Lookup(key, helper, create_context, priority, wait, stats);
|
||||
}
|
||||
|
||||
bool Ref(Handle* handle) override { return target_->Ref(handle); }
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
ObjectPtr Value(Handle* handle) override { return target_->Value(handle); }
|
||||
|
||||
void Erase(const Slice& key) override { target_->Erase(key); }
|
||||
uint64_t NewId() override { return target_->NewId(); }
|
||||
|
||||
void SetCapacity(size_t capacity) override { target_->SetCapacity(capacity); }
|
||||
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
size_t GetCapacity() const override { return target_->GetCapacity(); }
|
||||
|
||||
size_t GetUsage() const override { return target_->GetUsage(); }
|
||||
|
||||
size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
size_t GetPinnedUsage() const override { return target_->GetPinnedUsage(); }
|
||||
|
||||
size_t GetCharge(Handle* handle) const override {
|
||||
return target_->GetCharge(handle);
|
||||
}
|
||||
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override {
|
||||
return target_->GetCacheItemHelper(handle);
|
||||
}
|
||||
|
||||
void ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) override {
|
||||
target_->ApplyToAllEntries(callback, opts);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override { target_->EraseUnRefEntries(); }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Cache> target_;
|
||||
};
|
||||
|
||||
/*
|
||||
* A cache wrapper that tracks certain CacheEntryRole's cache charge, its
|
||||
* peaks and increments
|
||||
@@ -1002,6 +926,8 @@ class TargetCacheChargeTrackingCache : public CacheWrapper {
|
||||
public:
|
||||
explicit TargetCacheChargeTrackingCache(std::shared_ptr<Cache> target);
|
||||
|
||||
const char* Name() const override { return "TargetCacheChargeTrackingCache"; }
|
||||
|
||||
Status Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
|
||||
+14
-32
@@ -1633,6 +1633,8 @@ TEST_F(DBWALTest, RaceInstallFlushResultsWithWalObsoletion) {
|
||||
|
||||
TEST_F(DBWALTest, FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL) {
|
||||
Options options = CurrentOptions();
|
||||
// Small size to force manifest creation
|
||||
options.max_manifest_file_size = 1;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -1649,53 +1651,33 @@ TEST_F(DBWALTest, FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL) {
|
||||
// (2) SyncWAL() proceeds with the lock. It
|
||||
// creates a new manifest and syncs all the inactive wals before the latest
|
||||
// (i.e, active log), which is 4.log. Note that SyncWAL() is not aware of the
|
||||
// fact that 4.log has marked as to be obseleted. Prior to the fix, such wal
|
||||
// fact that 4.log has marked as to be obseleted. Such wal
|
||||
// sync will then add a WAL addition record of 4.log to the new manifest
|
||||
// without any special treatment.
|
||||
// (3) BackgroundFlush() will eventually purge 4.log.
|
||||
// without any special treatment. Prior to the fix, there is no WAL deletion
|
||||
// record to offset it. (3) BackgroundFlush() will eventually purge 4.log.
|
||||
|
||||
bool wal_synced = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"FindObsoleteFiles::PostMutexUnlock", [&](void*) {
|
||||
ASSERT_OK(env_->FileExists(wal_file_path));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::ProcessManifestWrites:"
|
||||
"PostDecidingCreateNewManifestOrNot",
|
||||
[&](void* arg) {
|
||||
bool* new_descriptor_log = (bool*)arg;
|
||||
*new_descriptor_log = true;
|
||||
});
|
||||
|
||||
uint64_t pre_sync_wal_manifest_no =
|
||||
dbfull()->TEST_Current_Manifest_FileNo();
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
uint64_t post_sync_wal_manifest_no =
|
||||
dbfull()->TEST_Current_Manifest_FileNo();
|
||||
bool new_manifest_created =
|
||||
post_sync_wal_manifest_no == pre_sync_wal_manifest_no + 1;
|
||||
ASSERT_TRUE(new_manifest_created);
|
||||
wal_synced = true;
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DeleteObsoleteFileImpl:AfterDeletion2", [&](void* arg) {
|
||||
std::string* file_name = (std::string*)arg;
|
||||
if (*file_name == wal_file_path) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBWALTest::"
|
||||
"FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL::"
|
||||
"PostDeleteWAL");
|
||||
}
|
||||
});
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCallFlush:FilesFound",
|
||||
"PreConfrimObsoletedWALSynced"},
|
||||
{"DBWALTest::FixSyncWalOnObseletedWalWithNewManifestCausingMissingWAL::"
|
||||
"PostDeleteWAL",
|
||||
"PreConfrimWALDeleted"}});
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForBackgroundWork());
|
||||
|
||||
TEST_SYNC_POINT("PreConfrimObsoletedWALSynced");
|
||||
ASSERT_TRUE(wal_synced);
|
||||
|
||||
TEST_SYNC_POINT("PreConfrimWALDeleted");
|
||||
// BackgroundFlush() purged 4.log
|
||||
// because the memtable associated with the WAL was flushed and new WAL was
|
||||
// created (i.e, 8.log)
|
||||
|
||||
@@ -612,6 +612,7 @@ TEST_P(DBWriteTest, LockWALInEffect) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
options.env = mock_env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
options.paranoid_checks = false;
|
||||
Reopen(options);
|
||||
// try the 1st WAL created during open
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
Status EventListener::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& id,
|
||||
std::shared_ptr<EventListener>* result) {
|
||||
return LoadSharedObject<EventListener>(config_options, id, nullptr, result);
|
||||
return LoadSharedObject<EventListener>(config_options, id, result);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -1182,6 +1182,7 @@ TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
|
||||
ASSERT_OK(sst_file_writer->Finish());
|
||||
|
||||
IngestExternalFileOptions ingest_opt;
|
||||
ASSERT_FALSE(ingest_opt.write_global_seqno); // new default
|
||||
if (i == 0) {
|
||||
ingest_opt.move_files = true;
|
||||
}
|
||||
|
||||
+8
-6
@@ -515,10 +515,11 @@ unsigned int Reader::ReadPhysicalRecord(Slice* result, size_t* drop_size,
|
||||
|
||||
size_t uncompressed_size = 0;
|
||||
int remaining = 0;
|
||||
const char* input = header + header_size;
|
||||
do {
|
||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
||||
uncompressed_buffer_.get(),
|
||||
&uncompressed_size);
|
||||
remaining = uncompress_->Uncompress(
|
||||
input, length, uncompressed_buffer_.get(), &uncompressed_size);
|
||||
input = nullptr;
|
||||
if (remaining < 0) {
|
||||
buffer_.clear();
|
||||
return kBadRecord;
|
||||
@@ -830,10 +831,11 @@ bool FragmentBufferedReader::TryReadFragment(
|
||||
uncompressed_record_.clear();
|
||||
size_t uncompressed_size = 0;
|
||||
int remaining = 0;
|
||||
const char* input = header + header_size;
|
||||
do {
|
||||
remaining = uncompress_->Uncompress(header + header_size, length,
|
||||
uncompressed_buffer_.get(),
|
||||
&uncompressed_size);
|
||||
remaining = uncompress_->Uncompress(
|
||||
input, length, uncompressed_buffer_.get(), &uncompressed_size);
|
||||
input = nullptr;
|
||||
if (remaining < 0) {
|
||||
buffer_.clear();
|
||||
*fragment_type_or_err = kBadRecord;
|
||||
|
||||
+35
-2
@@ -979,6 +979,38 @@ TEST_P(CompressionLogTest, Fragmentation) {
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
TEST_P(CompressionLogTest, AlignedFragmentation) {
|
||||
CompressionType compression_type = std::get<2>(GetParam());
|
||||
if (!StreamingCompressionTypeSupported(compression_type)) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires support for compression type");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(SetupTestEnv());
|
||||
Random rnd(301);
|
||||
int num_filler_records = 0;
|
||||
// Keep writing small records until the next record will be aligned at the
|
||||
// beginning of the block.
|
||||
while ((WrittenBytes() & (kBlockSize - 1)) >= kHeaderSize) {
|
||||
char entry = 'a';
|
||||
ASSERT_OK(writer_->AddRecord(Slice(&entry, 1)));
|
||||
num_filler_records++;
|
||||
}
|
||||
const std::vector<std::string> wal_entries = {
|
||||
rnd.RandomBinaryString(3 * kBlockSize),
|
||||
};
|
||||
for (const std::string& wal_entry : wal_entries) {
|
||||
Write(wal_entry);
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_filler_records; ++i) {
|
||||
ASSERT_EQ("a", Read());
|
||||
}
|
||||
for (const std::string& wal_entry : wal_entries) {
|
||||
ASSERT_EQ(wal_entry, Read());
|
||||
}
|
||||
ASSERT_EQ("EOF", Read());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Compression, CompressionLogTest,
|
||||
::testing::Combine(::testing::Values(0, 1), ::testing::Bool(),
|
||||
@@ -1026,10 +1058,11 @@ TEST_P(StreamingCompressionTest, Basic) {
|
||||
for (int i = 0; i < (int)compressed_buffers.size(); i++) {
|
||||
// Call uncompress till either the entire input is consumed or the output
|
||||
// buffer size is equal to the allocated output buffer size.
|
||||
const char* input = compressed_buffers[i].c_str();
|
||||
do {
|
||||
ret_val = uncompress->Uncompress(compressed_buffers[i].c_str(),
|
||||
compressed_buffers[i].size(),
|
||||
ret_val = uncompress->Uncompress(input, compressed_buffers[i].size(),
|
||||
uncompressed_output_buffer, &output_pos);
|
||||
input = nullptr;
|
||||
if (output_pos > 0) {
|
||||
std::string uncompressed_fragment;
|
||||
uncompressed_fragment.assign(uncompressed_output_buffer, output_pos);
|
||||
|
||||
+12
-6
@@ -1452,18 +1452,24 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
SequenceNumber dummy_seq;
|
||||
GetFromTable(*(iter->lkey), iter->max_covering_tombstone_seq, true,
|
||||
callback, &iter->is_blob_index, iter->value->GetSelf(),
|
||||
/*columns=*/nullptr, iter->timestamp, iter->s,
|
||||
&(iter->merge_context), &dummy_seq, &found_final_value,
|
||||
&merge_in_progress);
|
||||
callback, &iter->is_blob_index,
|
||||
iter->value ? iter->value->GetSelf() : nullptr, iter->columns,
|
||||
iter->timestamp, iter->s, &(iter->merge_context), &dummy_seq,
|
||||
&found_final_value, &merge_in_progress);
|
||||
|
||||
if (!found_final_value && merge_in_progress) {
|
||||
*(iter->s) = Status::MergeInProgress();
|
||||
}
|
||||
|
||||
if (found_final_value) {
|
||||
iter->value->PinSelf();
|
||||
range->AddValueSize(iter->value->size());
|
||||
if (iter->value) {
|
||||
iter->value->PinSelf();
|
||||
range->AddValueSize(iter->value->size());
|
||||
} else {
|
||||
assert(iter->columns);
|
||||
range->AddValueSize(iter->columns->serialized_size());
|
||||
}
|
||||
|
||||
range->MarkKeyDone(iter);
|
||||
RecordTick(moptions_.statistics, MEMTABLE_HIT);
|
||||
if (range->GetValueSize() > read_options.value_size_soft_limit) {
|
||||
|
||||
+6
-5
@@ -113,7 +113,7 @@ Status MergeHelper::TimedFullMerge(
|
||||
|
||||
if (!success) {
|
||||
RecordTick(statistics, NUMBER_MERGE_FAILURES);
|
||||
return Status::Corruption("Error: Could not perform merge.");
|
||||
return Status::Corruption(Status::SubCode::kMergeOperatorFailed);
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
@@ -578,14 +578,15 @@ CompactionFilter::Decision MergeHelper::FilterMerge(const Slice& user_key,
|
||||
}
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
auto ret = compaction_filter_->FilterV2(
|
||||
level_, user_key, CompactionFilter::ValueType::kMergeOperand, value_slice,
|
||||
&compaction_filter_value_, compaction_filter_skip_until_.rep());
|
||||
auto ret = compaction_filter_->FilterV3(
|
||||
level_, user_key, CompactionFilter::ValueType::kMergeOperand,
|
||||
&value_slice, /* existing_columns */ nullptr, &compaction_filter_value_,
|
||||
/* new_columns */ nullptr, compaction_filter_skip_until_.rep());
|
||||
if (ret == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
if (user_comparator_->Compare(*compaction_filter_skip_until_.rep(),
|
||||
user_key) <= 0) {
|
||||
// Invalid skip_until returned from compaction filter.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
// Keep the key as per FilterV2/FilterV3 documentation.
|
||||
ret = CompactionFilter::Decision::kKeep;
|
||||
} else {
|
||||
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
||||
|
||||
+1
-1
@@ -20,8 +20,8 @@
|
||||
#include "db/wal_edit.h"
|
||||
#include "memory/arena.h"
|
||||
#include "port/malloc.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/advanced_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "table/table_reader.h"
|
||||
#include "table/unique_id_impl.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
+55
-32
@@ -23,7 +23,6 @@
|
||||
#include "db/blob/blob_fetcher.h"
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_source.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
@@ -2177,26 +2176,35 @@ void Version::MultiGetBlob(
|
||||
|
||||
autovector<BlobReadRequest> blob_reqs_in_file;
|
||||
BlobReadContexts& blobs_in_file = ctx.second;
|
||||
for (const auto& blob : blobs_in_file) {
|
||||
const BlobIndex& blob_index = blob.first;
|
||||
const KeyContext& key_context = blob.second;
|
||||
for (auto& blob : blobs_in_file) {
|
||||
const BlobIndex& blob_index = blob.blob_index;
|
||||
const KeyContext* const key_context = blob.key_context;
|
||||
assert(key_context);
|
||||
assert(key_context->get_context);
|
||||
assert(key_context->s);
|
||||
|
||||
if (key_context->value) {
|
||||
key_context->value->Reset();
|
||||
} else {
|
||||
assert(key_context->columns);
|
||||
key_context->columns->Reset();
|
||||
}
|
||||
|
||||
if (!blob_file_meta) {
|
||||
*key_context.s = Status::Corruption("Invalid blob file number");
|
||||
*key_context->s = Status::Corruption("Invalid blob file number");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (blob_index.HasTTL() || blob_index.IsInlined()) {
|
||||
*key_context.s =
|
||||
*key_context->s =
|
||||
Status::Corruption("Unexpected TTL/inlined blob index");
|
||||
continue;
|
||||
}
|
||||
|
||||
key_context.value->Reset();
|
||||
blob_reqs_in_file.emplace_back(
|
||||
key_context.get_context->ukey_to_get_blob_value(),
|
||||
key_context->get_context->ukey_to_get_blob_value(),
|
||||
blob_index.offset(), blob_index.size(), blob_index.compression(),
|
||||
key_context.value, key_context.s);
|
||||
&blob.result, key_context->s);
|
||||
}
|
||||
if (blob_reqs_in_file.size() > 0) {
|
||||
const auto file_size = blob_file_meta->GetBlobFileSize();
|
||||
@@ -2211,18 +2219,29 @@ void Version::MultiGetBlob(
|
||||
|
||||
for (auto& ctx : blob_ctxs) {
|
||||
BlobReadContexts& blobs_in_file = ctx.second;
|
||||
for (const auto& blob : blobs_in_file) {
|
||||
const KeyContext& key_context = blob.second;
|
||||
if (key_context.s->ok()) {
|
||||
range.AddValueSize(key_context.value->size());
|
||||
if (range.GetValueSize() > read_options.value_size_soft_limit) {
|
||||
*key_context.s = Status::Aborted();
|
||||
for (auto& blob : blobs_in_file) {
|
||||
const KeyContext* const key_context = blob.key_context;
|
||||
assert(key_context);
|
||||
assert(key_context->get_context);
|
||||
assert(key_context->s);
|
||||
|
||||
if (key_context->s->ok()) {
|
||||
if (key_context->value) {
|
||||
*key_context->value = std::move(blob.result);
|
||||
range.AddValueSize(key_context->value->size());
|
||||
} else {
|
||||
assert(key_context->columns);
|
||||
key_context->columns->SetPlainValue(blob.result);
|
||||
range.AddValueSize(key_context->columns->serialized_size());
|
||||
}
|
||||
} else if (key_context.s->IsIncomplete()) {
|
||||
|
||||
if (range.GetValueSize() > read_options.value_size_soft_limit) {
|
||||
*key_context->s = Status::Aborted();
|
||||
}
|
||||
} else if (key_context->s->IsIncomplete()) {
|
||||
// read_options.read_tier == kBlockCacheTier
|
||||
// Cannot read blob(s): no disk I/O allowed
|
||||
assert(key_context.get_context);
|
||||
auto& get_context = *(key_context.get_context);
|
||||
auto& get_context = *(key_context->get_context);
|
||||
get_context.MarkKeyMayExist();
|
||||
}
|
||||
}
|
||||
@@ -2388,6 +2407,9 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
return;
|
||||
case GetContext::kMergeOperatorFailed:
|
||||
*status = Status::Corruption(Status::SubCode::kMergeOperatorFailed);
|
||||
return;
|
||||
}
|
||||
f = fp.GetNextFile();
|
||||
}
|
||||
@@ -2457,7 +2479,7 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
get_ctx.emplace_back(
|
||||
user_comparator(), merge_operator_, info_log_, db_statistics_,
|
||||
iter->s->ok() ? GetContext::kNotFound : GetContext::kMerge,
|
||||
iter->ukey_with_ts, iter->value, /*columns=*/nullptr, iter->timestamp,
|
||||
iter->ukey_with_ts, iter->value, iter->columns, iter->timestamp,
|
||||
nullptr, &(iter->merge_context), true,
|
||||
&iter->max_covering_tombstone_seq, clock_, nullptr,
|
||||
merge_operator_ ? &pinned_iters_mgr : nullptr, callback,
|
||||
@@ -2657,23 +2679,29 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
// merge_operands are in saver and we hit the beginning of the key history
|
||||
// do a final merge of nullptr and operands;
|
||||
std::string* str_value =
|
||||
iter->value != nullptr ? iter->value->GetSelf() : nullptr;
|
||||
std::string result;
|
||||
|
||||
// `op_failure_scope` (an output parameter) is not provided (set to
|
||||
// nullptr) since a failure must be propagated regardless of its value.
|
||||
*status = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, user_key, nullptr, iter->merge_context.GetOperands(),
|
||||
str_value, info_log_, db_statistics_, clock_,
|
||||
&result, info_log_, db_statistics_, clock_,
|
||||
/* result_operand */ nullptr, /* update_num_ops_stats */ true,
|
||||
/* op_failure_scope */ nullptr);
|
||||
if (LIKELY(iter->value != nullptr)) {
|
||||
*iter->value->GetSelf() = std::move(result);
|
||||
iter->value->PinSelf();
|
||||
range->AddValueSize(iter->value->size());
|
||||
range->MarkKeyDone(iter);
|
||||
if (range->GetValueSize() > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
assert(iter->columns);
|
||||
iter->columns->SetPlainValue(result);
|
||||
range->AddValueSize(iter->columns->serialized_size());
|
||||
}
|
||||
|
||||
range->MarkKeyDone(iter);
|
||||
if (range->GetValueSize() > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
range->MarkKeyDone(iter);
|
||||
@@ -5077,15 +5105,10 @@ Status VersionSet::ProcessManifestWrites(
|
||||
if (!descriptor_log_ ||
|
||||
manifest_file_size_ > db_options_->max_manifest_file_size) {
|
||||
TEST_SYNC_POINT("VersionSet::ProcessManifestWrites:BeforeNewManifest");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"VersionSet::ProcessManifestWrites:BeforeNewManifest", nullptr);
|
||||
new_descriptor_log = true;
|
||||
} else {
|
||||
pending_manifest_file_number_ = manifest_file_number_;
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"VersionSet::ProcessManifestWrites:PostDecidingCreateNewManifestOrNot",
|
||||
&new_descriptor_log);
|
||||
|
||||
// Local cached copy of state variable(s). WriteCurrentStateToManifest()
|
||||
// reads its content after releasing db mutex to avoid race with
|
||||
|
||||
+10
-2
@@ -33,6 +33,7 @@
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
#include "db/blob/blob_file_meta.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
@@ -889,8 +890,15 @@ class Version {
|
||||
FilePrefetchBuffer* prefetch_buffer, PinnableSlice* value,
|
||||
uint64_t* bytes_read) const;
|
||||
|
||||
using BlobReadContext =
|
||||
std::pair<BlobIndex, std::reference_wrapper<const KeyContext>>;
|
||||
struct BlobReadContext {
|
||||
BlobReadContext(const BlobIndex& blob_idx, const KeyContext* key_ctx)
|
||||
: blob_index(blob_idx), key_context(key_ctx) {}
|
||||
|
||||
BlobIndex blob_index;
|
||||
const KeyContext* key_context;
|
||||
PinnableSlice result;
|
||||
};
|
||||
|
||||
using BlobReadContexts = std::vector<BlobReadContext>;
|
||||
void MultiGetBlob(const ReadOptions& read_options, MultiGetRange& range,
|
||||
std::unordered_map<uint64_t, BlobReadContexts>& blob_ctxs);
|
||||
|
||||
@@ -101,23 +101,39 @@ DEFINE_SYNC_AND_ASYNC(Status, Version::MultiGetFromSST)
|
||||
file_range.MarkKeyDone(iter);
|
||||
|
||||
if (iter->is_blob_index) {
|
||||
BlobIndex blob_index;
|
||||
Status tmp_s;
|
||||
|
||||
if (iter->value) {
|
||||
TEST_SYNC_POINT_CALLBACK("Version::MultiGet::TamperWithBlobIndex",
|
||||
&(*iter));
|
||||
|
||||
const Slice& blob_index_slice = *(iter->value);
|
||||
BlobIndex blob_index;
|
||||
Status tmp_s = blob_index.DecodeFrom(blob_index_slice);
|
||||
if (tmp_s.ok()) {
|
||||
const uint64_t blob_file_num = blob_index.file_number();
|
||||
blob_ctxs[blob_file_num].emplace_back(
|
||||
std::make_pair(blob_index, std::cref(*iter)));
|
||||
} else {
|
||||
*(iter->s) = tmp_s;
|
||||
}
|
||||
tmp_s = blob_index.DecodeFrom(*(iter->value));
|
||||
|
||||
} else {
|
||||
assert(iter->columns);
|
||||
assert(!iter->columns->columns().empty());
|
||||
assert(iter->columns->columns().front().name() ==
|
||||
kDefaultWideColumnName);
|
||||
|
||||
tmp_s =
|
||||
blob_index.DecodeFrom(iter->columns->columns().front().value());
|
||||
}
|
||||
|
||||
if (tmp_s.ok()) {
|
||||
const uint64_t blob_file_num = blob_index.file_number();
|
||||
blob_ctxs[blob_file_num].emplace_back(blob_index, &*iter);
|
||||
} else {
|
||||
*(iter->s) = tmp_s;
|
||||
}
|
||||
} else {
|
||||
file_range.AddValueSize(iter->value->size());
|
||||
if (iter->value) {
|
||||
file_range.AddValueSize(iter->value->size());
|
||||
} else {
|
||||
assert(iter->columns);
|
||||
file_range.AddValueSize(iter->columns->serialized_size());
|
||||
}
|
||||
|
||||
if (file_range.GetValueSize() > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
@@ -141,6 +157,10 @@ DEFINE_SYNC_AND_ASYNC(Status, Version::MultiGetFromSST)
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
file_range.MarkKeyDone(iter);
|
||||
continue;
|
||||
case GetContext::kMergeOperatorFailed:
|
||||
*status = Status::Corruption(Status::SubCode::kMergeOperatorFailed);
|
||||
file_range.MarkKeyDone(iter);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <memory>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
@@ -104,6 +105,26 @@ TEST_F(DBWideBasicTest, PutEntity) {
|
||||
ASSERT_EQ(values[2], third_value);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr size_t num_keys = 3;
|
||||
|
||||
std::array<Slice, num_keys> keys{{first_key, second_key, third_key}};
|
||||
std::array<PinnableWideColumns, num_keys> results;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
&keys[0], &results[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), first_columns);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(results[1].columns(), second_columns);
|
||||
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(results[2].columns(), expected_third_columns);
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
|
||||
@@ -209,6 +230,40 @@ TEST_F(DBWideBasicTest, PutEntityColumnFamily) {
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
}
|
||||
|
||||
TEST_F(DBWideBasicTest, MultiCFMultiGetEntity) {
|
||||
Options options = GetDefaultOptions();
|
||||
CreateAndReopenWithCF({"corinthian"}, options);
|
||||
|
||||
constexpr char first_key[] = "first";
|
||||
WideColumns first_columns{{"attr_name1", "foo"}, {"attr_name2", "bar"}};
|
||||
|
||||
ASSERT_OK(db_->PutEntity(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
first_key, first_columns));
|
||||
|
||||
constexpr char second_key[] = "second";
|
||||
WideColumns second_columns{{"attr_one", "two"}, {"attr_three", "four"}};
|
||||
|
||||
ASSERT_OK(
|
||||
db_->PutEntity(WriteOptions(), handles_[1], second_key, second_columns));
|
||||
|
||||
constexpr size_t num_keys = 2;
|
||||
|
||||
std::array<ColumnFamilyHandle*, num_keys> column_families{
|
||||
{db_->DefaultColumnFamily(), handles_[1]}};
|
||||
std::array<Slice, num_keys> keys{{first_key, second_key}};
|
||||
std::array<PinnableWideColumns, num_keys> results;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), num_keys, &column_families[0], &keys[0],
|
||||
&results[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), first_columns);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(results[1].columns(), second_columns);
|
||||
}
|
||||
|
||||
TEST_F(DBWideBasicTest, MergePlainKeyValue) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -279,6 +334,26 @@ TEST_F(DBWideBasicTest, MergePlainKeyValue) {
|
||||
ASSERT_EQ(result.columns(), expected_third_columns);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr size_t num_keys = 3;
|
||||
|
||||
std::array<Slice, num_keys> keys{{first_key, second_key, third_key}};
|
||||
std::array<PinnableWideColumns, num_keys> results;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
&keys[0], &results[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), expected_first_columns);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(results[1].columns(), expected_second_columns);
|
||||
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(results[2].columns(), expected_third_columns);
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
|
||||
@@ -456,6 +531,23 @@ TEST_F(DBWideBasicTest, MergeEntity) {
|
||||
ASSERT_OK(statuses[1]);
|
||||
}
|
||||
|
||||
{
|
||||
constexpr size_t num_keys = 2;
|
||||
|
||||
std::array<Slice, num_keys> keys{{first_key, second_key}};
|
||||
std::array<PinnableWideColumns, num_keys> results;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGetEntity(ReadOptions(), db_->DefaultColumnFamily(), num_keys,
|
||||
&keys[0], &results[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(results[0].columns(), first_expected_columns);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(results[1].columns(), second_expected_columns);
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ReadOptions()));
|
||||
|
||||
@@ -593,6 +685,302 @@ TEST_F(DBWideBasicTest, MergeEntity) {
|
||||
verify_merge_ops_post_compaction();
|
||||
}
|
||||
|
||||
TEST_F(DBWideBasicTest, CompactionFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
// Wide-column entity with default column
|
||||
constexpr char first_key[] = "first";
|
||||
WideColumns first_columns{{kDefaultWideColumnName, "a"},
|
||||
{"attr_name1", "foo"},
|
||||
{"attr_name2", "bar"}};
|
||||
WideColumns first_columns_uppercase{{kDefaultWideColumnName, "A"},
|
||||
{"attr_name1", "FOO"},
|
||||
{"attr_name2", "BAR"}};
|
||||
|
||||
// Wide-column entity without default column
|
||||
constexpr char second_key[] = "second";
|
||||
WideColumns second_columns{{"attr_one", "two"}, {"attr_three", "four"}};
|
||||
WideColumns second_columns_uppercase{{"attr_one", "TWO"},
|
||||
{"attr_three", "FOUR"}};
|
||||
|
||||
// Plain old key-value
|
||||
constexpr char last_key[] = "last";
|
||||
constexpr char last_value[] = "baz";
|
||||
constexpr char last_value_uppercase[] = "BAZ";
|
||||
|
||||
auto write = [&] {
|
||||
ASSERT_OK(db_->PutEntity(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
first_key, first_columns));
|
||||
ASSERT_OK(db_->PutEntity(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
second_key, second_columns));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), db_->DefaultColumnFamily(), last_key,
|
||||
last_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /* begin */ nullptr,
|
||||
/* end */ nullptr));
|
||||
};
|
||||
|
||||
// Test a compaction filter that keeps all entries
|
||||
{
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV3(
|
||||
int /* level */, const Slice& /* key */, ValueType /* value_type */,
|
||||
const Slice* /* existing_value */,
|
||||
const WideColumns* /* existing_columns */,
|
||||
std::string* /* new_value */,
|
||||
std::vector<std::pair<std::string, std::string>>* /* new_columns */,
|
||||
std::string* /* skip_until */) const override {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "KeepFilter"; }
|
||||
};
|
||||
|
||||
KeepFilter filter;
|
||||
options.compaction_filter = &filter;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
write();
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
first_key, &result));
|
||||
ASSERT_EQ(result.columns(), first_columns);
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
second_key, &result));
|
||||
ASSERT_EQ(result.columns(), second_columns);
|
||||
}
|
||||
|
||||
// Note: GetEntity should return an entity with a single default column,
|
||||
// since last_key is a plain key-value
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
last_key, &result));
|
||||
|
||||
WideColumns expected_columns{{kDefaultWideColumnName, last_value}};
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
}
|
||||
}
|
||||
|
||||
// Test a compaction filter that removes all entries
|
||||
{
|
||||
class RemoveFilter : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV3(
|
||||
int /* level */, const Slice& /* key */, ValueType /* value_type */,
|
||||
const Slice* /* existing_value */,
|
||||
const WideColumns* /* existing_columns */,
|
||||
std::string* /* new_value */,
|
||||
std::vector<std::pair<std::string, std::string>>* /* new_columns */,
|
||||
std::string* /* skip_until */) const override {
|
||||
return Decision::kRemove;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "RemoveFilter"; }
|
||||
};
|
||||
|
||||
RemoveFilter filter;
|
||||
options.compaction_filter = &filter;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
write();
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_TRUE(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
first_key, &result)
|
||||
.IsNotFound());
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_TRUE(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
second_key, &result)
|
||||
.IsNotFound());
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_TRUE(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
last_key, &result)
|
||||
.IsNotFound());
|
||||
}
|
||||
}
|
||||
|
||||
// Test a compaction filter that changes the values of entries to uppercase.
|
||||
// The new entry is always a plain key-value; if the existing entry is a
|
||||
// wide-column entity, only the value of its first column is kept.
|
||||
{
|
||||
class ChangeValueFilter : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV3(
|
||||
int /* level */, const Slice& /* key */, ValueType value_type,
|
||||
const Slice* existing_value, const WideColumns* existing_columns,
|
||||
std::string* new_value,
|
||||
std::vector<std::pair<std::string, std::string>>* /* new_columns */,
|
||||
std::string* /* skip_until */) const override {
|
||||
assert(new_value);
|
||||
|
||||
auto upper = [](const std::string& str) {
|
||||
std::string result(str);
|
||||
|
||||
for (char& c : result) {
|
||||
c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
if (value_type == ValueType::kWideColumnEntity) {
|
||||
assert(existing_columns);
|
||||
|
||||
if (!existing_columns->empty()) {
|
||||
*new_value = upper(existing_columns->front().value().ToString());
|
||||
}
|
||||
} else {
|
||||
assert(existing_value);
|
||||
|
||||
*new_value = upper(existing_value->ToString());
|
||||
}
|
||||
|
||||
return Decision::kChangeValue;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "ChangeValueFilter"; }
|
||||
};
|
||||
|
||||
ChangeValueFilter filter;
|
||||
options.compaction_filter = &filter;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
write();
|
||||
|
||||
// Note: GetEntity should return entities with a single default column,
|
||||
// since all entries are now plain key-values
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
first_key, &result));
|
||||
|
||||
WideColumns expected_columns{
|
||||
{kDefaultWideColumnName, first_columns_uppercase[0].value()}};
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
second_key, &result));
|
||||
|
||||
WideColumns expected_columns{
|
||||
{kDefaultWideColumnName, second_columns_uppercase[0].value()}};
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
last_key, &result));
|
||||
|
||||
WideColumns expected_columns{
|
||||
{kDefaultWideColumnName, last_value_uppercase}};
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
}
|
||||
}
|
||||
|
||||
// Test a compaction filter that changes the column values of entries to
|
||||
// uppercase. The new entry is always a wide-column entity; if the existing
|
||||
// entry is a plain key-value, it is converted to a wide-column entity with a
|
||||
// single default column.
|
||||
{
|
||||
class ChangeEntityFilter : public CompactionFilter {
|
||||
public:
|
||||
Decision FilterV3(
|
||||
int /* level */, const Slice& /* key */, ValueType value_type,
|
||||
const Slice* existing_value, const WideColumns* existing_columns,
|
||||
std::string* /* new_value */,
|
||||
std::vector<std::pair<std::string, std::string>>* new_columns,
|
||||
std::string* /* skip_until */) const override {
|
||||
assert(new_columns);
|
||||
|
||||
auto upper = [](const std::string& str) {
|
||||
std::string result(str);
|
||||
|
||||
for (char& c : result) {
|
||||
c = static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
if (value_type == ValueType::kWideColumnEntity) {
|
||||
assert(existing_columns);
|
||||
|
||||
for (const auto& column : *existing_columns) {
|
||||
new_columns->emplace_back(column.name().ToString(),
|
||||
upper(column.value().ToString()));
|
||||
}
|
||||
} else {
|
||||
assert(existing_value);
|
||||
|
||||
new_columns->emplace_back(kDefaultWideColumnName.ToString(),
|
||||
upper(existing_value->ToString()));
|
||||
}
|
||||
|
||||
return Decision::kChangeWideColumnEntity;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "ChangeEntityFilter"; }
|
||||
};
|
||||
|
||||
ChangeEntityFilter filter;
|
||||
options.compaction_filter = &filter;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
write();
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
first_key, &result));
|
||||
ASSERT_EQ(result.columns(), first_columns_uppercase);
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
second_key, &result));
|
||||
ASSERT_EQ(result.columns(), second_columns_uppercase);
|
||||
}
|
||||
|
||||
{
|
||||
PinnableWideColumns result;
|
||||
ASSERT_OK(db_->GetEntity(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
last_key, &result));
|
||||
|
||||
WideColumns expected_columns{
|
||||
{kDefaultWideColumnName, last_value_uppercase}};
|
||||
ASSERT_EQ(result.columns(), expected_columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWideBasicTest, PutEntityTimestampError) {
|
||||
// Note: timestamps are currently not supported
|
||||
|
||||
|
||||
@@ -228,6 +228,7 @@ bool WriteThread::LinkOne(Writer* w, std::atomic<Writer*>* newest_writer) {
|
||||
assert(w->state == STATE_INIT);
|
||||
Writer* writers = newest_writer->load(std::memory_order_relaxed);
|
||||
while (true) {
|
||||
assert(writers != w);
|
||||
// If write stall in effect, and w->no_slowdown is not true,
|
||||
// block here until stall is cleared. If its true, then return
|
||||
// immediately
|
||||
@@ -325,6 +326,7 @@ void WriteThread::CompleteFollower(Writer* w, WriteGroup& write_group) {
|
||||
}
|
||||
|
||||
void WriteThread::BeginWriteStall() {
|
||||
++stall_begun_count_;
|
||||
LinkOne(&write_stall_dummy_, &newest_writer_);
|
||||
|
||||
// Walk writer list until w->write_group != nullptr. The current write group
|
||||
@@ -367,10 +369,34 @@ void WriteThread::EndWriteStall() {
|
||||
}
|
||||
newest_writer_.exchange(write_stall_dummy_.link_older);
|
||||
|
||||
++stall_ended_count_;
|
||||
|
||||
// Wake up writers
|
||||
stall_cv_.SignalAll();
|
||||
}
|
||||
|
||||
uint64_t WriteThread::GetBegunCountOfOutstandingStall() {
|
||||
if (stall_begun_count_ > stall_ended_count_) {
|
||||
// Oustanding stall in queue
|
||||
assert(newest_writer_.load(std::memory_order_relaxed) ==
|
||||
&write_stall_dummy_);
|
||||
return stall_begun_count_;
|
||||
} else {
|
||||
// No stall in queue
|
||||
assert(newest_writer_.load(std::memory_order_relaxed) !=
|
||||
&write_stall_dummy_);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteThread::WaitForStallEndedCount(uint64_t stall_count) {
|
||||
MutexLock lock(&stall_mu_);
|
||||
|
||||
while (stall_ended_count_ < stall_count) {
|
||||
stall_cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
static WriteThread::AdaptationContext jbg_ctx("JoinBatchGroup");
|
||||
void WriteThread::JoinBatchGroup(Writer* w) {
|
||||
TEST_SYNC_POINT_CALLBACK("WriteThread::JoinBatchGroup:Start", w);
|
||||
|
||||
@@ -357,11 +357,23 @@ class WriteThread {
|
||||
|
||||
// Insert a dummy writer at the tail of the write queue to indicate a write
|
||||
// stall, and fail any writers in the queue with no_slowdown set to true
|
||||
// REQUIRES: db mutex held, no other stall on this queue outstanding
|
||||
void BeginWriteStall();
|
||||
|
||||
// Remove the dummy writer and wake up waiting writers
|
||||
// REQUIRES: db mutex held
|
||||
void EndWriteStall();
|
||||
|
||||
// Number of BeginWriteStall(), or 0 if there is no active stall in the
|
||||
// write queue.
|
||||
// REQUIRES: db mutex held
|
||||
uint64_t GetBegunCountOfOutstandingStall();
|
||||
|
||||
// Wait for number of completed EndWriteStall() to reach >= `stall_count`,
|
||||
// which will generally have come from GetBegunCountOfOutstandingStall().
|
||||
// (Does not require db mutex held)
|
||||
void WaitForStallEndedCount(uint64_t stall_count);
|
||||
|
||||
private:
|
||||
// See AwaitState.
|
||||
const uint64_t max_yield_usec_;
|
||||
@@ -401,6 +413,18 @@ class WriteThread {
|
||||
port::Mutex stall_mu_;
|
||||
port::CondVar stall_cv_;
|
||||
|
||||
// Count the number of stalls begun, so that we can check whether
|
||||
// a particular stall has cleared (even if caught in another stall).
|
||||
// Controlled by DB mutex.
|
||||
// Because of the contract on BeginWriteStall() / EndWriteStall(),
|
||||
// stall_ended_count_ <= stall_begun_count_ <= stall_ended_count_ + 1.
|
||||
uint64_t stall_begun_count_ = 0;
|
||||
// Count the number of stalls ended, so that we can check whether
|
||||
// a particular stall has cleared (even if caught in another stall).
|
||||
// Writes controlled by DB mutex + stall_mu_, signalled by stall_cv_.
|
||||
// Read with stall_mu or DB mutex.
|
||||
uint64_t stall_ended_count_ = 0;
|
||||
|
||||
// Waits for w->state & goal_mask using w->StateMutex(). Returns
|
||||
// the state that satisfies goal_mask.
|
||||
uint8_t BlockingAwaitState(Writer* w, uint8_t goal_mask);
|
||||
|
||||
@@ -1073,4 +1073,6 @@ DEFINE_uint64(stats_dump_period_sec,
|
||||
ROCKSDB_NAMESPACE::Options().stats_dump_period_sec,
|
||||
"Gap between printing stats to log in seconds");
|
||||
|
||||
extern "C" bool RocksDbIOUringEnable() { return true; }
|
||||
|
||||
#endif // GFLAGS
|
||||
@@ -2901,7 +2901,9 @@ void StressTest::MaybeUseOlderTimestampForRangeScan(ThreadState* thread,
|
||||
read_opts.timestamp = &ts_slice;
|
||||
|
||||
// TODO (yanqin): support Merge with iter_start_ts
|
||||
if (!thread->rand.OneInOpt(3) || FLAGS_use_merge || FLAGS_use_full_merge_v1) {
|
||||
// TODO (yuzhangyu): support BlobDB with iter_start_ts
|
||||
if (!thread->rand.OneInOpt(3) || FLAGS_use_merge || FLAGS_use_full_merge_v1 ||
|
||||
FLAGS_enable_blob_files) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2926,10 +2928,6 @@ void CheckAndSetOptionsForUserTimestamp(Options& options) {
|
||||
fprintf(stderr, "TransactionDB does not support timestamp yet.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (FLAGS_enable_blob_files || FLAGS_use_blob_db) {
|
||||
fprintf(stderr, "BlobDB not supported with timestamp.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (FLAGS_test_cf_consistency || FLAGS_test_batches_snapshots) {
|
||||
fprintf(stderr,
|
||||
"Due to per-key ts-seq ordering constraint, only the (default) "
|
||||
|
||||
@@ -99,13 +99,6 @@ int db_stress_tool(int argc, char** argv) {
|
||||
|
||||
env_wrapper_guard = std::make_shared<CompositeEnvWrapper>(
|
||||
raw_env, std::make_shared<DbStressFSWrapper>(raw_env->GetFileSystem()));
|
||||
if (!env_opts) {
|
||||
// If using the default Env (Posix), wrap DbStressEnvWrapper with the
|
||||
// legacy EnvWrapper. This is a temporary fix for the ReadAsync interface
|
||||
// not being properly supported with Posix and db_stress. The EnvWrapper
|
||||
// has a default implementation of ReadAsync that redirects to Read.
|
||||
env_wrapper_guard = std::make_shared<EnvWrapper>(env_wrapper_guard);
|
||||
}
|
||||
db_stress_env = env_wrapper_guard.get();
|
||||
|
||||
FLAGS_rep_factory = StringToRepFactory(FLAGS_memtablerep.c_str());
|
||||
|
||||
@@ -53,6 +53,7 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
kGet,
|
||||
kGetEntity,
|
||||
kMultiGet,
|
||||
kMultiGetEntity,
|
||||
kGetMergeOperands,
|
||||
// Add any new items above kNumberOfMethods
|
||||
kNumberOfMethods
|
||||
@@ -163,11 +164,11 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
Status s =
|
||||
db_->GetEntity(options, column_families_[cf], key, &columns);
|
||||
|
||||
const WideColumns& columns_from_db = columns.columns();
|
||||
|
||||
std::string from_db;
|
||||
|
||||
if (s.ok()) {
|
||||
const WideColumns& columns_from_db = columns.columns();
|
||||
|
||||
if (!columns_from_db.empty() &&
|
||||
columns_from_db[0].name() == kDefaultWideColumnName) {
|
||||
from_db = columns_from_db[0].value().ToString();
|
||||
@@ -200,14 +201,14 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
size_t batch_size = thread->rand.Uniform(128) + 1;
|
||||
batch_size = std::min<size_t>(batch_size, end - i);
|
||||
|
||||
std::vector<std::string> keystrs(batch_size);
|
||||
std::vector<std::string> key_strs(batch_size);
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<PinnableSlice> values(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
|
||||
for (size_t j = 0; j < batch_size; ++j) {
|
||||
keystrs[j] = Key(i + j);
|
||||
keys[j] = Slice(keystrs[j].data(), keystrs[j].size());
|
||||
key_strs[j] = Key(i + j);
|
||||
keys[j] = Slice(key_strs[j]);
|
||||
}
|
||||
|
||||
db_->MultiGet(options, column_families_[cf], batch_size, keys.data(),
|
||||
@@ -226,6 +227,61 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
}
|
||||
}
|
||||
|
||||
i += batch_size;
|
||||
}
|
||||
} else if (method == VerificationMethod::kMultiGetEntity) {
|
||||
for (int64_t i = start; i < end;) {
|
||||
if (thread->shared->HasVerificationFailedYet()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Keep the batch size to some reasonable value
|
||||
size_t batch_size = thread->rand.Uniform(128) + 1;
|
||||
batch_size = std::min<size_t>(batch_size, end - i);
|
||||
|
||||
std::vector<std::string> key_strs(batch_size);
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<PinnableWideColumns> results(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
|
||||
for (size_t j = 0; j < batch_size; ++j) {
|
||||
key_strs[j] = Key(i + j);
|
||||
keys[j] = Slice(key_strs[j]);
|
||||
}
|
||||
|
||||
db_->MultiGetEntity(options, column_families_[cf], batch_size,
|
||||
keys.data(), results.data(), statuses.data());
|
||||
|
||||
for (size_t j = 0; j < batch_size; ++j) {
|
||||
std::string from_db;
|
||||
|
||||
if (statuses[j].ok()) {
|
||||
const WideColumns& columns_from_db = results[j].columns();
|
||||
|
||||
if (!columns_from_db.empty() &&
|
||||
columns_from_db[0].name() == kDefaultWideColumnName) {
|
||||
from_db = columns_from_db[0].value().ToString();
|
||||
}
|
||||
|
||||
const WideColumns expected_columns =
|
||||
GenerateExpectedWideColumns(GetValueBase(from_db), from_db);
|
||||
if (columns_from_db != expected_columns) {
|
||||
VerificationAbort(shared, static_cast<int>(cf), i, from_db,
|
||||
columns_from_db, expected_columns);
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrSyncValue(static_cast<int>(cf), i + j, options, shared,
|
||||
from_db,
|
||||
/* msg_prefix */ "MultiGetEntity verification",
|
||||
statuses[j], /* strict */ true);
|
||||
|
||||
if (!from_db.empty()) {
|
||||
PrintKeyValue(static_cast<int>(cf), static_cast<uint32_t>(i + j),
|
||||
from_db.data(), from_db.size());
|
||||
}
|
||||
}
|
||||
|
||||
i += batch_size;
|
||||
}
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
source 'https://rubygems.org'
|
||||
gem 'github-pages', '~> 225'
|
||||
gem 'github-pages', '~> 227'
|
||||
|
||||
gem "webrick", "~> 1.7"
|
||||
@@ -1,285 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activesupport (6.0.4.6)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
zeitwerk (~> 2.2, >= 2.2.2)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.11.1)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.23.7)
|
||||
concurrent-ruby (1.1.9)
|
||||
dnsruby (1.61.9)
|
||||
simpleidn (~> 0.1)
|
||||
em-websocket (0.5.3)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0)
|
||||
ethon (0.15.0)
|
||||
ffi (>= 1.15.0)
|
||||
eventmachine (1.2.7)
|
||||
execjs (2.8.1)
|
||||
faraday (1.10.0)
|
||||
faraday-em_http (~> 1.0)
|
||||
faraday-em_synchrony (~> 1.0)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-httpclient (~> 1.0)
|
||||
faraday-multipart (~> 1.0)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.0)
|
||||
faraday-patron (~> 1.0)
|
||||
faraday-rack (~> 1.0)
|
||||
faraday-retry (~> 1.0)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-em_http (1.0.0)
|
||||
faraday-em_synchrony (1.0.0)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-httpclient (1.0.1)
|
||||
faraday-multipart (1.0.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.2.0)
|
||||
faraday-patron (1.0.0)
|
||||
faraday-rack (1.0.0)
|
||||
faraday-retry (1.0.3)
|
||||
ffi (1.15.5)
|
||||
forwardable-extended (2.6.0)
|
||||
gemoji (3.0.1)
|
||||
github-pages (225)
|
||||
github-pages-health-check (= 1.17.9)
|
||||
jekyll (= 3.9.0)
|
||||
jekyll-avatar (= 0.7.0)
|
||||
jekyll-coffeescript (= 1.1.1)
|
||||
jekyll-commonmark-ghpages (= 0.2.0)
|
||||
jekyll-default-layout (= 0.1.4)
|
||||
jekyll-feed (= 0.15.1)
|
||||
jekyll-gist (= 1.5.0)
|
||||
jekyll-github-metadata (= 2.13.0)
|
||||
jekyll-include-cache (= 0.2.1)
|
||||
jekyll-mentions (= 1.6.0)
|
||||
jekyll-optional-front-matter (= 0.3.2)
|
||||
jekyll-paginate (= 1.1.0)
|
||||
jekyll-readme-index (= 0.3.0)
|
||||
jekyll-redirect-from (= 0.16.0)
|
||||
jekyll-relative-links (= 0.6.1)
|
||||
jekyll-remote-theme (= 0.4.3)
|
||||
jekyll-sass-converter (= 1.5.2)
|
||||
jekyll-seo-tag (= 2.8.0)
|
||||
jekyll-sitemap (= 1.4.0)
|
||||
jekyll-swiss (= 1.0.0)
|
||||
jekyll-theme-architect (= 0.2.0)
|
||||
jekyll-theme-cayman (= 0.2.0)
|
||||
jekyll-theme-dinky (= 0.2.0)
|
||||
jekyll-theme-hacker (= 0.2.0)
|
||||
jekyll-theme-leap-day (= 0.2.0)
|
||||
jekyll-theme-merlot (= 0.2.0)
|
||||
jekyll-theme-midnight (= 0.2.0)
|
||||
jekyll-theme-minimal (= 0.2.0)
|
||||
jekyll-theme-modernist (= 0.2.0)
|
||||
jekyll-theme-primer (= 0.6.0)
|
||||
jekyll-theme-slate (= 0.2.0)
|
||||
jekyll-theme-tactile (= 0.2.0)
|
||||
jekyll-theme-time-machine (= 0.2.0)
|
||||
jekyll-titles-from-headings (= 0.5.3)
|
||||
jemoji (= 0.12.0)
|
||||
kramdown (= 2.3.1)
|
||||
kramdown-parser-gfm (= 1.1.0)
|
||||
liquid (= 4.0.3)
|
||||
mercenary (~> 0.3)
|
||||
minima (= 2.5.1)
|
||||
nokogiri (>= 1.12.5, < 2.0)
|
||||
rouge (= 3.26.0)
|
||||
terminal-table (~> 1.4)
|
||||
github-pages-health-check (1.17.9)
|
||||
addressable (~> 2.3)
|
||||
dnsruby (~> 1.60)
|
||||
octokit (~> 4.0)
|
||||
public_suffix (>= 3.0, < 5.0)
|
||||
typhoeus (~> 1.3)
|
||||
html-pipeline (2.14.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
http_parser.rb (0.8.0)
|
||||
i18n (0.9.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jekyll (3.9.0)
|
||||
addressable (~> 2.4)
|
||||
colorator (~> 1.0)
|
||||
em-websocket (~> 0.5)
|
||||
i18n (~> 0.7)
|
||||
jekyll-sass-converter (~> 1.0)
|
||||
jekyll-watch (~> 2.0)
|
||||
kramdown (>= 1.17, < 3)
|
||||
liquid (~> 4.0)
|
||||
mercenary (~> 0.3.3)
|
||||
pathutil (~> 0.9)
|
||||
rouge (>= 1.7, < 4)
|
||||
safe_yaml (~> 1.0)
|
||||
jekyll-avatar (0.7.0)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-coffeescript (1.1.1)
|
||||
coffee-script (~> 2.2)
|
||||
coffee-script-source (~> 1.11.1)
|
||||
jekyll-commonmark (1.4.0)
|
||||
commonmarker (~> 0.22)
|
||||
jekyll-commonmark-ghpages (0.2.0)
|
||||
commonmarker (~> 0.23.4)
|
||||
jekyll (~> 3.9.0)
|
||||
jekyll-commonmark (~> 1.4.0)
|
||||
rouge (>= 2.0, < 4.0)
|
||||
jekyll-default-layout (0.1.4)
|
||||
jekyll (~> 3.0)
|
||||
jekyll-feed (0.15.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-gist (1.5.0)
|
||||
octokit (~> 4.2)
|
||||
jekyll-github-metadata (2.13.0)
|
||||
jekyll (>= 3.4, < 5.0)
|
||||
octokit (~> 4.0, != 4.4.0)
|
||||
jekyll-include-cache (0.2.1)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-mentions (1.6.0)
|
||||
html-pipeline (~> 2.3)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-optional-front-matter (0.3.2)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-paginate (1.1.0)
|
||||
jekyll-readme-index (0.3.0)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
jekyll-redirect-from (0.16.0)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-relative-links (0.6.1)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-remote-theme (0.4.3)
|
||||
addressable (~> 2.0)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0)
|
||||
rubyzip (>= 1.3.0, < 3.0)
|
||||
jekyll-sass-converter (1.5.2)
|
||||
sass (~> 3.4)
|
||||
jekyll-seo-tag (2.8.0)
|
||||
jekyll (>= 3.8, < 5.0)
|
||||
jekyll-sitemap (1.4.0)
|
||||
jekyll (>= 3.7, < 5.0)
|
||||
jekyll-swiss (1.0.0)
|
||||
jekyll-theme-architect (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-cayman (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-dinky (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-hacker (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-leap-day (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-merlot (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-midnight (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-minimal (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-modernist (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-primer (0.6.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-github-metadata (~> 2.9)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-slate (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-tactile (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-theme-time-machine (0.2.0)
|
||||
jekyll (> 3.5, < 5.0)
|
||||
jekyll-seo-tag (~> 2.0)
|
||||
jekyll-titles-from-headings (0.5.3)
|
||||
jekyll (>= 3.3, < 5.0)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
jemoji (0.12.0)
|
||||
gemoji (~> 3.0)
|
||||
html-pipeline (~> 2.2)
|
||||
jekyll (>= 3.0, < 5.0)
|
||||
kramdown (2.3.1)
|
||||
rexml
|
||||
kramdown-parser-gfm (1.1.0)
|
||||
kramdown (~> 2.0)
|
||||
liquid (4.0.3)
|
||||
listen (3.7.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
mercenary (0.3.6)
|
||||
mini_portile2 (2.8.0)
|
||||
minima (2.5.1)
|
||||
jekyll (>= 3.5, < 5.0)
|
||||
jekyll-feed (~> 0.9)
|
||||
jekyll-seo-tag (~> 2.1)
|
||||
minitest (5.15.0)
|
||||
multipart-post (2.1.1)
|
||||
nokogiri (1.13.10)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
octokit (4.22.0)
|
||||
faraday (>= 0.9)
|
||||
sawyer (~> 0.8.0, >= 0.5.3)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
public_suffix (4.0.6)
|
||||
racc (1.6.1)
|
||||
rb-fsevent (0.11.1)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rexml (3.2.5)
|
||||
rouge (3.26.0)
|
||||
ruby2_keywords (0.0.5)
|
||||
rubyzip (2.3.2)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sawyer (0.8.2)
|
||||
addressable (>= 2.3.5)
|
||||
faraday (> 0.8, < 2.0)
|
||||
simpleidn (0.2.1)
|
||||
unf (~> 0.1.4)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thread_safe (0.3.6)
|
||||
typhoeus (1.4.0)
|
||||
ethon (>= 0.9.0)
|
||||
tzinfo (1.2.10)
|
||||
thread_safe (~> 0.1)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8)
|
||||
unicode-display_width (1.8.0)
|
||||
webrick (1.7.0)
|
||||
zeitwerk (2.5.4)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
github-pages (~> 225)
|
||||
webrick (~> 1.7)
|
||||
|
||||
BUNDLED WITH
|
||||
2.2.3
|
||||
Vendored
+2
-3
@@ -641,7 +641,7 @@ Status Env::CreateFromString(const ConfigOptions& config_options,
|
||||
} else {
|
||||
RegisterSystemEnvs();
|
||||
Env* env = *result;
|
||||
Status s = LoadStaticObject<Env>(config_options, value, nullptr, &env);
|
||||
Status s = LoadStaticObject<Env>(config_options, value, &env);
|
||||
if (s.ok()) {
|
||||
*result = env;
|
||||
}
|
||||
@@ -1227,8 +1227,7 @@ Status SystemClock::CreateFromString(const ConfigOptions& config_options,
|
||||
std::call_once(once, [&]() {
|
||||
RegisterBuiltinSystemClocks(*(ObjectLibrary::Default().get()), "");
|
||||
});
|
||||
return LoadSharedObject<SystemClock>(config_options, value, nullptr,
|
||||
result);
|
||||
return LoadSharedObject<SystemClock>(config_options, value, result);
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+2
-3
@@ -1332,15 +1332,14 @@ Status BlockCipher::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<BlockCipher>* result) {
|
||||
RegisterEncryptionBuiltins();
|
||||
return LoadSharedObject<BlockCipher>(config_options, value, nullptr, result);
|
||||
return LoadSharedObject<BlockCipher>(config_options, value, result);
|
||||
}
|
||||
|
||||
Status EncryptionProvider::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::shared_ptr<EncryptionProvider>* result) {
|
||||
RegisterEncryptionBuiltins();
|
||||
return LoadSharedObject<EncryptionProvider>(config_options, value, nullptr,
|
||||
result);
|
||||
return LoadSharedObject<EncryptionProvider>(config_options, value, result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -91,7 +91,7 @@ Status FileSystem::CreateFromString(const ConfigOptions& config_options,
|
||||
std::call_once(once, [&]() {
|
||||
RegisterBuiltinFileSystems(*(ObjectLibrary::Default().get()), "");
|
||||
});
|
||||
return LoadSharedObject<FileSystem>(config_options, value, nullptr, result);
|
||||
return LoadSharedObject<FileSystem>(config_options, value, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -569,10 +569,11 @@ Status FilePrefetchBuffer::PrefetchAsyncInternal(
|
||||
bufs_[second].offset_ = rounddown_start2;
|
||||
assert(roundup_len2 >= chunk_len2);
|
||||
uint64_t read_len2 = static_cast<size_t>(roundup_len2 - chunk_len2);
|
||||
Status tmp_s = ReadAsync(opts, reader, read_len2, rounddown_start2, second);
|
||||
if (!tmp_s.ok()) {
|
||||
s = ReadAsync(opts, reader, read_len2, rounddown_start2, second);
|
||||
if (!s.ok()) {
|
||||
DestroyAndClearIOHandle(second);
|
||||
bufs_[second].buffer_.Clear();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+87
-90
@@ -1609,27 +1609,23 @@ TEST_P(PrefetchTest, ReadAsyncWithPosixFS) {
|
||||
num_keys++;
|
||||
}
|
||||
|
||||
ASSERT_EQ(num_keys, total_keys);
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
|
||||
// Check stats to make sure async prefetch is done.
|
||||
{
|
||||
if (read_async_called) {
|
||||
ASSERT_EQ(num_keys, total_keys);
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
// Check stats to make sure async prefetch is done.
|
||||
HistogramData async_read_bytes;
|
||||
options.statistics->histogramData(ASYNC_READ_BYTES, &async_read_bytes);
|
||||
HistogramData prefetched_bytes_discarded;
|
||||
options.statistics->histogramData(PREFETCHED_BYTES_DISCARDED,
|
||||
&prefetched_bytes_discarded);
|
||||
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(prefetched_bytes_discarded.count, 0);
|
||||
ASSERT_EQ(get_perf_context()->number_async_seek, 0);
|
||||
} else {
|
||||
// Not all platforms support iouring. In that case, ReadAsync in posix
|
||||
// won't submit async requests.
|
||||
if (read_async_called) {
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
} else {
|
||||
ASSERT_EQ(async_read_bytes.count, 0);
|
||||
}
|
||||
ASSERT_GT(prefetched_bytes_discarded.count, 0);
|
||||
ASSERT_EQ(iter->status(), Status::NotSupported());
|
||||
}
|
||||
ASSERT_EQ(get_perf_context()->number_async_seek, 0);
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -1740,22 +1736,19 @@ TEST_P(PrefetchTest, MultipleSeekWithPosixFS) {
|
||||
num_keys++;
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(num_keys, num_keys_first_batch);
|
||||
// Check stats to make sure async prefetch is done.
|
||||
{
|
||||
|
||||
if (read_async_called) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(num_keys, num_keys_first_batch);
|
||||
// Check stats to make sure async prefetch is done.
|
||||
HistogramData async_read_bytes;
|
||||
options.statistics->histogramData(ASYNC_READ_BYTES, &async_read_bytes);
|
||||
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(get_perf_context()->number_async_seek, 0);
|
||||
} else {
|
||||
// Not all platforms support iouring. In that case, ReadAsync in posix
|
||||
// won't submit async requests.
|
||||
if (read_async_called) {
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(get_perf_context()->number_async_seek, 0);
|
||||
} else {
|
||||
ASSERT_EQ(async_read_bytes.count, 0);
|
||||
ASSERT_EQ(get_perf_context()->number_async_seek, 0);
|
||||
}
|
||||
ASSERT_EQ(iter->status(), Status::NotSupported());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1771,29 +1764,26 @@ TEST_P(PrefetchTest, MultipleSeekWithPosixFS) {
|
||||
num_keys++;
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(num_keys, num_keys_second_batch);
|
||||
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
if (read_async_called) {
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(num_keys, num_keys_second_batch);
|
||||
|
||||
// Check stats to make sure async prefetch is done.
|
||||
{
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
|
||||
// Check stats to make sure async prefetch is done.
|
||||
HistogramData async_read_bytes;
|
||||
options.statistics->histogramData(ASYNC_READ_BYTES, &async_read_bytes);
|
||||
HistogramData prefetched_bytes_discarded;
|
||||
options.statistics->histogramData(PREFETCHED_BYTES_DISCARDED,
|
||||
&prefetched_bytes_discarded);
|
||||
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(get_perf_context()->number_async_seek, 0);
|
||||
ASSERT_GT(prefetched_bytes_discarded.count, 0);
|
||||
} else {
|
||||
// Not all platforms support iouring. In that case, ReadAsync in posix
|
||||
// won't submit async requests.
|
||||
if (read_async_called) {
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(get_perf_context()->number_async_seek, 0);
|
||||
} else {
|
||||
ASSERT_EQ(async_read_bytes.count, 0);
|
||||
ASSERT_EQ(get_perf_context()->number_async_seek, 0);
|
||||
}
|
||||
ASSERT_GT(prefetched_bytes_discarded.count, 0);
|
||||
ASSERT_EQ(iter->status(), Status::NotSupported());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1872,51 +1862,52 @@ TEST_P(PrefetchTest, SeekParallelizationTestWithPosix) {
|
||||
// Each block contains around 4 keys.
|
||||
auto iter = std::unique_ptr<Iterator>(db_->NewIterator(ro));
|
||||
iter->Seek(BuildKey(0)); // Prefetch data because of seek parallelization.
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
if (std::get<1>(GetParam()) && !read_async_called) {
|
||||
ASSERT_EQ(iter->status(), Status::NotSupported());
|
||||
} else {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
// New data block. Since num_file_reads in FilePrefetch after this read is
|
||||
// 2, it won't go for prefetching.
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// New data block. Since num_file_reads in FilePrefetch after this read is
|
||||
// 2, it won't go for prefetching.
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
// Prefetch data.
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// Prefetch data.
|
||||
iter->Next();
|
||||
|
||||
// Check stats to make sure async prefetch is done.
|
||||
{
|
||||
HistogramData async_read_bytes;
|
||||
options.statistics->histogramData(ASYNC_READ_BYTES, &async_read_bytes);
|
||||
// Not all platforms support iouring. In that case, ReadAsync in posix
|
||||
// won't submit async requests.
|
||||
if (read_async_called) {
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(get_perf_context()->number_async_seek, 0);
|
||||
if (std::get<1>(GetParam())) {
|
||||
ASSERT_EQ(buff_prefetch_count, 1);
|
||||
} else {
|
||||
ASSERT_EQ(buff_prefetch_count, 2);
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
// Check stats to make sure async prefetch is done.
|
||||
{
|
||||
HistogramData async_read_bytes;
|
||||
options.statistics->histogramData(ASYNC_READ_BYTES,
|
||||
&async_read_bytes);
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
ASSERT_GT(get_perf_context()->number_async_seek, 0);
|
||||
if (std::get<1>(GetParam())) {
|
||||
ASSERT_EQ(buff_prefetch_count, 1);
|
||||
} else {
|
||||
ASSERT_EQ(buff_prefetch_count, 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ASSERT_EQ(async_read_bytes.count, 0);
|
||||
ASSERT_EQ(get_perf_context()->number_async_seek, 0);
|
||||
ASSERT_EQ(buff_prefetch_count, 1);
|
||||
// Not all platforms support iouring. In that case, ReadAsync in posix
|
||||
// won't submit async requests.
|
||||
ASSERT_EQ(iter->status(), Status::NotSupported());
|
||||
}
|
||||
}
|
||||
|
||||
buff_prefetch_count = 0;
|
||||
}
|
||||
Close();
|
||||
}
|
||||
@@ -2009,20 +2000,17 @@ TEST_P(PrefetchTest, TraceReadAsyncWithCallbackWrapper) {
|
||||
ASSERT_OK(db_->EndIOTrace());
|
||||
ASSERT_OK(env_->FileExists(trace_file_path));
|
||||
|
||||
ASSERT_EQ(num_keys, total_keys);
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
|
||||
// Check stats to make sure async prefetch is done.
|
||||
{
|
||||
if (read_async_called) {
|
||||
ASSERT_EQ(num_keys, total_keys);
|
||||
ASSERT_GT(buff_prefetch_count, 0);
|
||||
// Check stats to make sure async prefetch is done.
|
||||
HistogramData async_read_bytes;
|
||||
options.statistics->histogramData(ASYNC_READ_BYTES, &async_read_bytes);
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
} else {
|
||||
// Not all platforms support iouring. In that case, ReadAsync in posix
|
||||
// won't submit async requests.
|
||||
if (read_async_called) {
|
||||
ASSERT_GT(async_read_bytes.count, 0);
|
||||
} else {
|
||||
ASSERT_EQ(async_read_bytes.count, 0);
|
||||
}
|
||||
ASSERT_EQ(iter->status(), Status::NotSupported());
|
||||
}
|
||||
|
||||
// Check the file to see if ReadAsync is logged.
|
||||
@@ -2102,8 +2090,13 @@ TEST_F(FilePrefetchBufferTest, SeekWithBlockCacheHit) {
|
||||
// Simulate a seek of 4096 bytes at offset 0. Due to the readahead settings,
|
||||
// it will do two reads of 4096+8192 and 8192
|
||||
Status s = fpb.PrefetchAsync(IOOptions(), r.get(), 0, 4096, &result);
|
||||
// Platforms that don't have IO uring may not support async IO
|
||||
ASSERT_TRUE(s.IsTryAgain() || s.IsNotSupported());
|
||||
|
||||
// Platforms that don't have IO uring may not support async IO.
|
||||
if (s.IsNotSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
// Simulate a block cache hit
|
||||
fpb.UpdateReadPattern(0, 4096, false);
|
||||
// Now read some data that straddles the two prefetch buffers - offset 8192 to
|
||||
@@ -2137,9 +2130,13 @@ TEST_F(FilePrefetchBufferTest, NoSyncWithAsyncIO) {
|
||||
// Simulate a seek of 4000 bytes at offset 3000. Due to the readahead
|
||||
// settings, it will do two reads of 4000+4096 and 4096
|
||||
Status s = fpb.PrefetchAsync(IOOptions(), r.get(), 3000, 4000, &async_result);
|
||||
// Platforms that don't have IO uring may not support async IO
|
||||
ASSERT_TRUE(s.IsTryAgain() || s.IsNotSupported());
|
||||
|
||||
// Platforms that don't have IO uring may not support async IO
|
||||
if (s.IsNotSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
ASSERT_TRUE(fpb.TryReadFromCacheAsync(IOOptions(), r.get(), /*offset=*/3000,
|
||||
/*length=*/4000, &async_result, &s,
|
||||
Env::IOPriority::IO_LOW));
|
||||
|
||||
@@ -0,0 +1,527 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// 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).
|
||||
//
|
||||
// APIs for customizing read caches in RocksDB.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/memory_allocator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Logger;
|
||||
class Statistics;
|
||||
|
||||
// A Cache maps keys to objects resident in memory, tracks reference counts
|
||||
// on those key-object entries, and is able to remove unreferenced entries
|
||||
// whenever it wants. All operations are fully thread safe except as noted.
|
||||
// Inserted entries have a specified "charge" which is some quantity in
|
||||
// unspecified units, typically bytes of memory used. A Cache will typically
|
||||
// have a finite capacity in units of charge, and evict entries as needed
|
||||
// to stay at or below that capacity.
|
||||
//
|
||||
// NOTE: This API is for expert use only and is intended more for customizing
|
||||
// cache behavior than for calling into outside of RocksDB. It is subject to
|
||||
// change as RocksDB evolves, especially the RocksDB block cache. Overriding
|
||||
// CacheWrapper is the preferred way of customizing some operations on an
|
||||
// existing implementation.
|
||||
//
|
||||
// INTERNAL: See typed_cache.h for convenient wrappers on top of this API.
|
||||
// New virtual functions must also be added to CacheWrapper below.
|
||||
class Cache {
|
||||
public: // types hidden from API client
|
||||
// Opaque handle to an entry stored in the cache.
|
||||
struct Handle {};
|
||||
|
||||
public: // types hidden from Cache implementation
|
||||
// Pointer to cached object of unspecified type. (This type alias is
|
||||
// provided for clarity, not really for type checking.)
|
||||
using ObjectPtr = void*;
|
||||
|
||||
// Opaque object providing context (settings, etc.) to create objects
|
||||
// for primary cache from saved (serialized) secondary cache entries.
|
||||
struct CreateContext {};
|
||||
|
||||
public: // type defs
|
||||
// Depending on implementation, cache entries with higher priority levels
|
||||
// could be less likely to get evicted than entries with lower priority
|
||||
// levels. The "high" priority level applies to certain SST metablocks (e.g.
|
||||
// index and filter blocks) if the option
|
||||
// cache_index_and_filter_blocks_with_high_priority is set. The "low" priority
|
||||
// level is used for other kinds of SST blocks (most importantly, data
|
||||
// blocks), as well as the above metablocks in case
|
||||
// cache_index_and_filter_blocks_with_high_priority is
|
||||
// not set. The "bottom" priority level is for BlobDB's blob values.
|
||||
enum class Priority { HIGH, LOW, BOTTOM };
|
||||
|
||||
// A set of callbacks to allow objects in the primary block cache to be
|
||||
// be persisted in a secondary cache. The purpose of the secondary cache
|
||||
// is to support other ways of caching the object, such as persistent or
|
||||
// compressed data, that may require the object to be parsed and transformed
|
||||
// in some way. Since the primary cache holds C++ objects and the secondary
|
||||
// cache may only hold flat data that doesn't need relocation, these
|
||||
// callbacks need to be provided by the user of the block
|
||||
// cache to do the conversion.
|
||||
// The CacheItemHelper is passed to Insert() and Lookup(). It has pointers
|
||||
// to callback functions for size, saving and deletion of the
|
||||
// object. The callbacks are defined in C-style in order to make them
|
||||
// stateless and not add to the cache metadata size.
|
||||
// Saving multiple std::function objects will take up 32 bytes per
|
||||
// function, even if its not bound to an object and does no capture.
|
||||
//
|
||||
// All the callbacks are C-style function pointers in order to simplify
|
||||
// lifecycle management. Objects in the cache can outlive the parent DB,
|
||||
// so anything required for these operations should be contained in the
|
||||
// object itself.
|
||||
//
|
||||
// The SizeCallback takes a pointer to the object and returns the size
|
||||
// of the persistable data. It can be used by the secondary cache to allocate
|
||||
// memory if needed.
|
||||
//
|
||||
// RocksDB callbacks are NOT exception-safe. A callback completing with an
|
||||
// exception can lead to undefined behavior in RocksDB, including data loss,
|
||||
// unreported corruption, deadlocks, and more.
|
||||
using SizeCallback = size_t (*)(ObjectPtr obj);
|
||||
|
||||
// The SaveToCallback takes an object pointer and saves the persistable
|
||||
// data into a buffer. The secondary cache may decide to not store it in a
|
||||
// contiguous buffer, in which case this callback will be called multiple
|
||||
// times with increasing offset
|
||||
using SaveToCallback = Status (*)(ObjectPtr from_obj, size_t from_offset,
|
||||
size_t length, char* out_buf);
|
||||
|
||||
// A function pointer type for destruction of a cache object. This will
|
||||
// typically call the destructor for the appropriate type of the object.
|
||||
// The Cache is responsible for copying and reclaiming space for the key,
|
||||
// but objects are managed in part using this callback. Generally a DeleterFn
|
||||
// can be nullptr if the ObjectPtr does not need destruction (e.g. nullptr or
|
||||
// pointer into static data).
|
||||
using DeleterFn = void (*)(ObjectPtr obj, MemoryAllocator* allocator);
|
||||
|
||||
// The CreateCallback is takes in a buffer from the NVM cache and constructs
|
||||
// an object using it. The callback doesn't have ownership of the buffer and
|
||||
// should copy the contents into its own buffer. The CreateContext* is
|
||||
// provided by Lookup and may be used to follow DB- or CF-specific settings.
|
||||
// In case of some error, non-OK is returned and the caller should ignore
|
||||
// any result in out_obj. (The implementation must clean up after itself.)
|
||||
using CreateCallback = Status (*)(const Slice& data, CreateContext* context,
|
||||
MemoryAllocator* allocator,
|
||||
ObjectPtr* out_obj, size_t* out_charge);
|
||||
|
||||
// A struct with pointers to helper functions for spilling items from the
|
||||
// cache into the secondary cache. May be extended in the future. An
|
||||
// instance of this struct is expected to outlive the cache.
|
||||
struct CacheItemHelper {
|
||||
// Function for deleting an object on its removal from the Cache.
|
||||
// nullptr is only for entries that require no destruction, such as
|
||||
// "placeholder" cache entries with nullptr object.
|
||||
DeleterFn del_cb; // (<- Most performance critical)
|
||||
// Next three are used for persisting values as described above.
|
||||
// If any is nullptr, then all three should be nullptr and persisting the
|
||||
// entry to/from secondary cache is not supported.
|
||||
SizeCallback size_cb;
|
||||
SaveToCallback saveto_cb;
|
||||
CreateCallback create_cb;
|
||||
// Classification of the entry for monitoring purposes in block cache.
|
||||
CacheEntryRole role;
|
||||
|
||||
constexpr CacheItemHelper()
|
||||
: del_cb(nullptr),
|
||||
size_cb(nullptr),
|
||||
saveto_cb(nullptr),
|
||||
create_cb(nullptr),
|
||||
role(CacheEntryRole::kMisc) {}
|
||||
|
||||
explicit constexpr CacheItemHelper(CacheEntryRole _role,
|
||||
DeleterFn _del_cb = nullptr,
|
||||
SizeCallback _size_cb = nullptr,
|
||||
SaveToCallback _saveto_cb = nullptr,
|
||||
CreateCallback _create_cb = nullptr)
|
||||
: del_cb(_del_cb),
|
||||
size_cb(_size_cb),
|
||||
saveto_cb(_saveto_cb),
|
||||
create_cb(_create_cb),
|
||||
role(_role) {
|
||||
// Either all three secondary cache callbacks are non-nullptr or
|
||||
// all three are nullptr
|
||||
assert((size_cb != nullptr) == (saveto_cb != nullptr));
|
||||
assert((size_cb != nullptr) == (create_cb != nullptr));
|
||||
}
|
||||
inline bool IsSecondaryCacheCompatible() const {
|
||||
return size_cb != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
public: // ctor/dtor/create
|
||||
Cache(std::shared_ptr<MemoryAllocator> allocator = nullptr)
|
||||
: memory_allocator_(std::move(allocator)) {}
|
||||
// No copying allowed
|
||||
Cache(const Cache&) = delete;
|
||||
Cache& operator=(const Cache&) = delete;
|
||||
|
||||
// Destroys all remaining entries by calling the associated "deleter"
|
||||
virtual ~Cache() {}
|
||||
|
||||
// Creates a new Cache based on the input value string and returns the result.
|
||||
// Currently, this method can be used to create LRUCaches only
|
||||
// @param config_options
|
||||
// @param value The value might be:
|
||||
// - an old-style cache ("1M") -- equivalent to NewLRUCache(1024*102(
|
||||
// - Name-value option pairs -- "capacity=1M; num_shard_bits=4;
|
||||
// For the LRUCache, the values are defined in LRUCacheOptions.
|
||||
// @param result The new Cache object
|
||||
// @return OK if the cache was successfully created
|
||||
// @return NotFound if an invalid name was specified in the value
|
||||
// @return InvalidArgument if either the options were not valid
|
||||
static Status CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result);
|
||||
|
||||
public: // functions
|
||||
// The type of the Cache
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// The Insert and Lookup APIs below are intended to allow cached objects
|
||||
// to be demoted/promoted between the primary block cache and a secondary
|
||||
// cache. The secondary cache could be a non-volatile cache, and will
|
||||
// likely store the object in a different representation. They rely on a
|
||||
// per object CacheItemHelper to do the conversions.
|
||||
// The secondary cache may persist across process and system restarts,
|
||||
// and may even be moved between hosts. Therefore, the cache key must
|
||||
// be repeatable across restarts/reboots, and globally unique if
|
||||
// multiple DBs share the same cache and the set of DBs can change
|
||||
// over time.
|
||||
|
||||
// Insert a mapping from key->object into the cache and assign it
|
||||
// the specified charge against the total cache capacity. If
|
||||
// strict_capacity_limit is true and cache reaches its full capacity,
|
||||
// return Status::MemoryLimit. `obj` must be non-nullptr if compatible
|
||||
// with secondary cache (helper->size_cb != nullptr), because Value() ==
|
||||
// nullptr is reserved for indicating some secondary cache failure cases.
|
||||
// On success, returns OK and takes ownership of `obj`, eventually deleting
|
||||
// it with helper->del_cb. On non-OK return, the caller maintains ownership
|
||||
// of `obj` so will often need to delete it in such cases.
|
||||
//
|
||||
// The helper argument is saved by the cache and will be used when the
|
||||
// inserted object is evicted or considered for promotion to the secondary
|
||||
// cache. Promotion to secondary cache is only enabled if helper->size_cb
|
||||
// != nullptr. The helper must outlive the cache. Callers may use
|
||||
// &kNoopCacheItemHelper as a trivial helper (no deleter for the object,
|
||||
// no secondary cache). `helper` must not be nullptr (efficiency).
|
||||
//
|
||||
// If `handle` is not nullptr and return status is OK, `handle` is set
|
||||
// to a Handle* for the entry. The caller must call this->Release(handle)
|
||||
// when the returned entry is no longer needed. If `handle` is nullptr, it is
|
||||
// as if Release is called immediately after Insert.
|
||||
//
|
||||
// Regardless of whether the item was inserted into the cache,
|
||||
// it will attempt to insert it into the secondary cache if one is
|
||||
// configured, and the helper supports it.
|
||||
// The cache implementation must support a secondary cache, otherwise
|
||||
// the item is only inserted into the primary cache. It may
|
||||
// defer the insertion to the secondary cache as it sees fit.
|
||||
//
|
||||
// When the inserted entry is no longer needed, it will be destroyed using
|
||||
// helper->del_cb (if non-nullptr).
|
||||
virtual Status Insert(const Slice& key, ObjectPtr obj,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) = 0;
|
||||
|
||||
// Lookup the key, returning nullptr if not found. If found, returns
|
||||
// a handle to the mapping that must eventually be passed to Release().
|
||||
//
|
||||
// If a non-nullptr helper argument is provided with a non-nullptr
|
||||
// create_cb, and a secondary cache is configured, then the secondary
|
||||
// cache is also queried if lookup in the primary cache fails. If found
|
||||
// in secondary cache, the provided create_db and create_context are
|
||||
// used to promote the entry to an object in the primary cache.
|
||||
// In that case, the helper may be saved and used later when the object
|
||||
// is evicted, so as usual, the pointed-to helper must outlive the cache.
|
||||
//
|
||||
// ======================== Async Lookup (wait=false) ======================
|
||||
// When wait=false, the handle returned might be in any of three states:
|
||||
// * Present - If Value() != nullptr, then the result is present and
|
||||
// the handle can be used just as if wait=true.
|
||||
// * Pending, not ready (IsReady() == false) - secondary cache is still
|
||||
// working to retrieve the value. Might become ready any time.
|
||||
// * Pending, ready (IsReady() == true) - secondary cache has the value
|
||||
// but it has not been loaded as an object into primary cache. Call to
|
||||
// Wait()/WaitAll() will not block.
|
||||
//
|
||||
// IMPORTANT: Pending handles are not thread-safe, and only these functions
|
||||
// are allowed on them: Value(), IsReady(), Wait(), WaitAll(). Even Release()
|
||||
// can only come after Wait() or WaitAll() even though a reference is held.
|
||||
//
|
||||
// Only Wait()/WaitAll() gets a Handle out of a Pending state. (Waiting is
|
||||
// safe and has no effect on other handle states.) After waiting on a Handle,
|
||||
// it is in one of two states:
|
||||
// * Present - if Value() != nullptr
|
||||
// * Failed - if Value() == nullptr, such as if the secondary cache
|
||||
// initially thought it had the value but actually did not.
|
||||
//
|
||||
// Note that given an arbitrary Handle, the only way to distinguish the
|
||||
// Pending+ready state from the Failed state is to Wait() on it. A cache
|
||||
// entry not compatible with secondary cache can also have Value()==nullptr
|
||||
// like the Failed state, but this is not generally a concern.
|
||||
virtual Handle* Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper = nullptr,
|
||||
CreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW, bool wait = true,
|
||||
Statistics* stats = nullptr) = 0;
|
||||
|
||||
// Convenience wrapper when secondary cache not supported
|
||||
inline Handle* BasicLookup(const Slice& key, Statistics* stats) {
|
||||
return Lookup(key, nullptr, nullptr, Priority::LOW, true, stats);
|
||||
}
|
||||
|
||||
// Increments the reference count for the handle if it refers to an entry in
|
||||
// the cache. Returns true if refcount was incremented; otherwise, returns
|
||||
// false.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual bool Ref(Handle* handle) = 0;
|
||||
|
||||
/**
|
||||
* Release a mapping returned by a previous Lookup(). A released entry might
|
||||
* still remain in cache in case it is later looked up by others. If
|
||||
* erase_if_last_ref is set then it also erases it from the cache if there is
|
||||
* no other reference to it. Erasing it should call the deleter function that
|
||||
* was provided when the entry was inserted.
|
||||
*
|
||||
* Returns true if the entry was also erased.
|
||||
*/
|
||||
// REQUIRES: handle must not have been released yet.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual bool Release(Handle* handle, bool erase_if_last_ref = false) = 0;
|
||||
|
||||
// Return the object assiciated with a handle returned by a successful
|
||||
// Lookup(). For historical reasons, this is also known at the "value"
|
||||
// associated with the key.
|
||||
// REQUIRES: handle must not have been released yet.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual ObjectPtr Value(Handle* handle) = 0;
|
||||
|
||||
// If the cache contains the entry for the key, erase it. Note that the
|
||||
// underlying entry will be kept around until all existing handles
|
||||
// to it have been released.
|
||||
virtual void Erase(const Slice& key) = 0;
|
||||
// Return a new numeric id. May be used by multiple clients who are
|
||||
// sharding the same cache to partition the key space. Typically the
|
||||
// client will allocate a new id at startup and prepend the id to
|
||||
// its cache keys.
|
||||
virtual uint64_t NewId() = 0;
|
||||
|
||||
// sets the maximum configured capacity of the cache. When the new
|
||||
// capacity is less than the old capacity and the existing usage is
|
||||
// greater than new capacity, the implementation will do its best job to
|
||||
// purge the released entries from the cache in order to lower the usage
|
||||
virtual void SetCapacity(size_t capacity) = 0;
|
||||
|
||||
// Set whether to return error on insertion when cache reaches its full
|
||||
// capacity.
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) = 0;
|
||||
|
||||
// Get the flag whether to return error on insertion when cache reaches its
|
||||
// full capacity.
|
||||
virtual bool HasStrictCapacityLimit() const = 0;
|
||||
|
||||
// Returns the maximum configured capacity of the cache
|
||||
virtual size_t GetCapacity() const = 0;
|
||||
|
||||
// Returns the memory size for the entries residing in the cache.
|
||||
virtual size_t GetUsage() const = 0;
|
||||
|
||||
// Returns the number of entries currently tracked in the table. SIZE_MAX
|
||||
// means "not supported." This is used for inspecting the load factor, along
|
||||
// with GetTableAddressCount().
|
||||
virtual size_t GetOccupancyCount() const { return SIZE_MAX; }
|
||||
|
||||
// Returns the number of ways the hash function is divided for addressing
|
||||
// entries. Zero means "not supported." This is used for inspecting the load
|
||||
// factor, along with GetOccupancyCount().
|
||||
virtual size_t GetTableAddressCount() const { return 0; }
|
||||
|
||||
// Returns the memory size for a specific entry in the cache.
|
||||
virtual size_t GetUsage(Handle* handle) const = 0;
|
||||
|
||||
// Returns the memory size for the entries in use by the system
|
||||
virtual size_t GetPinnedUsage() const = 0;
|
||||
|
||||
// Returns the charge for the specific entry in the cache.
|
||||
virtual size_t GetCharge(Handle* handle) const = 0;
|
||||
|
||||
// Returns the helper for the specified entry.
|
||||
virtual const CacheItemHelper* GetCacheItemHelper(Handle* handle) const = 0;
|
||||
|
||||
// Call this on shutdown if you want to speed it up. Cache will disown
|
||||
// any underlying data and will not free it on delete. This call will leak
|
||||
// memory - call this only if you're shutting down the process.
|
||||
// Any attempts of using cache after this call will fail terribly.
|
||||
// Always delete the DB object before calling this method!
|
||||
virtual void DisownData() {
|
||||
// default implementation is noop
|
||||
}
|
||||
|
||||
struct ApplyToAllEntriesOptions {
|
||||
// If the Cache uses locks, setting `average_entries_per_lock` to
|
||||
// a higher value suggests iterating over more entries each time a lock
|
||||
// is acquired, likely reducing the time for ApplyToAllEntries but
|
||||
// increasing latency for concurrent users of the Cache. Setting
|
||||
// `average_entries_per_lock` to a smaller value could be helpful if
|
||||
// callback is relatively expensive, such as using large data structures.
|
||||
size_t average_entries_per_lock = 256;
|
||||
};
|
||||
|
||||
// Apply a callback to all entries in the cache. The Cache must ensure
|
||||
// thread safety but does not guarantee that a consistent snapshot of all
|
||||
// entries is iterated over if other threads are operating on the Cache
|
||||
// also.
|
||||
virtual void ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, ObjectPtr obj, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) = 0;
|
||||
|
||||
// Remove all entries.
|
||||
// Prerequisite: no entry is referenced.
|
||||
virtual void EraseUnRefEntries() = 0;
|
||||
|
||||
virtual std::string GetPrintableOptions() const { return ""; }
|
||||
|
||||
// Check for any warnings or errors in the operation of the cache and
|
||||
// report them to the logger. This is intended only to be called
|
||||
// periodically so does not need to be very efficient. (Obscure calling
|
||||
// conventions for Logger inherited from env.h)
|
||||
virtual void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const {}
|
||||
|
||||
MemoryAllocator* memory_allocator() const { return memory_allocator_.get(); }
|
||||
|
||||
// EXPERIMENTAL
|
||||
// The following APIs are experimental and might change in the future.
|
||||
|
||||
// Release a mapping returned by a previous Lookup(). The "useful"
|
||||
// parameter specifies whether the data was actually used or not,
|
||||
// which may be used by the cache implementation to decide whether
|
||||
// to consider it as a hit for retention purposes. As noted elsewhere,
|
||||
// "pending" handles require Wait()/WaitAll() before Release().
|
||||
virtual bool Release(Handle* handle, bool /*useful*/,
|
||||
bool erase_if_last_ref) {
|
||||
return Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
// Determines if the handle returned by Lookup() can give a value without
|
||||
// blocking, though Wait()/WaitAll() might be required to publish it to
|
||||
// Value(). See secondary cache compatible Lookup() above for details.
|
||||
// This call is not thread safe on "pending" handles.
|
||||
virtual bool IsReady(Handle* /*handle*/) { return true; }
|
||||
|
||||
// Convert a "pending" handle into a full thread-shareable handle by
|
||||
// * If necessary, wait until secondary cache finishes loading the value.
|
||||
// * Construct the object for primary cache and set it in the handle.
|
||||
// Even after Wait() on a pending handle, the caller must check for
|
||||
// Value() == nullptr in case of failure. This call is not thread-safe
|
||||
// on pending handles. This call has no effect on non-pending handles.
|
||||
// See secondary cache compatible Lookup() above for details.
|
||||
virtual void Wait(Handle* /*handle*/) {}
|
||||
|
||||
// Wait for a vector of handles to become ready. As with Wait(), the user
|
||||
// should check the Value() of each handle for nullptr. This call is not
|
||||
// thread-safe on pending handles.
|
||||
virtual void WaitAll(std::vector<Handle*>& /*handles*/) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator_;
|
||||
};
|
||||
|
||||
// A wrapper around Cache that can easily be extended with instrumentation,
|
||||
// etc.
|
||||
class CacheWrapper : public Cache {
|
||||
public:
|
||||
explicit CacheWrapper(std::shared_ptr<Cache> target)
|
||||
: target_(std::move(target)) {}
|
||||
|
||||
// Only function that derived class must provide
|
||||
// const char* Name() const override { ... }
|
||||
|
||||
Status Insert(const Slice& key, ObjectPtr value,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
return target_->Insert(key, value, helper, charge, handle, priority);
|
||||
}
|
||||
|
||||
Handle* Lookup(const Slice& key, const CacheItemHelper* helper,
|
||||
CreateContext* create_context,
|
||||
Priority priority = Priority::LOW, bool wait = true,
|
||||
Statistics* stats = nullptr) override {
|
||||
return target_->Lookup(key, helper, create_context, priority, wait, stats);
|
||||
}
|
||||
|
||||
bool Ref(Handle* handle) override { return target_->Ref(handle); }
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool erase_if_last_ref = false) override {
|
||||
return target_->Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
ObjectPtr Value(Handle* handle) override { return target_->Value(handle); }
|
||||
|
||||
void Erase(const Slice& key) override { target_->Erase(key); }
|
||||
uint64_t NewId() override { return target_->NewId(); }
|
||||
|
||||
void SetCapacity(size_t capacity) override { target_->SetCapacity(capacity); }
|
||||
|
||||
void SetStrictCapacityLimit(bool strict_capacity_limit) override {
|
||||
target_->SetStrictCapacityLimit(strict_capacity_limit);
|
||||
}
|
||||
|
||||
bool HasStrictCapacityLimit() const override {
|
||||
return target_->HasStrictCapacityLimit();
|
||||
}
|
||||
|
||||
size_t GetCapacity() const override { return target_->GetCapacity(); }
|
||||
|
||||
size_t GetUsage() const override { return target_->GetUsage(); }
|
||||
|
||||
size_t GetUsage(Handle* handle) const override {
|
||||
return target_->GetUsage(handle);
|
||||
}
|
||||
|
||||
size_t GetPinnedUsage() const override { return target_->GetPinnedUsage(); }
|
||||
|
||||
size_t GetCharge(Handle* handle) const override {
|
||||
return target_->GetCharge(handle);
|
||||
}
|
||||
|
||||
const CacheItemHelper* GetCacheItemHelper(Handle* handle) const override {
|
||||
return target_->GetCacheItemHelper(handle);
|
||||
}
|
||||
|
||||
void ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, ObjectPtr value, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) override {
|
||||
target_->ApplyToAllEntries(callback, opts);
|
||||
}
|
||||
|
||||
void EraseUnRefEntries() override { target_->EraseUnRefEntries(); }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Cache> target_;
|
||||
};
|
||||
|
||||
// Useful for cache entries requiring no clean-up, such as for cache
|
||||
// reservations
|
||||
inline constexpr Cache::CacheItemHelper kNoopCacheItemHelper{};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+18
-432
@@ -7,26 +7,22 @@
|
||||
// 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.
|
||||
//
|
||||
// Various APIs for creating and customizing read caches in RocksDB.
|
||||
// Various APIs for configuring, creating, and monitoring read caches.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/compression_type.h"
|
||||
#include "rocksdb/data_structure.h"
|
||||
#include "rocksdb/memory_allocator.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Cache;
|
||||
class Cache; // defined in advanced_cache.h
|
||||
struct ConfigOptions;
|
||||
class Logger;
|
||||
class SecondaryCache;
|
||||
|
||||
// Classifications of block cache entries.
|
||||
@@ -75,6 +71,9 @@ constexpr uint32_t kNumCacheEntryRoles =
|
||||
// Obtain a hyphen-separated, lowercase name of a `CacheEntryRole`.
|
||||
const std::string& GetCacheEntryRoleName(CacheEntryRole);
|
||||
|
||||
// A fast bit set for CacheEntryRoles
|
||||
using CacheEntryRoleSet = SmallEnumSet<CacheEntryRole, CacheEntryRole::kMisc>;
|
||||
|
||||
// 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.
|
||||
@@ -240,6 +239,10 @@ struct CompressedSecondaryCacheOptions : LRUCacheOptions {
|
||||
// into chunks so that they may better fit jemalloc bins.
|
||||
bool enable_custom_split_merge = false;
|
||||
|
||||
// Kinds of entries that should not be compressed, but can be stored.
|
||||
// (Filter blocks are essentially non-compressible but others usually are.)
|
||||
CacheEntryRoleSet do_not_compress_roles = {CacheEntryRole::kFilterBlock};
|
||||
|
||||
CompressedSecondaryCacheOptions() {}
|
||||
CompressedSecondaryCacheOptions(
|
||||
size_t _capacity, int _num_shard_bits, bool _strict_capacity_limit,
|
||||
@@ -250,14 +253,17 @@ struct CompressedSecondaryCacheOptions : LRUCacheOptions {
|
||||
kDefaultCacheMetadataChargePolicy,
|
||||
CompressionType _compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t _compress_format_version = 2,
|
||||
bool _enable_custom_split_merge = false)
|
||||
bool _enable_custom_split_merge = false,
|
||||
const CacheEntryRoleSet& _do_not_compress_roles =
|
||||
{CacheEntryRole::kFilterBlock})
|
||||
: LRUCacheOptions(_capacity, _num_shard_bits, _strict_capacity_limit,
|
||||
_high_pri_pool_ratio, std::move(_memory_allocator),
|
||||
_use_adaptive_mutex, _metadata_charge_policy,
|
||||
_low_pri_pool_ratio),
|
||||
compression_type(_compression_type),
|
||||
compress_format_version(_compress_format_version),
|
||||
enable_custom_split_merge(_enable_custom_split_merge) {}
|
||||
enable_custom_split_merge(_enable_custom_split_merge),
|
||||
do_not_compress_roles(_do_not_compress_roles) {}
|
||||
};
|
||||
|
||||
// EXPERIMENTAL
|
||||
@@ -272,7 +278,9 @@ extern std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
kDefaultCacheMetadataChargePolicy,
|
||||
CompressionType compression_type = CompressionType::kLZ4Compression,
|
||||
uint32_t compress_format_version = 2,
|
||||
bool enable_custom_split_merge = false);
|
||||
bool enable_custom_split_merge = false,
|
||||
const CacheEntryRoleSet& _do_not_compress_roles = {
|
||||
CacheEntryRole::kFilterBlock});
|
||||
|
||||
extern std::shared_ptr<SecondaryCache> NewCompressedSecondaryCache(
|
||||
const CompressedSecondaryCacheOptions& opts);
|
||||
@@ -352,426 +360,4 @@ extern std::shared_ptr<Cache> NewClockCache(
|
||||
CacheMetadataChargePolicy metadata_charge_policy =
|
||||
kDefaultCacheMetadataChargePolicy);
|
||||
|
||||
// A Cache maps keys to objects resident in memory, tracks reference counts
|
||||
// on those key-object entries, and is able to remove unreferenced entries
|
||||
// whenever it wants. All operations are fully thread safe except as noted.
|
||||
// Inserted entries have a specified "charge" which is some quantity in
|
||||
// unspecified units, typically bytes of memory used. A Cache will typically
|
||||
// have a finite capacity in units of charge, and evict entries as needed
|
||||
// to stay at or below that capacity.
|
||||
//
|
||||
// NOTE: This API is for expert use only and is more intended for providing
|
||||
// custom implementations than for calling into. It is subject to change
|
||||
// as RocksDB evolves, especially the RocksDB block cache.
|
||||
//
|
||||
// INTERNAL: See typed_cache.h for convenient wrappers on top of this API.
|
||||
class Cache {
|
||||
public: // types hidden from API client
|
||||
// Opaque handle to an entry stored in the cache.
|
||||
struct Handle {};
|
||||
|
||||
public: // types hidden from Cache implementation
|
||||
// Pointer to cached object of unspecified type. (This type alias is
|
||||
// provided for clarity, not really for type checking.)
|
||||
using ObjectPtr = void*;
|
||||
|
||||
// Opaque object providing context (settings, etc.) to create objects
|
||||
// for primary cache from saved (serialized) secondary cache entries.
|
||||
struct CreateContext {};
|
||||
|
||||
public: // type defs
|
||||
// Depending on implementation, cache entries with higher priority levels
|
||||
// could be less likely to get evicted than entries with lower priority
|
||||
// levels. The "high" priority level applies to certain SST metablocks (e.g.
|
||||
// index and filter blocks) if the option
|
||||
// cache_index_and_filter_blocks_with_high_priority is set. The "low" priority
|
||||
// level is used for other kinds of SST blocks (most importantly, data
|
||||
// blocks), as well as the above metablocks in case
|
||||
// cache_index_and_filter_blocks_with_high_priority is
|
||||
// not set. The "bottom" priority level is for BlobDB's blob values.
|
||||
enum class Priority { HIGH, LOW, BOTTOM };
|
||||
|
||||
// A set of callbacks to allow objects in the primary block cache to be
|
||||
// be persisted in a secondary cache. The purpose of the secondary cache
|
||||
// is to support other ways of caching the object, such as persistent or
|
||||
// compressed data, that may require the object to be parsed and transformed
|
||||
// in some way. Since the primary cache holds C++ objects and the secondary
|
||||
// cache may only hold flat data that doesn't need relocation, these
|
||||
// callbacks need to be provided by the user of the block
|
||||
// cache to do the conversion.
|
||||
// The CacheItemHelper is passed to Insert() and Lookup(). It has pointers
|
||||
// to callback functions for size, saving and deletion of the
|
||||
// object. The callbacks are defined in C-style in order to make them
|
||||
// stateless and not add to the cache metadata size.
|
||||
// Saving multiple std::function objects will take up 32 bytes per
|
||||
// function, even if its not bound to an object and does no capture.
|
||||
//
|
||||
// All the callbacks are C-style function pointers in order to simplify
|
||||
// lifecycle management. Objects in the cache can outlive the parent DB,
|
||||
// so anything required for these operations should be contained in the
|
||||
// object itself.
|
||||
//
|
||||
// The SizeCallback takes a pointer to the object and returns the size
|
||||
// of the persistable data. It can be used by the secondary cache to allocate
|
||||
// memory if needed.
|
||||
//
|
||||
// RocksDB callbacks are NOT exception-safe. A callback completing with an
|
||||
// exception can lead to undefined behavior in RocksDB, including data loss,
|
||||
// unreported corruption, deadlocks, and more.
|
||||
using SizeCallback = size_t (*)(ObjectPtr obj);
|
||||
|
||||
// The SaveToCallback takes an object pointer and saves the persistable
|
||||
// data into a buffer. The secondary cache may decide to not store it in a
|
||||
// contiguous buffer, in which case this callback will be called multiple
|
||||
// times with increasing offset
|
||||
using SaveToCallback = Status (*)(ObjectPtr from_obj, size_t from_offset,
|
||||
size_t length, char* out_buf);
|
||||
|
||||
// A function pointer type for destruction of a cache object. This will
|
||||
// typically call the destructor for the appropriate type of the object.
|
||||
// The Cache is responsible for copying and reclaiming space for the key,
|
||||
// but objects are managed in part using this callback. Generally a DeleterFn
|
||||
// can be nullptr if the ObjectPtr does not need destruction (e.g. nullptr or
|
||||
// pointer into static data).
|
||||
using DeleterFn = void (*)(ObjectPtr obj, MemoryAllocator* allocator);
|
||||
|
||||
// The CreateCallback is takes in a buffer from the NVM cache and constructs
|
||||
// an object using it. The callback doesn't have ownership of the buffer and
|
||||
// should copy the contents into its own buffer. The CreateContext* is
|
||||
// provided by Lookup and may be used to follow DB- or CF-specific settings.
|
||||
// In case of some error, non-OK is returned and the caller should ignore
|
||||
// any result in out_obj. (The implementation must clean up after itself.)
|
||||
using CreateCallback = Status (*)(const Slice& data, CreateContext* context,
|
||||
MemoryAllocator* allocator,
|
||||
ObjectPtr* out_obj, size_t* out_charge);
|
||||
|
||||
// A struct with pointers to helper functions for spilling items from the
|
||||
// cache into the secondary cache. May be extended in the future. An
|
||||
// instance of this struct is expected to outlive the cache.
|
||||
struct CacheItemHelper {
|
||||
// Function for deleting an object on its removal from the Cache.
|
||||
// nullptr is only for entries that require no destruction, such as
|
||||
// "placeholder" cache entries with nullptr object.
|
||||
DeleterFn del_cb; // (<- Most performance critical)
|
||||
// Next three are used for persisting values as described above.
|
||||
// If any is nullptr, then all three should be nullptr and persisting the
|
||||
// entry to/from secondary cache is not supported.
|
||||
SizeCallback size_cb;
|
||||
SaveToCallback saveto_cb;
|
||||
CreateCallback create_cb;
|
||||
// Classification of the entry for monitoring purposes in block cache.
|
||||
CacheEntryRole role;
|
||||
|
||||
constexpr CacheItemHelper()
|
||||
: del_cb(nullptr),
|
||||
size_cb(nullptr),
|
||||
saveto_cb(nullptr),
|
||||
create_cb(nullptr),
|
||||
role(CacheEntryRole::kMisc) {}
|
||||
|
||||
explicit constexpr CacheItemHelper(CacheEntryRole _role,
|
||||
DeleterFn _del_cb = nullptr,
|
||||
SizeCallback _size_cb = nullptr,
|
||||
SaveToCallback _saveto_cb = nullptr,
|
||||
CreateCallback _create_cb = nullptr)
|
||||
: del_cb(_del_cb),
|
||||
size_cb(_size_cb),
|
||||
saveto_cb(_saveto_cb),
|
||||
create_cb(_create_cb),
|
||||
role(_role) {
|
||||
// Either all three secondary cache callbacks are non-nullptr or
|
||||
// all three are nullptr
|
||||
assert((size_cb != nullptr) == (saveto_cb != nullptr));
|
||||
assert((size_cb != nullptr) == (create_cb != nullptr));
|
||||
}
|
||||
inline bool IsSecondaryCacheCompatible() const {
|
||||
return size_cb != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
public: // ctor/dtor/create
|
||||
Cache(std::shared_ptr<MemoryAllocator> allocator = nullptr)
|
||||
: memory_allocator_(std::move(allocator)) {}
|
||||
// No copying allowed
|
||||
Cache(const Cache&) = delete;
|
||||
Cache& operator=(const Cache&) = delete;
|
||||
|
||||
// Destroys all remaining entries by calling the associated "deleter"
|
||||
virtual ~Cache() {}
|
||||
|
||||
// Creates a new Cache based on the input value string and returns the result.
|
||||
// Currently, this method can be used to create LRUCaches only
|
||||
// @param config_options
|
||||
// @param value The value might be:
|
||||
// - an old-style cache ("1M") -- equivalent to NewLRUCache(1024*102(
|
||||
// - Name-value option pairs -- "capacity=1M; num_shard_bits=4;
|
||||
// For the LRUCache, the values are defined in LRUCacheOptions.
|
||||
// @param result The new Cache object
|
||||
// @return OK if the cache was successfully created
|
||||
// @return NotFound if an invalid name was specified in the value
|
||||
// @return InvalidArgument if either the options were not valid
|
||||
static Status CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result);
|
||||
|
||||
public: // functions
|
||||
// The type of the Cache
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// The Insert and Lookup APIs below are intended to allow cached objects
|
||||
// to be demoted/promoted between the primary block cache and a secondary
|
||||
// cache. The secondary cache could be a non-volatile cache, and will
|
||||
// likely store the object in a different representation. They rely on a
|
||||
// per object CacheItemHelper to do the conversions.
|
||||
// The secondary cache may persist across process and system restarts,
|
||||
// and may even be moved between hosts. Therefore, the cache key must
|
||||
// be repeatable across restarts/reboots, and globally unique if
|
||||
// multiple DBs share the same cache and the set of DBs can change
|
||||
// over time.
|
||||
|
||||
// Insert a mapping from key->object into the cache and assign it
|
||||
// the specified charge against the total cache capacity. If
|
||||
// strict_capacity_limit is true and cache reaches its full capacity,
|
||||
// return Status::MemoryLimit. `obj` must be non-nullptr if compatible
|
||||
// with secondary cache (helper->size_cb != nullptr), because Value() ==
|
||||
// nullptr is reserved for indicating some secondary cache failure cases.
|
||||
// On success, returns OK and takes ownership of `obj`, eventually deleting
|
||||
// it with helper->del_cb. On non-OK return, the caller maintains ownership
|
||||
// of `obj` so will often need to delete it in such cases.
|
||||
//
|
||||
// The helper argument is saved by the cache and will be used when the
|
||||
// inserted object is evicted or considered for promotion to the secondary
|
||||
// cache. Promotion to secondary cache is only enabled if helper->size_cb
|
||||
// != nullptr. The helper must outlive the cache. Callers may use
|
||||
// &kNoopCacheItemHelper as a trivial helper (no deleter for the object,
|
||||
// no secondary cache). `helper` must not be nullptr (efficiency).
|
||||
//
|
||||
// If `handle` is not nullptr and return status is OK, `handle` is set
|
||||
// to a Handle* for the entry. The caller must call this->Release(handle)
|
||||
// when the returned entry is no longer needed. If `handle` is nullptr, it is
|
||||
// as if Release is called immediately after Insert.
|
||||
//
|
||||
// Regardless of whether the item was inserted into the cache,
|
||||
// it will attempt to insert it into the secondary cache if one is
|
||||
// configured, and the helper supports it.
|
||||
// The cache implementation must support a secondary cache, otherwise
|
||||
// the item is only inserted into the primary cache. It may
|
||||
// defer the insertion to the secondary cache as it sees fit.
|
||||
//
|
||||
// When the inserted entry is no longer needed, it will be destroyed using
|
||||
// helper->del_cb (if non-nullptr).
|
||||
virtual Status Insert(const Slice& key, ObjectPtr obj,
|
||||
const CacheItemHelper* helper, size_t charge,
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) = 0;
|
||||
|
||||
// Lookup the key, returning nullptr if not found. If found, returns
|
||||
// a handle to the mapping that must eventually be passed to Release().
|
||||
//
|
||||
// If a non-nullptr helper argument is provided with a non-nullptr
|
||||
// create_cb, and a secondary cache is configured, then the secondary
|
||||
// cache is also queried if lookup in the primary cache fails. If found
|
||||
// in secondary cache, the provided create_db and create_context are
|
||||
// used to promote the entry to an object in the primary cache.
|
||||
// In that case, the helper may be saved and used later when the object
|
||||
// is evicted, so as usual, the pointed-to helper must outlive the cache.
|
||||
//
|
||||
// ======================== Async Lookup (wait=false) ======================
|
||||
// When wait=false, the handle returned might be in any of three states:
|
||||
// * Present - If Value() != nullptr, then the result is present and
|
||||
// the handle can be used just as if wait=true.
|
||||
// * Pending, not ready (IsReady() == false) - secondary cache is still
|
||||
// working to retrieve the value. Might become ready any time.
|
||||
// * Pending, ready (IsReady() == true) - secondary cache has the value
|
||||
// but it has not been loaded as an object into primary cache. Call to
|
||||
// Wait()/WaitAll() will not block.
|
||||
//
|
||||
// IMPORTANT: Pending handles are not thread-safe, and only these functions
|
||||
// are allowed on them: Value(), IsReady(), Wait(), WaitAll(). Even Release()
|
||||
// can only come after Wait() or WaitAll() even though a reference is held.
|
||||
//
|
||||
// Only Wait()/WaitAll() gets a Handle out of a Pending state. (Waiting is
|
||||
// safe and has no effect on other handle states.) After waiting on a Handle,
|
||||
// it is in one of two states:
|
||||
// * Present - if Value() != nullptr
|
||||
// * Failed - if Value() == nullptr, such as if the secondary cache
|
||||
// initially thought it had the value but actually did not.
|
||||
//
|
||||
// Note that given an arbitrary Handle, the only way to distinguish the
|
||||
// Pending+ready state from the Failed state is to Wait() on it. A cache
|
||||
// entry not compatible with secondary cache can also have Value()==nullptr
|
||||
// like the Failed state, but this is not generally a concern.
|
||||
virtual Handle* Lookup(const Slice& key,
|
||||
const CacheItemHelper* helper = nullptr,
|
||||
CreateContext* create_context = nullptr,
|
||||
Priority priority = Priority::LOW, bool wait = true,
|
||||
Statistics* stats = nullptr) = 0;
|
||||
|
||||
// Convenience wrapper when secondary cache not supported
|
||||
inline Handle* BasicLookup(const Slice& key, Statistics* stats) {
|
||||
return Lookup(key, nullptr, nullptr, Priority::LOW, true, stats);
|
||||
}
|
||||
|
||||
// Increments the reference count for the handle if it refers to an entry in
|
||||
// the cache. Returns true if refcount was incremented; otherwise, returns
|
||||
// false.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual bool Ref(Handle* handle) = 0;
|
||||
|
||||
/**
|
||||
* Release a mapping returned by a previous Lookup(). A released entry might
|
||||
* still remain in cache in case it is later looked up by others. If
|
||||
* erase_if_last_ref is set then it also erases it from the cache if there is
|
||||
* no other reference to it. Erasing it should call the deleter function that
|
||||
* was provided when the entry was inserted.
|
||||
*
|
||||
* Returns true if the entry was also erased.
|
||||
*/
|
||||
// REQUIRES: handle must not have been released yet.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual bool Release(Handle* handle, bool erase_if_last_ref = false) = 0;
|
||||
|
||||
// Return the object assiciated with a handle returned by a successful
|
||||
// Lookup(). For historical reasons, this is also known at the "value"
|
||||
// associated with the key.
|
||||
// REQUIRES: handle must not have been released yet.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual ObjectPtr Value(Handle* handle) = 0;
|
||||
|
||||
// If the cache contains the entry for the key, erase it. Note that the
|
||||
// underlying entry will be kept around until all existing handles
|
||||
// to it have been released.
|
||||
virtual void Erase(const Slice& key) = 0;
|
||||
// Return a new numeric id. May be used by multiple clients who are
|
||||
// sharding the same cache to partition the key space. Typically the
|
||||
// client will allocate a new id at startup and prepend the id to
|
||||
// its cache keys.
|
||||
virtual uint64_t NewId() = 0;
|
||||
|
||||
// sets the maximum configured capacity of the cache. When the new
|
||||
// capacity is less than the old capacity and the existing usage is
|
||||
// greater than new capacity, the implementation will do its best job to
|
||||
// purge the released entries from the cache in order to lower the usage
|
||||
virtual void SetCapacity(size_t capacity) = 0;
|
||||
|
||||
// Set whether to return error on insertion when cache reaches its full
|
||||
// capacity.
|
||||
virtual void SetStrictCapacityLimit(bool strict_capacity_limit) = 0;
|
||||
|
||||
// Get the flag whether to return error on insertion when cache reaches its
|
||||
// full capacity.
|
||||
virtual bool HasStrictCapacityLimit() const = 0;
|
||||
|
||||
// Returns the maximum configured capacity of the cache
|
||||
virtual size_t GetCapacity() const = 0;
|
||||
|
||||
// Returns the memory size for the entries residing in the cache.
|
||||
virtual size_t GetUsage() const = 0;
|
||||
|
||||
// Returns the number of entries currently tracked in the table. SIZE_MAX
|
||||
// means "not supported." This is used for inspecting the load factor, along
|
||||
// with GetTableAddressCount().
|
||||
virtual size_t GetOccupancyCount() const { return SIZE_MAX; }
|
||||
|
||||
// Returns the number of ways the hash function is divided for addressing
|
||||
// entries. Zero means "not supported." This is used for inspecting the load
|
||||
// factor, along with GetOccupancyCount().
|
||||
virtual size_t GetTableAddressCount() const { return 0; }
|
||||
|
||||
// Returns the memory size for a specific entry in the cache.
|
||||
virtual size_t GetUsage(Handle* handle) const = 0;
|
||||
|
||||
// Returns the memory size for the entries in use by the system
|
||||
virtual size_t GetPinnedUsage() const = 0;
|
||||
|
||||
// Returns the charge for the specific entry in the cache.
|
||||
virtual size_t GetCharge(Handle* handle) const = 0;
|
||||
|
||||
// Returns the helper for the specified entry.
|
||||
virtual const CacheItemHelper* GetCacheItemHelper(Handle* handle) const = 0;
|
||||
|
||||
// Call this on shutdown if you want to speed it up. Cache will disown
|
||||
// any underlying data and will not free it on delete. This call will leak
|
||||
// memory - call this only if you're shutting down the process.
|
||||
// Any attempts of using cache after this call will fail terribly.
|
||||
// Always delete the DB object before calling this method!
|
||||
virtual void DisownData() {
|
||||
// default implementation is noop
|
||||
}
|
||||
|
||||
struct ApplyToAllEntriesOptions {
|
||||
// If the Cache uses locks, setting `average_entries_per_lock` to
|
||||
// a higher value suggests iterating over more entries each time a lock
|
||||
// is acquired, likely reducing the time for ApplyToAllEntries but
|
||||
// increasing latency for concurrent users of the Cache. Setting
|
||||
// `average_entries_per_lock` to a smaller value could be helpful if
|
||||
// callback is relatively expensive, such as using large data structures.
|
||||
size_t average_entries_per_lock = 256;
|
||||
};
|
||||
|
||||
// Apply a callback to all entries in the cache. The Cache must ensure
|
||||
// thread safety but does not guarantee that a consistent snapshot of all
|
||||
// entries is iterated over if other threads are operating on the Cache
|
||||
// also.
|
||||
virtual void ApplyToAllEntries(
|
||||
const std::function<void(const Slice& key, ObjectPtr obj, size_t charge,
|
||||
const CacheItemHelper* helper)>& callback,
|
||||
const ApplyToAllEntriesOptions& opts) = 0;
|
||||
|
||||
// Remove all entries.
|
||||
// Prerequisite: no entry is referenced.
|
||||
virtual void EraseUnRefEntries() = 0;
|
||||
|
||||
virtual std::string GetPrintableOptions() const { return ""; }
|
||||
|
||||
// Check for any warnings or errors in the operation of the cache and
|
||||
// report them to the logger. This is intended only to be called
|
||||
// periodically so does not need to be very efficient. (Obscure calling
|
||||
// conventions for Logger inherited from env.h)
|
||||
virtual void ReportProblems(
|
||||
const std::shared_ptr<Logger>& /*info_log*/) const {}
|
||||
|
||||
MemoryAllocator* memory_allocator() const { return memory_allocator_.get(); }
|
||||
|
||||
// EXPERIMENTAL
|
||||
// The following APIs are experimental and might change in the future.
|
||||
|
||||
// Release a mapping returned by a previous Lookup(). The "useful"
|
||||
// parameter specifies whether the data was actually used or not,
|
||||
// which may be used by the cache implementation to decide whether
|
||||
// to consider it as a hit for retention purposes. As noted elsewhere,
|
||||
// "pending" handles require Wait()/WaitAll() before Release().
|
||||
virtual bool Release(Handle* handle, bool /*useful*/,
|
||||
bool erase_if_last_ref) {
|
||||
return Release(handle, erase_if_last_ref);
|
||||
}
|
||||
|
||||
// Determines if the handle returned by Lookup() can give a value without
|
||||
// blocking, though Wait()/WaitAll() might be required to publish it to
|
||||
// Value(). See secondary cache compatible Lookup() above for details.
|
||||
// This call is not thread safe on "pending" handles.
|
||||
virtual bool IsReady(Handle* /*handle*/) { return true; }
|
||||
|
||||
// Convert a "pending" handle into a full thread-shareable handle by
|
||||
// * If necessary, wait until secondary cache finishes loading the value.
|
||||
// * Construct the object for primary cache and set it in the handle.
|
||||
// Even after Wait() on a pending handle, the caller must check for
|
||||
// Value() == nullptr in case of failure. This call is not thread-safe
|
||||
// on pending handles. This call has no effect on non-pending handles.
|
||||
// See secondary cache compatible Lookup() above for details.
|
||||
virtual void Wait(Handle* /*handle*/) {}
|
||||
|
||||
// Wait for a vector of handles to become ready. As with Wait(), the user
|
||||
// should check the Value() of each handle for nullptr. This call is not
|
||||
// thread-safe on pending handles.
|
||||
virtual void WaitAll(std::vector<Handle*>& /*handles*/) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MemoryAllocator> memory_allocator_;
|
||||
};
|
||||
|
||||
// Useful for cache entries requiring no clean-up, such as for cache
|
||||
// reservations
|
||||
inline constexpr Cache::CacheItemHelper kNoopCacheItemHelper{};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -11,11 +11,13 @@
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/customizable.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/wide_columns.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -34,6 +36,7 @@ class CompactionFilter : public Customizable {
|
||||
kValue,
|
||||
kMergeOperand,
|
||||
kBlobIndex, // used internally by BlobDB.
|
||||
kWideColumnEntity,
|
||||
};
|
||||
|
||||
enum class Decision {
|
||||
@@ -44,6 +47,7 @@ class CompactionFilter : public Customizable {
|
||||
kChangeBlobIndex, // used internally by BlobDB.
|
||||
kIOError, // used internally by BlobDB.
|
||||
kPurge, // used for keys that can only be SingleDelete'ed
|
||||
kChangeWideColumnEntity,
|
||||
kUndetermined,
|
||||
};
|
||||
|
||||
@@ -176,15 +180,57 @@ class CompactionFilter : public Customizable {
|
||||
}
|
||||
return value_changed ? Decision::kChangeValue : Decision::kKeep;
|
||||
}
|
||||
|
||||
case ValueType::kMergeOperand: {
|
||||
bool rv = FilterMergeOperand(level, key, existing_value);
|
||||
return rv ? Decision::kRemove : Decision::kKeep;
|
||||
}
|
||||
|
||||
case ValueType::kBlobIndex:
|
||||
return Decision::kKeep;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return Decision::kKeep;
|
||||
}
|
||||
assert(false);
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
// Wide column aware API. Called for plain values, merge operands, and
|
||||
// wide-column entities; the `value_type` parameter indicates the type of the
|
||||
// key-value. When the key-value is a plain value or a merge operand, the
|
||||
// `existing_value` parameter contains the existing value and the
|
||||
// `existing_columns` parameter is invalid (nullptr). When the key-value is a
|
||||
// wide-column entity, the `existing_columns` parameter contains the wide
|
||||
// columns of the existing entity and the `existing_value` parameter is
|
||||
// invalid (nullptr). The output parameters `new_value` and `new_columns` can
|
||||
// be used to change the value or wide columns of the key-value when
|
||||
// `kChangeValue` or `kChangeWideColumnEntity` is returned. See above for more
|
||||
// information on the semantics of the potential return values.
|
||||
//
|
||||
// For compatibility, the default implementation keeps all wide-column
|
||||
// entities, and falls back to FilterV2 for plain values and merge operands.
|
||||
// If you override this method, there is no need to override FilterV2 (or
|
||||
// Filter/FilterMergeOperand).
|
||||
virtual Decision FilterV3(
|
||||
int level, const Slice& key, ValueType value_type,
|
||||
const Slice* existing_value, const WideColumns* existing_columns,
|
||||
std::string* new_value,
|
||||
std::vector<std::pair<std::string, std::string>>* /* new_columns */,
|
||||
std::string* skip_until) const {
|
||||
#ifdef NDEBUG
|
||||
(void)existing_columns;
|
||||
#endif
|
||||
|
||||
assert(!existing_value || !existing_columns);
|
||||
assert(value_type == ValueType::kWideColumnEntity || existing_value);
|
||||
assert(value_type != ValueType::kWideColumnEntity || existing_columns);
|
||||
|
||||
if (value_type == ValueType::kWideColumnEntity) {
|
||||
return Decision::kKeep;
|
||||
}
|
||||
|
||||
return FilterV2(level, key, value_type, *existing_value, new_value,
|
||||
skip_until);
|
||||
}
|
||||
|
||||
// Internal (BlobDB) use only. Do not override in application code.
|
||||
@@ -211,7 +257,7 @@ class CompactionFilter : public Customizable {
|
||||
// In the case of BlobDB, it may be possible to reach a decision with only
|
||||
// the key without reading the actual value. Keys whose value_type is
|
||||
// kBlobIndex will be checked by this method.
|
||||
// Returning kUndetermined will cause FilterV2() to be called to make a
|
||||
// Returning kUndetermined will cause FilterV3() to be called to make a
|
||||
// decision as usual.
|
||||
virtual Decision FilterBlobByKey(int /*level*/, const Slice& /*key*/,
|
||||
std::string* /*new_value*/,
|
||||
|
||||
@@ -160,9 +160,10 @@ struct ConfigOptions {
|
||||
// "kCompactionStyleNone".
|
||||
//
|
||||
|
||||
// Take a default ColumnFamilyOptions "base_options" in addition to a
|
||||
// map "opts_map" of option name to option value to construct the new
|
||||
// ColumnFamilyOptions "new_options".
|
||||
// Take a ConfigOptions `config_options` and a ColumnFamilyOptions
|
||||
// "base_options" as the default option in addition to a map "opts_map" of
|
||||
// option name to option value to construct the new ColumnFamilyOptions
|
||||
// "new_options".
|
||||
//
|
||||
// Below are the instructions of how to config some non-primitive-typed
|
||||
// options in ColumnFamilyOptions:
|
||||
@@ -238,11 +239,6 @@ struct ConfigOptions {
|
||||
// cf_opt.compression_opts.strategy = 6;
|
||||
// cf_opt.compression_opts.max_dict_bytes = 7;
|
||||
//
|
||||
// The GetColumnFamilyOptionsFromMap(ConfigOptions, ...) should be used; the
|
||||
// alternative signature may be deprecated in a future release. The equivalent
|
||||
// functionality can be achieved by setting the corresponding options in
|
||||
// the ConfigOptions parameter.
|
||||
//
|
||||
// @param config_options controls how the map is processed.
|
||||
// @param base_options the default options of the output "new_options".
|
||||
// @param opts_map an option name to value map for specifying how "new_options"
|
||||
@@ -267,15 +263,10 @@ Status GetColumnFamilyOptionsFromMap(
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
ColumnFamilyOptions* new_options);
|
||||
Status GetColumnFamilyOptionsFromMap(
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
ColumnFamilyOptions* new_options, bool input_strings_escaped = false,
|
||||
bool ignore_unknown_options = false);
|
||||
|
||||
// Take a default DBOptions "base_options" in addition to a
|
||||
// map "opts_map" of option name to option value to construct the new
|
||||
// DBOptions "new_options".
|
||||
// Take a ConfigOptions `config_options` and a DBOptions "base_options" as the
|
||||
// default option in addition to a map "opts_map" of option name to option value
|
||||
// to construct the new DBOptions "new_options".
|
||||
//
|
||||
// Below are the instructions of how to config some non-primitive-typed
|
||||
// options in DBOptions:
|
||||
@@ -286,11 +277,6 @@ Status GetColumnFamilyOptionsFromMap(
|
||||
// - Passing {"rate_limiter_bytes_per_sec", "1024"} is equivalent to
|
||||
// passing NewGenericRateLimiter(1024) to rate_limiter_bytes_per_sec.
|
||||
//
|
||||
// The GetDBOptionsFromMap(ConfigOptions, ...) should be used; the
|
||||
// alternative signature may be deprecated in a future release. The equivalent
|
||||
// functionality can be achieved by setting the corresponding options in
|
||||
// the ConfigOptions parameter.
|
||||
//
|
||||
// @param config_options controls how the map is processed.
|
||||
// @param base_options the default options of the output "new_options".
|
||||
// @param opts_map an option name to value map for specifying how "new_options"
|
||||
@@ -314,15 +300,11 @@ Status GetDBOptionsFromMap(
|
||||
const ConfigOptions& cfg_options, const DBOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
DBOptions* new_options);
|
||||
Status GetDBOptionsFromMap(
|
||||
const DBOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
DBOptions* new_options, bool input_strings_escaped = false,
|
||||
bool ignore_unknown_options = false);
|
||||
|
||||
// Take a default BlockBasedTableOptions "table_options" in addition to a
|
||||
// map "opts_map" of option name to option value to construct the new
|
||||
// BlockBasedTableOptions "new_table_options".
|
||||
// Take a ConfigOptions `config_options` and a BlockBasedTableOptions
|
||||
// "table_options" as the default option in addition to a map "opts_map" of
|
||||
// option name to option value to construct the new BlockBasedTableOptions
|
||||
// "new_table_options".
|
||||
//
|
||||
// Below are the instructions of how to config some non-primitive-typed
|
||||
// options in BlockBasedTableOptions:
|
||||
@@ -345,11 +327,6 @@ Status GetDBOptionsFromMap(
|
||||
// - Passing {"block_cache", "1M"} in GetBlockBasedTableOptionsFromMap is
|
||||
// equivalent to setting block_cache using NewLRUCache(1024 * 1024).
|
||||
//
|
||||
// The GetBlockBasedTableOptionsFromMap(ConfigOptions, ...) should be used;
|
||||
// the alternative signature may be deprecated in a future release. The
|
||||
// equivalent functionality can be achieved by setting the corresponding
|
||||
// options in the ConfigOptions parameter.
|
||||
//
|
||||
// @param config_options controls how the map is processed.
|
||||
// @param table_options the default options of the output "new_table_options".
|
||||
// @param opts_map an option name to value map for specifying how
|
||||
@@ -369,20 +346,11 @@ Status GetBlockBasedTableOptionsFromMap(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
BlockBasedTableOptions* new_table_options);
|
||||
Status GetBlockBasedTableOptionsFromMap(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
BlockBasedTableOptions* new_table_options,
|
||||
bool input_strings_escaped = false, bool ignore_unknown_options = false);
|
||||
|
||||
// Take a default PlainTableOptions "table_options" in addition to a
|
||||
// map "opts_map" of option name to option value to construct the new
|
||||
// PlainTableOptions "new_table_options".
|
||||
//
|
||||
// The GetPlainTableOptionsFromMap(ConfigOptions, ...) should be used; the
|
||||
// alternative signature may be deprecated in a future release. The equivalent
|
||||
// functionality can be achieved by setting the corresponding options in
|
||||
// the ConfigOptions parameter.
|
||||
// Take a ConfigOptions `config_options` and a default PlainTableOptions
|
||||
// "table_options" as the default option in addition to a map "opts_map" of
|
||||
// option name to option value to construct the new PlainTableOptions
|
||||
// "new_table_options".
|
||||
//
|
||||
// @param config_options controls how the map is processed.
|
||||
// @param table_options the default options of the output "new_table_options".
|
||||
@@ -402,43 +370,26 @@ Status GetPlainTableOptionsFromMap(
|
||||
const ConfigOptions& config_options, const PlainTableOptions& table_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
PlainTableOptions* new_table_options);
|
||||
Status GetPlainTableOptionsFromMap(
|
||||
const PlainTableOptions& table_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
PlainTableOptions* new_table_options, bool input_strings_escaped = false,
|
||||
bool ignore_unknown_options = false);
|
||||
|
||||
// Take a string representation of option names and values, apply them into the
|
||||
// base_options, and return the new options as a result. The string has the
|
||||
// following format:
|
||||
// Take a ConfigOptions `config_options`, a string representation of option
|
||||
// names and values, apply them into the base_options, and return the new
|
||||
// options as a result. The string has the following format:
|
||||
// "write_buffer_size=1024;max_write_buffer_number=2"
|
||||
// Nested options config is also possible. For example, you can define
|
||||
// BlockBasedTableOptions as part of the string for block-based table factory:
|
||||
// "write_buffer_size=1024;block_based_table_factory={block_size=4k};"
|
||||
// "max_write_buffer_num=2"
|
||||
//
|
||||
//
|
||||
// The GetColumnFamilyOptionsFromString(ConfigOptions, ...) should be used; the
|
||||
// alternative signature may be deprecated in a future release. The equivalent
|
||||
// functionality can be achieved by setting the corresponding options in
|
||||
// the ConfigOptions parameter.
|
||||
Status GetColumnFamilyOptionsFromString(const ConfigOptions& config_options,
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
ColumnFamilyOptions* new_options);
|
||||
Status GetColumnFamilyOptionsFromString(const ColumnFamilyOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
ColumnFamilyOptions* new_options);
|
||||
|
||||
Status GetDBOptionsFromString(const ConfigOptions& config_options,
|
||||
const DBOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
DBOptions* new_options);
|
||||
|
||||
Status GetDBOptionsFromString(const DBOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
DBOptions* new_options);
|
||||
|
||||
Status GetStringFromDBOptions(const ConfigOptions& config_options,
|
||||
const DBOptions& db_options,
|
||||
std::string* opts_str);
|
||||
@@ -458,17 +409,11 @@ Status GetStringFromCompressionType(std::string* compression_str,
|
||||
|
||||
std::vector<CompressionType> GetSupportedCompressions();
|
||||
|
||||
Status GetBlockBasedTableOptionsFromString(
|
||||
const BlockBasedTableOptions& table_options, const std::string& opts_str,
|
||||
BlockBasedTableOptions* new_table_options);
|
||||
Status GetBlockBasedTableOptionsFromString(
|
||||
const ConfigOptions& config_options,
|
||||
const BlockBasedTableOptions& table_options, const std::string& opts_str,
|
||||
BlockBasedTableOptions* new_table_options);
|
||||
|
||||
Status GetPlainTableOptionsFromString(const PlainTableOptions& table_options,
|
||||
const std::string& opts_str,
|
||||
PlainTableOptions* new_table_options);
|
||||
Status GetPlainTableOptionsFromString(const ConfigOptions& config_options,
|
||||
const PlainTableOptions& table_options,
|
||||
const std::string& opts_str,
|
||||
|
||||
@@ -15,37 +15,172 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// This is a data structure specifically designed as a "Set" for a
|
||||
// pretty small scale of Enum structure. For now, it can support up
|
||||
// to 64 element, and it is expandable in the future.
|
||||
template <typename ENUM_TYPE, ENUM_TYPE MAX_VALUE>
|
||||
namespace detail {
|
||||
int CountTrailingZeroBitsForSmallEnumSet(uint64_t);
|
||||
} // namespace detail
|
||||
|
||||
// Represents a set of values of some enum type with a small number of
|
||||
// possible enumerators. For now, it supports enums where no enumerator
|
||||
// exceeds 63 when converted to int.
|
||||
template <typename ENUM_TYPE, ENUM_TYPE MAX_ENUMERATOR>
|
||||
class SmallEnumSet {
|
||||
private:
|
||||
using StateT = uint64_t;
|
||||
static constexpr int kStateBits = sizeof(StateT) * 8;
|
||||
static constexpr int kMaxMax = kStateBits - 1;
|
||||
static constexpr int kMaxValue = static_cast<int>(MAX_ENUMERATOR);
|
||||
static_assert(kMaxValue >= 0);
|
||||
static_assert(kMaxValue <= kMaxMax);
|
||||
|
||||
public:
|
||||
// construct / create
|
||||
SmallEnumSet() : state_(0) {}
|
||||
|
||||
~SmallEnumSet() {}
|
||||
|
||||
// Return true if the input enum is included in the "Set" (i.e., changes the
|
||||
// internal scalar state successfully), otherwise, it will return false.
|
||||
bool Add(const ENUM_TYPE value) {
|
||||
static_assert(MAX_VALUE <= 63, "Size currently limited to 64");
|
||||
assert(value >= 0 && value <= MAX_VALUE);
|
||||
uint64_t old_state = state_;
|
||||
uint64_t tmp = 1;
|
||||
state_ |= (tmp << value);
|
||||
return old_state != state_;
|
||||
template <class... TRest>
|
||||
/*implicit*/ constexpr SmallEnumSet(const ENUM_TYPE e, TRest... rest) {
|
||||
*this = SmallEnumSet(rest...).With(e);
|
||||
}
|
||||
|
||||
// Return the set that includes all valid values, assuming the enum
|
||||
// is "dense" (includes all values converting to 0 through kMaxValue)
|
||||
static constexpr SmallEnumSet All() {
|
||||
StateT tmp = StateT{1} << kMaxValue;
|
||||
return SmallEnumSet(RawStateMarker(), tmp | (tmp - 1));
|
||||
}
|
||||
|
||||
// equality
|
||||
bool operator==(const SmallEnumSet& that) const {
|
||||
return this->state_ == that.state_;
|
||||
}
|
||||
bool operator!=(const SmallEnumSet& that) const { return !(*this == that); }
|
||||
|
||||
// query
|
||||
|
||||
// Return true if the input enum is contained in the "Set".
|
||||
bool Contains(const ENUM_TYPE value) {
|
||||
static_assert(MAX_VALUE <= 63, "Size currently limited to 64");
|
||||
assert(value >= 0 && value <= MAX_VALUE);
|
||||
uint64_t tmp = 1;
|
||||
bool Contains(const ENUM_TYPE e) const {
|
||||
int value = static_cast<int>(e);
|
||||
assert(value >= 0 && value <= kMaxValue);
|
||||
StateT tmp = 1;
|
||||
return state_ & (tmp << value);
|
||||
}
|
||||
|
||||
bool empty() const { return state_ == 0; }
|
||||
|
||||
// iterator
|
||||
class const_iterator {
|
||||
public:
|
||||
// copy
|
||||
const_iterator(const const_iterator& that) = default;
|
||||
const_iterator& operator=(const const_iterator& that) = default;
|
||||
|
||||
// move
|
||||
const_iterator(const_iterator&& that) noexcept = default;
|
||||
const_iterator& operator=(const_iterator&& that) noexcept = default;
|
||||
|
||||
// equality
|
||||
bool operator==(const const_iterator& that) const {
|
||||
assert(set_ == that.set_);
|
||||
return this->pos_ == that.pos_;
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& that) const {
|
||||
return !(*this == that);
|
||||
}
|
||||
|
||||
// ++iterator
|
||||
const_iterator& operator++() {
|
||||
if (pos_ < kMaxValue) {
|
||||
pos_ = set_->SkipUnset(pos_ + 1);
|
||||
} else {
|
||||
pos_ = kStateBits;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// iterator++
|
||||
const_iterator operator++(int) {
|
||||
auto old = *this;
|
||||
++*this;
|
||||
return old;
|
||||
}
|
||||
|
||||
ENUM_TYPE operator*() const {
|
||||
assert(pos_ <= kMaxValue);
|
||||
return static_cast<ENUM_TYPE>(pos_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SmallEnumSet;
|
||||
const_iterator(const SmallEnumSet* set, int pos) : set_(set), pos_(pos) {}
|
||||
const SmallEnumSet* set_;
|
||||
int pos_;
|
||||
};
|
||||
|
||||
const_iterator begin() const { return const_iterator(this, SkipUnset(0)); }
|
||||
|
||||
const_iterator end() const { return const_iterator(this, kStateBits); }
|
||||
|
||||
// mutable ops
|
||||
|
||||
// Modifies the set (if needed) to include the given value. Returns true
|
||||
// iff the set was modified.
|
||||
bool Add(const ENUM_TYPE e) {
|
||||
int value = static_cast<int>(e);
|
||||
assert(value >= 0 && value <= kMaxValue);
|
||||
StateT old_state = state_;
|
||||
state_ |= (StateT{1} << value);
|
||||
return old_state != state_;
|
||||
}
|
||||
|
||||
// Modifies the set (if needed) not to include the given value. Returns true
|
||||
// iff the set was modified.
|
||||
bool Remove(const ENUM_TYPE e) {
|
||||
int value = static_cast<int>(e);
|
||||
assert(value >= 0 && value <= kMaxValue);
|
||||
StateT old_state = state_;
|
||||
state_ &= ~(StateT{1} << value);
|
||||
return old_state != state_;
|
||||
}
|
||||
|
||||
// applicative ops
|
||||
|
||||
// Return a new set based on this one with the additional value(s) inserted
|
||||
constexpr SmallEnumSet With(const ENUM_TYPE e) const {
|
||||
int value = static_cast<int>(e);
|
||||
assert(value >= 0 && value <= kMaxValue);
|
||||
return SmallEnumSet(RawStateMarker(), state_ | (StateT{1} << value));
|
||||
}
|
||||
template <class... TRest>
|
||||
constexpr SmallEnumSet With(const ENUM_TYPE e1, const ENUM_TYPE e2,
|
||||
TRest... rest) const {
|
||||
return With(e1).With(e2, rest...);
|
||||
}
|
||||
|
||||
// Return a new set based on this one excluding the given value(s)
|
||||
constexpr SmallEnumSet Without(const ENUM_TYPE e) const {
|
||||
int value = static_cast<int>(e);
|
||||
assert(value >= 0 && value <= kMaxValue);
|
||||
return SmallEnumSet(RawStateMarker(), state_ & ~(StateT{1} << value));
|
||||
}
|
||||
template <class... TRest>
|
||||
constexpr SmallEnumSet Without(const ENUM_TYPE e1, const ENUM_TYPE e2,
|
||||
TRest... rest) const {
|
||||
return Without(e1).Without(e2, rest...);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t state_;
|
||||
int SkipUnset(int pos) const {
|
||||
StateT tmp = state_ >> pos;
|
||||
if (tmp == 0) {
|
||||
return kStateBits;
|
||||
} else {
|
||||
return pos + detail::CountTrailingZeroBitsForSmallEnumSet(tmp);
|
||||
}
|
||||
}
|
||||
struct RawStateMarker {};
|
||||
explicit SmallEnumSet(RawStateMarker, StateT state) : state_(state) {}
|
||||
|
||||
StateT state_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -769,6 +769,68 @@ class DB {
|
||||
}
|
||||
}
|
||||
|
||||
// Batched MultiGet-like API that returns wide-column entities from a single
|
||||
// column family. For any given "key[i]" in "keys" (where 0 <= "i" <
|
||||
// "num_keys"), if the column family specified by "column_family" contains an
|
||||
// entry, it is returned it as a wide-column entity in "results[i]". If the
|
||||
// entry is a wide-column entity, it is returned as-is; if it is a plain
|
||||
// key-value, it is returned as an entity with a single anonymous column (see
|
||||
// kDefaultWideColumnName) which contains the value.
|
||||
//
|
||||
// "statuses[i]" is set to OK if "keys[i]" is successfully retrieved. It is
|
||||
// set to NotFound and an empty wide-column entity is returned in "results[i]"
|
||||
// if there is no entry for "keys[i]". Finally, "statuses[i]" is set to some
|
||||
// other non-OK status on error.
|
||||
//
|
||||
// If "keys" are sorted according to the column family's comparator, the
|
||||
// "sorted_input" flag can be set for a small performance improvement.
|
||||
//
|
||||
// Note that it is the caller's responsibility to ensure that "keys",
|
||||
// "results", and "statuses" point to "num_keys" number of contiguous objects
|
||||
// (Slices, PinnableWideColumns, and Statuses respectively).
|
||||
virtual void MultiGetEntity(const ReadOptions& /* options */,
|
||||
ColumnFamilyHandle* /* column_family */,
|
||||
size_t num_keys, const Slice* /* keys */,
|
||||
PinnableWideColumns* /* results */,
|
||||
Status* statuses,
|
||||
bool /* sorted_input */ = false) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = Status::NotSupported("MultiGetEntity not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// Batched MultiGet-like API that returns wide-column entities potentially
|
||||
// from multiple column families. For any given "key[i]" in "keys" (where 0 <=
|
||||
// "i" < "num_keys"), if the column family specified by "column_families[i]"
|
||||
// contains an entry, it is returned it as a wide-column entity in
|
||||
// "results[i]". If the entry is a wide-column entity, it is returned as-is;
|
||||
// if it is a plain key-value, it is returned as an entity with a single
|
||||
// anonymous column (see kDefaultWideColumnName) which contains the value.
|
||||
//
|
||||
// "statuses[i]" is set to OK if "keys[i]" is successfully retrieved. It is
|
||||
// set to NotFound and an empty wide-column entity is returned in "results[i]"
|
||||
// if there is no entry for "keys[i]". Finally, "statuses[i]" is set to some
|
||||
// other non-OK status on error.
|
||||
//
|
||||
// If "keys" are sorted by column family id and within each column family,
|
||||
// according to the column family's comparator, the "sorted_input" flag can be
|
||||
// set for a small performance improvement.
|
||||
//
|
||||
// Note that it is the caller's responsibility to ensure that
|
||||
// "column_families", "keys", "results", and "statuses" point to "num_keys"
|
||||
// number of contiguous objects (ColumnFamilyHandle pointers, Slices,
|
||||
// PinnableWideColumns, and Statuses respectively).
|
||||
virtual void MultiGetEntity(const ReadOptions& /* options */, size_t num_keys,
|
||||
ColumnFamilyHandle** /* column_families */,
|
||||
const Slice* /* keys */,
|
||||
PinnableWideColumns* /* results */,
|
||||
Status* statuses,
|
||||
bool /* sorted_input */ = false) {
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
statuses[i] = Status::NotSupported("MultiGetEntity not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// If the key definitely does not exist in the database, then this method
|
||||
// returns false, else true. If the caller wants to obtain value when the key
|
||||
// is found in memory, a bool for 'value_found' must be passed. 'value_found'
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "rocksdb/customizable.h"
|
||||
#include "rocksdb/functor_wrapper.h"
|
||||
#include "rocksdb/port_defs.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/thread_status.h"
|
||||
|
||||
@@ -69,13 +70,6 @@ struct ConfigOptions;
|
||||
|
||||
const size_t kDefaultPageSize = 4 * 1024;
|
||||
|
||||
enum class CpuPriority {
|
||||
kIdle = 0,
|
||||
kLow = 1,
|
||||
kNormal = 2,
|
||||
kHigh = 3,
|
||||
};
|
||||
|
||||
// Options while opening a file to read/write
|
||||
struct EnvOptions {
|
||||
// Construct with default Options
|
||||
|
||||
+10
-13
@@ -1923,7 +1923,8 @@ struct IngestExternalFileOptions {
|
||||
bool snapshot_consistency = true;
|
||||
// If set to false, IngestExternalFile() will fail if the file key range
|
||||
// overlaps with existing keys or tombstones or output of ongoing compaction
|
||||
// during file ingestion in the DB.
|
||||
// during file ingestion in the DB (the conditions under which a global_seqno
|
||||
// must be assigned to the ingested file).
|
||||
bool allow_global_seqno = true;
|
||||
// If set to false and the file key range overlaps with the memtable key range
|
||||
// (memtable flush required), IngestExternalFile will fail.
|
||||
@@ -1936,18 +1937,14 @@ struct IngestExternalFileOptions {
|
||||
// with allow_ingest_behind=true since the dawn of time.
|
||||
// All files will be ingested at the bottommost level with seqno=0.
|
||||
bool ingest_behind = false;
|
||||
// Set to true if you would like to write global_seqno to a given offset in
|
||||
// the external SST file for backward compatibility. Older versions of
|
||||
// RocksDB writes a global_seqno to a given offset within ingested SST files,
|
||||
// and new versions of RocksDB do not. If you ingest an external SST using
|
||||
// new version of RocksDB and would like to be able to downgrade to an
|
||||
// older version of RocksDB, you should set 'write_global_seqno' to true. If
|
||||
// your service is just starting to use the new RocksDB, we recommend that
|
||||
// you set this option to false, which brings two benefits:
|
||||
// 1. No extra random write for global_seqno during ingestion.
|
||||
// 2. Without writing external SST file, it's possible to do checksum.
|
||||
// We have a plan to set this option to false by default in the future.
|
||||
bool write_global_seqno = true;
|
||||
// DEPRECATED - Set to true if you would like to write global_seqno to
|
||||
// the external SST file on ingestion for backward compatibility before
|
||||
// RocksDB 5.16.0. Such old versions of RocksDB expect any global_seqno to
|
||||
// be written to the SST file rather than recorded in the DB manifest.
|
||||
// This functionality was deprecated because (a) random writes might be
|
||||
// costly or unsupported on some FileSystems, and (b) the file checksum
|
||||
// changes with such a write.
|
||||
bool write_global_seqno = false;
|
||||
// Set to true if you would like to verify the checksums of each block of the
|
||||
// external SST file before ingestion.
|
||||
// Warning: setting this to true causes slowdown in file ingestion because
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
// 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).
|
||||
//
|
||||
// This file includes the common definitions used in the port/,
|
||||
// the public API (this directory), and other directories
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
enum class CpuPriority {
|
||||
kIdle = 0,
|
||||
kLow = 1,
|
||||
kNormal = 2,
|
||||
kHigh = 3,
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/customizable.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
|
||||
@@ -113,6 +113,7 @@ class Status {
|
||||
kOverwritten = 12,
|
||||
kTxnNotPrepared = 13,
|
||||
kIOFenced = 14,
|
||||
kMergeOperatorFailed = 15,
|
||||
kMaxSubCode
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// for more information on how to develop and use customizable objects
|
||||
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -24,24 +23,6 @@
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// The FactoryFunc functions are used to create a new customizable object
|
||||
// without going through the ObjectRegistry. This methodology is especially
|
||||
// useful in LITE mode, where there is no ObjectRegistry. The methods take
|
||||
// in an ID of the object to create and a pointer to store the created object.
|
||||
// If the factory successfully recognized the input ID, the method should return
|
||||
// success; otherwise false should be returned. On success, the object
|
||||
// parameter contains the new object.
|
||||
template <typename T>
|
||||
using SharedFactoryFunc =
|
||||
std::function<bool(const std::string&, std::shared_ptr<T>*)>;
|
||||
|
||||
template <typename T>
|
||||
using UniqueFactoryFunc =
|
||||
std::function<bool(const std::string&, std::unique_ptr<T>*)>;
|
||||
|
||||
template <typename T>
|
||||
using StaticFactoryFunc = std::function<bool(const std::string&, T**)>;
|
||||
|
||||
// Creates a new shared customizable instance object based on the
|
||||
// input parameters using the object registry.
|
||||
//
|
||||
@@ -156,12 +137,10 @@ static Status NewManagedObject(
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to create and initailize the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T>
|
||||
static Status LoadSharedObject(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
const SharedFactoryFunc<T>& func,
|
||||
std::shared_ptr<T>* result) {
|
||||
std::string id;
|
||||
std::unordered_map<std::string, std::string> opt_map;
|
||||
@@ -170,12 +149,8 @@ static Status LoadSharedObject(const ConfigOptions& config_options,
|
||||
value, &id, &opt_map);
|
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status;
|
||||
} else if (func == nullptr ||
|
||||
!func(id, result)) { // No factory, or it failed
|
||||
return NewSharedObject(config_options, id, opt_map, result);
|
||||
} else {
|
||||
return Customizable::ConfigureNewObject(config_options, result->get(),
|
||||
opt_map);
|
||||
return NewSharedObject(config_options, id, opt_map, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +179,6 @@ static Status LoadSharedObject(const ConfigOptions& config_options,
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to create and initailize the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T>
|
||||
static Status LoadManagedObject(const ConfigOptions& config_options,
|
||||
@@ -270,12 +244,10 @@ static Status NewUniqueObject(
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to create and initailize the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T>
|
||||
static Status LoadUniqueObject(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
const UniqueFactoryFunc<T>& func,
|
||||
std::unique_ptr<T>* result) {
|
||||
std::string id;
|
||||
std::unordered_map<std::string, std::string> opt_map;
|
||||
@@ -283,12 +255,8 @@ static Status LoadUniqueObject(const ConfigOptions& config_options,
|
||||
value, &id, &opt_map);
|
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status;
|
||||
} else if (func == nullptr ||
|
||||
!func(id, result)) { // No factory, or it failed
|
||||
return NewUniqueObject(config_options, id, opt_map, result);
|
||||
} else {
|
||||
return Customizable::ConfigureNewObject(config_options, result->get(),
|
||||
opt_map);
|
||||
return NewUniqueObject(config_options, id, opt_map, result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,23 +305,18 @@ static Status NewStaticObject(
|
||||
// handled
|
||||
// @param value Either the simple name of the instance to create, or a set of
|
||||
// name-value pairs to create and initailize the object
|
||||
// @param func Optional function to call to attempt to create an instance
|
||||
// @param result The newly created instance.
|
||||
template <typename T>
|
||||
static Status LoadStaticObject(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
const StaticFactoryFunc<T>& func, T** result) {
|
||||
const std::string& value, T** result) {
|
||||
std::string id;
|
||||
std::unordered_map<std::string, std::string> opt_map;
|
||||
Status status = Customizable::GetOptionsMap(config_options, *result, value,
|
||||
&id, &opt_map);
|
||||
if (!status.ok()) { // GetOptionsMap failed
|
||||
return status;
|
||||
} else if (func == nullptr ||
|
||||
!func(id, result)) { // No factory, or it failed
|
||||
return NewStaticObject(config_options, id, opt_map, result);
|
||||
} else {
|
||||
return Customizable::ConfigureNewObject(config_options, *result, opt_map);
|
||||
return NewStaticObject(config_options, id, opt_map, result);
|
||||
}
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
@@ -42,13 +42,10 @@ extern std::shared_ptr<SimCache> NewSimCache(std::shared_ptr<Cache> sim_cache,
|
||||
std::shared_ptr<Cache> cache,
|
||||
int num_shard_bits);
|
||||
|
||||
class SimCache : public Cache {
|
||||
// An abstract base class (public interface) to the SimCache implementation
|
||||
class SimCache : public CacheWrapper {
|
||||
public:
|
||||
SimCache() {}
|
||||
|
||||
~SimCache() override {}
|
||||
|
||||
const char* Name() const override { return "SimCache"; }
|
||||
using CacheWrapper::CacheWrapper;
|
||||
|
||||
// returns the maximum configured capacity of the simcache for simulation
|
||||
virtual size_t GetSimCapacity() const = 0;
|
||||
|
||||
@@ -136,6 +136,24 @@ class StackableDB : public DB {
|
||||
statuses, sorted_input);
|
||||
}
|
||||
|
||||
using DB::MultiGetEntity;
|
||||
|
||||
void MultiGetEntity(const ReadOptions& options,
|
||||
ColumnFamilyHandle* column_family, size_t num_keys,
|
||||
const Slice* keys, PinnableWideColumns* results,
|
||||
Status* statuses, bool sorted_input) override {
|
||||
db_->MultiGetEntity(options, column_family, num_keys, keys, results,
|
||||
statuses, sorted_input);
|
||||
}
|
||||
|
||||
void MultiGetEntity(const ReadOptions& options, size_t num_keys,
|
||||
ColumnFamilyHandle** column_families, const Slice* keys,
|
||||
PinnableWideColumns* results, Status* statuses,
|
||||
bool sorted_input) override {
|
||||
db_->MultiGetEntity(options, num_keys, column_families, keys, results,
|
||||
statuses, sorted_input);
|
||||
}
|
||||
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
|
||||
@@ -110,6 +110,7 @@ JAVA_TESTS = \
|
||||
org.rocksdb.BlobOptionsTest\
|
||||
org.rocksdb.BlockBasedTableConfigTest\
|
||||
org.rocksdb.BuiltinComparatorTest\
|
||||
org.rocksdb.ByteBufferUnsupportedOperationTest\
|
||||
org.rocksdb.BytewiseComparatorRegressionTest\
|
||||
org.rocksdb.util.BytewiseComparatorTest\
|
||||
org.rocksdb.util.BytewiseComparatorIntTest\
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
// This file implements the "bridge" between Java and C++ for
|
||||
// ROCKSDB_NAMESPACE::Cache.
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "include/org_rocksdb_Cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
|
||||
/*
|
||||
* Class: org_rocksdb_Cache
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
ComparatorJniCallback::ComparatorJniCallback(
|
||||
JNIEnv* env, jobject jcomparator,
|
||||
const ComparatorJniCallbackOptions* options)
|
||||
: JniCallback(env, jcomparator), m_options(options) {
|
||||
: JniCallback(env, jcomparator),
|
||||
m_options(std::make_unique<ComparatorJniCallbackOptions>(*options)) {
|
||||
// cache the AbstractComparatorJniBridge class as we will reuse it many times
|
||||
// for each callback
|
||||
m_abstract_comparator_jni_bridge_clazz = static_cast<jclass>(
|
||||
|
||||
@@ -45,15 +45,12 @@ enum ReusedSynchronisationType {
|
||||
struct ComparatorJniCallbackOptions {
|
||||
// Set the synchronisation type used to guard the reused buffers.
|
||||
// Only used if max_reused_buffer_size > 0.
|
||||
// Default: ADAPTIVE_MUTEX
|
||||
ReusedSynchronisationType reused_synchronisation_type =
|
||||
ReusedSynchronisationType::ADAPTIVE_MUTEX;
|
||||
ReusedSynchronisationType reused_synchronisation_type = ADAPTIVE_MUTEX;
|
||||
|
||||
// Indicates if a direct byte buffer (i.e. outside of the normal
|
||||
// garbage-collected heap) is used for the callbacks to Java,
|
||||
// as opposed to a non-direct byte buffer which is a wrapper around
|
||||
// an on-heap byte[].
|
||||
// Default: true
|
||||
bool direct_buffer = true;
|
||||
|
||||
// Maximum size of a buffer (in bytes) that will be reused.
|
||||
@@ -63,7 +60,6 @@ struct ComparatorJniCallbackOptions {
|
||||
// if it requires less than max_reused_buffer_size, then an
|
||||
// existing buffer will be reused, else a new buffer will be
|
||||
// allocated just for that callback. -1 to disable.
|
||||
// Default: 64 bytes
|
||||
int32_t max_reused_buffer_size = 64;
|
||||
};
|
||||
|
||||
@@ -92,7 +88,7 @@ class ComparatorJniCallback : public JniCallback, public Comparator {
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const;
|
||||
virtual void FindShortSuccessor(std::string* key) const;
|
||||
const ComparatorJniCallbackOptions* m_options;
|
||||
const std::unique_ptr<ComparatorJniCallbackOptions> m_options;
|
||||
|
||||
private:
|
||||
struct ThreadLocalBuf {
|
||||
|
||||
@@ -3990,9 +3990,13 @@ jlong Java_org_rocksdb_ColumnFamilyOptions_getColumnFamilyOptionsFromProps__Ljav
|
||||
}
|
||||
|
||||
auto* cf_options = new ROCKSDB_NAMESPACE::ColumnFamilyOptions();
|
||||
ROCKSDB_NAMESPACE::ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
ROCKSDB_NAMESPACE::Status status =
|
||||
ROCKSDB_NAMESPACE::GetColumnFamilyOptionsFromString(
|
||||
ROCKSDB_NAMESPACE::ColumnFamilyOptions(), opt_string, cf_options);
|
||||
config_options, ROCKSDB_NAMESPACE::ColumnFamilyOptions(), opt_string,
|
||||
cf_options);
|
||||
|
||||
env->ReleaseStringUTFChars(jopt_string, opt_string);
|
||||
|
||||
@@ -5848,9 +5852,13 @@ jlong Java_org_rocksdb_DBOptions_getDBOptionsFromProps__Ljava_lang_String_2(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ROCKSDB_NAMESPACE::DBOptions base_options;
|
||||
auto* db_options = new ROCKSDB_NAMESPACE::DBOptions();
|
||||
ROCKSDB_NAMESPACE::ConfigOptions config_options(base_options);
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
ROCKSDB_NAMESPACE::Status status = ROCKSDB_NAMESPACE::GetDBOptionsFromString(
|
||||
ROCKSDB_NAMESPACE::DBOptions(), opt_string, db_options);
|
||||
config_options, base_options, opt_string, db_options);
|
||||
|
||||
env->ReleaseStringUTFChars(jopt_string, opt_string);
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "include/org_rocksdb_WriteBufferManager.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksjni/cplusplus_to_java_convert.h"
|
||||
|
||||
@@ -20,8 +20,8 @@ public abstract class AbstractComparator
|
||||
super();
|
||||
}
|
||||
|
||||
protected AbstractComparator(final ComparatorOptions copt) {
|
||||
super(copt.nativeHandle_);
|
||||
protected AbstractComparator(final ComparatorOptions comparatorOptions) {
|
||||
super(comparatorOptions.nativeHandle_);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,8 +11,8 @@ import java.util.*;
|
||||
/**
|
||||
* ColumnFamilyOptions to control the behavior of a database. It will be used
|
||||
* during the creation of a {@link org.rocksdb.RocksDB} (i.e., RocksDB.open()).
|
||||
*
|
||||
* As a descendent of {@link AbstractNativeReference}, this class is {@link AutoCloseable}
|
||||
* <p>
|
||||
* As a descendant of {@link AbstractNativeReference}, this class is {@link AutoCloseable}
|
||||
* and will be automatically released if opened in the preamble of a try with resources block.
|
||||
*/
|
||||
public class ColumnFamilyOptions extends RocksObject
|
||||
@@ -24,7 +24,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Construct ColumnFamilyOptions.
|
||||
*
|
||||
* <p>
|
||||
* This constructor will create (by allocating a block of memory)
|
||||
* an {@code rocksdb::ColumnFamilyOptions} in the c++ side.
|
||||
*/
|
||||
@@ -34,13 +34,13 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Copy constructor for ColumnFamilyOptions.
|
||||
*
|
||||
* <p>
|
||||
* NOTE: This does a shallow copy, which means comparator, merge_operator, compaction_filter,
|
||||
* compaction_filter_factory and other pointers will be cloned!
|
||||
*
|
||||
* @param other The ColumnFamilyOptions to copy.
|
||||
*/
|
||||
public ColumnFamilyOptions(ColumnFamilyOptions other) {
|
||||
public ColumnFamilyOptions(final ColumnFamilyOptions other) {
|
||||
super(copyColumnFamilyOptions(other.nativeHandle_));
|
||||
this.memTableConfig_ = other.memTableConfig_;
|
||||
this.tableFormatConfig_ = other.tableFormatConfig_;
|
||||
@@ -707,7 +707,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setBloomLocality(int bloomLocality) {
|
||||
public ColumnFamilyOptions setBloomLocality(final int bloomLocality) {
|
||||
setBloomLocality(nativeHandle_, bloomLocality);
|
||||
return this;
|
||||
}
|
||||
@@ -742,9 +742,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions
|
||||
setMemtableHugePageSize(
|
||||
long memtableHugePageSize) {
|
||||
public ColumnFamilyOptions setMemtableHugePageSize(final long memtableHugePageSize) {
|
||||
setMemtableHugePageSize(nativeHandle_,
|
||||
memtableHugePageSize);
|
||||
return this;
|
||||
@@ -756,7 +754,8 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setSoftPendingCompactionBytesLimit(long softPendingCompactionBytesLimit) {
|
||||
public ColumnFamilyOptions setSoftPendingCompactionBytesLimit(
|
||||
final long softPendingCompactionBytesLimit) {
|
||||
setSoftPendingCompactionBytesLimit(nativeHandle_,
|
||||
softPendingCompactionBytesLimit);
|
||||
return this;
|
||||
@@ -768,7 +767,8 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setHardPendingCompactionBytesLimit(long hardPendingCompactionBytesLimit) {
|
||||
public ColumnFamilyOptions setHardPendingCompactionBytesLimit(
|
||||
final long hardPendingCompactionBytesLimit) {
|
||||
setHardPendingCompactionBytesLimit(nativeHandle_, hardPendingCompactionBytesLimit);
|
||||
return this;
|
||||
}
|
||||
@@ -779,7 +779,8 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setLevel0FileNumCompactionTrigger(int level0FileNumCompactionTrigger) {
|
||||
public ColumnFamilyOptions setLevel0FileNumCompactionTrigger(
|
||||
final int level0FileNumCompactionTrigger) {
|
||||
setLevel0FileNumCompactionTrigger(nativeHandle_, level0FileNumCompactionTrigger);
|
||||
return this;
|
||||
}
|
||||
@@ -790,7 +791,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setLevel0SlowdownWritesTrigger(int level0SlowdownWritesTrigger) {
|
||||
public ColumnFamilyOptions setLevel0SlowdownWritesTrigger(final int level0SlowdownWritesTrigger) {
|
||||
setLevel0SlowdownWritesTrigger(nativeHandle_, level0SlowdownWritesTrigger);
|
||||
return this;
|
||||
}
|
||||
@@ -801,7 +802,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setLevel0StopWritesTrigger(int level0StopWritesTrigger) {
|
||||
public ColumnFamilyOptions setLevel0StopWritesTrigger(final int level0StopWritesTrigger) {
|
||||
setLevel0StopWritesTrigger(nativeHandle_, level0StopWritesTrigger);
|
||||
return this;
|
||||
}
|
||||
@@ -812,7 +813,8 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setMaxBytesForLevelMultiplierAdditional(int[] maxBytesForLevelMultiplierAdditional) {
|
||||
public ColumnFamilyOptions setMaxBytesForLevelMultiplierAdditional(
|
||||
final int[] maxBytesForLevelMultiplierAdditional) {
|
||||
setMaxBytesForLevelMultiplierAdditional(nativeHandle_, maxBytesForLevelMultiplierAdditional);
|
||||
return this;
|
||||
}
|
||||
@@ -823,7 +825,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setParanoidFileChecks(boolean paranoidFileChecks) {
|
||||
public ColumnFamilyOptions setParanoidFileChecks(final boolean paranoidFileChecks) {
|
||||
setParanoidFileChecks(nativeHandle_, paranoidFileChecks);
|
||||
return this;
|
||||
}
|
||||
@@ -931,7 +933,8 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColumnFamilyOptions setSstPartitionerFactory(SstPartitionerFactory sstPartitionerFactory) {
|
||||
public ColumnFamilyOptions setSstPartitionerFactory(
|
||||
final SstPartitionerFactory sstPartitionerFactory) {
|
||||
setSstPartitionerFactory(nativeHandle_, sstPartitionerFactory.nativeHandle_);
|
||||
this.sstPartitionerFactory_ = sstPartitionerFactory;
|
||||
return this;
|
||||
@@ -967,9 +970,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* for reads. See also the options min_blob_size, blob_file_size,
|
||||
* blob_compression_type, enable_blob_garbage_collection, and
|
||||
* blob_garbage_collection_age_cutoff below.
|
||||
*
|
||||
* <p>
|
||||
* Default: false
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -990,9 +993,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* for reads. See also the options min_blob_size, blob_file_size,
|
||||
* blob_compression_type, enable_blob_garbage_collection, and
|
||||
* blob_garbage_collection_age_cutoff below.
|
||||
*
|
||||
* <p>
|
||||
* Default: false
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1008,9 +1011,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* alongside the keys in SST files in the usual fashion. A value of zero for
|
||||
* this option means that all values are stored in blob files. Note that
|
||||
* enable_blob_files has to be set in order for this option to have any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1029,9 +1032,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* alongside the keys in SST files in the usual fashion. A value of zero for
|
||||
* this option means that all values are stored in blob files. Note that
|
||||
* enable_blob_files has to be set in order for this option to have any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1046,9 +1049,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* Set the size limit for blob files. When writing blob files, a new file is opened
|
||||
* once this limit is reached. Note that enable_blob_files has to be set in
|
||||
* order for this option to have any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 256 MB
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1066,9 +1069,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* Get the size limit for blob files. When writing blob files, a new file is opened
|
||||
* once this limit is reached. Note that enable_blob_files has to be set in
|
||||
* order for this option to have any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 256 MB
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1083,9 +1086,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* Set the compression algorithm to use for large values stored in blob files. Note
|
||||
* that enable_blob_files has to be set in order for this option to have any
|
||||
* effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: no compression
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1103,9 +1106,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* Get the compression algorithm to use for large values stored in blob files. Note
|
||||
* that enable_blob_files has to be set in order for this option to have any
|
||||
* effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: no compression
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1122,7 +1125,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* relocated to new files as they are encountered during compaction, which makes
|
||||
* it possible to clean up blob files once they contain nothing but
|
||||
* obsolete/garbage blobs. See also blob_garbage_collection_age_cutoff below.
|
||||
*
|
||||
* <p>
|
||||
* Default: false
|
||||
*
|
||||
* @param enableBlobGarbageCollection true iff blob garbage collection is to be enabled
|
||||
@@ -1142,7 +1145,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* relocated to new files as they are encountered during compaction, which makes
|
||||
* it possible to clean up blob files once they contain nothing but
|
||||
* obsolete/garbage blobs. See also blob_garbage_collection_age_cutoff below.
|
||||
*
|
||||
* <p>
|
||||
* Default: false
|
||||
*
|
||||
* @return true iff blob garbage collection is currently enabled
|
||||
@@ -1158,7 +1161,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* where N = garbage_collection_cutoff * number_of_blob_files. Note that
|
||||
* enable_blob_garbage_collection has to be set in order for this option to have
|
||||
* any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0.25
|
||||
*
|
||||
* @param blobGarbageCollectionAgeCutoff the new blob garbage collection age cutoff
|
||||
@@ -1178,7 +1181,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* where N = garbage_collection_cutoff * number_of_blob_files. Note that
|
||||
* enable_blob_garbage_collection has to be set in order for this option to have
|
||||
* any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0.25
|
||||
*
|
||||
* @return the current blob garbage collection age cutoff
|
||||
@@ -1194,12 +1197,12 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
* the blob files in question, assuming they are all eligible based on the
|
||||
* value of {@link #blobGarbageCollectionAgeCutoff} above. This option is
|
||||
* currently only supported with leveled compactions.
|
||||
*
|
||||
* <p>
|
||||
* Note that {@link #enableBlobGarbageCollection} has to be set in order for this
|
||||
* option to have any effect.
|
||||
*
|
||||
* <p>
|
||||
* Default: 1.0
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through the SetOptions() API
|
||||
*
|
||||
* @param blobGarbageCollectionForceThreshold new value for the threshold
|
||||
@@ -1223,9 +1226,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Set compaction readahead for blob files.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1252,9 +1255,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Set a certain LSM tree level to enable blob files.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1270,7 +1273,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Get the starting LSM tree level to enable blob files.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* @return the current LSM tree level to enable blob files.
|
||||
@@ -1282,9 +1285,9 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Set a certain prepopulate blob cache option.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* <p>
|
||||
* Dynamically changeable through
|
||||
* {@link RocksDB#setOptions(ColumnFamilyHandle, MutableColumnFamilyOptions)}.
|
||||
*
|
||||
@@ -1301,7 +1304,7 @@ public class ColumnFamilyOptions extends RocksObject
|
||||
|
||||
/**
|
||||
* Get the prepopulate blob cache option.
|
||||
*
|
||||
* <p>
|
||||
* Default: 0
|
||||
*
|
||||
* @return the current prepopulate blob cache option.
|
||||
|
||||
@@ -14,6 +14,7 @@ public class CompactRangeOptions extends RocksObject {
|
||||
private final static byte VALUE_kSkip = 0;
|
||||
private final static byte VALUE_kIfHaveCompactionFilter = 1;
|
||||
private final static byte VALUE_kForce = 2;
|
||||
private final static byte VALUE_kForceOptimized = 3;
|
||||
|
||||
// For level based compaction, we can configure if we want to skip/force bottommost level
|
||||
// compaction. The order of this enum MUST follow the C++ layer. See BottommostLevelCompaction in
|
||||
@@ -30,7 +31,12 @@ public class CompactRangeOptions extends RocksObject {
|
||||
/**
|
||||
* Always compact bottommost level
|
||||
*/
|
||||
kForce(VALUE_kForce);
|
||||
kForce(VALUE_kForce),
|
||||
/**
|
||||
* Always compact bottommost level but in bottommost level avoid
|
||||
* double-compacting files created in the same compaction
|
||||
*/
|
||||
kForceOptimized(VALUE_kForceOptimized);
|
||||
|
||||
private final byte value;
|
||||
|
||||
@@ -57,6 +63,8 @@ public class CompactRangeOptions extends RocksObject {
|
||||
case VALUE_kSkip: return kSkip;
|
||||
case VALUE_kIfHaveCompactionFilter: return kIfHaveCompactionFilter;
|
||||
case VALUE_kForce: return kForce;
|
||||
case VALUE_kForceOptimized:
|
||||
return kForceOptimized;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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).
|
||||
|
||||
package org.rocksdb;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.rocksdb.util.ReverseBytewiseComparator;
|
||||
|
||||
public class ByteBufferUnsupportedOperationTest {
|
||||
@ClassRule
|
||||
public static final RocksNativeLibraryResource ROCKS_NATIVE_LIBRARY_RESOURCE =
|
||||
new RocksNativeLibraryResource();
|
||||
|
||||
@Rule public TemporaryFolder dbFolder = new TemporaryFolder();
|
||||
|
||||
public static class Handler {
|
||||
private final RocksDB database;
|
||||
private final Map<UUID, ColumnFamilyHandle> columnFamilies;
|
||||
|
||||
public Handler(final String path, final Options options) throws RocksDBException {
|
||||
RocksDB.destroyDB(path, options);
|
||||
this.database = RocksDB.open(options, path);
|
||||
this.columnFamilies = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public void addTable(final UUID streamID) throws RocksDBException {
|
||||
final ColumnFamilyOptions tableOptions = new ColumnFamilyOptions();
|
||||
tableOptions.optimizeUniversalStyleCompaction();
|
||||
try (final ComparatorOptions comparatorOptions = new ComparatorOptions()) {
|
||||
// comparatorOptions.setReusedSynchronisationType(ReusedSynchronisationType.ADAPTIVE_MUTEX);
|
||||
tableOptions.setComparator(new ReverseBytewiseComparator(comparatorOptions));
|
||||
final ColumnFamilyDescriptor tableDescriptor = new ColumnFamilyDescriptor(
|
||||
streamID.toString().getBytes(StandardCharsets.UTF_8), tableOptions);
|
||||
final ColumnFamilyHandle tableHandle = database.createColumnFamily(tableDescriptor);
|
||||
columnFamilies.put(streamID, tableHandle);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAll(final List<byte[][]> keyValuePairs, final UUID streamID)
|
||||
throws RocksDBException {
|
||||
final ColumnFamilyHandle currTable = columnFamilies.get(streamID);
|
||||
try (final WriteBatch batchedWrite = new WriteBatch();
|
||||
final WriteOptions writeOptions = new WriteOptions()) {
|
||||
for (final byte[][] pair : keyValuePairs) {
|
||||
final byte[] keyBytes = pair[0];
|
||||
final byte[] valueBytes = pair[1];
|
||||
batchedWrite.put(currTable, keyBytes, valueBytes);
|
||||
}
|
||||
database.write(writeOptions, batchedWrite);
|
||||
}
|
||||
}
|
||||
public boolean containsValue(final byte[] encodedValue, final UUID streamID) {
|
||||
try (final RocksIterator iter = database.newIterator(columnFamilies.get(streamID))) {
|
||||
iter.seekToFirst();
|
||||
while (iter.isValid()) {
|
||||
final byte[] val = iter.value();
|
||||
if (Arrays.equals(val, encodedValue)) {
|
||||
return true;
|
||||
}
|
||||
iter.next();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (final ColumnFamilyHandle handle : columnFamilies.values()) {
|
||||
handle.close();
|
||||
}
|
||||
database.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void inner(final int numRepeats) throws RocksDBException {
|
||||
final Options opts = new Options();
|
||||
opts.setCreateIfMissing(true);
|
||||
final Handler handler = new Handler("testDB", opts);
|
||||
final UUID stream1 = UUID.randomUUID();
|
||||
|
||||
final List<byte[][]> entries = new ArrayList<>();
|
||||
for (int i = 0; i < numRepeats; i++) {
|
||||
final byte[] value = value(i);
|
||||
final byte[] key = key(i);
|
||||
entries.add(new byte[][] {key, value});
|
||||
}
|
||||
handler.addTable(stream1);
|
||||
handler.updateAll(entries, stream1);
|
||||
|
||||
for (int i = 0; i < numRepeats; i++) {
|
||||
final byte[] val = value(i);
|
||||
final boolean hasValue = handler.containsValue(val, stream1);
|
||||
if (!hasValue) {
|
||||
throw new IllegalStateException("not has value " + i);
|
||||
}
|
||||
}
|
||||
|
||||
handler.close();
|
||||
}
|
||||
|
||||
private static byte[] key(final int i) {
|
||||
return ("key" + i).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static byte[] value(final int i) {
|
||||
return ("value" + i).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unsupportedOperation() throws RocksDBException {
|
||||
final int numRepeats = 1000;
|
||||
final int repeatTest = 10;
|
||||
|
||||
// the error is not always reproducible... let's try to increase the odds by repeating the main
|
||||
// test body
|
||||
for (int i = 0; i < repeatTest; i++) {
|
||||
try {
|
||||
inner(numRepeats);
|
||||
} catch (final RuntimeException runtimeException) {
|
||||
System.out.println("Exception on repeat " + i);
|
||||
throw runtimeException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,9 @@ public class CompactRangeOptionsTest {
|
||||
value = BottommostLevelCompaction.kIfHaveCompactionFilter;
|
||||
opt.setBottommostLevelCompaction(value);
|
||||
assertThat(opt.bottommostLevelCompaction()).isEqualTo(value);
|
||||
value = BottommostLevelCompaction.kForceOptimized;
|
||||
opt.setBottommostLevelCompaction(value);
|
||||
assertThat(opt.bottommostLevelCompaction()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -99,9 +99,9 @@ public class IngestExternalFileOptionsTest {
|
||||
public void writeGlobalSeqno() {
|
||||
try (final IngestExternalFileOptions options =
|
||||
new IngestExternalFileOptions()) {
|
||||
assertThat(options.writeGlobalSeqno()).isTrue();
|
||||
options.setWriteGlobalSeqno(false);
|
||||
assertThat(options.writeGlobalSeqno()).isFalse();
|
||||
options.setWriteGlobalSeqno(true);
|
||||
assertThat(options.writeGlobalSeqno()).isTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -300,7 +300,7 @@ Status Statistics::CreateFromString(const ConfigOptions& config_options,
|
||||
} else if (id == kNullptrString) {
|
||||
result->reset();
|
||||
} else {
|
||||
s = LoadSharedObject<Statistics>(config_options, id, nullptr, result);
|
||||
s = LoadSharedObject<Statistics>(config_options, id, result);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "options/options_helper.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "rocksdb/concurrent_task_limiter.h"
|
||||
#include "rocksdb/configurable.h"
|
||||
|
||||
@@ -160,19 +160,6 @@ class BCustomizable : public TestCustomizable {
|
||||
BOptions opts_;
|
||||
};
|
||||
|
||||
static bool LoadSharedB(const std::string& id,
|
||||
std::shared_ptr<TestCustomizable>* result) {
|
||||
if (id == "B") {
|
||||
result->reset(new BCustomizable(id));
|
||||
return true;
|
||||
} else if (id.empty()) {
|
||||
result->reset();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int A_count = 0;
|
||||
static int RegisterCustomTestObjects(ObjectLibrary& library,
|
||||
const std::string& /*arg*/) {
|
||||
@@ -184,6 +171,12 @@ static int RegisterCustomTestObjects(ObjectLibrary& library,
|
||||
A_count++;
|
||||
return guard->get();
|
||||
});
|
||||
library.AddFactory<TestCustomizable>(
|
||||
"B", [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
|
||||
std::string* /* msg */) {
|
||||
guard->reset(new BCustomizable(name));
|
||||
return guard->get();
|
||||
});
|
||||
|
||||
library.AddFactory<TestCustomizable>(
|
||||
"S", [](const std::string& name,
|
||||
@@ -252,46 +245,19 @@ static void GetMapFromProperties(
|
||||
Status TestCustomizable::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::shared_ptr<TestCustomizable>* result) {
|
||||
return LoadSharedObject<TestCustomizable>(config_options, value, LoadSharedB,
|
||||
result);
|
||||
return LoadSharedObject<TestCustomizable>(config_options, value, result);
|
||||
}
|
||||
|
||||
Status TestCustomizable::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::unique_ptr<TestCustomizable>* result) {
|
||||
return LoadUniqueObject<TestCustomizable>(
|
||||
config_options, value,
|
||||
[](const std::string& id, std::unique_ptr<TestCustomizable>* u) {
|
||||
if (id == "B") {
|
||||
u->reset(new BCustomizable(id));
|
||||
return true;
|
||||
} else if (id.empty()) {
|
||||
u->reset();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
result);
|
||||
return LoadUniqueObject<TestCustomizable>(config_options, value, result);
|
||||
}
|
||||
|
||||
Status TestCustomizable::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
TestCustomizable** result) {
|
||||
return LoadStaticObject<TestCustomizable>(
|
||||
config_options, value,
|
||||
[](const std::string& id, TestCustomizable** ptr) {
|
||||
if (id == "B") {
|
||||
*ptr = new BCustomizable(id);
|
||||
return true;
|
||||
} else if (id.empty()) {
|
||||
*ptr = nullptr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
result);
|
||||
return LoadStaticObject<TestCustomizable>(config_options, value, result);
|
||||
}
|
||||
|
||||
class CustomizableTest : public testing::Test {
|
||||
@@ -980,72 +946,36 @@ TEST_F(CustomizableTest, IgnoreUnknownObjects) {
|
||||
std::unique_ptr<TestCustomizable> unique;
|
||||
TestCustomizable* pointer = nullptr;
|
||||
ignore.ignore_unsupported_options = false;
|
||||
ASSERT_NOK(
|
||||
LoadSharedObject<TestCustomizable>(ignore, "Unknown", nullptr, &shared));
|
||||
ASSERT_NOK(
|
||||
LoadUniqueObject<TestCustomizable>(ignore, "Unknown", nullptr, &unique));
|
||||
ASSERT_NOK(
|
||||
LoadStaticObject<TestCustomizable>(ignore, "Unknown", nullptr, &pointer));
|
||||
ASSERT_NOK(LoadSharedObject<TestCustomizable>(ignore, "Unknown", &shared));
|
||||
ASSERT_NOK(LoadUniqueObject<TestCustomizable>(ignore, "Unknown", &unique));
|
||||
ASSERT_NOK(LoadStaticObject<TestCustomizable>(ignore, "Unknown", &pointer));
|
||||
ASSERT_EQ(shared.get(), nullptr);
|
||||
ASSERT_EQ(unique.get(), nullptr);
|
||||
ASSERT_EQ(pointer, nullptr);
|
||||
ignore.ignore_unsupported_options = true;
|
||||
ASSERT_OK(
|
||||
LoadSharedObject<TestCustomizable>(ignore, "Unknown", nullptr, &shared));
|
||||
ASSERT_OK(
|
||||
LoadUniqueObject<TestCustomizable>(ignore, "Unknown", nullptr, &unique));
|
||||
ASSERT_OK(
|
||||
LoadStaticObject<TestCustomizable>(ignore, "Unknown", nullptr, &pointer));
|
||||
ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "Unknown", &shared));
|
||||
ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "Unknown", &unique));
|
||||
ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "Unknown", &pointer));
|
||||
ASSERT_EQ(shared.get(), nullptr);
|
||||
ASSERT_EQ(unique.get(), nullptr);
|
||||
ASSERT_EQ(pointer, nullptr);
|
||||
ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown", nullptr,
|
||||
&shared));
|
||||
ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown", nullptr,
|
||||
&unique));
|
||||
ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown", nullptr,
|
||||
&pointer));
|
||||
ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown", &shared));
|
||||
ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown", &unique));
|
||||
ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown", &pointer));
|
||||
ASSERT_EQ(shared.get(), nullptr);
|
||||
ASSERT_EQ(unique.get(), nullptr);
|
||||
ASSERT_EQ(pointer, nullptr);
|
||||
ASSERT_OK(LoadSharedObject<TestCustomizable>(ignore, "id=Unknown;option=bad",
|
||||
nullptr, &shared));
|
||||
&shared));
|
||||
ASSERT_OK(LoadUniqueObject<TestCustomizable>(ignore, "id=Unknown;option=bad",
|
||||
nullptr, &unique));
|
||||
&unique));
|
||||
ASSERT_OK(LoadStaticObject<TestCustomizable>(ignore, "id=Unknown;option=bad",
|
||||
nullptr, &pointer));
|
||||
&pointer));
|
||||
ASSERT_EQ(shared.get(), nullptr);
|
||||
ASSERT_EQ(unique.get(), nullptr);
|
||||
ASSERT_EQ(pointer, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(CustomizableTest, FactoryFunctionTest) {
|
||||
std::shared_ptr<TestCustomizable> shared;
|
||||
std::unique_ptr<TestCustomizable> unique;
|
||||
TestCustomizable* pointer = nullptr;
|
||||
ConfigOptions ignore = config_options_;
|
||||
ignore.ignore_unsupported_options = false;
|
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &shared));
|
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &unique));
|
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "B", &pointer));
|
||||
ASSERT_NE(shared.get(), nullptr);
|
||||
ASSERT_NE(unique.get(), nullptr);
|
||||
ASSERT_NE(pointer, nullptr);
|
||||
delete pointer;
|
||||
pointer = nullptr;
|
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "id=", &shared));
|
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "id=", &unique));
|
||||
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "id=", &pointer));
|
||||
ASSERT_EQ(shared.get(), nullptr);
|
||||
ASSERT_EQ(unique.get(), nullptr);
|
||||
ASSERT_EQ(pointer, nullptr);
|
||||
ASSERT_NOK(TestCustomizable::CreateFromString(ignore, "option=bad", &shared));
|
||||
ASSERT_NOK(TestCustomizable::CreateFromString(ignore, "option=bad", &unique));
|
||||
ASSERT_NOK(
|
||||
TestCustomizable::CreateFromString(ignore, "option=bad", &pointer));
|
||||
ASSERT_EQ(pointer, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(CustomizableTest, URLFactoryTest) {
|
||||
std::unique_ptr<TestCustomizable> unique;
|
||||
config_options_.registry->AddLibrary("URL")->AddFactory<TestCustomizable>(
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "options/options_helper.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/configurable.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
|
||||
@@ -658,18 +658,6 @@ Status GetStringFromCompressionType(std::string* compression_str,
|
||||
}
|
||||
}
|
||||
|
||||
Status GetColumnFamilyOptionsFromMap(
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
ColumnFamilyOptions* new_options, bool input_strings_escaped,
|
||||
bool ignore_unknown_options) {
|
||||
ConfigOptions config_options;
|
||||
config_options.ignore_unknown_options = ignore_unknown_options;
|
||||
config_options.input_strings_escaped = input_strings_escaped;
|
||||
return GetColumnFamilyOptionsFromMap(config_options, base_options, opts_map,
|
||||
new_options);
|
||||
}
|
||||
|
||||
Status GetColumnFamilyOptionsFromMap(
|
||||
const ConfigOptions& config_options,
|
||||
const ColumnFamilyOptions& base_options,
|
||||
@@ -691,17 +679,6 @@ Status GetColumnFamilyOptionsFromMap(
|
||||
}
|
||||
}
|
||||
|
||||
Status GetColumnFamilyOptionsFromString(
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
ColumnFamilyOptions* new_options) {
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
return GetColumnFamilyOptionsFromString(config_options, base_options,
|
||||
opts_str, new_options);
|
||||
}
|
||||
|
||||
Status GetColumnFamilyOptionsFromString(const ConfigOptions& config_options,
|
||||
const ColumnFamilyOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
@@ -716,18 +693,6 @@ Status GetColumnFamilyOptionsFromString(const ConfigOptions& config_options,
|
||||
new_options);
|
||||
}
|
||||
|
||||
Status GetDBOptionsFromMap(
|
||||
const DBOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
DBOptions* new_options, bool input_strings_escaped,
|
||||
bool ignore_unknown_options) {
|
||||
ConfigOptions config_options(base_options);
|
||||
config_options.input_strings_escaped = input_strings_escaped;
|
||||
config_options.ignore_unknown_options = ignore_unknown_options;
|
||||
return GetDBOptionsFromMap(config_options, base_options, opts_map,
|
||||
new_options);
|
||||
}
|
||||
|
||||
Status GetDBOptionsFromMap(
|
||||
const ConfigOptions& config_options, const DBOptions& base_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
@@ -746,17 +711,6 @@ Status GetDBOptionsFromMap(
|
||||
}
|
||||
}
|
||||
|
||||
Status GetDBOptionsFromString(const DBOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
DBOptions* new_options) {
|
||||
ConfigOptions config_options(base_options);
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
|
||||
return GetDBOptionsFromString(config_options, base_options, opts_str,
|
||||
new_options);
|
||||
}
|
||||
|
||||
Status GetDBOptionsFromString(const ConfigOptions& config_options,
|
||||
const DBOptions& base_options,
|
||||
const std::string& opts_str,
|
||||
|
||||
@@ -167,8 +167,13 @@ TEST_F(OptionsSettableTest, BlockBasedTableOptionsAllFieldsSettable) {
|
||||
kBbtoExcluded);
|
||||
|
||||
// Need to update the option string if a new option is added.
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
config_options.invoke_prepare_options = false;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
*bbto,
|
||||
config_options, *bbto,
|
||||
"cache_index_and_filter_blocks=1;"
|
||||
"cache_index_and_filter_blocks_with_high_priority=true;"
|
||||
"metadata_cache_options={top_level_index_pinning=kFallback;"
|
||||
@@ -273,8 +278,11 @@ TEST_F(OptionsSettableTest, DBOptionsAllFieldsSettable) {
|
||||
FillWithSpecialChar(new_options_ptr, sizeof(DBOptions), kDBOptionsExcluded);
|
||||
|
||||
// Need to update the option string if a new option is added.
|
||||
ConfigOptions config_options(*options);
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(
|
||||
GetDBOptionsFromString(*options,
|
||||
GetDBOptionsFromString(config_options, *options,
|
||||
"wal_bytes_per_sync=4295048118;"
|
||||
"delete_obsolete_files_period_micros=4294967758;"
|
||||
"WAL_ttl_seconds=4295008036;"
|
||||
@@ -461,8 +469,11 @@ TEST_F(OptionsSettableTest, ColumnFamilyOptionsAllFieldsSettable) {
|
||||
kColumnFamilyOptionsExcluded);
|
||||
|
||||
// Need to update the option string if a new option is added.
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
*options,
|
||||
config_options, *options,
|
||||
"compaction_filter_factory=mpudlojcujCompactionFilterFactory;"
|
||||
"table_factory=PlainTable;"
|
||||
"prefix_extractor=rocksdb.CappedPrefix.13;"
|
||||
|
||||
+258
-181
@@ -811,9 +811,11 @@ TEST_F(OptionsTest, OldInterfaceTest) {
|
||||
ColumnFamilyOptions base_cf_opt;
|
||||
ColumnFamilyOptions new_cf_opt;
|
||||
ConfigOptions exact;
|
||||
|
||||
ConfigOptions cf_config_options;
|
||||
cf_config_options.input_strings_escaped = false;
|
||||
cf_config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt,
|
||||
cf_config_options, base_cf_opt,
|
||||
"write_buffer_size=18;prefix_extractor=capped:8;"
|
||||
"arena_block_size=19",
|
||||
&new_cf_opt));
|
||||
@@ -824,7 +826,7 @@ TEST_F(OptionsTest, OldInterfaceTest) {
|
||||
|
||||
// And with a bad option
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt,
|
||||
cf_config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={xx_block_size=4;}",
|
||||
&new_cf_opt));
|
||||
@@ -836,15 +838,17 @@ TEST_F(OptionsTest, OldInterfaceTest) {
|
||||
{"max_write_buffer_number", "2"},
|
||||
{"min_write_buffer_number_to_merge", "3"},
|
||||
};
|
||||
ASSERT_OK(
|
||||
GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map, &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
cf_options_map["unknown_option"] = "1";
|
||||
ASSERT_NOK(
|
||||
GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map, &new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
ASSERT_OK(
|
||||
RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map,
|
||||
&new_cf_opt, true, true));
|
||||
cf_config_options.input_strings_escaped = true;
|
||||
cf_config_options.ignore_unknown_options = true;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
|
||||
DBOptions base_db_opt;
|
||||
DBOptions new_db_opt;
|
||||
@@ -857,7 +861,12 @@ TEST_F(OptionsTest, OldInterfaceTest) {
|
||||
{"verify_sst_unique_id_in_manifest", "true"},
|
||||
{"max_open_files", "32"},
|
||||
};
|
||||
ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
|
||||
|
||||
ConfigOptions db_config_options(base_db_opt);
|
||||
db_config_options.input_strings_escaped = false;
|
||||
db_config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt));
|
||||
ASSERT_EQ(new_db_opt.create_if_missing, false);
|
||||
ASSERT_EQ(new_db_opt.create_missing_column_families, true);
|
||||
ASSERT_EQ(new_db_opt.error_if_exists, false);
|
||||
@@ -866,23 +875,28 @@ TEST_F(OptionsTest, OldInterfaceTest) {
|
||||
ASSERT_EQ(new_db_opt.verify_sst_unique_id_in_manifest, true);
|
||||
ASSERT_EQ(new_db_opt.max_open_files, 32);
|
||||
db_options_map["unknown_option"] = "1";
|
||||
Status s = GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt);
|
||||
Status s = GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
|
||||
ASSERT_OK(
|
||||
RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
|
||||
ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt, true,
|
||||
true));
|
||||
db_config_options.input_strings_escaped = true;
|
||||
db_config_options.ignore_unknown_options = true;
|
||||
ASSERT_OK(GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt));
|
||||
db_config_options.input_strings_escaped = false;
|
||||
db_config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetDBOptionsFromString(
|
||||
base_db_opt,
|
||||
db_config_options, base_db_opt,
|
||||
"create_if_missing=false;error_if_exists=false;max_open_files=42;",
|
||||
&new_db_opt));
|
||||
ASSERT_EQ(new_db_opt.create_if_missing, false);
|
||||
ASSERT_EQ(new_db_opt.error_if_exists, false);
|
||||
ASSERT_EQ(new_db_opt.max_open_files, 42);
|
||||
s = GetDBOptionsFromString(
|
||||
base_db_opt,
|
||||
db_config_options, base_db_opt,
|
||||
"create_if_missing=false;error_if_exists=false;max_open_files=42;"
|
||||
"unknown_option=1;",
|
||||
&new_db_opt);
|
||||
@@ -2355,8 +2369,11 @@ TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
|
||||
|
||||
ColumnFamilyOptions base_cf_opt;
|
||||
ColumnFamilyOptions new_cf_opt;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(
|
||||
base_cf_opt, cf_options_map, &new_cf_opt));
|
||||
ConfigOptions cf_config_options;
|
||||
cf_config_options.ignore_unknown_options = false;
|
||||
cf_config_options.input_strings_escaped = false;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 1U);
|
||||
ASSERT_EQ(new_cf_opt.max_write_buffer_number, 2);
|
||||
ASSERT_EQ(new_cf_opt.min_write_buffer_number_to_merge, 3);
|
||||
@@ -2444,8 +2461,8 @@ TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
|
||||
ASSERT_EQ(new_cf_opt.bottommost_temperature, Temperature::kWarm);
|
||||
|
||||
cf_options_map["write_buffer_size"] = "hello";
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromMap(
|
||||
base_cf_opt, cf_options_map, &new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
ConfigOptions exact, loose;
|
||||
exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
|
||||
loose.sanity_level = ConfigOptions::kSanityLevelLooselyCompatible;
|
||||
@@ -2453,18 +2470,18 @@ TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
cf_options_map["write_buffer_size"] = "1";
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(
|
||||
base_cf_opt, cf_options_map, &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
|
||||
cf_options_map["unknown_option"] = "1";
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromMap(
|
||||
base_cf_opt, cf_options_map, &new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(base_cf_opt, cf_options_map,
|
||||
&new_cf_opt,
|
||||
false, /* input_strings_escaped */
|
||||
true /* ignore_unknown_options */));
|
||||
cf_config_options.input_strings_escaped = false;
|
||||
cf_config_options.ignore_unknown_options = true;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromMap(cf_config_options, base_cf_opt,
|
||||
cf_options_map, &new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(
|
||||
loose, base_cf_opt, new_cf_opt, nullptr /* new_opt_map */));
|
||||
ASSERT_NOK(RocksDBOptionsParser::VerifyCFOptions(
|
||||
@@ -2472,7 +2489,11 @@ TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
|
||||
|
||||
DBOptions base_db_opt;
|
||||
DBOptions new_db_opt;
|
||||
ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
|
||||
ConfigOptions db_config_options(base_db_opt);
|
||||
db_config_options.input_strings_escaped = false;
|
||||
db_config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt));
|
||||
ASSERT_EQ(new_db_opt.create_if_missing, false);
|
||||
ASSERT_EQ(new_db_opt.create_missing_column_families, true);
|
||||
ASSERT_EQ(new_db_opt.error_if_exists, false);
|
||||
@@ -2515,18 +2536,21 @@ TEST_F(OptionsOldApiTest, GetOptionsFromMapTest) {
|
||||
ASSERT_EQ(new_db_opt.strict_bytes_per_sync, true);
|
||||
|
||||
db_options_map["max_open_files"] = "hello";
|
||||
ASSERT_NOK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
|
||||
ASSERT_NOK(GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
|
||||
|
||||
// unknow options should fail parsing without ignore_unknown_options = true
|
||||
db_options_map["unknown_db_option"] = "1";
|
||||
ASSERT_NOK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt));
|
||||
ASSERT_NOK(GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
|
||||
|
||||
ASSERT_OK(GetDBOptionsFromMap(base_db_opt, db_options_map, &new_db_opt,
|
||||
false, /* input_strings_escaped */
|
||||
true /* ignore_unknown_options */));
|
||||
db_config_options.input_strings_escaped = false;
|
||||
db_config_options.ignore_unknown_options = true;
|
||||
ASSERT_OK(GetDBOptionsFromMap(db_config_options, base_db_opt, db_options_map,
|
||||
&new_db_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(loose, base_db_opt, new_db_opt));
|
||||
ASSERT_NOK(RocksDBOptionsParser::VerifyDBOptions(exact, base_db_opt, new_db_opt));
|
||||
}
|
||||
@@ -2535,33 +2559,38 @@ TEST_F(OptionsOldApiTest, GetColumnFamilyOptionsFromStringTest) {
|
||||
ColumnFamilyOptions base_cf_opt;
|
||||
ColumnFamilyOptions new_cf_opt;
|
||||
base_cf_opt.table_factory.reset();
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt, "", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=5", &new_cf_opt));
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt, "",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt, "write_buffer_size=5", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 5U);
|
||||
ASSERT_TRUE(new_cf_opt.table_factory == nullptr);
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=6;", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt, "write_buffer_size=6;", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 6U);
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
" write_buffer_size = 7 ", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt, " write_buffer_size = 7 ", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 7U);
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
" write_buffer_size = 8 ; ", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt, " write_buffer_size = 8 ; ", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 8U);
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=9;max_write_buffer_number=10", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 9U);
|
||||
ASSERT_EQ(new_cf_opt.max_write_buffer_number, 10);
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=11; max_write_buffer_number = 12 ;",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=11; max_write_buffer_number = 12 ;", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 11U);
|
||||
ASSERT_EQ(new_cf_opt.max_write_buffer_number, 12);
|
||||
// Wrong name "max_write_buffer_number_"
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=13;max_write_buffer_number_=14;",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=13;max_write_buffer_number_=14;", &new_cf_opt));
|
||||
ConfigOptions exact;
|
||||
exact.sanity_level = ConfigOptions::kSanityLevelExactMatch;
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
@@ -2574,30 +2603,34 @@ TEST_F(OptionsOldApiTest, GetColumnFamilyOptionsFromStringTest) {
|
||||
std::unique_ptr<const Comparator>* /*guard*/,
|
||||
std::string* /* errmsg */) { return ReverseBytewiseComparator(); });
|
||||
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt, "comparator=" + kCompName + ";", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
|
||||
"comparator=" + kCompName + ";",
|
||||
&new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.comparator, ReverseBytewiseComparator());
|
||||
|
||||
// MergeOperator from object registry
|
||||
std::unique_ptr<BytesXOROperator> bxo(new BytesXOROperator());
|
||||
std::string kMoName = bxo->Name();
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt, "merge_operator=" + kMoName + ";", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
|
||||
"merge_operator=" + kMoName + ";",
|
||||
&new_cf_opt));
|
||||
ASSERT_EQ(kMoName, std::string(new_cf_opt.merge_operator->Name()));
|
||||
|
||||
// Wrong key/value pair
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=13;max_write_buffer_number;", &new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=13;max_write_buffer_number;", &new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
// Error Paring value
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=13;max_write_buffer_number=;", &new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
// Missing option name
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=13; =100;", &new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt, "write_buffer_size=13; =100;", &new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
const uint64_t kilo = 1024UL;
|
||||
@@ -2607,17 +2640,17 @@ TEST_F(OptionsOldApiTest, GetColumnFamilyOptionsFromStringTest) {
|
||||
|
||||
// Units (k)
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
|
||||
config_options, base_cf_opt, "max_write_buffer_number=15K", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.max_write_buffer_number, 15 * kilo);
|
||||
// Units (m)
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"max_write_buffer_number=16m;inplace_update_num_locks=17M",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"max_write_buffer_number=16m;inplace_update_num_locks=17M", &new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.max_write_buffer_number, 16 * mega);
|
||||
ASSERT_EQ(new_cf_opt.inplace_update_num_locks, 17u * mega);
|
||||
// Units (g)
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt,
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=18g;prefix_extractor=capped:8;"
|
||||
"arena_block_size=19G",
|
||||
&new_cf_opt));
|
||||
@@ -2628,107 +2661,119 @@ TEST_F(OptionsOldApiTest, GetColumnFamilyOptionsFromStringTest) {
|
||||
ASSERT_EQ(new_cf_opt.prefix_extractor->AsString(), "rocksdb.CappedPrefix.8");
|
||||
|
||||
// Units (t)
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=20t;arena_block_size=21T", &new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt, "write_buffer_size=20t;arena_block_size=21T",
|
||||
&new_cf_opt));
|
||||
ASSERT_EQ(new_cf_opt.write_buffer_size, 20 * tera);
|
||||
ASSERT_EQ(new_cf_opt.arena_block_size, 21 * tera);
|
||||
|
||||
// Nested block based table options
|
||||
// Empty
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={};arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={};arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
|
||||
// Non-empty
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_cache=1M;block_size=4;};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_cache=1M;block_size=4;};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
|
||||
// Last one
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_cache=1M;block_size=4;}",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_cache=1M;block_size=4;}",
|
||||
&new_cf_opt));
|
||||
ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
|
||||
// Mismatch curly braces
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={{{block_size=4;};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={{{block_size=4;};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
// Unexpected chars after closing curly brace
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_size=4;}};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_size=4;}};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_size=4;}xdfa;"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_size=4;}xdfa;"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_size=4;}xdfa",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={block_size=4;}xdfa",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
// Invalid block based table option
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={xx_block_size=4;}",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"block_based_table_factory={xx_block_size=4;}",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"optimize_filters_for_hits=true",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"optimize_filters_for_hits=false",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
|
||||
"optimize_filters_for_hits=true",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
|
||||
"optimize_filters_for_hits=false",
|
||||
&new_cf_opt));
|
||||
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"optimize_filters_for_hits=junk",
|
||||
&new_cf_opt));
|
||||
ASSERT_NOK(GetColumnFamilyOptionsFromString(config_options, base_cf_opt,
|
||||
"optimize_filters_for_hits=junk",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(exact, base_cf_opt, new_cf_opt));
|
||||
|
||||
// Nested plain table options
|
||||
// Empty
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"plain_table_factory={};arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"plain_table_factory={};arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
|
||||
ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
|
||||
// Non-empty
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"plain_table_factory={user_key_len=66;bloom_bits_per_key=20;};"
|
||||
"arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_TRUE(new_cf_opt.table_factory != nullptr);
|
||||
ASSERT_EQ(std::string(new_cf_opt.table_factory->Name()), "PlainTable");
|
||||
|
||||
// memtable factory
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"memtable=skip_list:10;arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
config_options, base_cf_opt,
|
||||
"write_buffer_size=10;max_write_buffer_number=16;"
|
||||
"memtable=skip_list:10;arena_block_size=1024",
|
||||
&new_cf_opt));
|
||||
ASSERT_TRUE(new_cf_opt.memtable_factory != nullptr);
|
||||
ASSERT_TRUE(new_cf_opt.memtable_factory->IsInstanceOf("SkipListFactory"));
|
||||
|
||||
// blob cache
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
base_cf_opt,
|
||||
config_options, base_cf_opt,
|
||||
"blob_cache={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;high_pri_pool_ratio=0.5;};",
|
||||
&new_cf_opt));
|
||||
@@ -2832,9 +2877,15 @@ TEST_F(OptionsTest, SliceTransformCreateFromString) {
|
||||
TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
|
||||
BlockBasedTableOptions table_opt;
|
||||
BlockBasedTableOptions new_opt;
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
config_options.invoke_prepare_options = false;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
|
||||
// make sure default values are overwritten by something else
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
table_opt,
|
||||
config_options, table_opt,
|
||||
"cache_index_and_filter_blocks=1;index_type=kHashSearch;"
|
||||
"checksum=kxxHash;no_block_cache=1;"
|
||||
"block_cache=1M;block_cache_compressed=1k;block_size=1024;"
|
||||
@@ -2860,54 +2911,57 @@ TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
|
||||
EXPECT_EQ(bfp->GetWholeBitsPerKey(), 5);
|
||||
|
||||
// unknown option
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
|
||||
"bad_option=1",
|
||||
&new_opt));
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(
|
||||
config_options, table_opt,
|
||||
"cache_index_and_filter_blocks=1;index_type=kBinarySearch;"
|
||||
"bad_option=1",
|
||||
&new_opt));
|
||||
ASSERT_EQ(static_cast<bool>(table_opt.cache_index_and_filter_blocks),
|
||||
new_opt.cache_index_and_filter_blocks);
|
||||
ASSERT_EQ(table_opt.index_type, new_opt.index_type);
|
||||
|
||||
// unrecognized index type
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"cache_index_and_filter_blocks=1;index_type=kBinarySearchXX",
|
||||
&new_opt));
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(
|
||||
config_options, table_opt,
|
||||
"cache_index_and_filter_blocks=1;index_type=kBinarySearchXX", &new_opt));
|
||||
ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
|
||||
new_opt.cache_index_and_filter_blocks);
|
||||
ASSERT_EQ(table_opt.index_type, new_opt.index_type);
|
||||
|
||||
// unrecognized checksum type
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"cache_index_and_filter_blocks=1;checksum=kxxHashXX",
|
||||
&new_opt));
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(
|
||||
config_options, table_opt,
|
||||
"cache_index_and_filter_blocks=1;checksum=kxxHashXX", &new_opt));
|
||||
ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
|
||||
new_opt.cache_index_and_filter_blocks);
|
||||
ASSERT_EQ(table_opt.index_type, new_opt.index_type);
|
||||
|
||||
// unrecognized filter policy name
|
||||
ASSERT_NOK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"cache_index_and_filter_blocks=1;"
|
||||
"filter_policy=bloomfilterxx:4:true",
|
||||
&new_opt));
|
||||
ASSERT_NOK(
|
||||
GetBlockBasedTableOptionsFromString(config_options, table_opt,
|
||||
"cache_index_and_filter_blocks=1;"
|
||||
"filter_policy=bloomfilterxx:4:true",
|
||||
&new_opt));
|
||||
ASSERT_EQ(table_opt.cache_index_and_filter_blocks,
|
||||
new_opt.cache_index_and_filter_blocks);
|
||||
ASSERT_EQ(table_opt.filter_policy, new_opt.filter_policy);
|
||||
|
||||
// Used to be rejected, now accepted
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
table_opt, "filter_policy=bloomfilter:4", &new_opt));
|
||||
config_options, table_opt, "filter_policy=bloomfilter:4", &new_opt));
|
||||
bfp = dynamic_cast<const BloomFilterPolicy*>(new_opt.filter_policy.get());
|
||||
EXPECT_EQ(bfp->GetMillibitsPerKey(), 4000);
|
||||
EXPECT_EQ(bfp->GetWholeBitsPerKey(), 4);
|
||||
|
||||
// Check block cache options are overwritten when specified
|
||||
// in new format as a struct.
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"block_cache={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;high_pri_pool_ratio=0.5;};"
|
||||
"block_cache_compressed={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;high_pri_pool_ratio=0.5;}",
|
||||
&new_opt));
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
config_options, table_opt,
|
||||
"block_cache={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;high_pri_pool_ratio=0.5;};"
|
||||
"block_cache_compressed={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;high_pri_pool_ratio=0.5;}",
|
||||
&new_opt));
|
||||
ASSERT_TRUE(new_opt.block_cache != nullptr);
|
||||
ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
|
||||
ASSERT_EQ(std::dynamic_pointer_cast<ShardedCacheBase>(new_opt.block_cache)
|
||||
@@ -2919,10 +2973,11 @@ TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
|
||||
|
||||
// Set only block cache capacity. Check other values are
|
||||
// reset to default values.
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"block_cache={capacity=2M};"
|
||||
"block_cache_compressed={capacity=2M}",
|
||||
&new_opt));
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
config_options, table_opt,
|
||||
"block_cache={capacity=2M};"
|
||||
"block_cache_compressed={capacity=2M}",
|
||||
&new_opt));
|
||||
ASSERT_TRUE(new_opt.block_cache != nullptr);
|
||||
ASSERT_EQ(new_opt.block_cache->GetCapacity(), 2*1024UL*1024UL);
|
||||
// Default values
|
||||
@@ -2936,7 +2991,7 @@ TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
|
||||
|
||||
// Set couple of block cache options.
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
table_opt,
|
||||
config_options, table_opt,
|
||||
"block_cache={num_shard_bits=5;high_pri_pool_ratio=0.5;};"
|
||||
"block_cache_compressed={num_shard_bits=5;"
|
||||
"high_pri_pool_ratio=0.0;}",
|
||||
@@ -2950,12 +3005,13 @@ TEST_F(OptionsOldApiTest, GetBlockBasedTableOptionsFromString) {
|
||||
new_opt.block_cache)->GetHighPriPoolRatio(), 0.5);
|
||||
|
||||
// Set couple of block cache options.
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(table_opt,
|
||||
"block_cache={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;};"
|
||||
"block_cache_compressed={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;}",
|
||||
&new_opt));
|
||||
ASSERT_OK(GetBlockBasedTableOptionsFromString(
|
||||
config_options, table_opt,
|
||||
"block_cache={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;};"
|
||||
"block_cache_compressed={capacity=1M;num_shard_bits=4;"
|
||||
"strict_capacity_limit=true;}",
|
||||
&new_opt));
|
||||
ASSERT_TRUE(new_opt.block_cache != nullptr);
|
||||
ASSERT_EQ(new_opt.block_cache->GetCapacity(), 1024UL*1024UL);
|
||||
ASSERT_EQ(std::dynamic_pointer_cast<ShardedCacheBase>(new_opt.block_cache)
|
||||
@@ -2971,11 +3027,16 @@ TEST_F(OptionsOldApiTest, GetPlainTableOptionsFromString) {
|
||||
PlainTableOptions table_opt;
|
||||
PlainTableOptions new_opt;
|
||||
// make sure default values are overwritten by something else
|
||||
ASSERT_OK(GetPlainTableOptionsFromString(table_opt,
|
||||
"user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
|
||||
"index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
|
||||
"full_scan_mode=true;store_index_in_file=true",
|
||||
&new_opt));
|
||||
ConfigOptions config_options_from_string;
|
||||
config_options_from_string.input_strings_escaped = false;
|
||||
config_options_from_string.ignore_unknown_options = false;
|
||||
config_options_from_string.invoke_prepare_options = false;
|
||||
ASSERT_OK(GetPlainTableOptionsFromString(
|
||||
config_options_from_string, table_opt,
|
||||
"user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
|
||||
"index_sparseness=8;huge_page_tlb_size=4;encoding_type=kPrefix;"
|
||||
"full_scan_mode=true;store_index_in_file=true",
|
||||
&new_opt));
|
||||
ASSERT_EQ(new_opt.user_key_len, 66u);
|
||||
ASSERT_EQ(new_opt.bloom_bits_per_key, 20);
|
||||
ASSERT_EQ(new_opt.hash_table_ratio, 0.5);
|
||||
@@ -2988,22 +3049,28 @@ TEST_F(OptionsOldApiTest, GetPlainTableOptionsFromString) {
|
||||
std::unordered_map<std::string, std::string> opt_map;
|
||||
ASSERT_OK(StringToMap(
|
||||
"user_key_len=55;bloom_bits_per_key=10;huge_page_tlb_size=8;", &opt_map));
|
||||
ASSERT_OK(GetPlainTableOptionsFromMap(table_opt, opt_map, &new_opt));
|
||||
ConfigOptions config_options_from_map;
|
||||
config_options_from_map.input_strings_escaped = false;
|
||||
config_options_from_map.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetPlainTableOptionsFromMap(config_options_from_map, table_opt,
|
||||
opt_map, &new_opt));
|
||||
ASSERT_EQ(new_opt.user_key_len, 55u);
|
||||
ASSERT_EQ(new_opt.bloom_bits_per_key, 10);
|
||||
ASSERT_EQ(new_opt.huge_page_tlb_size, 8);
|
||||
|
||||
// unknown option
|
||||
ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
|
||||
"user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
|
||||
"bad_option=1",
|
||||
&new_opt));
|
||||
ASSERT_NOK(GetPlainTableOptionsFromString(
|
||||
config_options_from_string, table_opt,
|
||||
"user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
|
||||
"bad_option=1",
|
||||
&new_opt));
|
||||
|
||||
// unrecognized EncodingType
|
||||
ASSERT_NOK(GetPlainTableOptionsFromString(table_opt,
|
||||
"user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
|
||||
"encoding_type=kPrefixXX",
|
||||
&new_opt));
|
||||
ASSERT_NOK(GetPlainTableOptionsFromString(
|
||||
config_options_from_string, table_opt,
|
||||
"user_key_len=66;bloom_bits_per_key=20;hash_table_ratio=0.5;"
|
||||
"encoding_type=kPrefixXX",
|
||||
&new_opt));
|
||||
}
|
||||
|
||||
TEST_F(OptionsOldApiTest, GetOptionsFromStringTest) {
|
||||
@@ -3085,10 +3152,15 @@ TEST_F(OptionsOldApiTest, DBOptionsSerialization) {
|
||||
|
||||
// Phase 3: Set new_options from the derived string and expect
|
||||
// new_options == base_options
|
||||
ASSERT_OK(GetDBOptionsFromString(DBOptions(), base_options_file_content,
|
||||
&new_options));
|
||||
ConfigOptions config_options;
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(config_options, base_options, new_options));
|
||||
const DBOptions base_db_options;
|
||||
ConfigOptions db_config_options(base_db_options);
|
||||
db_config_options.input_strings_escaped = false;
|
||||
db_config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(GetDBOptionsFromString(db_config_options, base_db_options,
|
||||
base_options_file_content, &new_options));
|
||||
ConfigOptions verify_db_config_options;
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyDBOptions(verify_db_config_options,
|
||||
base_options, new_options));
|
||||
}
|
||||
|
||||
TEST_F(OptionsOldApiTest, ColumnFamilyOptionsSerialization) {
|
||||
@@ -3106,10 +3178,15 @@ TEST_F(OptionsOldApiTest, ColumnFamilyOptionsSerialization) {
|
||||
|
||||
// Phase 3: Set new_opt from the derived string and expect
|
||||
// new_opt == base_opt
|
||||
ASSERT_OK(GetColumnFamilyOptionsFromString(
|
||||
ColumnFamilyOptions(), base_options_file_content, &new_opt));
|
||||
ConfigOptions config_options;
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(config_options, base_opt, new_opt));
|
||||
ConfigOptions cf_config_options;
|
||||
cf_config_options.input_strings_escaped = false;
|
||||
cf_config_options.ignore_unknown_options = false;
|
||||
ASSERT_OK(
|
||||
GetColumnFamilyOptionsFromString(cf_config_options, ColumnFamilyOptions(),
|
||||
base_options_file_content, &new_opt));
|
||||
ConfigOptions verify_cf_config_options;
|
||||
ASSERT_OK(RocksDBOptionsParser::VerifyCFOptions(verify_cf_config_options,
|
||||
base_opt, new_opt));
|
||||
if (base_opt.compaction_filter) {
|
||||
delete base_opt.compaction_filter;
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/port_defs.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
// size_t printf formatting named in the manner of C99 standard formatting
|
||||
|
||||
+86
-4
@@ -31,12 +31,18 @@ void* SaveStack(int* /*num_frames*/, int /*first_frames_to_skip*/) {
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(OS_FREEBSD)
|
||||
#ifdef OS_FREEBSD
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#endif // OS_FREEBSD
|
||||
#ifdef OS_LINUX
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 30)
|
||||
#include <sys/syscall.h>
|
||||
#define gettid() syscall(SYS_gettid)
|
||||
#endif // GLIBC version
|
||||
#endif // OS_LINUX
|
||||
|
||||
#include "port/lang.h"
|
||||
|
||||
@@ -135,6 +141,80 @@ void PrintStack(void* frames[], int num_frames) {
|
||||
}
|
||||
|
||||
void PrintStack(int first_frames_to_skip) {
|
||||
#if defined(ROCKSDB_DLL) && defined(OS_LINUX)
|
||||
// LIB_MODE=shared build produces mediocre information from the above
|
||||
// backtrace+addr2line stack trace method. Try to use GDB in that case, but
|
||||
// only on Linux where we know how to attach to a particular thread.
|
||||
bool linux_dll = true;
|
||||
#else
|
||||
bool linux_dll = false;
|
||||
#endif
|
||||
// Also support invoking interactive debugger on stack trace, with this
|
||||
// envvar set to non-empty
|
||||
char* debug_env = getenv("ROCKSDB_DEBUG");
|
||||
bool debug = debug_env != nullptr && strlen(debug_env) > 0;
|
||||
|
||||
if (linux_dll || debug) {
|
||||
// Allow ouside debugger to attach, even with Yama security restrictions
|
||||
#ifdef PR_SET_PTRACER_ANY
|
||||
(void)prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
#endif
|
||||
// Try to invoke GDB, either for stack trace or debugging.
|
||||
long long attach_id = getpid();
|
||||
|
||||
// `gdb -p PID` seems to always attach to main thread, but `gdb -p TID`
|
||||
// seems to be able to attach to a particular thread in a process, which
|
||||
// makes sense as the main thread TID == PID of the process.
|
||||
// But I haven't found that gdb capability documented anywhere, so leave
|
||||
// a back door to attach to main thread.
|
||||
#ifdef OS_LINUX
|
||||
if (getenv("ROCKSDB_DEBUG_USE_PID") == nullptr) {
|
||||
attach_id = gettid();
|
||||
}
|
||||
#endif
|
||||
char attach_id_str[20];
|
||||
snprintf(attach_id_str, sizeof(attach_id_str), "%lld", attach_id);
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// child process
|
||||
if (debug) {
|
||||
fprintf(stderr, "Invoking GDB for debugging (ROCKSDB_DEBUG=%s)...\n",
|
||||
debug_env);
|
||||
execlp(/*cmd in PATH*/ "gdb", /*arg0*/ "gdb", "-p", attach_id_str,
|
||||
(char*)nullptr);
|
||||
return;
|
||||
} else {
|
||||
fprintf(stderr, "Invoking GDB for stack trace...\n");
|
||||
|
||||
// Skip top ~4 frames here in PrintStack
|
||||
// See https://stackoverflow.com/q/40991943/454544
|
||||
auto bt_in_gdb =
|
||||
"frame apply level 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 "
|
||||
"21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 "
|
||||
"42 43 44 -q frame";
|
||||
// Redirect child stdout to original stderr
|
||||
dup2(2, 1);
|
||||
// No child stdin (don't use pager)
|
||||
close(0);
|
||||
// -n : Loading config files can apparently cause failures with the
|
||||
// other options here.
|
||||
// -batch : non-interactive; suppress banners as much as possible
|
||||
execlp(/*cmd in PATH*/ "gdb", /*arg0*/ "gdb", "-n", "-batch", "-p",
|
||||
attach_id_str, "-ex", bt_in_gdb, (char*)nullptr);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// parent process; wait for child
|
||||
int wstatus;
|
||||
waitpid(child_pid, &wstatus, 0);
|
||||
if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus) == EXIT_SUCCESS) {
|
||||
// Good
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "GDB failed; falling back on backtrace+addr2line...\n");
|
||||
}
|
||||
|
||||
const int kMaxFrames = 100;
|
||||
void* frames[kMaxFrames];
|
||||
|
||||
@@ -189,7 +269,9 @@ void InstallStackTraceHandler() {
|
||||
signal(SIGSEGV, StackTraceHandler);
|
||||
signal(SIGBUS, StackTraceHandler);
|
||||
signal(SIGABRT, StackTraceHandler);
|
||||
// Allow ouside debugger to attach, even with Yama security restrictions
|
||||
// Allow ouside debugger to attach, even with Yama security restrictions.
|
||||
// This is needed even outside of PrintStack() so that external mechanisms
|
||||
// can dump stacks if they suspect that a test has hung.
|
||||
#ifdef PR_SET_PTRACER_ANY
|
||||
(void)prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -97,7 +97,7 @@ class WinClock : public SystemClock {
|
||||
|
||||
Status GetCurrentTime(int64_t* unix_time) override;
|
||||
// Converts seconds-since-Jan-01-1970 to a printable string
|
||||
virtual std::string TimeToString(uint64_t time);
|
||||
std::string TimeToString(uint64_t time) override;
|
||||
|
||||
uint64_t GetPerfCounterFrequency() const { return perf_counter_frequency_; }
|
||||
|
||||
@@ -116,7 +116,7 @@ class WinFileSystem : public FileSystem {
|
||||
~WinFileSystem() {}
|
||||
static const char* kClassName() { return "WinFS"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
const char* NickName() const { return kDefaultName(); }
|
||||
const char* NickName() const override { return kDefaultName(); }
|
||||
|
||||
static size_t GetSectorSize(const std::string& fname);
|
||||
size_t GetPageSize() const { return page_size_; }
|
||||
|
||||
+12
-11
@@ -17,21 +17,22 @@
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
//^^ <windows.h> should be included first before other system lib
|
||||
#include <intrin.h>
|
||||
#include <malloc.h>
|
||||
#include <process.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <condition_variable>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <string.h>
|
||||
#include <mutex>
|
||||
#include <limits>
|
||||
#include <condition_variable>
|
||||
#include <malloc.h>
|
||||
#include <intrin.h>
|
||||
#include <process.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "port/win/win_thread.h"
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/port_defs.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
@@ -234,6 +234,7 @@ LIB_SOURCES = \
|
||||
util/concurrent_task_limiter_impl.cc \
|
||||
util/crc32c.cc \
|
||||
util/crc32c_arm64.cc \
|
||||
util/data_structure.cc \
|
||||
util/dynamic_bloom.cc \
|
||||
util/hash.cc \
|
||||
util/murmurhash.cc \
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "port/malloc.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
|
||||
@@ -909,18 +909,6 @@ Status BlockBasedTableFactory::ParseOption(const ConfigOptions& config_options,
|
||||
return status;
|
||||
}
|
||||
|
||||
Status GetBlockBasedTableOptionsFromString(
|
||||
const BlockBasedTableOptions& table_options, const std::string& opts_str,
|
||||
BlockBasedTableOptions* new_table_options) {
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = false;
|
||||
config_options.ignore_unknown_options = false;
|
||||
config_options.invoke_prepare_options = false;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
|
||||
return GetBlockBasedTableOptionsFromString(config_options, table_options,
|
||||
opts_str, new_table_options);
|
||||
}
|
||||
Status GetBlockBasedTableOptionsFromString(
|
||||
const ConfigOptions& config_options,
|
||||
const BlockBasedTableOptions& table_options, const std::string& opts_str,
|
||||
@@ -940,20 +928,6 @@ Status GetBlockBasedTableOptionsFromString(
|
||||
}
|
||||
}
|
||||
|
||||
Status GetBlockBasedTableOptionsFromMap(
|
||||
const BlockBasedTableOptions& table_options,
|
||||
const std::unordered_map<std::string, std::string>& opts_map,
|
||||
BlockBasedTableOptions* new_table_options, bool input_strings_escaped,
|
||||
bool ignore_unknown_options) {
|
||||
ConfigOptions config_options;
|
||||
config_options.input_strings_escaped = input_strings_escaped;
|
||||
config_options.ignore_unknown_options = ignore_unknown_options;
|
||||
config_options.invoke_prepare_options = false;
|
||||
|
||||
return GetBlockBasedTableOptionsFromMap(config_options, table_options,
|
||||
opts_map, new_table_options);
|
||||
}
|
||||
|
||||
Status GetBlockBasedTableOptionsFromMap(
|
||||
const ConfigOptions& config_options,
|
||||
const BlockBasedTableOptions& table_options,
|
||||
|
||||
@@ -1929,6 +1929,16 @@ Status BlockBasedTable::ApproximateKeyAnchors(const ReadOptions& read_options,
|
||||
// likely not to be a problem. We are compacting the whole file, so all
|
||||
// keys will be read out anyway. An extra read to index block might be
|
||||
// a small share of the overhead. We can try to optimize if needed.
|
||||
//
|
||||
// `CacheDependencies()` brings all the blocks into cache using one I/O. That
|
||||
// way the full index scan usually finds the index data it is looking for in
|
||||
// cache rather than doing an I/O for each "dependency" (partition).
|
||||
Status s =
|
||||
rep_->index_reader->CacheDependencies(read_options, false /* pin */);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
IndexBlockIter iiter_on_stack;
|
||||
auto iiter = NewIndexIterator(
|
||||
read_options, /*disable_prefix_seek=*/false, &iiter_on_stack,
|
||||
|
||||
@@ -253,7 +253,7 @@ TEST_P(BlockBasedTableReaderTest, MultiGet) {
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
true /* do_merge */, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr);
|
||||
key_context.emplace_back(nullptr, keys[i], &values[i], nullptr,
|
||||
key_context.emplace_back(nullptr, keys[i], &values[i], nullptr, nullptr,
|
||||
&statuses.back());
|
||||
key_context.back().get_context = &get_context.back();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <type_traits>
|
||||
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/advanced_cache.h"
|
||||
#include "rocksdb/cleanable.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user