mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-08 07:05:23 +08:00
Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ad2f927b6 | |||
| aaece2a98d | |||
| c7432cc3c0 | |||
| a8170d774c | |||
| 6cbe9d9762 | |||
| 31bd2d790e | |||
| e85cbdb4e8 | |||
| 0b45a68c59 | |||
| 2f3261831b | |||
| 02df00d97b | |||
| 055b4d25f8 | |||
| b7c825d5cf | |||
| afa3518839 | |||
| 0f85d163e6 | |||
| f005dac2d9 | |||
| 0b8c549b3f | |||
| ffe08ffcc2 | |||
| 22e5c513c2 | |||
| 0a17d95357 | |||
| 172adce767 | |||
| 43f8a9dcce | |||
| 78e291b17e | |||
| 9360776cb9 | |||
| 2adb7e3768 | |||
| 556972e964 | |||
| bfc9737aca | |||
| 38f988d3b4 | |||
| 14eca6bf04 | |||
| 298b00a396 | |||
| 8d87e9cea1 | |||
| 66942e8158 | |||
| 02f59ed669 | |||
| bcb9e41080 | |||
| 038e02d8d9 | |||
| 0c56fc4d66 | |||
| c5abf78bca | |||
| 961c7590d6 | |||
| e3f953a863 | |||
| bcefc59e9f | |||
| 9060e6fa79 | |||
| 82a82c76e7 | |||
| bd68bfb41b | |||
| b464a85e33 | |||
| b559dba817 | |||
| 803a517b48 | |||
| bcd32560dd | |||
| 35a25a3fb9 | |||
| 826295a5e9 | |||
| 292bcf6227 | |||
| 38be686160 | |||
| c7aedf1b48 | |||
| eb04bb86c6 | |||
| 545e14b53b | |||
| aaafcb80ab | |||
| a1523efcdf | |||
| 39b24432d4 | |||
| 1551f1011a | |||
| 0ac0098705 | |||
| ada700b906 | |||
| b9d65f5aa6 | |||
| d790e6004f | |||
| 50ba245f6d | |||
| 91b7553293 | |||
| b11a8b1b9a | |||
| 06a2dcebea | |||
| 50d63a2af0 | |||
| 3bea276fc8 | |||
| 244797aa4b | |||
| 07204837ce | |||
| 4a4b8a1344 | |||
| 70aaa9ceeb | |||
| c384c08a4f | |||
| 3f218074ee | |||
| 2e9324718a | |||
| f0e8731b72 | |||
| 3a1c29d40e | |||
| a50ea71c00 | |||
| d9cd33516a | |||
| e72e2167fd | |||
| 1c84660457 | |||
| 12825894a2 | |||
| 94265234de | |||
| 3730b05dc9 | |||
| f286fb344b | |||
| b27a1448b6 |
@@ -6,7 +6,7 @@ orbs:
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
machine:
|
||||
image: 'windows-server-2019-vs2019:stable'
|
||||
image: 'windows-server-2019-vs2019:201908-06'
|
||||
resource_class: windows.2xlarge
|
||||
shell: bash.exe
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
args: https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: make check-format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
|
||||
@@ -85,3 +85,5 @@ buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
|
||||
compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
|
||||
+33
-2
@@ -53,6 +53,8 @@ env:
|
||||
- JOB_NAME=cmake-gcc9 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9-c++20 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
- JOB_NAME=make-gcc4.8
|
||||
- JOB_NAME=status_checked
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
@@ -64,6 +66,8 @@ matrix:
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: osx
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: osx
|
||||
arch: ppc64le
|
||||
- os: osx
|
||||
@@ -71,9 +75,15 @@ matrix:
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
compiler: clang
|
||||
# Exclude all but most unique cmake variants for pull requests, but build all in branches
|
||||
@@ -197,6 +207,17 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
os : osx
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=status_checked
|
||||
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
@@ -213,6 +234,10 @@ install:
|
||||
- if [ "${JOB_NAME}" == cmake-mingw ]; then
|
||||
sudo apt-get install -y mingw-w64 ;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == make-gcc4.8 ]; then
|
||||
sudo apt-get install -y g++-4.8 ;
|
||||
CC=gcc-4.8 && CXX=g++-4.8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
CMAKE_DIST_URL="https://rocksdb-deps.s3-us-west-2.amazonaws.com/cmake/cmake-3.14.5-Linux-$(uname -m).tar.bz2";
|
||||
TAR_OPT="--strip-components=1 -xj";
|
||||
@@ -269,7 +294,7 @@ script:
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 static_lib tools
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 all
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
@@ -285,7 +310,13 @@ script:
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 .. && make -j4 && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
make-gcc4.8)
|
||||
OPT=-DTRAVIS V=1 SKIP_LINK=1 make -j4 all && [ "Linking broken because libgflags compiled with newer ABI" ]
|
||||
;;
|
||||
status_checked)
|
||||
OPT=-DTRAVIS V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
|
||||
+46
-19
@@ -107,18 +107,32 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# No config file for this
|
||||
if(WITH_GFLAGS)
|
||||
find_package(gflags REQUIRED)
|
||||
# Config with namespace available since gflags 2.2.2
|
||||
option(GFLAGS_USE_TARGET_NAMESPACE "Use gflags import target with namespace." ON)
|
||||
find_package(gflags CONFIG)
|
||||
if(gflags_FOUND)
|
||||
if(TARGET ${GFLAGS_TARGET})
|
||||
# Config with GFLAGS_TARGET available since gflags 2.2.0
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_TARGET})
|
||||
else()
|
||||
# Config with GFLAGS_LIBRARIES available since gflags 2.1.0
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
find_package(gflags REQUIRED)
|
||||
list(APPEND THIRDPARTY_LIBS gflags::gflags)
|
||||
endif()
|
||||
add_definitions(-DGFLAGS=1)
|
||||
include_directories(${gflags_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS gflags::gflags)
|
||||
endif()
|
||||
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
find_package(Snappy CONFIG)
|
||||
if(NOT Snappy_FOUND)
|
||||
find_package(Snappy REQUIRED)
|
||||
endif()
|
||||
add_definitions(-DSNAPPY)
|
||||
list(APPEND THIRDPARTY_LIBS snappy::snappy)
|
||||
list(APPEND THIRDPARTY_LIBS Snappy::snappy)
|
||||
endif()
|
||||
|
||||
if(WITH_ZLIB)
|
||||
@@ -509,7 +523,6 @@ endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
endif()
|
||||
@@ -790,9 +803,13 @@ if(WIN32)
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc)
|
||||
|
||||
port/win/win_logger.cc)
|
||||
if(NOT MINGW)
|
||||
# Mingw only supports std::thread when using
|
||||
# posix threads.
|
||||
list(APPEND SOURCES
|
||||
port/win/win_thread.cc)
|
||||
endif()
|
||||
if(WITH_XPRESS)
|
||||
list(APPEND SOURCES
|
||||
port/win/xpress_win.cc)
|
||||
@@ -839,12 +856,12 @@ else()
|
||||
endif()
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB}
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
@@ -872,6 +889,16 @@ else()
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
# Tests are excluded from Release builds
|
||||
CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON
|
||||
"CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
|
||||
option(WITH_BENCHMARK_TOOLS "build with benchmarks" ON)
|
||||
option(WITH_CORE_TOOLS "build with ldb and sst_dump" ON)
|
||||
option(WITH_TOOLS "build with tools" ON)
|
||||
|
||||
if(WITH_TESTS OR WITH_BENCHMARK_TOOLS OR WITH_TOOLS OR WITH_JNI OR JNI)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
endif()
|
||||
if(WITH_JNI OR JNI)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
@@ -909,6 +936,8 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
|
||||
install(DIRECTORY include/rocksdb COMPONENT devel DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/modules" COMPONENT devel DESTINATION ${package_config_destination})
|
||||
|
||||
install(
|
||||
TARGETS ${ROCKSDB_STATIC_LIB}
|
||||
EXPORT RocksDBTargets
|
||||
@@ -945,15 +974,11 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Tests are excluded from Release builds
|
||||
CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON
|
||||
"CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
|
||||
if(WITH_TESTS)
|
||||
add_subdirectory(third-party/gtest-1.8.1/fused-src/gtest)
|
||||
add_library(testharness STATIC
|
||||
test_util/testharness.cc)
|
||||
target_link_libraries(testharness gtest)
|
||||
|
||||
set(TESTS
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
@@ -1053,6 +1078,7 @@ if(WITH_TESTS)
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_based_table_reader_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
table/block_based/full_filter_block_test.cc
|
||||
@@ -1158,6 +1184,10 @@ if(WITH_TESTS)
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
if("${exename}" MATCHES "env_librados_test")
|
||||
# env_librados_test.cc uses librados directly
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} rados)
|
||||
endif()
|
||||
endforeach(sourcefile ${TESTS})
|
||||
|
||||
if(WIN32)
|
||||
@@ -1181,7 +1211,6 @@ if(WITH_TESTS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_BENCHMARK_TOOLS "build with benchmarks" ON)
|
||||
if(WITH_BENCHMARK_TOOLS)
|
||||
add_executable(db_bench
|
||||
tools/db_bench.cc
|
||||
@@ -1220,8 +1249,6 @@ if(WITH_BENCHMARK_TOOLS)
|
||||
${ROCKSDB_LIB})
|
||||
endif()
|
||||
|
||||
option(WITH_CORE_TOOLS "build with ldb and sst_dump" ON)
|
||||
option(WITH_TOOLS "build with tools" ON)
|
||||
if(WITH_CORE_TOOLS OR WITH_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
add_custom_target(core_tools
|
||||
|
||||
+18
@@ -1,12 +1,30 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Behavior Changes
|
||||
* Disable delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode and num_levels = 1 in order to avoid a corruption bug.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
||||
* Fix possible false NotFound status from batched MultiGet using index type kHashSearch.
|
||||
* Fix corruption caused by enabling delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode, along with parallel compactions. The bug can result in two parallel compactions picking the same input files, resulting in the DB resurrecting older and deleted versions of some keys.
|
||||
* Fix a use-after-free bug in best-efforts recovery. column_family_memtables_ needs to point to valid ColumnFamilySet.
|
||||
* Let best-efforts recovery ignore corrupted files during table loading.
|
||||
* Fix corrupt key read from ingested file when iterator direction switches from reverse to forward at a key that is a prefix of another key in the same file. It is only possible in files with a non-zero global seqno.
|
||||
* Fix abnormally large estimate from GetApproximateSizes when a range starts near the end of one SST file and near the beginning of another. Now GetApproximateSizes consistently and fairly includes the size of SST metadata in addition to data blocks, attributing metadata proportionally among the data blocks based on their size.
|
||||
* Fix potential file descriptor leakage in PosixEnv's IsDirectory() and NewRandomAccessFile().
|
||||
|
||||
### Public API Change
|
||||
* Flush(..., column_family) may return Status::ColumnFamilyDropped() instead of Status::InvalidArgument() if column_family is dropped while processing the flush request.
|
||||
* BlobDB now explicitly disallows using the default column family's storage directories as blob directory.
|
||||
* DeleteRange now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
* ldb now uses options.force_consistency_checks = true by default and "--disable_consistency_checks" is added to disable it.
|
||||
* DB::OpenForReadOnly no longer creates files or directories if the named DB does not exist, unless create_if_missing is set to true.
|
||||
* The consistency checks that validate LSM state changes (table file additions/deletions during flushes and compactions) are now stricter, more efficient, and no longer optional, i.e. they are performed even if `force_consistency_checks` is `false`.
|
||||
|
||||
### New Feature
|
||||
* sst_dump to add a new --readahead_size argument. Users can specify read size when scanning the data. Sst_dump also tries to prefetch tail part of the SST files so usually some number of I/Os are saved there too.
|
||||
* Generate file checksum in SstFileWriter if Options.file_checksum_gen_factory is set. The checksum and checksum function name are stored in ExternalSstFileInfo after the sst file write is finished.
|
||||
* Add a value_size_soft_limit in read options which limits the cumulative value size of keys read in batches in MultiGet. Once the cumulative value size of found keys exceeds read_options.value_size_soft_limit, all the remaining keys are returned with status Abort without further finding their values. By default the value_size_soft_limit is std::numeric_limits<uint64_t>::max().
|
||||
|
||||
## 6.10 (5/2/2020)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
PYTHON?=$(shell which python)
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
PYTHON?=$(shell which python3 || which python || echo python3)
|
||||
export PYTHON
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -166,6 +168,12 @@ else
|
||||
CXXFLAGS += -fno-rtti
|
||||
endif
|
||||
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
ifeq ($(filter -DROCKSDB_ASSERT_STATUS_CHECKED,$(OPT)),)
|
||||
OPT += -DROCKSDB_ASSERT_STATUS_CHECKED
|
||||
endif
|
||||
endif
|
||||
|
||||
$(warning Warning: Compiling in debug mode. Don't use the resulting binary in production)
|
||||
endif
|
||||
|
||||
@@ -187,12 +195,16 @@ AM_V_CC = $(am__v_CC_$(V))
|
||||
am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CC_0 = @echo " CC " $@;
|
||||
am__v_CC_1 =
|
||||
CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
AM_V_CCLD = $(am__v_CCLD_$(V))
|
||||
am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
|
||||
ifneq ($(SKIP_LINK), 1)
|
||||
am__v_CCLD_0 = @echo " CCLD " $@;
|
||||
am__v_CCLD_1 =
|
||||
else
|
||||
am__v_CCLD_0 = @echo " !CCLD " $@; true skip
|
||||
am__v_CCLD_1 = true skip
|
||||
endif
|
||||
AM_V_AR = $(am__v_AR_$(V))
|
||||
am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
@@ -204,6 +216,7 @@ LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
@@ -321,7 +334,7 @@ endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1
|
||||
export GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.8.1/fused-src
|
||||
GTEST_DIR = third-party/gtest-1.8.1/fused-src
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(GTEST_DIR)
|
||||
@@ -533,6 +546,7 @@ TESTS = \
|
||||
random_access_file_reader_test \
|
||||
file_reader_writer_test \
|
||||
block_based_filter_block_test \
|
||||
block_based_table_reader_test \
|
||||
full_filter_block_test \
|
||||
partitioned_filter_block_test \
|
||||
hash_table_test \
|
||||
@@ -626,10 +640,6 @@ TESTS = \
|
||||
timer_test \
|
||||
db_with_timestamp_compaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
db_bloom_filter_test \
|
||||
@@ -653,10 +663,67 @@ PARALLEL_TEST = \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
PARALLEL_TEST += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
# options_settable_test doesn't pass with UBSAN as we use hack in the test
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
TESTS := $(shell echo $(TESTS) | sed 's/\boptions_settable_test\b//g')
|
||||
endif
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
# This is a new check for which we will add support incrementally. The
|
||||
# whitelist can be removed once support is fully added.
|
||||
TESTS_WHITELIST = \
|
||||
arena_test \
|
||||
autovector_test \
|
||||
blob_file_addition_test \
|
||||
blob_file_garbage_test \
|
||||
bloom_test \
|
||||
cassandra_format_test \
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
cleanable_test \
|
||||
coding_test \
|
||||
crc32c_test \
|
||||
dbformat_test \
|
||||
defer_test \
|
||||
dynamic_bloom_test \
|
||||
event_logger_test \
|
||||
file_indexer_test \
|
||||
folly_synchronization_distributed_mutex_test \
|
||||
hash_table_test \
|
||||
hash_test \
|
||||
heap_test \
|
||||
histogram_test \
|
||||
inlineskiplist_test \
|
||||
io_posix_test \
|
||||
iostats_context_test \
|
||||
memkind_kmem_allocator_test \
|
||||
merger_test \
|
||||
mock_env_test \
|
||||
object_registry_test \
|
||||
options_settable_test \
|
||||
options_test \
|
||||
random_test \
|
||||
range_del_aggregator_test \
|
||||
range_tombstone_fragmenter_test \
|
||||
repeatable_thread_test \
|
||||
skiplist_test \
|
||||
slice_test \
|
||||
statistics_test \
|
||||
thread_local_test \
|
||||
timer_queue_test \
|
||||
timer_test \
|
||||
util_merge_operators_test \
|
||||
version_edit_test \
|
||||
work_queue_test \
|
||||
write_controller_test \
|
||||
|
||||
TESTS := $(filter $(TESTS_WHITELIST),$(TESTS))
|
||||
PARALLEL_TEST := $(filter $(TESTS_WHITELIST),$(PARALLEL_TEST))
|
||||
endif
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
SUBSET := $(shell echo $(SUBSET) | sed 's/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/')
|
||||
@@ -955,6 +1022,9 @@ CLEAN_FILES += t LOG $(TMPD)
|
||||
watch-log:
|
||||
$(WATCH) --interval=0 'sort -k7,7nr -k4,4gr LOG|$(quoted_perl_command)'
|
||||
|
||||
dump-log:
|
||||
bash -c '$(quoted_perl_command)' < LOG
|
||||
|
||||
# If J != 1 and GNU parallel is installed, run the tests in parallel,
|
||||
# via the check_0 rule above. Otherwise, run them sequentially.
|
||||
check: all
|
||||
@@ -972,9 +1042,11 @@ check: all
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
$(PYTHON) tools/check_all_python.py
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
$(MAKE) check-format
|
||||
$(MAKE) check-buck-targets
|
||||
@@ -1266,9 +1338,9 @@ db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
arena_test: memory/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
|
||||
memkind_kmem_allocator_test: memory/memkind_kmem_allocator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
@@ -1554,6 +1626,9 @@ file_reader_writer_test: util/file_reader_writer_test.o $(LIBOBJECTS) $(TESTHARN
|
||||
block_based_filter_block_test: table/block_based/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_table_reader_test: table/block_based/block_based_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
full_filter_block_test: table/block_based/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2199,6 +2274,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# FIXME: nothing checks that entries in MAIN_SOURCES actually exist
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(ANALYZER_LIB_SOURCES) $(STRESS_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.cc.d)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# This file @generated by `python buckifier/buckify_rocksdb.py`
|
||||
# This file @generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -26,7 +26,6 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -79,6 +78,7 @@ ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
"-I" + REPO_PATH + "third-party/gtest-1.8.1/fused-src/",
|
||||
]
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
@@ -109,6 +109,11 @@ ROCKSDB_OS_DEPS += ([(
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
srcs = [
|
||||
@@ -393,7 +398,10 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
deps = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_third_party_gtest",
|
||||
],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
@@ -437,10 +445,20 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
],
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_third_party_gtest",
|
||||
srcs = ["third-party/gtest-1.8.1/fused-src/gtest/gtest-all.cc"],
|
||||
headers = ["third-party/gtest-1.8.1/fused-src/gtest/gtest.h"],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
@@ -515,6 +533,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_based_table_reader_test",
|
||||
"table/block_based/block_based_table_reader_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_cache_trace_analyzer_test",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc",
|
||||
|
||||
@@ -20,10 +20,10 @@ from util import ColorString
|
||||
# User can pass extra dependencies as a JSON object via command line, and this
|
||||
# script can include these dependencies in the generate TARGETS file.
|
||||
# Usage:
|
||||
# $python buckifier/buckify_rocksdb.py
|
||||
# $python3 buckifier/buckify_rocksdb.py
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
# tests.)
|
||||
# $python buckifier/buckify_rocksdb.py \
|
||||
# $python3 buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": { \
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"], \
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"], \
|
||||
@@ -109,6 +109,15 @@ def get_tests(repo_path):
|
||||
return tests
|
||||
|
||||
|
||||
# Get gtest dir from Makefile
|
||||
def get_gtest_dir(repo_path):
|
||||
for line in open(repo_path + "/Makefile"):
|
||||
if line.strip().startswith("GTEST_DIR ="):
|
||||
return line.split("=")[1].strip()
|
||||
# if not found
|
||||
exit_with_error("Unable to find GTEST_DIR in Makefile")
|
||||
|
||||
|
||||
# Parse extra dependencies passed by user from command line
|
||||
def get_dependencies():
|
||||
deps_map = {
|
||||
@@ -142,11 +151,14 @@ def generate_targets(repo_path, deps_map):
|
||||
cc_files = get_cc_files(repo_path)
|
||||
# get tests from Makefile
|
||||
tests = get_tests(repo_path)
|
||||
# get gtest dir
|
||||
gtest_dir = get_gtest_dir(repo_path) + "/"
|
||||
|
||||
if src_mk is None or cc_files is None or tests is None:
|
||||
return False
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path)
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, gtest_dir)
|
||||
|
||||
# rocksdb_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
@@ -159,7 +171,7 @@ def generate_targets(repo_path, deps_map):
|
||||
src_mk.get("TEST_LIB_SOURCES", []) +
|
||||
src_mk.get("EXP_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"])
|
||||
[":rocksdb_lib", ":rocksdb_third_party_gtest"])
|
||||
# rocksdb_tools_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
@@ -168,14 +180,19 @@ def generate_targets(repo_path, deps_map):
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_stress_lib
|
||||
TARGETS.add_library(
|
||||
TARGETS.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get('STRESS_LIB_SOURCES', [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib", ":rocksdb_test_lib"])
|
||||
+ ["test_util/testutil.cc"])
|
||||
# rocksdb_third_party_gtest
|
||||
TARGETS.add_library(
|
||||
"rocksdb_third_party_gtest",
|
||||
[gtest_dir + "gtest/gtest-all.cc"],
|
||||
[],
|
||||
[gtest_dir + "gtest/gtest.h"])
|
||||
|
||||
print("Extra dependencies:\n{0}".format(str(deps_map)))
|
||||
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
|
||||
# test for every test we found in the Makefile
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test in sorted(tests):
|
||||
@@ -196,8 +213,8 @@ def generate_targets(repo_path, deps_map):
|
||||
test_target_name,
|
||||
match_src[0],
|
||||
is_parallel,
|
||||
deps['extra_deps'],
|
||||
deps['extra_compiler_flags'])
|
||||
json.dumps(deps['extra_deps']),
|
||||
json.dumps(deps['extra_compiler_flags']))
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
@@ -220,6 +237,7 @@ def get_rocksdb_path():
|
||||
|
||||
return rocksdb_path
|
||||
|
||||
|
||||
def exit_with_error(msg):
|
||||
print(ColorString.error(msg))
|
||||
sys.exit(1)
|
||||
|
||||
@@ -15,7 +15,7 @@ echo Backup original TARGETS file.
|
||||
|
||||
cp TARGETS TARGETS.bkp
|
||||
|
||||
python buckifier/buckify_rocksdb.py
|
||||
${PYTHON:-python3} buckifier/buckify_rocksdb.py
|
||||
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
@@ -24,8 +24,9 @@ then
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 0
|
||||
else
|
||||
echo "Please run 'python buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Do not manually update TARGETS file."
|
||||
${PYTHON:-python3} --version
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -25,10 +25,12 @@ def pretty_list(lst, indent=8):
|
||||
|
||||
|
||||
class TARGETSBuilder(object):
|
||||
def __init__(self, path):
|
||||
def __init__(self, path, gtest_dir):
|
||||
self.path = path
|
||||
self.targets_file = open(path, 'w')
|
||||
self.targets_file.write(targets_cfg.rocksdb_target_header)
|
||||
header = targets_cfg.rocksdb_target_header_template.format(
|
||||
gtest_dir=gtest_dir)
|
||||
self.targets_file.write(header)
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
@@ -42,6 +44,8 @@ class TARGETSBuilder(object):
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
@@ -50,6 +54,20 @@ class TARGETSBuilder(object):
|
||||
deps=pretty_list(deps)))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.rocksdb_library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(self, name, srcs, deps=None):
|
||||
self.targets_file.write(targets_cfg.binary_template % (
|
||||
name,
|
||||
|
||||
@@ -4,7 +4,8 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
rocksdb_target_header = """# This file \100generated by `python buckifier/buckify_rocksdb.py`
|
||||
rocksdb_target_header_template = \
|
||||
"""# This file \100generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -32,7 +33,6 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -85,13 +85,14 @@ ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
"-I" + REPO_PATH + "{gtest_dir}",
|
||||
]
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {{
|
||||
"x86_64": [
|
||||
"-DHAVE_PCLMUL",
|
||||
],
|
||||
}
|
||||
}}
|
||||
|
||||
build_mode = read_config("fbcode", "build_mode")
|
||||
|
||||
@@ -114,6 +115,11 @@ ROCKSDB_OS_DEPS += ([(
|
||||
"linux",
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
"""
|
||||
|
||||
|
||||
@@ -132,6 +138,21 @@ cpp_library(
|
||||
)
|
||||
"""
|
||||
|
||||
rocksdb_library_template = """
|
||||
cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
|
||||
binary_template = """
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
|
||||
+68
-40
@@ -26,53 +26,77 @@ while getopts ':ch' OPTION; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
then
|
||||
CLANG_FORMAT_DIFF="clang-format-diff.py"
|
||||
fi
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
# Check clang-format-diff.py
|
||||
if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
if [ ! -f ./clang-format-diff.py ]
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
if [ "$CLANG_FORMAT_DIFF" ]; then
|
||||
echo "Note: CLANG_FORMAT_DIFF='$CLANG_FORMAT_DIFF'"
|
||||
# Dry run to confirm dependencies like argparse
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
if [ -x ./clang-format-diff.py ]
|
||||
then
|
||||
PATH=$PATH:.
|
||||
exit 128
|
||||
fi
|
||||
else
|
||||
# First try directly executing the possibilities
|
||||
if clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=clang-format-diff.py
|
||||
elif $REPO_ROOT/clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=$REPO_ROOT/clang-format-diff.py
|
||||
else
|
||||
# This probably means we need to directly invoke the interpreter.
|
||||
# But first find clang-format-diff.py
|
||||
if [ -f "$REPO_ROOT/clang-format-diff.py" ]; then
|
||||
CFD_PATH="$REPO_ROOT/clang-format-diff.py"
|
||||
elif which clang-format-diff.py &> /dev/null; then
|
||||
CFD_PATH="$(which clang-format-diff.py)"
|
||||
else
|
||||
CLANG_FORMAT_DIFF="python ./clang-format-diff.py"
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
fi
|
||||
# Check argparse pre-req on interpreter, or it will fail
|
||||
if echo import argparse | ${PYTHON:-python3}; then
|
||||
true # Good
|
||||
else
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
# Unfortunately, some machines have a Python2 clang-format-diff.py
|
||||
# installed but only a Python3 interpreter installed. Rather than trying
|
||||
# different Python versions that might be installed, we can try migrating
|
||||
# the code to Python3 if it looks like Python2
|
||||
if grep -q "print '" "$CFD_PATH" && \
|
||||
${PYTHON:-python3} --version | grep -q 'ython 3'; then
|
||||
if [ ! -f "$REPO_ROOT/.py3/clang-format-diff.py" ]; then
|
||||
echo "Migrating $CFD_PATH to Python3 in a hidden file"
|
||||
mkdir -p "$REPO_ROOT/.py3"
|
||||
${PYTHON:-python3} -m lib2to3 -w -n -o "$REPO_ROOT/.py3" "$CFD_PATH" > /dev/null || exit 128
|
||||
fi
|
||||
CFD_PATH="$REPO_ROOT/.py3/clang-format-diff.py"
|
||||
fi
|
||||
CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
|
||||
# This had better work after all those checks
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
exit 128
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check argparse, a library that clang-format-diff.py requires.
|
||||
python 2>/dev/null << EOF
|
||||
import argparse
|
||||
EOF
|
||||
|
||||
if [ "$?" != 0 ]
|
||||
then
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
|
||||
# TODO(kailiu) following work is not complete since we still need to figure
|
||||
# out how to add the modified files done pre-commit hook to git's commit index.
|
||||
#
|
||||
@@ -124,6 +148,10 @@ then
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
if [ $VERBOSE_CHECK ]; then
|
||||
clang-format --version
|
||||
echo "$diffs"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Vendored
+30
-5
@@ -14,7 +14,30 @@
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Status Cache::CreateFromString(const ConfigOptions& /*opts*/,
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
lru_cache_options_type_info = {
|
||||
{"capacity",
|
||||
{offsetof(struct LRUCacheOptions, capacity), OptionType::kSizeT,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, capacity)}},
|
||||
{"num_shard_bits",
|
||||
{offsetof(struct LRUCacheOptions, num_shard_bits), OptionType::kInt,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, num_shard_bits)}},
|
||||
{"strict_capacity_limit",
|
||||
{offsetof(struct LRUCacheOptions, strict_capacity_limit),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, strict_capacity_limit)}},
|
||||
{"high_pri_pool_ratio",
|
||||
{offsetof(struct LRUCacheOptions, high_pri_pool_ratio),
|
||||
OptionType::kDouble, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, high_pri_pool_ratio)}}};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
Status status;
|
||||
@@ -24,12 +47,14 @@ Status Cache::CreateFromString(const ConfigOptions& /*opts*/,
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
LRUCacheOptions cache_opts;
|
||||
if (!ParseOptionHelper(reinterpret_cast<char*>(&cache_opts),
|
||||
OptionType::kLRUCacheOptions, value)) {
|
||||
status = Status::InvalidArgument("Invalid cache options");
|
||||
status = OptionTypeInfo::ParseStruct(
|
||||
config_options, "", &lru_cache_options_type_info, "", value,
|
||||
reinterpret_cast<char*>(&cache_opts));
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
cache = NewLRUCache(cache_opts);
|
||||
#else
|
||||
(void)config_options;
|
||||
status = Status::NotSupported("Cannot load cache in LITE mode ", value);
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -182,7 +182,7 @@ struct CacheHandle {
|
||||
void (*deleter)(const Slice&, void* value);
|
||||
|
||||
// Flags and counters associated with the cache handle:
|
||||
// lowest bit: n-cache bit
|
||||
// lowest bit: in-cache bit
|
||||
// second lowest bit: usage bit
|
||||
// the rest bits: reference count
|
||||
// The handle is unused when flags equals to 0. The thread decreases the count
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
set(GFLAGS_USE_TARGET_NAMESPACE @GFLAGS_USE_TARGET_NAMESPACE@)
|
||||
|
||||
if(@WITH_JEMALLOC@)
|
||||
find_dependency(JeMalloc)
|
||||
endif()
|
||||
|
||||
if(@WITH_GFLAGS@)
|
||||
find_dependency(gflags CONFIG)
|
||||
if(NOT gflags_FOUND)
|
||||
find_dependency(gflags)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(@WITH_SNAPPY@)
|
||||
find_dependency(Snappy CONFIG)
|
||||
if(NOT Snappy_FOUND)
|
||||
find_dependency(Snappy)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(@WITH_ZLIB@)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if(@WITH_BZ2@)
|
||||
find_dependency(BZip2)
|
||||
endif()
|
||||
|
||||
if(@WITH_LZ4@)
|
||||
find_dependency(lz4)
|
||||
endif()
|
||||
|
||||
if(@WITH_ZSTD@)
|
||||
find_dependency(zstd)
|
||||
endif()
|
||||
|
||||
if(@WITH_NUMA@)
|
||||
find_dependency(NUMA)
|
||||
endif()
|
||||
|
||||
if(@WITH_TBB@)
|
||||
find_dependency(TBB)
|
||||
endif()
|
||||
|
||||
find_dependency(Threads)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/RocksDBTargets.cmake")
|
||||
check_required_components(RocksDB)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# Snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# Snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# Snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(Snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(Snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Snappy DEFAULT_MSG Snappy_LIBRARIES Snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
Snappy_LIBRARIES
|
||||
Snappy_INCLUDE_DIRS)
|
||||
|
||||
if(Snappy_FOUND AND NOT (TARGET Snappy::snappy))
|
||||
add_library (Snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(Snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${Snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${Snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -1,8 +1,8 @@
|
||||
# - Find gflags library
|
||||
# Find the gflags includes and library
|
||||
#
|
||||
# gflags_INCLUDE_DIR - where to find gflags.h.
|
||||
# gflags_LIBRARIES - List of libraries when using gflags.
|
||||
# GFLAGS_INCLUDE_DIR - where to find gflags.h.
|
||||
# GFLAGS_LIBRARIES - List of libraries when using gflags.
|
||||
# gflags_FOUND - True if gflags found.
|
||||
|
||||
find_path(GFLAGS_INCLUDE_DIR
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG snappy_LIBRARIES snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
snappy_LIBRARIES
|
||||
snappy_INCLUDE_DIRS)
|
||||
|
||||
if(snappy_FOUND AND NOT (TARGET snappy::snappy))
|
||||
add_library (snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -12,7 +12,7 @@ fi
|
||||
ROOT=".."
|
||||
# Fetch right version of gcov
|
||||
if [ -d /mnt/gvfs/third-party -a -z "$CXX" ]; then
|
||||
source $ROOT/build_tools/fbcode_config.sh
|
||||
source $ROOT/build_tools/fbcode_config_platform007.sh
|
||||
GCOV=$GCC_BASE/bin/gcov
|
||||
else
|
||||
GCOV=$(which gcov)
|
||||
|
||||
@@ -1466,6 +1466,11 @@ void rocksdb_writebatch_delete(
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_singledelete(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen) {
|
||||
b->rep.SingleDelete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_cf(
|
||||
rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1473,6 +1478,12 @@ void rocksdb_writebatch_delete_cf(
|
||||
b->rep.Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_singledelete_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep.SingleDelete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_deletev(
|
||||
rocksdb_writebatch_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
@@ -1723,6 +1734,11 @@ void rocksdb_writebatch_wi_delete(
|
||||
b->rep->Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_singledelete(rocksdb_writebatch_wi_t* b,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->SingleDelete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1730,6 +1746,12 @@ void rocksdb_writebatch_wi_delete_cf(
|
||||
b->rep->Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_singledelete_cf(
|
||||
rocksdb_writebatch_wi_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->SingleDelete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_deletev(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
@@ -2154,6 +2176,10 @@ void rocksdb_options_destroy(rocksdb_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create_copy(rocksdb_options_t* options) {
|
||||
return new rocksdb_options_t(*options);
|
||||
}
|
||||
|
||||
void rocksdb_options_increase_parallelism(
|
||||
rocksdb_options_t* opt, int total_threads) {
|
||||
opt->rep.IncreaseParallelism(total_threads);
|
||||
@@ -2179,6 +2205,10 @@ void rocksdb_options_set_allow_ingest_behind(
|
||||
opt->rep.allow_ingest_behind = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_allow_ingest_behind(rocksdb_options_t* opt) {
|
||||
return opt->rep.allow_ingest_behind;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_compactionfilter_t* filter) {
|
||||
@@ -2196,6 +2226,10 @@ void rocksdb_options_compaction_readahead_size(
|
||||
opt->rep.compaction_readahead_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_compaction_readahead_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.compaction_readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_comparator(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_comparator_t* cmp) {
|
||||
@@ -2208,27 +2242,43 @@ void rocksdb_options_set_merge_operator(
|
||||
opt->rep.merge_operator = std::shared_ptr<MergeOperator>(merge_operator);
|
||||
}
|
||||
|
||||
|
||||
void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_if_missing = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_create_if_missing(rocksdb_options_t* opt) {
|
||||
return opt->rep.create_if_missing;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_create_missing_column_families(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_missing_column_families = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_create_missing_column_families(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.create_missing_column_families;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_error_if_exists(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.error_if_exists = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_error_if_exists(rocksdb_options_t* opt) {
|
||||
return opt->rep.error_if_exists;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_paranoid_checks(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.paranoid_checks = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_paranoid_checks(rocksdb_options_t* opt) {
|
||||
return opt->rep.paranoid_checks;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
size_t num_paths) {
|
||||
@@ -2254,52 +2304,98 @@ void rocksdb_options_set_info_log_level(
|
||||
opt->rep.info_log_level = static_cast<InfoLogLevel>(v);
|
||||
}
|
||||
|
||||
int rocksdb_options_get_info_log_level(rocksdb_options_t* opt) {
|
||||
return static_cast<int>(opt->rep.info_log_level);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_write_buffer_size(rocksdb_options_t* opt,
|
||||
size_t s) {
|
||||
opt->rep.db_write_buffer_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_db_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.db_write_buffer_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_buffer_size(rocksdb_options_t* opt, size_t s) {
|
||||
opt->rep.write_buffer_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.write_buffer_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_open_files(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_open_files = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_open_files(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_open_files;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_file_opening_threads(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_file_opening_threads = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_file_opening_threads(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_file_opening_threads;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_total_wal_size(rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_total_wal_size = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_max_total_wal_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_total_wal_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.target_file_size_base = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_target_file_size_base(rocksdb_options_t* opt) {
|
||||
return opt->rep.target_file_size_base;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_multiplier(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.target_file_size_multiplier = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_target_file_size_multiplier(rocksdb_options_t* opt) {
|
||||
return opt->rep.target_file_size_multiplier;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_bytes_for_level_base = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_max_bytes_for_level_base(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_bytes_for_level_base;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.level_compaction_dynamic_level_bytes = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.level_compaction_dynamic_level_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier(rocksdb_options_t* opt,
|
||||
double n) {
|
||||
opt->rep.max_bytes_for_level_multiplier = n;
|
||||
}
|
||||
|
||||
double rocksdb_options_get_max_bytes_for_level_multiplier(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_bytes_for_level_multiplier;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_compaction_bytes(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.max_compaction_bytes = n;
|
||||
@@ -2322,30 +2418,57 @@ void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt,
|
||||
opt->rep.skip_stats_update_on_db_open = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_skip_stats_update_on_db_open(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.skip_stats_update_on_db_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt, unsigned char val) {
|
||||
opt->rep.skip_checking_sst_file_sizes_on_db_open = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.skip_checking_sst_file_sizes_on_db_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_num_levels(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.num_levels = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_num_levels(rocksdb_options_t* opt) {
|
||||
return opt->rep.num_levels;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_file_num_compaction_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_file_num_compaction_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_file_num_compaction_trigger(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_file_num_compaction_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_slowdown_writes_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_slowdown_writes_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_slowdown_writes_trigger(rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_slowdown_writes_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_stop_writes_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_stop_writes_trigger(rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_stop_writes_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* /*opt*/,
|
||||
int /*n*/) {}
|
||||
|
||||
@@ -2357,6 +2480,10 @@ void rocksdb_options_set_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.bottommost_compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_per_level(rocksdb_options_t* opt,
|
||||
int* level_values,
|
||||
size_t num_levels) {
|
||||
@@ -2371,7 +2498,7 @@ void rocksdb_options_set_bottommost_compression_options(rocksdb_options_t* opt,
|
||||
int w_bits, int level,
|
||||
int strategy,
|
||||
int max_dict_bytes,
|
||||
bool enabled) {
|
||||
unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.window_bits = w_bits;
|
||||
opt->rep.bottommost_compression_opts.level = level;
|
||||
opt->rep.bottommost_compression_opts.strategy = strategy;
|
||||
@@ -2379,6 +2506,13 @@ void rocksdb_options_set_bottommost_compression_options(rocksdb_options_t* opt,
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes, unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.zstd_max_train_bytes =
|
||||
zstd_max_train_bytes;
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
int level, int strategy,
|
||||
int max_dict_bytes) {
|
||||
@@ -2388,6 +2522,11 @@ void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
opt->rep.compression_opts.max_dict_bytes = max_dict_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes) {
|
||||
opt->rep.compression_opts.zstd_max_train_bytes = zstd_max_train_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_prefix_extractor(
|
||||
rocksdb_options_t* opt, rocksdb_slicetransform_t* prefix_extractor) {
|
||||
opt->rep.prefix_extractor.reset(prefix_extractor);
|
||||
@@ -2527,35 +2666,67 @@ void rocksdb_options_set_max_write_buffer_number(rocksdb_options_t* opt, int n)
|
||||
opt->rep.max_write_buffer_number = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.min_write_buffer_number_to_merge = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_min_write_buffer_number_to_merge(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.min_write_buffer_number_to_merge;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_write_buffer_number_to_maintain = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt, int64_t n) {
|
||||
opt->rep.max_write_buffer_size_to_maintain = n;
|
||||
}
|
||||
|
||||
int64_t rocksdb_options_get_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_size_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_enable_pipelined_write(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_pipelined_write;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_unordered_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.unordered_write = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_unordered_write(rocksdb_options_t* opt) {
|
||||
return opt->rep.unordered_write;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_subcompactions(rocksdb_options_t* opt,
|
||||
uint32_t n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_options_get_max_subcompactions(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_subcompactions;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_background_jobs(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_background_jobs = n;
|
||||
}
|
||||
@@ -4456,6 +4627,10 @@ void rocksdb_approximate_memory_usage_destroy(rocksdb_memory_usage_t* usage) {
|
||||
delete usage;
|
||||
}
|
||||
|
||||
void rocksdb_cancel_all_background_work(rocksdb_t* db, unsigned char wait) {
|
||||
CancelAllBackgroundWork(db->rep, wait);
|
||||
}
|
||||
|
||||
} // end extern "C"
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
+291
@@ -1461,6 +1461,294 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cuckoo_options_destroy(cuckoo_options);
|
||||
}
|
||||
|
||||
StartPhase("options");
|
||||
{
|
||||
rocksdb_options_t* o;
|
||||
o = rocksdb_options_create();
|
||||
|
||||
// Set and check options.
|
||||
rocksdb_options_set_allow_ingest_behind(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(o));
|
||||
|
||||
rocksdb_options_compaction_readahead_size(o, 10);
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_create_if_missing(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(o));
|
||||
|
||||
rocksdb_options_set_create_missing_column_families(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_create_missing_column_families(o));
|
||||
|
||||
rocksdb_options_set_error_if_exists(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(o));
|
||||
|
||||
rocksdb_options_set_paranoid_checks(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(o));
|
||||
|
||||
rocksdb_options_set_info_log_level(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(o));
|
||||
|
||||
rocksdb_options_set_write_buffer_size(o, 100);
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_db_write_buffer_size(o, 1000);
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_max_open_files(o, 21);
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(o));
|
||||
|
||||
rocksdb_options_set_max_file_opening_threads(o, 5);
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(o));
|
||||
|
||||
rocksdb_options_set_max_total_wal_size(o, 400);
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(o));
|
||||
|
||||
rocksdb_options_set_num_levels(o, 7);
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(o));
|
||||
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(o, 4);
|
||||
CheckCondition(4 ==
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(o, 6);
|
||||
CheckCondition(6 == rocksdb_options_get_level0_slowdown_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_stop_writes_trigger(o, 8);
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_base(o, 256);
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_multiplier(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_base(o, 1024);
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(o));
|
||||
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(o, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(o, 2.0);
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(o, 1);
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(o, 97);
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(o, 23);
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(o, 64);
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(o, 50000);
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_enable_pipelined_write(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(o));
|
||||
|
||||
rocksdb_options_set_unordered_write(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(o));
|
||||
|
||||
rocksdb_options_set_max_subcompactions(o, 123456);
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(o));
|
||||
|
||||
// Create a copy that should be equal to the original.
|
||||
rocksdb_options_t* copy;
|
||||
copy = rocksdb_options_create_copy(o);
|
||||
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(copy));
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(copy));
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_create_missing_column_families(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(copy));
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(copy));
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(copy));
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(copy));
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(copy));
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(copy));
|
||||
CheckCondition(
|
||||
4 == rocksdb_options_get_level0_file_num_compaction_trigger(copy));
|
||||
CheckCondition(6 ==
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(copy));
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(copy));
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(copy));
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_level_compaction_dynamic_level_bytes(copy));
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(
|
||||
64 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(copy));
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(copy));
|
||||
|
||||
// Copies should be independent.
|
||||
rocksdb_options_set_allow_ingest_behind(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_allow_ingest_behind(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(o));
|
||||
|
||||
rocksdb_options_compaction_readahead_size(copy, 20);
|
||||
CheckCondition(20 == rocksdb_options_get_compaction_readahead_size(copy));
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_create_if_missing(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_create_if_missing(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(o));
|
||||
|
||||
rocksdb_options_set_create_missing_column_families(copy, 0);
|
||||
CheckCondition(0 ==
|
||||
rocksdb_options_get_create_missing_column_families(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_missing_column_families(o));
|
||||
|
||||
rocksdb_options_set_error_if_exists(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_error_if_exists(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(o));
|
||||
|
||||
rocksdb_options_set_paranoid_checks(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_paranoid_checks(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(o));
|
||||
|
||||
rocksdb_options_set_info_log_level(copy, 2);
|
||||
CheckCondition(2 == rocksdb_options_get_info_log_level(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(o));
|
||||
|
||||
rocksdb_options_set_write_buffer_size(copy, 200);
|
||||
CheckCondition(200 == rocksdb_options_get_write_buffer_size(copy));
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_db_write_buffer_size(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_db_write_buffer_size(copy));
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_max_open_files(copy, 42);
|
||||
CheckCondition(42 == rocksdb_options_get_max_open_files(copy));
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(o));
|
||||
|
||||
rocksdb_options_set_max_file_opening_threads(copy, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_max_file_opening_threads(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(o));
|
||||
|
||||
rocksdb_options_set_max_total_wal_size(copy, 4000);
|
||||
CheckCondition(4000 == rocksdb_options_get_max_total_wal_size(copy));
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(o));
|
||||
|
||||
rocksdb_options_set_num_levels(copy, 6);
|
||||
CheckCondition(6 == rocksdb_options_get_num_levels(copy));
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(o));
|
||||
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(copy, 14);
|
||||
CheckCondition(
|
||||
14 == rocksdb_options_get_level0_file_num_compaction_trigger(copy));
|
||||
CheckCondition(4 ==
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(copy, 61);
|
||||
CheckCondition(61 ==
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(copy));
|
||||
CheckCondition(6 == rocksdb_options_get_level0_slowdown_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_stop_writes_trigger(copy, 17);
|
||||
CheckCondition(17 == rocksdb_options_get_level0_stop_writes_trigger(copy));
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_base(copy, 128);
|
||||
CheckCondition(128 == rocksdb_options_get_target_file_size_base(copy));
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_multiplier(copy, 13);
|
||||
CheckCondition(13 == rocksdb_options_get_target_file_size_multiplier(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_base(copy, 900);
|
||||
CheckCondition(900 == rocksdb_options_get_max_bytes_for_level_base(copy));
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(o));
|
||||
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_level_compaction_dynamic_level_bytes(copy));
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(copy, 8.0);
|
||||
CheckCondition(8.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(copy, 146);
|
||||
CheckCondition(146 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(copy, 128);
|
||||
CheckCondition(
|
||||
128 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(copy, 9000);
|
||||
CheckCondition(9000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_enable_pipelined_write(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(o));
|
||||
|
||||
rocksdb_options_set_unordered_write(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_unordered_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(o));
|
||||
|
||||
rocksdb_options_set_max_subcompactions(copy, 90001);
|
||||
CheckCondition(90001 == rocksdb_options_get_max_subcompactions(copy));
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(o));
|
||||
|
||||
rocksdb_options_destroy(copy);
|
||||
rocksdb_options_destroy(o);
|
||||
}
|
||||
|
||||
StartPhase("iterate_upper_bound");
|
||||
{
|
||||
// Create new empty database
|
||||
@@ -1840,6 +2128,9 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
|
||||
@@ -542,7 +542,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// Greedily add ranges to the subcompaction until the sum of the ranges'
|
||||
// sizes becomes >= the expected mean size of a subcompaction
|
||||
sum = 0;
|
||||
for (size_t i = 0; i < ranges.size() - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < ranges.size(); i++) {
|
||||
sum += ranges[i].size;
|
||||
if (subcompactions == 1) {
|
||||
// If there's only one left to schedule then it goes to the end so no
|
||||
|
||||
@@ -1085,6 +1085,8 @@ void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
Random64 rnd(/* seed */ reinterpret_cast<uint64_t>(vstorage));
|
||||
size_t random_file_index = static_cast<size_t>(rnd.Uniform(
|
||||
static_cast<uint64_t>(vstorage->FilesMarkedForCompaction().size())));
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::PickFilesMarkedForCompaction",
|
||||
&random_file_index);
|
||||
|
||||
if (continuation(vstorage->FilesMarkedForCompaction()[random_file_index])) {
|
||||
// found the compaction!
|
||||
|
||||
@@ -78,8 +78,17 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
// Create a new VersionStorageInfo object so we can add mode files and then
|
||||
// merge it with the existing VersionStorageInfo
|
||||
void AddVersionStorage() {
|
||||
temp_vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, ioptions_.compaction_style,
|
||||
vstorage_.get(), false));
|
||||
}
|
||||
|
||||
void DeleteVersionStorage() {
|
||||
vstorage_.reset();
|
||||
temp_vstorage_.reset();
|
||||
files_.clear();
|
||||
file_map_.clear();
|
||||
input_files_.clear();
|
||||
@@ -88,18 +97,24 @@ class CompactionPickerTest : public testing::Test {
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false) {
|
||||
VersionStorageInfo* vstorage;
|
||||
if (temp_vstorage_) {
|
||||
vstorage = temp_vstorage_.get();
|
||||
} else {
|
||||
vstorage = vstorage_.get();
|
||||
}
|
||||
assert(level < vstorage->num_levels());
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size,
|
||||
InternalKey(smallest, smallest_seq, kTypeValue),
|
||||
InternalKey(largest, largest_seq, kTypeValue), smallest_seq,
|
||||
largest_seq, /* marked_for_compact */ false, kInvalidBlobFileNumber,
|
||||
largest_seq, marked_for_compact, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
vstorage_->AddFile(level, f);
|
||||
vstorage->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
file_map_.insert({file_number, {f, level}});
|
||||
}
|
||||
@@ -122,6 +137,12 @@ class CompactionPickerTest : public testing::Test {
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
if (temp_vstorage_) {
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr,
|
||||
vstorage_.get(), nullptr);
|
||||
builder.SaveTo(temp_vstorage_.get());
|
||||
vstorage_ = std::move(temp_vstorage_);
|
||||
}
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
@@ -132,6 +153,28 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->ComputeFilesMarkedForCompaction();
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
void AddFileToVersionStorage(int level, uint32_t file_number,
|
||||
const char* smallest, const char* largest,
|
||||
uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0,
|
||||
bool marked_for_compact = false) {
|
||||
VersionStorageInfo* base_vstorage = vstorage_.release();
|
||||
vstorage_.reset(new VersionStorageInfo(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleUniversal,
|
||||
base_vstorage, false));
|
||||
Add(level, file_number, smallest, largest, file_size, path_id, smallest_seq,
|
||||
largest_seq, compensated_file_size, marked_for_compact);
|
||||
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr, base_vstorage,
|
||||
nullptr);
|
||||
builder.SaveTo(vstorage_.get());
|
||||
UpdateVersionStorageInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<VersionStorageInfo> temp_vstorage_;
|
||||
};
|
||||
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
@@ -371,8 +414,8 @@ TEST_F(CompactionPickerTest, LevelTriggerDynamic4) {
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 4U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 2U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 3U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 4U, "400", "450", 3U);
|
||||
Add(num_levels - 2, 5U, "150", "180", 300U);
|
||||
Add(num_levels - 2, 6U, "181", "350", 500U);
|
||||
@@ -739,7 +782,7 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
Add(2, 8U, "201", "300",
|
||||
60000000U); // Overlaps with file 28, 29, total size 521M
|
||||
|
||||
Add(3, 26U, "100", "110", 261000000U);
|
||||
Add(3, 25U, "100", "110", 261000000U);
|
||||
Add(3, 26U, "150", "170", 261000000U);
|
||||
Add(3, 27U, "171", "179", 260000000U);
|
||||
Add(3, 28U, "191", "220", 260000000U);
|
||||
@@ -1255,7 +1298,7 @@ TEST_F(CompactionPickerTest, EstimateCompactionBytesNeeded1) {
|
||||
// Size ratio L4/L3 is 9.9
|
||||
// After merge from L3, L4 size is 1000900
|
||||
Add(4, 11U, "400", "500", 999900);
|
||||
Add(5, 11U, "400", "500", 8007200);
|
||||
Add(5, 12U, "400", "500", 8007200);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -1568,8 +1611,8 @@ TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
|
||||
|
||||
Add(3, 5U, "120", "130", 7000U);
|
||||
Add(3, 6U, "170", "180", 7000U);
|
||||
Add(3, 5U, "220", "230", 7000U);
|
||||
Add(3, 5U, "270", "280", 7000U);
|
||||
Add(3, 7U, "220", "230", 7000U);
|
||||
Add(3, 8U, "270", "280", 7000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -1733,6 +1776,165 @@ TEST_F(CompactionPickerTest, IntraL0ForEarliestSeqno) {
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a "regular" universal compaction is
|
||||
// scheduled first, followed by a delete triggered compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300);
|
||||
Add(3, 5U, "010", "080", 8 * kFileSize, 0, 200, 251);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a compaction to reduce sorted runs
|
||||
ASSERT_EQ(CompactionReason::kUniversalSortedRunNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
|
||||
AddVersionStorage();
|
||||
// Simulate a flush and mark the file for compaction
|
||||
Add(0, 7U, "150", "200", kFileSize, 0, 551, 600, 0, true);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a delete triggered compaction is
|
||||
// scheduled first, followed by a "regular" compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 5U, "240", "290", 8 * kFileSize, 0, 201, 250);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
|
||||
AddVersionStorage();
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionStartOutputOverlap) {
|
||||
// The case where universal periodic compaction can be picked
|
||||
// with some newer files being compacted.
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
bool input_level_overlap = false;
|
||||
bool output_level_overlap = false;
|
||||
// Let's mark 2 files in 2 different levels for compaction. The
|
||||
// compaction picker will randomly pick one, so use the sync point to
|
||||
// ensure a deterministic order. Loop until both cases are covered
|
||||
size_t random_index = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::PickFilesMarkedForCompaction", [&](void* arg) {
|
||||
size_t* index = static_cast<size_t*>(arg);
|
||||
*index = random_index;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
while (!input_level_overlap || !output_level_overlap) {
|
||||
// Ensure that the L0 file gets picked first
|
||||
random_index = !input_level_overlap ? 0 : 1;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 2U, "010", "020", 2 * kFileSize, 0, 201, 248);
|
||||
Add(3, 3U, "250", "270", 2 * kFileSize, 0, 202, 249);
|
||||
Add(3, 4U, "290", "310", 2 * kFileSize, 0, 203, 250);
|
||||
Add(3, 5U, "310", "320", 2 * kFileSize, 0, 204, 251, 0, true);
|
||||
Add(4, 6U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 7U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_TRUE(compaction->start_level() == 0 ||
|
||||
compaction->start_level() == 3);
|
||||
if (compaction->start_level() == 0) {
|
||||
// The L0 file was picked. The next compaction will detect an
|
||||
// overlap on its input level
|
||||
input_level_overlap = true;
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
} else {
|
||||
// The level 3 file was picked. The next compaction will pick
|
||||
// the L0 file and will detect overlap when adding output
|
||||
// level inputs
|
||||
output_level_overlap = true;
|
||||
ASSERT_EQ(4, compaction->output_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
}
|
||||
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
// After recomputing the compaction score, only one marked file will remain
|
||||
random_index = 0;
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -120,8 +120,7 @@ class UniversalCompactionBuilder {
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
const VersionStorageInfo& vstorage);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -325,8 +324,7 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& /*ioptions*/,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
const VersionStorageInfo& vstorage) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
@@ -336,27 +334,16 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
} else {
|
||||
// Compaction always includes all files for a non-zero level, so for a
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move
|
||||
// is false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
// Size amp, read amp and periodic compactions always include all files
|
||||
// for a non-zero level. However, a delete triggered compaction and
|
||||
// a trivial move might pick a subset of files in a sorted run. So
|
||||
// always check all files in a sorted run and mark the entire run as
|
||||
// being compacted if one or more files are being compacted
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
@@ -372,8 +359,7 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, ioptions_, mutable_cf_options_);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -779,7 +765,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
}
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (size_t loop = 0; loop < sorted_runs_.size() - 1; loop++) {
|
||||
for (size_t loop = 0; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (!sr->being_compacted) {
|
||||
start_index = loop; // Consider this as the first candidate.
|
||||
@@ -807,7 +793,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
}
|
||||
|
||||
// keep adding up all the remaining files
|
||||
for (size_t loop = start_index; loop < sorted_runs_.size() - 1; loop++) {
|
||||
for (size_t loop = start_index; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
@@ -855,6 +841,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
|
||||
if (vstorage_->num_levels() == 1) {
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
// This is single level universal. Since we're basically trying to reclaim
|
||||
// space by processing files marked for compaction due to high tombstone
|
||||
// density, let's do the same thing as compaction to reduce size amp which
|
||||
@@ -877,6 +864,11 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
#else
|
||||
// Disable due to a known race condition.
|
||||
// TODO: Reenable once the race condition is fixed
|
||||
return nullptr;
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
} else {
|
||||
int start_level;
|
||||
|
||||
|
||||
+2
-1
@@ -61,7 +61,8 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions, options.prefix_extractor.get(), env_options,
|
||||
internal_comparator, false /* skip_filters */,
|
||||
!kImmortal, -1 /* level */),
|
||||
!kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */),
|
||||
std::move(file_reader), file_size, &table_reader,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
|
||||
+261
-11
@@ -1295,14 +1295,18 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleUnsorted) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) {
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSortedMultiFile) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
// To expand the power of this test, generate > 1 table file and
|
||||
// mix with memtable
|
||||
ASSERT_OK(Put(1, "k1", "v1"));
|
||||
ASSERT_OK(Put(1, "k2", "v2"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "k3", "v3"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k5", "v5"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
@@ -1333,7 +1337,7 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) {
|
||||
ASSERT_TRUE(s[5].IsNotFound());
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
@@ -1497,6 +1501,221 @@ TEST_F(DBBasicTest, MultiGetBatchedMultiLevelMerge) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSizeInMemory) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_OK(Put(1, "k1", "v_1"));
|
||||
ASSERT_OK(Put(1, "k2", "v_2"));
|
||||
ASSERT_OK(Put(1, "k3", "v_3"));
|
||||
ASSERT_OK(Put(1, "k4", "v_4"));
|
||||
ASSERT_OK(Put(1, "k5", "v_5"));
|
||||
ASSERT_OK(Put(1, "k6", "v_6"));
|
||||
std::vector<Slice> keys = {"k1", "k2", "k3", "k4", "k5", "k6"};
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<Status> s(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
ReadOptions ro;
|
||||
ro.value_size_soft_limit = 11;
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
ASSERT_EQ(std::string(values[i].data(), values[i].size()),
|
||||
"v_" + std::to_string(i + 1));
|
||||
}
|
||||
|
||||
for (unsigned int i = 4; i < 6; i++) {
|
||||
ASSERT_TRUE(s[i].IsAborted());
|
||||
}
|
||||
|
||||
ASSERT_EQ(12, (int)get_perf_context()->multiget_read_bytes);
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSize) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
|
||||
ASSERT_OK(Put(1, "k6", "v6"));
|
||||
ASSERT_OK(Put(1, "k7", "v7_"));
|
||||
ASSERT_OK(Put(1, "k3", "v3_"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k11", "v11"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
ASSERT_OK(Put(1, "k8", "v8_"));
|
||||
ASSERT_OK(Put(1, "k13", "v13"));
|
||||
ASSERT_OK(Put(1, "k14", "v14"));
|
||||
ASSERT_OK(Put(1, "k15", "v15"));
|
||||
ASSERT_OK(Put(1, "k16", "v16"));
|
||||
ASSERT_OK(Put(1, "k17", "v17"));
|
||||
Flush(1);
|
||||
|
||||
ASSERT_OK(Put(1, "k1", "v1_"));
|
||||
ASSERT_OK(Put(1, "k2", "v2_"));
|
||||
ASSERT_OK(Put(1, "k5", "v5_"));
|
||||
ASSERT_OK(Put(1, "k9", "v9_"));
|
||||
ASSERT_OK(Put(1, "k10", "v10"));
|
||||
ASSERT_OK(Delete(1, "k2"));
|
||||
ASSERT_OK(Delete(1, "k6"));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
std::vector<Slice> keys({"k1", "k10", "k11", "k12", "k13", "k14", "k15",
|
||||
"k16", "k17", "k2", "k3", "k4", "k5", "k6", "k7",
|
||||
"k8", "k9", "no_key"});
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.value_size_soft_limit = 20;
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
|
||||
// In memory keys
|
||||
ASSERT_EQ(std::string(values[0].data(), values[0].size()), "v1_");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v10");
|
||||
ASSERT_TRUE(s[9].IsNotFound()); // k2
|
||||
ASSERT_EQ(std::string(values[12].data(), values[12].size()), "v5_");
|
||||
ASSERT_TRUE(s[13].IsNotFound()); // k6
|
||||
ASSERT_EQ(std::string(values[16].data(), values[16].size()), "v9_");
|
||||
|
||||
// In sst files
|
||||
ASSERT_EQ(std::string(values[2].data(), values[1].size()), "v11");
|
||||
ASSERT_EQ(std::string(values[4].data(), values[4].size()), "v13");
|
||||
ASSERT_EQ(std::string(values[5].data(), values[5].size()), "v14");
|
||||
|
||||
// Remaining aborted after value_size exceeds.
|
||||
ASSERT_TRUE(s[3].IsAborted());
|
||||
ASSERT_TRUE(s[6].IsAborted());
|
||||
ASSERT_TRUE(s[7].IsAborted());
|
||||
ASSERT_TRUE(s[8].IsAborted());
|
||||
ASSERT_TRUE(s[10].IsAborted());
|
||||
ASSERT_TRUE(s[11].IsAborted());
|
||||
ASSERT_TRUE(s[14].IsAborted());
|
||||
ASSERT_TRUE(s[15].IsAborted());
|
||||
ASSERT_TRUE(s[17].IsAborted());
|
||||
|
||||
// 6 kv pairs * 3 bytes per value (i.e. 18)
|
||||
ASSERT_EQ(21, (int)get_perf_context()->multiget_read_bytes);
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
int num_keys = 0;
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
ASSERT_OK(Put("key_" + std::to_string(i), "val_l2_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 64; i += 3) {
|
||||
ASSERT_OK(Merge("key_" + std::to_string(i), "val_l1_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
for (int i = 0; i < 64; i += 5) {
|
||||
ASSERT_OK(Merge("key_" + std::to_string(i), "val_l0_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
ASSERT_EQ(0, num_keys);
|
||||
|
||||
for (int i = 0; i < 64; i += 9) {
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(i), "val_mem_" + std::to_string(i)));
|
||||
}
|
||||
|
||||
std::vector<std::string> keys_str;
|
||||
for (int i = 10; i < 50; ++i) {
|
||||
keys_str.push_back("key_" + std::to_string(i));
|
||||
}
|
||||
|
||||
std::vector<Slice> keys(keys_str.size());
|
||||
for (int i = 0; i < 40; i++) {
|
||||
keys[i] = Slice(keys_str[i]);
|
||||
}
|
||||
|
||||
std::vector<PinnableSlice> values(keys_str.size());
|
||||
std::vector<Status> statuses(keys_str.size());
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
read_options.value_size_soft_limit = 380;
|
||||
db_->MultiGet(read_options, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
|
||||
uint64_t curr_value_size = 0;
|
||||
for (unsigned int j = 0; j < 26; ++j) {
|
||||
int key = j + 10;
|
||||
std::string value;
|
||||
value.append("val_l2_" + std::to_string(key));
|
||||
if (key % 3 == 0) {
|
||||
value.append(",");
|
||||
value.append("val_l1_" + std::to_string(key));
|
||||
}
|
||||
if (key % 5 == 0) {
|
||||
value.append(",");
|
||||
value.append("val_l0_" + std::to_string(key));
|
||||
}
|
||||
if (key % 9 == 0) {
|
||||
value.append(",");
|
||||
value.append("val_mem_" + std::to_string(key));
|
||||
}
|
||||
curr_value_size += value.size();
|
||||
ASSERT_EQ(values[j], value);
|
||||
ASSERT_OK(statuses[j]);
|
||||
}
|
||||
// ASSERT_TRUE(curr_value_size <= read_options.value_size_hard_limit);
|
||||
|
||||
// All remaning keys status is set Status::Abort
|
||||
for (unsigned int j = 26; j < 40; j++) {
|
||||
ASSERT_TRUE(statuses[j].IsAborted());
|
||||
}
|
||||
}
|
||||
|
||||
// Test class for batched MultiGet with prefix extractor
|
||||
// Param bool - If true, use partitioned filters
|
||||
// If false, use full filter block
|
||||
@@ -1681,13 +1900,13 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
|
||||
// Check non-default column family
|
||||
for (size_t i = 0; i != kNumInserts - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != kNumInserts; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumUpdates - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != kNumUpdates; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value1"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumDeletes - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != kNumDeletes; ++i) {
|
||||
ASSERT_OK(Delete(1, std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(ROCKSDB_NAMESPACE::GetAllKeyVersions(
|
||||
@@ -1836,11 +2055,16 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
ASSERT_OK(Flush(static_cast<int>(cf)));
|
||||
}
|
||||
|
||||
// Delete files
|
||||
// Delete and corrupt files
|
||||
for (size_t i = 0; i < all_cf_names.size(); ++i) {
|
||||
std::vector<std::string>& files = listener->GetFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(3, files.size());
|
||||
for (int j = static_cast<int>(files.size() - 1); j >= static_cast<int>(i);
|
||||
std::string corrupted_data;
|
||||
ASSERT_OK(ReadFileToString(env_, files[files.size() - 1], &corrupted_data));
|
||||
ASSERT_OK(WriteStringToFile(
|
||||
env_, corrupted_data.substr(0, corrupted_data.size() - 2),
|
||||
files[files.size() - 1], /*should_sync=*/true));
|
||||
for (int j = static_cast<int>(files.size() - 2); j >= static_cast<int>(i);
|
||||
--j) {
|
||||
ASSERT_OK(env_->DeleteFile(files[j]));
|
||||
}
|
||||
@@ -1872,6 +2096,32 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
{
|
||||
// Hack by adding a new MANIFEST with high file number
|
||||
std::string garbage(10, '\0');
|
||||
ASSERT_OK(WriteStringToFile(env_, garbage, dbname_ + "/MANIFEST-001000",
|
||||
/*should_sync=*/true));
|
||||
}
|
||||
{
|
||||
// Hack by adding a corrupted SST not referenced by any MANIFEST
|
||||
std::string garbage(10, '\0');
|
||||
ASSERT_OK(WriteStringToFile(env_, garbage, dbname_ + "/001001.sst",
|
||||
/*should_sync=*/true));
|
||||
}
|
||||
|
||||
options.best_efforts_recovery = true;
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -1938,11 +2188,11 @@ TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||
class DBBasicTestMultiGet : public DBTestBase {
|
||||
public:
|
||||
DBBasicTestMultiGet(std::string test_dir, int num_cfs, bool compressed_cache,
|
||||
bool uncompressed_cache, bool compression_enabled,
|
||||
bool fill_cache, uint32_t compression_parallel_threads)
|
||||
bool uncompressed_cache, bool _compression_enabled,
|
||||
bool _fill_cache, uint32_t compression_parallel_threads)
|
||||
: DBTestBase(test_dir) {
|
||||
compression_enabled_ = compression_enabled;
|
||||
fill_cache_ = fill_cache;
|
||||
compression_enabled_ = _compression_enabled;
|
||||
fill_cache_ = _fill_cache;
|
||||
|
||||
if (compressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
|
||||
@@ -187,7 +187,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -209,12 +209,12 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
iter = nullptr;
|
||||
|
||||
// Release iterators and access cache again.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iterators[i].reset();
|
||||
CheckCacheCounters(options, 0, 0, 0, 0);
|
||||
}
|
||||
ASSERT_EQ(0, cache->GetPinnedUsage());
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -243,7 +243,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
@@ -1063,12 +1063,21 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.whole_key_filtering = !use_prefix_;
|
||||
bbto.metadata_block_size = 128;
|
||||
if (use_prefix_) { // (not related to prefix, just alternating between)
|
||||
// Make sure code appropriately deals with metadata block size setting
|
||||
// that is "too small" (smaller than minimum size for filter builder)
|
||||
bbto.metadata_block_size = 63;
|
||||
} else {
|
||||
// Make sure the test will work even on platforms with large minimum
|
||||
// filter size, due to large cache line size.
|
||||
// (Largest cache line size + 10+% overhead.)
|
||||
bbto.metadata_block_size = 290;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
ReadOptions ropts;
|
||||
|
||||
constexpr uint32_t N = 10000;
|
||||
constexpr uint32_t N = 12000;
|
||||
// Add N/2 evens
|
||||
for (uint32_t i = 0; i < N; i += 2) {
|
||||
ASSERT_OK(Put(UKey(i), UKey(i)));
|
||||
|
||||
+2
-2
@@ -499,13 +499,13 @@ TEST_P(DBAtomicFlushTest, AtomicFlushTriggeredByMemTableFull) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::AtomicFlushTriggeredByMemTableFull:BeforeCheck");
|
||||
if (options.atomic_flush) {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_FALSE(cfh->cfd()->mem()->IsEmpty());
|
||||
|
||||
+24
-5
@@ -1787,6 +1787,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t num_found = 0;
|
||||
size_t keys_read;
|
||||
uint64_t curr_value_size = 0;
|
||||
for (keys_read = 0; keys_read < num_keys; ++keys_read) {
|
||||
merge_context.Clear();
|
||||
Status& s = stat_list[keys_read];
|
||||
@@ -1830,6 +1831,13 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (s.ok()) {
|
||||
bytes_read += value->size();
|
||||
num_found++;
|
||||
curr_value_size += value->size();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
while (++keys_read < num_keys) {
|
||||
stat_list[keys_read] = Status::Aborted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options.deadline.count() &&
|
||||
@@ -2084,11 +2092,11 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsTimedOut());
|
||||
assert(s.IsTimedOut() || s.IsAborted());
|
||||
for (++cf_iter; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
for (size_t i = cf_iter->start; i < cf_iter->start + cf_iter->num_keys;
|
||||
++i) {
|
||||
*sorted_keys[i]->s = Status::TimedOut();
|
||||
*sorted_keys[i]->s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2243,7 +2251,7 @@ void DBImpl::MultiGetWithCallback(
|
||||
Status s = MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum,
|
||||
nullptr, nullptr);
|
||||
assert(s.ok() || s.IsTimedOut());
|
||||
assert(s.ok() || s.IsTimedOut() || s.IsAborted());
|
||||
ReturnAndCleanupSuperVersion(multiget_cf_data[0].cfd,
|
||||
multiget_cf_data[0].super_version);
|
||||
}
|
||||
@@ -2271,6 +2279,7 @@ Status DBImpl::MultiGetImpl(
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t keys_left = num_keys;
|
||||
Status s;
|
||||
uint64_t curr_value_size = 0;
|
||||
while (keys_left) {
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
@@ -2285,6 +2294,7 @@ Status DBImpl::MultiGetImpl(
|
||||
MultiGetContext ctx(sorted_keys, start_key + num_keys - keys_left,
|
||||
batch_size, snapshot, read_options);
|
||||
MultiGetRange range = ctx.GetMultiGetRange();
|
||||
range.AddValueSize(curr_value_size);
|
||||
bool lookup_current = false;
|
||||
|
||||
keys_left -= batch_size;
|
||||
@@ -2315,6 +2325,11 @@ Status DBImpl::MultiGetImpl(
|
||||
super_version->current->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
}
|
||||
curr_value_size = range.GetValueSize();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
@@ -2329,11 +2344,11 @@ Status DBImpl::MultiGetImpl(
|
||||
}
|
||||
}
|
||||
if (keys_left) {
|
||||
assert(s.IsTimedOut());
|
||||
assert(s.IsTimedOut() || s.IsAborted());
|
||||
for (size_t i = start_key + num_keys - keys_left; i < start_key + num_keys;
|
||||
++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
*key->s = Status::TimedOut();
|
||||
*key->s = s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4238,6 +4253,10 @@ Status DBImpl::IngestExternalFiles(
|
||||
"cannot ingest an external file into a dropped CF");
|
||||
break;
|
||||
}
|
||||
if (args[i].options.skip_memtable_flush) {
|
||||
need_flush[i] = false;
|
||||
continue;
|
||||
}
|
||||
bool tmp = false;
|
||||
status = ingestion_jobs[i].NeedsFlush(&tmp, cfd->GetSuperVersion());
|
||||
need_flush[i] = tmp;
|
||||
|
||||
@@ -573,7 +573,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && io_s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && !io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
|
||||
@@ -686,13 +686,15 @@ uint64_t PrecomputeMinLogNumberToKeep(
|
||||
Status DBImpl::FinishBestEffortsRecovery() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(dbname_);
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
for (const auto& db_path : immutable_db_options_.db_paths) {
|
||||
paths.push_back(db_path.path);
|
||||
paths.push_back(
|
||||
NormalizePath(db_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
|
||||
paths.push_back(cf_path.path);
|
||||
paths.push_back(
|
||||
NormalizePath(cf_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
}
|
||||
// Dedup paths
|
||||
@@ -711,7 +713,8 @@ Status DBImpl::FinishBestEffortsRecovery() {
|
||||
if (!ParseFileName(fname, &number, &type)) {
|
||||
continue;
|
||||
}
|
||||
const std::string normalized_fpath = NormalizePath(path + fname);
|
||||
// path ends with '/' or '\\'
|
||||
const std::string normalized_fpath = path + fname;
|
||||
largest_file_number = std::max(largest_file_number, number);
|
||||
if (type == kTableFile && number >= next_file_number &&
|
||||
files_to_delete.find(normalized_fpath) == files_to_delete.end()) {
|
||||
|
||||
@@ -425,6 +425,9 @@ Status DBImpl::Recover(
|
||||
s = versions_->TryRecover(column_families, read_only, &db_id_,
|
||||
&missing_table_file);
|
||||
if (s.ok()) {
|
||||
// TryRecover may delete previous column_family_set_.
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
s = FinishBestEffortsRecovery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,12 +128,38 @@ Status DBImplReadOnly::NewIterators(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Return OK if dbname exists in the file system
|
||||
// or create_if_missing is false
|
||||
Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
|
||||
const std::string& dbname) {
|
||||
Status s;
|
||||
if (!db_options.create_if_missing) {
|
||||
// Attempt to read "CURRENT" file
|
||||
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
|
||||
std::string manifest_path;
|
||||
uint64_t manifest_file_number;
|
||||
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), &manifest_path,
|
||||
&manifest_file_number);
|
||||
if (!s.ok()) {
|
||||
return Status::NotFound(CurrentFileName(dbname), "does not exist");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DB** dbptr, bool /*error_if_log_file_exist*/) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
*dbptr = nullptr;
|
||||
|
||||
// Try to first open DB as fully compacted DB
|
||||
Status s;
|
||||
s = CompactedDBImpl::Open(options, dbname, dbptr);
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
@@ -146,7 +172,8 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
s = DB::OpenForReadOnly(db_options, dbname, column_families, &handles, dbptr);
|
||||
s = DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
db_options, dbname, column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
// i can delete the handle since DBImpl is always holding a
|
||||
@@ -161,6 +188,22 @@ Status DB::OpenForReadOnly(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(db_options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
db_options, dbname, column_families, handles, dbptr,
|
||||
error_if_log_file_exist);
|
||||
}
|
||||
|
||||
Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
*dbptr = nullptr;
|
||||
handles->clear();
|
||||
|
||||
|
||||
@@ -130,6 +130,15 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
private:
|
||||
// A "helper" function for DB::OpenForReadOnly without column families
|
||||
// to reduce unnecessary I/O
|
||||
// It has the same functionality as DB::OpenForReadOnly with column families
|
||||
// but does not check the existence of dbname in the file system
|
||||
static Status OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist = false);
|
||||
friend class DB;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -1837,8 +1837,30 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key) {
|
||||
WriteBatch batch;
|
||||
batch.Delete(column_family, key);
|
||||
if (nullptr == opt.timestamp) {
|
||||
WriteBatch batch;
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
const Slice* ts = opt.timestamp;
|
||||
assert(ts != nullptr);
|
||||
const size_t ts_sz = ts->size();
|
||||
constexpr size_t kKeyAndValueLenSize = 11;
|
||||
constexpr size_t kWriteBatchOverhead =
|
||||
WriteBatchInternal::kHeader + sizeof(ValueType) + kKeyAndValueLenSize;
|
||||
WriteBatch batch(key.size() + ts_sz + kWriteBatchOverhead, /*max_bytes=*/0,
|
||||
ts_sz);
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = batch.AssignTimestamp(*ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -258,7 +258,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// prone to bugs causing the same user key with the same sequence number.
|
||||
// Note that with current timestamp implementation, the same user key can
|
||||
// have different timestamps and zero sequence number on the bottommost
|
||||
// level. This will change in the future.
|
||||
// level. This may change in the future.
|
||||
if ((!is_prev_key_seqnum_zero || timestamp_size_ > 0) &&
|
||||
skipping_saved_key &&
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
@@ -276,6 +276,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
reseek_done = false;
|
||||
switch (ikey_.type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
|
||||
@@ -189,7 +189,7 @@ TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
|
||||
for (size_t i = 0; i < files[1].size() - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 < files[1].size(); ++i) {
|
||||
ASSERT_TRUE(InternalKeyComparator(opts.comparator)
|
||||
.Compare(files[1][i].largest, files[1][i + 1].smallest) <
|
||||
0);
|
||||
|
||||
+52
-27
@@ -1489,18 +1489,24 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
// Roughly 4 keys per data block, 1000 keys per file,
|
||||
// with filter substantially larger than a data block
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(16));
|
||||
table_options.block_size = 100;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 1024 * 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.write_buffer_size = 24 * 1024;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.target_file_size_base = 1024 * 1024;
|
||||
options.target_file_size_base = 24 * 1024;
|
||||
DestroyAndReopen(options);
|
||||
const auto default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
const int N = 64000;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 24)));
|
||||
}
|
||||
// Flush everything to files
|
||||
Flush();
|
||||
@@ -1509,7 +1515,7 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
|
||||
// Write more keys
|
||||
for (int i = N; i < (N + N / 4); i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 24)));
|
||||
}
|
||||
// Flush everything to files again
|
||||
Flush();
|
||||
@@ -1517,27 +1523,45 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
{
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that metadata is not falsely attributed only to the last data in
|
||||
// the file. (In some applications, filters can be large portion of data
|
||||
// size.)
|
||||
// Perform many queries over small range, enough to ensure crossing file
|
||||
// boundary, and make sure we never see a spike for large filter.
|
||||
for (int i = 0; i < 3000; i += 10) {
|
||||
const std::string start = Key(i);
|
||||
const std::string end = Key(i + 11); // overlap by 1 key
|
||||
const Range r(start, end);
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_LE(size, 11 * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetApproximateMemTableStats) {
|
||||
@@ -1675,12 +1699,13 @@ TEST_F(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
||||
ASSERT_TRUE(Between(Size("", Key(2), 1), 20000, 21000));
|
||||
ASSERT_TRUE(Between(Size("", Key(3), 1), 120000, 121000));
|
||||
ASSERT_TRUE(Between(Size("", Key(4), 1), 130000, 131000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 231000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 241000));
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540000, 541000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550000, 560000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 232000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 242000));
|
||||
// Ensure some overhead is accounted for, even without including all
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540500, 545000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550500, 555000));
|
||||
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110000, 111000));
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110100, 111000));
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
}
|
||||
|
||||
+200
@@ -25,6 +25,67 @@ class DBTest2 : public DBTestBase {
|
||||
DBTest2() : DBTestBase("/db_test2") {}
|
||||
};
|
||||
|
||||
TEST_F(DBTest2, OpenForReadOnly) {
|
||||
DB* db_ptr = nullptr;
|
||||
std::string dbname = test::PerThreadDBPath("db_readonly");
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// OpenForReadOnly should fail but will create <dbname> in the file system
|
||||
ASSERT_NOK(DB::OpenForReadOnly(options, dbname, &db_ptr));
|
||||
// Since <dbname> is created, we should be able to delete the dir
|
||||
// We first get the list files under <dbname>
|
||||
// There should not be any subdirectories -- this is not checked here
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||
for (auto& f : files) {
|
||||
if (f != "." && f != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
}
|
||||
// <dbname> should be empty now and we should be able to delete it
|
||||
ASSERT_OK(env_->DeleteDir(dbname));
|
||||
options.create_if_missing = false;
|
||||
// OpenForReadOnly should fail since <dbname> was successfully deleted
|
||||
ASSERT_NOK(DB::OpenForReadOnly(options, dbname, &db_ptr));
|
||||
// With create_if_missing false, there should not be a dir in the file system
|
||||
ASSERT_NOK(env_->FileExists(dbname));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, OpenForReadOnlyWithColumnFamilies) {
|
||||
DB* db_ptr = nullptr;
|
||||
std::string dbname = test::PerThreadDBPath("db_readonly");
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
column_families.push_back(ColumnFamilyDescriptor("goku", cf_options));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
// OpenForReadOnly should fail but will create <dbname> in the file system
|
||||
ASSERT_NOK(
|
||||
DB::OpenForReadOnly(options, dbname, column_families, &handles, &db_ptr));
|
||||
// Since <dbname> is created, we should be able to delete the dir
|
||||
// We first get the list files under <dbname>
|
||||
// There should not be any subdirectories -- this is not checked here
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||
for (auto& f : files) {
|
||||
if (f != "." && f != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
}
|
||||
// <dbname> should be empty now and we should be able to delete it
|
||||
ASSERT_OK(env_->DeleteDir(dbname));
|
||||
options.create_if_missing = false;
|
||||
// OpenForReadOnly should fail since <dbname> was successfully deleted
|
||||
ASSERT_NOK(
|
||||
DB::OpenForReadOnly(options, dbname, column_families, &handles, &db_ptr));
|
||||
// With create_if_missing false, there should not be a dir in the file system
|
||||
ASSERT_NOK(env_->FileExists(dbname));
|
||||
}
|
||||
|
||||
class PrefixFullBloomWithReverseComparator
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
@@ -1268,6 +1329,145 @@ class CompactionCompressionListener : public EventListener {
|
||||
const Options* db_options_;
|
||||
};
|
||||
|
||||
TEST_F(DBTest2, CompressionFailures) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.max_bytes_for_level_base = 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 7;
|
||||
options.max_background_compactions = 1;
|
||||
options.target_file_size_base = 512;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 512;
|
||||
table_options.verify_compression = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
|
||||
enum CompressionFailureType {
|
||||
kTestCompressionFail,
|
||||
kTestDecompressionFail,
|
||||
kTestDecompressionCorruption
|
||||
} curr_compression_failure_type;
|
||||
std::vector<CompressionFailureType> compression_failure_types = {
|
||||
kTestCompressionFail, kTestDecompressionFail,
|
||||
kTestDecompressionCorruption};
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::CompressBlockInternal:TamperWithReturnValue",
|
||||
[&curr_compression_failure_type](void* arg) {
|
||||
bool* ret = static_cast<bool*>(arg);
|
||||
if (curr_compression_failure_type == kTestCompressionFail) {
|
||||
*ret = false;
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockContentsForCompressionType:TamperWithReturnValue",
|
||||
[&curr_compression_failure_type](void* arg) {
|
||||
Status* ret = static_cast<Status*>(arg);
|
||||
ASSERT_OK(*ret);
|
||||
if (curr_compression_failure_type == kTestDecompressionFail) {
|
||||
*ret = Status::Corruption("kTestDecompressionFail");
|
||||
}
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockContentsForCompressionType:"
|
||||
"TamperWithDecompressionOutput",
|
||||
[&curr_compression_failure_type](void* arg) {
|
||||
if (curr_compression_failure_type == kTestDecompressionCorruption) {
|
||||
BlockContents* contents = static_cast<BlockContents*>(arg);
|
||||
// Ensure uncompressed data != original data
|
||||
const size_t len = contents->data.size() + 1;
|
||||
std::unique_ptr<char[]> fake_data(new char[len]());
|
||||
*contents = BlockContents(std::move(fake_data), len);
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<CompressionType> compression_types = GetSupportedCompressions();
|
||||
std::vector<uint32_t> compression_max_dict_bytes = {0, 10};
|
||||
std::vector<uint32_t> compression_parallel_threads = {1, 4};
|
||||
|
||||
std::map<std::string, std::string> key_value_written;
|
||||
|
||||
const int kKeySize = 5;
|
||||
const int kValUnitSize = 16;
|
||||
const int kValSize = 256;
|
||||
Random rnd(405);
|
||||
|
||||
Status s = Status::OK();
|
||||
|
||||
for (auto compression_failure_type : compression_failure_types) {
|
||||
curr_compression_failure_type = compression_failure_type;
|
||||
for (auto compression_type : compression_types) {
|
||||
if (compression_type == kNoCompression) {
|
||||
continue;
|
||||
}
|
||||
for (auto parallel_threads : compression_parallel_threads) {
|
||||
for (auto max_dict_bytes : compression_max_dict_bytes) {
|
||||
options.compression = compression_type;
|
||||
options.compression_opts.parallel_threads = parallel_threads;
|
||||
options.compression_opts.max_dict_bytes = max_dict_bytes;
|
||||
options.bottommost_compression_opts.parallel_threads =
|
||||
parallel_threads;
|
||||
options.bottommost_compression_opts.max_dict_bytes = max_dict_bytes;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Write 10 random files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
std::string key = RandomString(&rnd, kKeySize);
|
||||
// Ensure good compression ratio
|
||||
std::string valueUnit = RandomString(&rnd, kValUnitSize);
|
||||
std::string value;
|
||||
for (int k = 0; k < kValSize; k += kValUnitSize) {
|
||||
value += valueUnit;
|
||||
}
|
||||
s = Put(key, value);
|
||||
if (compression_failure_type == kTestCompressionFail) {
|
||||
key_value_written[key] = value;
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
s = Flush();
|
||||
if (compression_failure_type == kTestCompressionFail) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
if (compression_failure_type == kTestCompressionFail) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
if (i == 4) {
|
||||
// Make compression fail at the mid of table building
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
if (compression_failure_type == kTestCompressionFail) {
|
||||
// Should be kNoCompression, check content consistency
|
||||
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
std::string key = db_iter->key().ToString();
|
||||
std::string value = db_iter->value().ToString();
|
||||
ASSERT_NE(key_value_written.find(key), key_value_written.end());
|
||||
ASSERT_EQ(key_value_written[key], value);
|
||||
key_value_written.erase(key);
|
||||
}
|
||||
ASSERT_EQ(0, key_value_written.size());
|
||||
} else if (compression_failure_type == kTestDecompressionFail) {
|
||||
ASSERT_EQ(std::string(s.getState()),
|
||||
"Could not decompress: kTestDecompressionFail");
|
||||
} else if (compression_failure_type == kTestDecompressionCorruption) {
|
||||
ASSERT_EQ(std::string(s.getState()),
|
||||
"Decompressed block did not match raw block");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CompressionOptions) {
|
||||
if (!Zlib_Supported() || !Snappy_Supported()) {
|
||||
return;
|
||||
|
||||
@@ -1953,6 +1953,7 @@ TEST_F(DBTestUniversalCompaction2, BasicL0toL1) {
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
TEST_F(DBTestUniversalCompaction2, SingleLevel) {
|
||||
const int kNumKeys = 3000;
|
||||
const int kWindowSize = 100;
|
||||
@@ -1991,6 +1992,7 @@ TEST_F(DBTestUniversalCompaction2, SingleLevel) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
|
||||
TEST_F(DBTestUniversalCompaction2, MultipleLevels) {
|
||||
const int kWindowSize = 100;
|
||||
|
||||
@@ -449,6 +449,65 @@ TEST_F(DBBasicTestWithTimestamp, MaxKeysSkipped) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Create two L0, and compact them to a new L1. In this test, L1 is L_bottom.
|
||||
// Two L0s:
|
||||
// f1 f2
|
||||
// <a, 1, kTypeValue> <a, 3, kTypeDeletionWithTimestamp>...<b, 2, kTypeValue>
|
||||
// Since f2.smallest < f1.largest < f2.largest
|
||||
// f1 and f2 will be the inputs of a real compaction instead of trivial move.
|
||||
TEST_F(DBBasicTestWithTimestamp, CompactDeletionWithTimestampMarkerToBottom) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.num_levels = 2;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "a", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "b", "value0"));
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Delete(write_opts, "a"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ReadOptions read_opts;
|
||||
ts_str = Timestamp(1, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "a", &value);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("value0", value);
|
||||
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
s = db_->Get(read_opts, "a", &value);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
|
||||
// Time-travel to the past before deletion
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
s = db_->Get(read_opts, "a", &value);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("value0", value);
|
||||
Close();
|
||||
}
|
||||
|
||||
class DBBasicTestWithTimestampCompressionSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
@@ -536,6 +595,104 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGet) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampCompressionSettings, PutDeleteGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
const int kNumKeysPerFile = 1024;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<0>(GetParam());
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
|
||||
const CompressionType comp_type = std::get<1>(GetParam());
|
||||
#if LZ4_VERSION_NUMBER < 10400 // r124+
|
||||
if (comp_type == kLZ4Compression || comp_type == kLZ4HCCompression) {
|
||||
return;
|
||||
}
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
if (!ZSTD_Supported() && comp_type == kZSTD) {
|
||||
return;
|
||||
}
|
||||
if (!Zlib_Supported() && comp_type == kZlibCompression) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.compression = comp_type;
|
||||
options.compression_opts.max_dict_bytes = std::get<2>(GetParam());
|
||||
if (comp_type == kZSTD) {
|
||||
options.compression_opts.zstd_max_train_bytes = std::get<2>(GetParam());
|
||||
}
|
||||
options.compression_opts.parallel_threads = std::get<3>(GetParam());
|
||||
options.target_file_size_base = 1 << 26; // 64MB
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kNumL0Files =
|
||||
static_cast<size_t>(Options().level0_file_num_compaction_trigger);
|
||||
{
|
||||
// Generate enough L0 files with ts=1 to trigger compaction to L1
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
WriteOptions wopts;
|
||||
wopts.timestamp = &ts;
|
||||
for (size_t i = 0; i != kNumL0Files; ++i) {
|
||||
for (int j = 0; j != kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(db_->Put(wopts, Key1(j), "value" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// Generate another L0 at ts=3
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
for (int i = 0; i != kNumKeysPerFile; ++i) {
|
||||
std::string key_str = Key1(i);
|
||||
Slice key(key_str);
|
||||
if ((i % 3) == 0) {
|
||||
ASSERT_OK(db_->Delete(wopts, key));
|
||||
} else {
|
||||
ASSERT_OK(db_->Put(wopts, key, "new_value"));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
// Populate memtable at ts=5
|
||||
ts_str = Timestamp(5, 0);
|
||||
ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
for (int i = 0; i != kNumKeysPerFile; ++i) {
|
||||
std::string key_str = Key1(i);
|
||||
Slice key(key_str);
|
||||
if ((i % 3) == 1) {
|
||||
ASSERT_OK(db_->Delete(wopts, key));
|
||||
} else if ((i % 3) == 2) {
|
||||
ASSERT_OK(db_->Put(wopts, key, "new_value_2"));
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string ts_str = Timestamp(6, 0);
|
||||
Slice ts = ts_str;
|
||||
ReadOptions ropts;
|
||||
ropts.timestamp = &ts;
|
||||
for (uint64_t i = 0; i != static_cast<uint64_t>(kNumKeysPerFile); ++i) {
|
||||
std::string value;
|
||||
Status s = db_->Get(ropts, Key1(i), &value);
|
||||
if ((i % 3) == 2) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("new_value_2", value);
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A class which remembers the name of each flushed file.
|
||||
class FlushedFileCollector : public EventListener {
|
||||
@@ -870,15 +1027,10 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
for (size_t i = 0; i != write_ts_list.size(); ++i) {
|
||||
Slice write_ts = write_ts_list[i];
|
||||
write_opts.timestamp = &write_ts;
|
||||
uint64_t key = kMinKey;
|
||||
do {
|
||||
for (uint64_t key = kMaxKey; key >= kMinKey; --key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
if (key == kMaxKey) {
|
||||
break;
|
||||
}
|
||||
++key;
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
const std::vector<std::string> read_ts_list = {Timestamp(5, 0xffffffff),
|
||||
@@ -962,6 +1114,90 @@ INSTANTIATE_TEST_CASE_P(
|
||||
false))),
|
||||
::testing::Bool()));
|
||||
|
||||
class DBBasicTestWithTsIterTombstones
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const SliceTransform>,
|
||||
std::shared_ptr<const FilterPolicy>, int>> {
|
||||
public:
|
||||
DBBasicTestWithTsIterTombstones()
|
||||
: DBBasicTestWithTimestampBase("/db_basic_ts_iter_tombstones") {}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTsIterTombstones, ForwardIterDelete) {
|
||||
constexpr size_t kNumKeysPerFile = 128;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.prefix_extractor = std::get<0>(GetParam());
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<1>(GetParam());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.num_levels = std::get<2>(GetParam());
|
||||
DestroyAndReopen(options);
|
||||
std::vector<std::string> write_ts_strs = {Timestamp(2, 0), Timestamp(4, 0)};
|
||||
constexpr uint64_t kMaxKey = 0xffffffffffffffff;
|
||||
constexpr uint64_t kMinKey = 0xfffffffffffff000;
|
||||
// Insert kMinKey...kMaxKey
|
||||
uint64_t key = kMinKey;
|
||||
WriteOptions write_opts;
|
||||
Slice ts = write_ts_strs[0];
|
||||
write_opts.timestamp = &ts;
|
||||
do {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
if (kMaxKey == key) {
|
||||
break;
|
||||
}
|
||||
++key;
|
||||
} while (true);
|
||||
// Delete them all
|
||||
ts = write_ts_strs[1];
|
||||
write_opts.timestamp = &ts;
|
||||
for (key = kMaxKey; key >= kMinKey; --key) {
|
||||
Status s;
|
||||
if (0 != (key % 2)) {
|
||||
s = db_->Put(write_opts, Key1(key), "value1" + std::to_string(key));
|
||||
} else {
|
||||
s = db_->Delete(write_opts, Key1(key));
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
{
|
||||
std::string read_ts = Timestamp(4, 0);
|
||||
ts = read_ts;
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
size_t count = 0;
|
||||
key = kMinKey + 1;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), ++count, key += 2) {
|
||||
ASSERT_EQ(Key1(key), iter->key());
|
||||
ASSERT_EQ("value1" + std::to_string(key), iter->value());
|
||||
}
|
||||
ASSERT_EQ((kMaxKey - kMinKey + 1) / 2, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DBBasicTestWithTsIterTombstones,
|
||||
::testing::Combine(
|
||||
::testing::Values(
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(7)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(8))),
|
||||
::testing::Values(std::shared_ptr<const FilterPolicy>(nullptr),
|
||||
std::shared_ptr<const FilterPolicy>(
|
||||
NewBloomFilterPolicy(10, false)),
|
||||
std::shared_ptr<const FilterPolicy>(
|
||||
NewBloomFilterPolicy(20, false))),
|
||||
::testing::Values(2, 6)));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// and the value type is embedded as the low 8 bits in the sequence
|
||||
// number in internal keys, we need to use the highest-numbered
|
||||
// ValueType, not the lowest).
|
||||
const ValueType kValueTypeForSeek = kTypeBlobIndex;
|
||||
const ValueType kValueTypeForSeek = kTypeDeletionWithTimestamp;
|
||||
const ValueType kValueTypeForSeekForPrev = kTypeDeletion;
|
||||
|
||||
uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {
|
||||
|
||||
+4
-2
@@ -69,7 +69,8 @@ enum ValueType : unsigned char {
|
||||
// generated by WriteUnprepared write policy is not mistakenly read by
|
||||
// another.
|
||||
kTypeBeginUnprepareXID = 0x13, // WAL only.
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
kTypeDeletionWithTimestamp = 0x14,
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
};
|
||||
|
||||
// Defined in dbformat.cc
|
||||
@@ -79,7 +80,8 @@ extern const ValueType kValueTypeForSeekForPrev;
|
||||
// Checks whether a type is an inline value type
|
||||
// (i.e. a type used in memtable skiplist and sst file datablock).
|
||||
inline bool IsValueType(ValueType t) {
|
||||
return t <= kTypeMerge || t == kTypeSingleDeletion || t == kTypeBlobIndex;
|
||||
return t <= kTypeMerge || t == kTypeSingleDeletion || t == kTypeBlobIndex ||
|
||||
kTypeDeletionWithTimestamp == t;
|
||||
}
|
||||
|
||||
// Checks whether a type is from user operation
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -174,6 +175,111 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
ASSERT_EQ(file1_info.num_range_del_entries, 0);
|
||||
ASSERT_EQ(file1_info.smallest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.largest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.file_checksum, kUnknownFileChecksum);
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, kUnknownFileChecksumFuncName);
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Put(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Add file using file path
|
||||
s = DeprecatedAddFile({file1});
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
class ChecksumVerifyHelper {
|
||||
private:
|
||||
Options options_;
|
||||
|
||||
public:
|
||||
ChecksumVerifyHelper(Options& options) : options_(options) {}
|
||||
~ChecksumVerifyHelper() {}
|
||||
|
||||
Status GetSingleFileChecksumAndFuncName(
|
||||
const std::string& file_path, std::string* file_checksum,
|
||||
std::string* file_checksum_func_name) {
|
||||
Status s;
|
||||
EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFile> file_reader;
|
||||
s = options_.env->NewSequentialFile(file_path, &file_reader, soptions);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<char[]> scratch(new char[2048]);
|
||||
Slice result;
|
||||
FileChecksumGenFactory* file_checksum_gen_factory =
|
||||
options_.file_checksum_gen_factory.get();
|
||||
if (file_checksum_gen_factory == nullptr) {
|
||||
*file_checksum = kUnknownFileChecksum;
|
||||
*file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
return Status::OK();
|
||||
} else {
|
||||
FileChecksumGenContext gen_context;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
file_checksum_gen_factory->CreateFileChecksumGenerator(gen_context);
|
||||
*file_checksum_func_name = file_checksum_gen->Name();
|
||||
s = file_reader->Read(2048, &result, scratch.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
while (result.size() != 0) {
|
||||
file_checksum_gen->Update(scratch.get(), result.size());
|
||||
s = file_reader->Read(2048, &result, scratch.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
file_checksum_gen->Finalize();
|
||||
*file_checksum = file_checksum_gen->GetChecksum();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
ChecksumVerifyHelper checksum_helper(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a
|
||||
// file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::string file_checksum, file_checksum_func_name;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file1, &file_checksum, &file_checksum_func_name));
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(99));
|
||||
ASSERT_EQ(file1_info.num_range_del_entries, 0);
|
||||
ASSERT_EQ(file1_info.smallest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.largest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.file_checksum, file_checksum);
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name);
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Put(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
@@ -68,7 +68,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
info2->smallest_internal_key) < 0;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < num_files - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < num_files; i++) {
|
||||
if (sstableKeyCompare(ucmp, sorted_files[i]->largest_internal_key,
|
||||
sorted_files[i + 1]->smallest_internal_key) >= 0) {
|
||||
files_overlap_ = true;
|
||||
|
||||
@@ -2801,7 +2801,7 @@ TEST_P(ExternalSSTFileTest, IngestFilesTriggerFlushingWithTwoWriteQueue) {
|
||||
GenerateAndAddExternalFile(options, data);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresents) {
|
||||
TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
constexpr size_t kValueSize = 8;
|
||||
@@ -2842,6 +2842,61 @@ TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresents) {
|
||||
ASSERT_EQ(value, Get(key2));
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest,
|
||||
DeltaEncodingWhileGlobalSeqnoPresentIteratorSwitch) {
|
||||
// Regression test for bug where global seqno corrupted the shared bytes
|
||||
// buffer when switching from reverse iteration to forward iteration.
|
||||
constexpr size_t kValueSize = 8;
|
||||
Options options = CurrentOptions();
|
||||
|
||||
Random rnd(301);
|
||||
std::string value(RandomString(&rnd, kValueSize));
|
||||
|
||||
std::string key0 = "aa";
|
||||
std::string key1 = "ab";
|
||||
// Make the prefix of key2 is same with key1 add zero seqno. The tail of every
|
||||
// key is composed as (seqno << 8 | value_type), and here `1` represents
|
||||
// ValueType::kTypeValue
|
||||
std::string key2 = "ab";
|
||||
PutFixed64(&key2, PackSequenceAndType(0, kTypeValue));
|
||||
key2 += "cdefghijkl";
|
||||
std::string key3 = key2 + "_";
|
||||
|
||||
// Write some key to make global seqno larger than zero
|
||||
ASSERT_OK(Put(key0, value));
|
||||
|
||||
std::string fname = sst_files_dir_ + "test_file";
|
||||
rocksdb::SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.Open(fname));
|
||||
|
||||
// key0 is a dummy to ensure the turnaround point (key1) comes from Prev
|
||||
// cache rather than block (restart keys are pinned in block).
|
||||
ASSERT_OK(writer.Put(key0, value));
|
||||
ASSERT_OK(writer.Put(key1, value));
|
||||
ASSERT_OK(writer.Put(key2, value));
|
||||
ASSERT_OK(writer.Put(key3, value));
|
||||
|
||||
ExternalSstFileInfo info;
|
||||
ASSERT_OK(writer.Finish(&info));
|
||||
|
||||
ASSERT_OK(dbfull()->IngestExternalFile({info.file_path},
|
||||
IngestExternalFileOptions()));
|
||||
ReadOptions read_opts;
|
||||
// Prevents Seek() when switching directions, which circumvents the bug.
|
||||
read_opts.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(read_opts);
|
||||
// Scan backwards to key2. File iterator will then be positioned at key1.
|
||||
iter->Seek(key3);
|
||||
ASSERT_EQ(key3, iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_EQ(key2, iter->key());
|
||||
// Scan forwards and make sure key3 is present. Previously key3 would be
|
||||
// corrupted by the global seqno from key1.
|
||||
iter->Next();
|
||||
ASSERT_EQ(key3, iter->key());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
|
||||
@@ -724,6 +724,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
return false;
|
||||
}
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
case kTypeRangeDeletion: {
|
||||
if (*(s->merge_in_progress)) {
|
||||
@@ -929,8 +930,18 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
|
||||
if (found_final_value) {
|
||||
iter->value->PinSelf();
|
||||
range->AddValueSize(iter->value->size());
|
||||
range->MarkKeyDone(iter);
|
||||
RecordTick(moptions_.statistics, MEMTABLE_HIT);
|
||||
if (range->GetValueSize() > read_options.value_size_soft_limit) {
|
||||
// Set all remaining keys in range to Abort
|
||||
for (auto range_iter = range->begin(); range_iter != range->end();
|
||||
++range_iter) {
|
||||
range->MarkKeyDone(range_iter);
|
||||
*(range_iter->s) = Status::Aborted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PERF_COUNTER_ADD(get_from_memtable_count, 1);
|
||||
|
||||
+11
-13
@@ -43,22 +43,20 @@ void MemTableListVersion::UnrefMemTable(autovector<MemTable*>* to_delete,
|
||||
}
|
||||
|
||||
MemTableListVersion::MemTableListVersion(
|
||||
size_t* parent_memtable_list_memory_usage, MemTableListVersion* old)
|
||||
size_t* parent_memtable_list_memory_usage, const MemTableListVersion& old)
|
||||
: max_write_buffer_number_to_maintain_(
|
||||
old->max_write_buffer_number_to_maintain_),
|
||||
old.max_write_buffer_number_to_maintain_),
|
||||
max_write_buffer_size_to_maintain_(
|
||||
old->max_write_buffer_size_to_maintain_),
|
||||
old.max_write_buffer_size_to_maintain_),
|
||||
parent_memtable_list_memory_usage_(parent_memtable_list_memory_usage) {
|
||||
if (old != nullptr) {
|
||||
memlist_ = old->memlist_;
|
||||
for (auto& m : memlist_) {
|
||||
m->Ref();
|
||||
}
|
||||
memlist_ = old.memlist_;
|
||||
for (auto& m : memlist_) {
|
||||
m->Ref();
|
||||
}
|
||||
|
||||
memlist_history_ = old->memlist_history_;
|
||||
for (auto& m : memlist_history_) {
|
||||
m->Ref();
|
||||
}
|
||||
memlist_history_ = old.memlist_history_;
|
||||
for (auto& m : memlist_history_) {
|
||||
m->Ref();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +602,7 @@ void MemTableList::InstallNewVersion() {
|
||||
} else {
|
||||
// somebody else holds the current version, we need to create new one
|
||||
MemTableListVersion* version = current_;
|
||||
current_ = new MemTableListVersion(¤t_memory_usage_, current_);
|
||||
current_ = new MemTableListVersion(¤t_memory_usage_, *version);
|
||||
current_->Ref();
|
||||
version->Unref();
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ struct FlushJobInfo;
|
||||
class MemTableListVersion {
|
||||
public:
|
||||
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
|
||||
MemTableListVersion* old = nullptr);
|
||||
const MemTableListVersion& old);
|
||||
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
|
||||
int max_write_buffer_number_to_maintain,
|
||||
int64_t max_write_buffer_size_to_maintain);
|
||||
|
||||
+2
-1
@@ -123,7 +123,8 @@ Status TableCache::GetTableReader(
|
||||
s = ioptions_.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions_, prefix_extractor, file_options,
|
||||
internal_comparator, skip_filters, immortal_tables_,
|
||||
level, fd.largest_seqno, block_cache_tracer_),
|
||||
false /* force_direct_prefetch */, level,
|
||||
fd.largest_seqno, block_cache_tracer_),
|
||||
std::move(file_reader), fd.GetFileSize(), table_reader,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
TEST_SYNC_POINT("TableCache::GetTableReader:0");
|
||||
|
||||
+253
-155
@@ -86,6 +86,43 @@ class VersionBuilder::Rep {
|
||||
std::unordered_map<uint64_t, FileMetaData*> added_files;
|
||||
};
|
||||
|
||||
class BlobFileMetaDataDelta {
|
||||
public:
|
||||
bool IsEmpty() const {
|
||||
return !shared_meta_ && !additional_garbage_count_ &&
|
||||
!additional_garbage_bytes_;
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedBlobFileMetaData> GetSharedMeta() const {
|
||||
return shared_meta_;
|
||||
}
|
||||
|
||||
uint64_t GetAdditionalGarbageCount() const {
|
||||
return additional_garbage_count_;
|
||||
}
|
||||
|
||||
uint64_t GetAdditionalGarbageBytes() const {
|
||||
return additional_garbage_bytes_;
|
||||
}
|
||||
|
||||
void SetSharedMeta(std::shared_ptr<SharedBlobFileMetaData> shared_meta) {
|
||||
assert(!shared_meta_);
|
||||
assert(shared_meta);
|
||||
|
||||
shared_meta_ = std::move(shared_meta);
|
||||
}
|
||||
|
||||
void AddGarbage(uint64_t count, uint64_t bytes) {
|
||||
additional_garbage_count_ += count;
|
||||
additional_garbage_bytes_ += bytes;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
|
||||
uint64_t additional_garbage_count_ = 0;
|
||||
uint64_t additional_garbage_bytes_ = 0;
|
||||
};
|
||||
|
||||
const FileOptions& file_options_;
|
||||
const ImmutableCFOptions* const ioptions_;
|
||||
TableCache* table_cache_;
|
||||
@@ -93,19 +130,21 @@ class VersionBuilder::Rep {
|
||||
VersionSet* version_set_;
|
||||
int num_levels_;
|
||||
LevelState* levels_;
|
||||
// Store states of levels larger than num_levels_. We do this instead of
|
||||
// Store sizes of levels larger than num_levels_. We do this instead of
|
||||
// storing them in levels_ to avoid regression in case there are no files
|
||||
// on invalid levels. The version is not consistent if in the end the files
|
||||
// on invalid levels don't cancel out.
|
||||
std::map<int, std::unordered_set<uint64_t>> invalid_levels_;
|
||||
std::unordered_map<int, size_t> invalid_level_sizes_;
|
||||
// Whether there are invalid new files or invalid deletion on levels larger
|
||||
// than num_levels_.
|
||||
bool has_invalid_levels_;
|
||||
// Current levels of table files affected by additions/deletions.
|
||||
std::unordered_map<uint64_t, int> table_file_levels_;
|
||||
FileComparator level_zero_cmp_;
|
||||
FileComparator level_nonzero_cmp_;
|
||||
|
||||
// Metadata for all blob files affected by the series of version edits.
|
||||
std::map<uint64_t, std::shared_ptr<BlobFileMetaData>> changed_blob_files_;
|
||||
// Metadata delta for all blob files affected by the series of version edits.
|
||||
std::map<uint64_t, BlobFileMetaDataDelta> blob_file_meta_deltas_;
|
||||
|
||||
public:
|
||||
Rep(const FileOptions& file_options, const ImmutableCFOptions* ioptions,
|
||||
@@ -150,14 +189,12 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFileMetaData> GetBlobFileMetaData(
|
||||
uint64_t blob_file_number) const {
|
||||
auto changed_it = changed_blob_files_.find(blob_file_number);
|
||||
if (changed_it != changed_blob_files_.end()) {
|
||||
const auto& meta = changed_it->second;
|
||||
assert(meta);
|
||||
|
||||
return meta;
|
||||
bool IsBlobFileInVersion(uint64_t blob_file_number) const {
|
||||
auto delta_it = blob_file_meta_deltas_.find(blob_file_number);
|
||||
if (delta_it != blob_file_meta_deltas_.end()) {
|
||||
if (delta_it->second.GetSharedMeta()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
assert(base_vstorage_);
|
||||
@@ -166,13 +203,13 @@ class VersionBuilder::Rep {
|
||||
|
||||
auto base_it = base_blob_files.find(blob_file_number);
|
||||
if (base_it != base_blob_files.end()) {
|
||||
const auto& meta = base_it->second;
|
||||
assert(meta);
|
||||
assert(base_it->second);
|
||||
assert(base_it->second->GetSharedMeta());
|
||||
|
||||
return meta;
|
||||
return true;
|
||||
}
|
||||
|
||||
return std::shared_ptr<BlobFileMetaData>();
|
||||
return false;
|
||||
}
|
||||
|
||||
Status CheckConsistencyOfOldestBlobFileReference(
|
||||
@@ -189,8 +226,7 @@ class VersionBuilder::Rep {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
const auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (!meta) {
|
||||
if (!IsBlobFileInVersion(blob_file_number)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number
|
||||
<< " is not part of this version";
|
||||
@@ -326,73 +362,26 @@ class VersionBuilder::Rep {
|
||||
return ret_s;
|
||||
}
|
||||
|
||||
Status CheckConsistencyForDeletes(VersionEdit* /*edit*/, uint64_t number,
|
||||
int level) {
|
||||
#ifdef NDEBUG
|
||||
if (!base_vstorage_->force_consistency_checks()) {
|
||||
// Dont run consistency checks in release mode except if
|
||||
// explicitly asked to
|
||||
return Status::OK();
|
||||
}
|
||||
#endif
|
||||
// a file to be deleted better exist in the previous version
|
||||
bool found = false;
|
||||
for (int l = 0; !found && l < num_levels_; l++) {
|
||||
const std::vector<FileMetaData*>& base_files =
|
||||
base_vstorage_->LevelFiles(l);
|
||||
for (size_t i = 0; i < base_files.size(); i++) {
|
||||
FileMetaData* f = base_files[i];
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the file did not exist in the previous version, then it
|
||||
// is possibly moved from lower level to higher level in current
|
||||
// version
|
||||
for (int l = level + 1; !found && l < num_levels_; l++) {
|
||||
auto& level_added = levels_[l].added_files;
|
||||
auto got = level_added.find(number);
|
||||
if (got != level_added.end()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// maybe this file was added in a previous edit that was Applied
|
||||
if (!found) {
|
||||
auto& level_added = levels_[level].added_files;
|
||||
auto got = level_added.find(number);
|
||||
if (got != level_added.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fprintf(stderr, "not found %" PRIu64 "\n", number);
|
||||
return Status::Corruption("not found " + NumberToString(number));
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool CheckConsistencyForNumLevels() {
|
||||
bool CheckConsistencyForNumLevels() const {
|
||||
// Make sure there are no files on or beyond num_levels().
|
||||
if (has_invalid_levels_) {
|
||||
return false;
|
||||
}
|
||||
for (auto& level : invalid_levels_) {
|
||||
if (level.second.size() > 0) {
|
||||
|
||||
for (const auto& pair : invalid_level_sizes_) {
|
||||
const size_t level_size = pair.second;
|
||||
if (level_size != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ApplyBlobFileAddition(const BlobFileAddition& blob_file_addition) {
|
||||
const uint64_t blob_file_number = blob_file_addition.GetBlobFileNumber();
|
||||
|
||||
auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (meta) {
|
||||
if (IsBlobFileInVersion(blob_file_number)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number << " already added";
|
||||
|
||||
@@ -423,12 +412,8 @@ class VersionBuilder::Rep {
|
||||
blob_file_addition.GetChecksumMethod(),
|
||||
blob_file_addition.GetChecksumValue(), deleter);
|
||||
|
||||
constexpr uint64_t garbage_blob_count = 0;
|
||||
constexpr uint64_t garbage_blob_bytes = 0;
|
||||
auto new_meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
changed_blob_files_.emplace(blob_file_number, std::move(new_meta));
|
||||
blob_file_meta_deltas_[blob_file_number].SetSharedMeta(
|
||||
std::move(shared_meta));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -436,84 +421,162 @@ class VersionBuilder::Rep {
|
||||
Status ApplyBlobFileGarbage(const BlobFileGarbage& blob_file_garbage) {
|
||||
const uint64_t blob_file_number = blob_file_garbage.GetBlobFileNumber();
|
||||
|
||||
auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (!meta) {
|
||||
if (!IsBlobFileInVersion(blob_file_number)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number << " not found";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
assert(meta->GetBlobFileNumber() == blob_file_number);
|
||||
blob_file_meta_deltas_[blob_file_number].AddGarbage(
|
||||
blob_file_garbage.GetGarbageBlobCount(),
|
||||
blob_file_garbage.GetGarbageBlobBytes());
|
||||
|
||||
auto new_meta = BlobFileMetaData::Create(
|
||||
meta->GetSharedMeta(),
|
||||
meta->GetGarbageBlobCount() + blob_file_garbage.GetGarbageBlobCount(),
|
||||
meta->GetGarbageBlobBytes() + blob_file_garbage.GetGarbageBlobBytes());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
changed_blob_files_[blob_file_number] = std::move(new_meta);
|
||||
int GetCurrentLevelForTableFile(uint64_t file_number) const {
|
||||
auto it = table_file_levels_.find(file_number);
|
||||
if (it != table_file_levels_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
assert(base_vstorage_);
|
||||
return base_vstorage_->GetFileLocation(file_number).GetLevel();
|
||||
}
|
||||
|
||||
Status ApplyFileDeletion(int level, uint64_t file_number) {
|
||||
assert(level != VersionStorageInfo::FileLocation::Invalid().GetLevel());
|
||||
|
||||
const int current_level = GetCurrentLevelForTableFile(file_number);
|
||||
|
||||
if (level != current_level) {
|
||||
if (level >= num_levels_) {
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Cannot delete table file #" << file_number << " from level "
|
||||
<< level << " since it is ";
|
||||
if (current_level ==
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel()) {
|
||||
oss << "not in the LSM tree";
|
||||
} else {
|
||||
oss << "on level " << current_level;
|
||||
}
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
if (level >= num_levels_) {
|
||||
assert(invalid_level_sizes_[level] > 0);
|
||||
--invalid_level_sizes_[level];
|
||||
|
||||
table_file_levels_[file_number] =
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto& level_state = levels_[level];
|
||||
|
||||
auto& add_files = level_state.added_files;
|
||||
auto add_it = add_files.find(file_number);
|
||||
if (add_it != add_files.end()) {
|
||||
UnrefFile(add_it->second);
|
||||
add_files.erase(add_it);
|
||||
} else {
|
||||
auto& del_files = level_state.deleted_files;
|
||||
assert(del_files.find(file_number) == del_files.end());
|
||||
del_files.emplace(file_number);
|
||||
}
|
||||
|
||||
table_file_levels_[file_number] =
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ApplyFileAddition(int level, const FileMetaData& meta) {
|
||||
assert(level != VersionStorageInfo::FileLocation::Invalid().GetLevel());
|
||||
|
||||
const uint64_t file_number = meta.fd.GetNumber();
|
||||
|
||||
const int current_level = GetCurrentLevelForTableFile(file_number);
|
||||
|
||||
if (current_level !=
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel()) {
|
||||
if (level >= num_levels_) {
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Cannot add table file #" << file_number << " to level " << level
|
||||
<< " since it is already in the LSM tree on level " << current_level;
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
if (level >= num_levels_) {
|
||||
++invalid_level_sizes_[level];
|
||||
table_file_levels_[file_number] = level;
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto& level_state = levels_[level];
|
||||
|
||||
auto& del_files = level_state.deleted_files;
|
||||
auto del_it = del_files.find(file_number);
|
||||
if (del_it != del_files.end()) {
|
||||
del_files.erase(del_it);
|
||||
} else {
|
||||
FileMetaData* const f = new FileMetaData(meta);
|
||||
f->refs = 1;
|
||||
|
||||
auto& add_files = level_state.added_files;
|
||||
assert(add_files.find(file_number) == add_files.end());
|
||||
add_files.emplace(file_number, f);
|
||||
}
|
||||
|
||||
table_file_levels_[file_number] = level;
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Apply all of the edits in *edit to the current state.
|
||||
Status Apply(VersionEdit* edit) {
|
||||
Status s = CheckConsistency(base_vstorage_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
{
|
||||
const Status s = CheckConsistency(base_vstorage_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete files
|
||||
const auto& del = edit->GetDeletedFiles();
|
||||
for (const auto& del_file : del) {
|
||||
const auto level = del_file.first;
|
||||
const auto number = del_file.second;
|
||||
if (level < num_levels_) {
|
||||
levels_[level].deleted_files.insert(number);
|
||||
s = CheckConsistencyForDeletes(edit, number, level);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
for (const auto& deleted_file : edit->GetDeletedFiles()) {
|
||||
const int level = deleted_file.first;
|
||||
const uint64_t file_number = deleted_file.second;
|
||||
|
||||
auto exising = levels_[level].added_files.find(number);
|
||||
if (exising != levels_[level].added_files.end()) {
|
||||
UnrefFile(exising->second);
|
||||
levels_[level].added_files.erase(exising);
|
||||
}
|
||||
} else {
|
||||
if (invalid_levels_[level].erase(number) == 0) {
|
||||
// Deleting an non-existing file on invalid level.
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
const Status s = ApplyFileDeletion(level, file_number);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new files
|
||||
for (const auto& new_file : edit->GetNewFiles()) {
|
||||
const int level = new_file.first;
|
||||
if (level < num_levels_) {
|
||||
FileMetaData* f = new FileMetaData(new_file.second);
|
||||
f->refs = 1;
|
||||
const FileMetaData& meta = new_file.second;
|
||||
|
||||
assert(levels_[level].added_files.find(f->fd.GetNumber()) ==
|
||||
levels_[level].added_files.end());
|
||||
levels_[level].deleted_files.erase(f->fd.GetNumber());
|
||||
levels_[level].added_files[f->fd.GetNumber()] = f;
|
||||
} else {
|
||||
uint64_t number = new_file.second.fd.GetNumber();
|
||||
auto& lvls = invalid_levels_[level];
|
||||
if (lvls.count(number) == 0) {
|
||||
lvls.insert(number);
|
||||
} else {
|
||||
// Creating an already existing file on invalid level.
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
const Status s = ApplyFileAddition(level, meta);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new blob files
|
||||
for (const auto& blob_file_addition : edit->GetBlobFileAdditions()) {
|
||||
s = ApplyBlobFileAddition(blob_file_addition);
|
||||
const Status s = ApplyBlobFileAddition(blob_file_addition);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -521,13 +584,47 @@ class VersionBuilder::Rep {
|
||||
|
||||
// Increase the amount of garbage for blob files affected by GC
|
||||
for (const auto& blob_file_garbage : edit->GetBlobFileGarbages()) {
|
||||
s = ApplyBlobFileGarbage(blob_file_garbage);
|
||||
const Status s = ApplyBlobFileGarbage(blob_file_garbage);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData> CreateMetaDataForNewBlobFile(
|
||||
const BlobFileMetaDataDelta& delta) {
|
||||
auto shared_meta = delta.GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
|
||||
auto meta = BlobFileMetaData::Create(std::move(shared_meta),
|
||||
delta.GetAdditionalGarbageCount(),
|
||||
delta.GetAdditionalGarbageBytes());
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData>
|
||||
GetOrCreateMetaDataForExistingBlobFile(
|
||||
const std::shared_ptr<BlobFileMetaData>& base_meta,
|
||||
const BlobFileMetaDataDelta& delta) {
|
||||
assert(base_meta);
|
||||
assert(!delta.GetSharedMeta());
|
||||
|
||||
if (delta.IsEmpty()) {
|
||||
return base_meta;
|
||||
}
|
||||
|
||||
auto shared_meta = base_meta->GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
|
||||
auto meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta),
|
||||
base_meta->GetGarbageBlobCount() + delta.GetAdditionalGarbageCount(),
|
||||
base_meta->GetGarbageBlobBytes() + delta.GetAdditionalGarbageBytes());
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
void AddBlobFileIfNeeded(
|
||||
@@ -551,42 +648,41 @@ class VersionBuilder::Rep {
|
||||
auto base_it = base_blob_files.begin();
|
||||
const auto base_it_end = base_blob_files.end();
|
||||
|
||||
auto changed_it = changed_blob_files_.begin();
|
||||
const auto changed_it_end = changed_blob_files_.end();
|
||||
auto delta_it = blob_file_meta_deltas_.begin();
|
||||
const auto delta_it_end = blob_file_meta_deltas_.end();
|
||||
|
||||
while (base_it != base_it_end && changed_it != changed_it_end) {
|
||||
while (base_it != base_it_end && delta_it != delta_it_end) {
|
||||
const uint64_t base_blob_file_number = base_it->first;
|
||||
const uint64_t changed_blob_file_number = changed_it->first;
|
||||
const uint64_t delta_blob_file_number = delta_it->first;
|
||||
|
||||
const auto& base_meta = base_it->second;
|
||||
const auto& changed_meta = changed_it->second;
|
||||
|
||||
assert(base_meta);
|
||||
assert(changed_meta);
|
||||
|
||||
if (base_blob_file_number < changed_blob_file_number) {
|
||||
if (base_blob_file_number < delta_blob_file_number) {
|
||||
const auto& base_meta = base_it->second;
|
||||
assert(base_meta);
|
||||
assert(base_meta->GetGarbageBlobCount() <
|
||||
base_meta->GetTotalBlobCount());
|
||||
|
||||
vstorage->AddBlobFile(base_meta);
|
||||
|
||||
++base_it;
|
||||
} else if (changed_blob_file_number < base_blob_file_number) {
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
} else if (delta_blob_file_number < base_blob_file_number) {
|
||||
// Note: blob file numbers are strictly increasing over time and
|
||||
// once blob files get marked obsolete, they never reappear. Thus,
|
||||
// this case is not possible.
|
||||
assert(false);
|
||||
|
||||
++changed_it;
|
||||
++delta_it;
|
||||
} else {
|
||||
assert(base_blob_file_number == changed_blob_file_number);
|
||||
assert(base_meta->GetSharedMeta() == changed_meta->GetSharedMeta());
|
||||
assert(base_meta->GetGarbageBlobCount() <=
|
||||
changed_meta->GetGarbageBlobCount());
|
||||
assert(base_meta->GetGarbageBlobBytes() <=
|
||||
changed_meta->GetGarbageBlobBytes());
|
||||
assert(base_blob_file_number == delta_blob_file_number);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
const auto& base_meta = base_it->second;
|
||||
const auto& delta = delta_it->second;
|
||||
|
||||
auto meta = GetOrCreateMetaDataForExistingBlobFile(base_meta, delta);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, meta);
|
||||
|
||||
++base_it;
|
||||
++changed_it;
|
||||
++delta_it;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,12 +695,14 @@ class VersionBuilder::Rep {
|
||||
++base_it;
|
||||
}
|
||||
|
||||
while (changed_it != changed_it_end) {
|
||||
const auto& changed_meta = changed_it->second;
|
||||
assert(changed_meta);
|
||||
while (delta_it != delta_it_end) {
|
||||
const auto& delta = delta_it->second;
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
++changed_it;
|
||||
auto meta = CreateMetaDataForNewBlobFile(delta);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, meta);
|
||||
|
||||
++delta_it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+230
-1
@@ -54,7 +54,7 @@ class VersionBuilderTest : public testing::Test {
|
||||
return InternalKey(ukey, smallest_seq, kTypeValue);
|
||||
}
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
void Add(int level, uint64_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 0, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
uint64_t num_entries = 0, uint64_t num_deletions = 0,
|
||||
@@ -367,6 +367,235 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileDeletionIncorrectLevel) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
|
||||
Add(level, file_number, smallest, largest);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int incorrect_level = 3;
|
||||
|
||||
edit.DeleteFile(incorrect_level, file_number);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot delete table file #2345 from level 3 since "
|
||||
"it is on level 1"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileDeletionNotInLSMTree) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int level = 3;
|
||||
constexpr uint64_t file_number = 1234;
|
||||
|
||||
edit.DeleteFile(level, file_number);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot delete table file #1234 from level 3 since "
|
||||
"it is not in the LSM tree"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileDeletionAndAddition) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
|
||||
Add(level, file_number, smallest, largest);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit deletion;
|
||||
|
||||
deletion.DeleteFile(level, file_number);
|
||||
|
||||
ASSERT_OK(builder.Apply(&deletion));
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
addition.AddFile(level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
ASSERT_EQ(new_vstorage.GetFileLocation(file_number).GetLevel(), level);
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyInBase) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
|
||||
Add(level, file_number, smallest, largest);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int new_level = 2;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit.AddFile(new_level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot add table file #2345 to level 2 since it is "
|
||||
"already in the LSM tree on level 1"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyApplied) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int level = 3;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit.AddFile(level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
VersionEdit other_edit;
|
||||
|
||||
constexpr int new_level = 2;
|
||||
|
||||
other_edit.AddFile(new_level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
const Status s = builder.Apply(&other_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot add table file #2345 to level 2 since it is "
|
||||
"already in the LSM tree on level 3"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileAdditionAndDeletion) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
addition.AddFile(level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
VersionEdit deletion;
|
||||
|
||||
deletion.DeleteFile(level, file_number);
|
||||
|
||||
ASSERT_OK(builder.Apply(&deletion));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
ASSERT_FALSE(new_vstorage.GetFileLocation(file_number).IsValid());
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileAddition) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
|
||||
@@ -415,7 +415,8 @@ Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
version_set_->db_options_->max_file_opening_threads,
|
||||
prefetch_index_and_filter_in_cache, is_initial_load,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
if (s.IsPathNotFound() && no_error_if_table_files_missing_) {
|
||||
if ((s.IsPathNotFound() || s.IsCorruption()) &&
|
||||
no_error_if_table_files_missing_) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok() && !version_set_->db_options_->paranoid_checks) {
|
||||
@@ -546,7 +547,7 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
const std::string fpath =
|
||||
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_num);
|
||||
s = version_set_->VerifyFileMetadata(fpath, meta);
|
||||
if (s.IsPathNotFound() || s.IsNotFound()) {
|
||||
if (s.IsPathNotFound() || s.IsNotFound() || s.IsCorruption()) {
|
||||
missing_files.insert(file_num);
|
||||
s = Status::OK();
|
||||
}
|
||||
|
||||
+57
-12
@@ -1806,11 +1806,11 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||
fp.IsHitFileLastInLevel()),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
// TODO: examine the behavior for corrupted key
|
||||
if (timer_enabled) {
|
||||
PERF_COUNTER_BY_LEVEL_ADD(get_from_table_nanos, timer.ElapsedNanos(),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
}
|
||||
if (!status->ok()) {
|
||||
return;
|
||||
@@ -1932,6 +1932,7 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
&storage_info_.level_files_brief_, storage_info_.num_non_empty_levels_,
|
||||
&storage_info_.file_indexer_, user_comparator(), internal_comparator());
|
||||
FdWithKeyRange* f = fp.GetNextFile();
|
||||
Status s;
|
||||
|
||||
while (f != nullptr) {
|
||||
MultiGetRange file_range = fp.CurrentFileRange();
|
||||
@@ -1939,17 +1940,17 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
GetPerfLevel() >= PerfLevel::kEnableTimeExceptForMutex &&
|
||||
get_perf_context()->per_level_perf_context_enabled;
|
||||
StopWatchNano timer(env_, timer_enabled /* auto_start */);
|
||||
Status s = table_cache_->MultiGet(
|
||||
s = table_cache_->MultiGet(
|
||||
read_options, *internal_comparator(), *f->file_metadata, &file_range,
|
||||
mutable_cf_options_.prefix_extractor.get(),
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||
fp.IsHitFileLastInLevel()),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
// TODO: examine the behavior for corrupted key
|
||||
if (timer_enabled) {
|
||||
PERF_COUNTER_BY_LEVEL_ADD(get_from_table_nanos, timer.ElapsedNanos(),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// TODO: Set status for individual keys appropriately
|
||||
@@ -1960,7 +1961,8 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
return;
|
||||
}
|
||||
uint64_t batch_size = 0;
|
||||
for (auto iter = file_range.begin(); iter != file_range.end(); ++iter) {
|
||||
for (auto iter = file_range.begin(); s.ok() && iter != file_range.end();
|
||||
++iter) {
|
||||
GetContext& get_context = *iter->get_context;
|
||||
Status* status = iter->s;
|
||||
// The Status in the KeyContext takes precedence over GetContext state
|
||||
@@ -2006,7 +2008,12 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
PERF_COUNTER_BY_LEVEL_ADD(user_key_return_count, 1,
|
||||
fp.GetHitFileLevel());
|
||||
file_range.AddValueSize(iter->value->size());
|
||||
file_range.MarkKeyDone(iter);
|
||||
if (file_range.GetValueSize() > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GetContext::kDeleted:
|
||||
// Use empty error message for speed
|
||||
@@ -2028,14 +2035,14 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
}
|
||||
RecordInHistogram(db_statistics_, SST_BATCH_SIZE, batch_size);
|
||||
if (file_picker_range.empty()) {
|
||||
if (!s.ok() || file_picker_range.empty()) {
|
||||
break;
|
||||
}
|
||||
f = fp.GetNextFile();
|
||||
}
|
||||
|
||||
// Process any left over keys
|
||||
for (auto iter = range->begin(); iter != range->end(); ++iter) {
|
||||
for (auto iter = range->begin(); s.ok() && iter != range->end(); ++iter) {
|
||||
GetContext& get_context = *iter->get_context;
|
||||
Status* status = iter->s;
|
||||
Slice user_key = iter->lkey->user_key();
|
||||
@@ -2060,12 +2067,23 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
nullptr /* result_operand */, true);
|
||||
if (LIKELY(iter->value != nullptr)) {
|
||||
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 {
|
||||
range->MarkKeyDone(iter);
|
||||
*status = Status::NotFound(); // Use an empty error message for speed
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = range->begin(); iter != range->end(); ++iter) {
|
||||
range->MarkKeyDone(iter);
|
||||
*(iter->s) = s;
|
||||
}
|
||||
}
|
||||
|
||||
bool Version::IsFilterSkipped(int level, bool is_file_last_in_level) {
|
||||
@@ -2087,6 +2105,9 @@ void VersionStorageInfo::GenerateLevelFilesBrief() {
|
||||
void Version::PrepareApply(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool update_stats) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"Version::PrepareApply:forced_check",
|
||||
reinterpret_cast<void*>(&storage_info_.force_consistency_checks_));
|
||||
UpdateAccumulatedStats(update_stats);
|
||||
storage_info_.UpdateNumNonEmptyLevels();
|
||||
storage_info_.CalculateBaseBytes(*cfd_->ioptions(), mutable_cf_options);
|
||||
@@ -2388,6 +2409,11 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// compaction score for the whole DB. Adding other levels as if
|
||||
// they are L0 files.
|
||||
for (int i = 1; i < num_levels(); i++) {
|
||||
// Its possible that a subset of the files in a level may be in a
|
||||
// compaction, due to delete triggered compaction or trivial move.
|
||||
// In that case, the below check may not catch a level being
|
||||
// compacted as it only checks the first file. The worst that can
|
||||
// happen is a scheduled compaction thread will find nothing to do.
|
||||
if (!files_[i].empty() && !files_[i][0]->being_compacted) {
|
||||
num_sorted_runs++;
|
||||
}
|
||||
@@ -2610,6 +2636,12 @@ void VersionStorageInfo::AddFile(int level, FileMetaData* f, Logger* info_log) {
|
||||
#endif
|
||||
f->refs++;
|
||||
level_files->push_back(f);
|
||||
|
||||
const uint64_t file_number = f->fd.GetNumber();
|
||||
|
||||
assert(file_locations_.find(file_number) == file_locations_.end());
|
||||
file_locations_.emplace(file_number,
|
||||
FileLocation(level, level_files->size() - 1));
|
||||
}
|
||||
|
||||
void VersionStorageInfo::AddBlobFile(
|
||||
@@ -4945,7 +4977,19 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
|
||||
}
|
||||
|
||||
if (first_nonempty_level > 0) {
|
||||
new_files_list[new_levels - 1] = vstorage->LevelFiles(first_nonempty_level);
|
||||
auto& new_last_level = new_files_list[new_levels - 1];
|
||||
|
||||
new_last_level = vstorage->LevelFiles(first_nonempty_level);
|
||||
|
||||
for (size_t i = 0; i < new_last_level.size(); ++i) {
|
||||
const FileMetaData* const meta = new_last_level[i];
|
||||
assert(meta);
|
||||
|
||||
const uint64_t file_number = meta->fd.GetNumber();
|
||||
|
||||
vstorage->file_locations_[file_number] =
|
||||
VersionStorageInfo::FileLocation(new_levels - 1, i);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] vstorage -> files_;
|
||||
@@ -5445,7 +5489,8 @@ uint64_t VersionSet::ApproximateSize(const SizeApproximationOptions& options,
|
||||
static_cast<uint64_t>(total_full_size * margin)) {
|
||||
total_full_size += total_intersecting_size / 2;
|
||||
} else {
|
||||
// Estimate for all the first files, at each level
|
||||
// Estimate for all the first files (might also be last files), at each
|
||||
// level
|
||||
for (const auto file_ptr : first_files) {
|
||||
total_full_size += ApproximateSize(v, *file_ptr, start, end, caller);
|
||||
}
|
||||
@@ -5627,7 +5672,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
/*file_read_hist=*/nullptr, TableReaderCaller::kCompaction,
|
||||
/*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/static_cast<int>(which),
|
||||
/*skip_filters=*/false, /*level=*/static_cast<int>(c->level(which)),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
@@ -5641,7 +5686,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
/*should_sample=*/false,
|
||||
/*no per level latency histogram=*/nullptr,
|
||||
TableReaderCaller::kCompaction, /*skip_filters=*/false,
|
||||
/*level=*/static_cast<int>(which), range_del_agg,
|
||||
/*level=*/static_cast<int>(c->level(which)), range_del_agg,
|
||||
c->boundaries(which));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +283,47 @@ class VersionStorageInfo {
|
||||
return files_[level];
|
||||
}
|
||||
|
||||
class FileLocation {
|
||||
public:
|
||||
FileLocation() = default;
|
||||
FileLocation(int level, size_t position)
|
||||
: level_(level), position_(position) {}
|
||||
|
||||
int GetLevel() const { return level_; }
|
||||
size_t GetPosition() const { return position_; }
|
||||
|
||||
bool IsValid() const { return level_ >= 0; }
|
||||
|
||||
bool operator==(const FileLocation& rhs) const {
|
||||
return level_ == rhs.level_ && position_ == rhs.position_;
|
||||
}
|
||||
|
||||
bool operator!=(const FileLocation& rhs) const { return !(*this == rhs); }
|
||||
|
||||
static FileLocation Invalid() { return FileLocation(); }
|
||||
|
||||
private:
|
||||
int level_ = -1;
|
||||
size_t position_ = 0;
|
||||
};
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
FileLocation GetFileLocation(uint64_t file_number) const {
|
||||
const auto it = file_locations_.find(file_number);
|
||||
|
||||
if (it == file_locations_.end()) {
|
||||
return FileLocation::Invalid();
|
||||
}
|
||||
|
||||
assert(it->second.GetLevel() < num_levels_);
|
||||
assert(it->second.GetPosition() < files_[it->second.GetLevel()].size());
|
||||
assert(files_[it->second.GetLevel()][it->second.GetPosition()]);
|
||||
assert(files_[it->second.GetLevel()][it->second.GetPosition()]
|
||||
->fd.GetNumber() == file_number);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
using BlobFiles = std::map<uint64_t, std::shared_ptr<BlobFileMetaData>>;
|
||||
const BlobFiles& GetBlobFiles() const { return blob_files_; }
|
||||
@@ -461,6 +502,11 @@ class VersionStorageInfo {
|
||||
// in increasing order of keys
|
||||
std::vector<FileMetaData*>* files_;
|
||||
|
||||
// Map of all table files in version. Maps file number to (level, position on
|
||||
// level).
|
||||
using FileLocations = std::unordered_map<uint64_t, FileLocation>;
|
||||
FileLocations file_locations_;
|
||||
|
||||
// Map of blob files in version by number.
|
||||
BlobFiles blob_files_;
|
||||
|
||||
|
||||
+25
-8
@@ -372,19 +372,19 @@ TEST_F(VersionStorageInfoTest, EstimateLiveDataSize) {
|
||||
Add(2, 3U, "6", "8", 1U); // Partial overlap with last level
|
||||
Add(3, 4U, "1", "9", 1U); // Contains range of last level
|
||||
Add(4, 5U, "4", "5", 1U); // Inside range of last level
|
||||
Add(4, 5U, "6", "7", 1U); // Inside range of last level
|
||||
Add(5, 6U, "4", "7", 10U);
|
||||
Add(4, 6U, "6", "7", 1U); // Inside range of last level
|
||||
Add(5, 7U, "4", "7", 10U);
|
||||
ASSERT_EQ(10U, vstorage_.EstimateLiveDataSize());
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, EstimateLiveDataSize2) {
|
||||
Add(0, 1U, "9", "9", 1U); // Level 0 is not ordered
|
||||
Add(0, 1U, "5", "6", 1U); // Ignored because of [5,6] in l1
|
||||
Add(1, 1U, "1", "2", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 2U, "3", "4", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 3U, "5", "6", 1U);
|
||||
Add(2, 4U, "2", "3", 1U);
|
||||
Add(3, 5U, "7", "8", 1U);
|
||||
Add(0, 2U, "5", "6", 1U); // Ignored because of [5,6] in l1
|
||||
Add(1, 3U, "1", "2", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 4U, "3", "4", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 5U, "5", "6", 1U);
|
||||
Add(2, 6U, "2", "3", 1U);
|
||||
Add(3, 7U, "7", "8", 1U);
|
||||
ASSERT_EQ(4U, vstorage_.EstimateLiveDataSize());
|
||||
}
|
||||
|
||||
@@ -421,6 +421,23 @@ TEST_F(VersionStorageInfoTest, GetOverlappingInputs) {
|
||||
1, {"i", 0, kTypeValue}, {"j", 0, kTypeValue}));
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, FileLocation) {
|
||||
Add(0, 11U, "1", "2", 5000U);
|
||||
Add(0, 12U, "1", "2", 5000U);
|
||||
|
||||
Add(2, 7U, "1", "2", 8000U);
|
||||
|
||||
ASSERT_EQ(vstorage_.GetFileLocation(11U),
|
||||
VersionStorageInfo::FileLocation(0, 0));
|
||||
ASSERT_EQ(vstorage_.GetFileLocation(12U),
|
||||
VersionStorageInfo::FileLocation(0, 1));
|
||||
|
||||
ASSERT_EQ(vstorage_.GetFileLocation(7U),
|
||||
VersionStorageInfo::FileLocation(2, 0));
|
||||
|
||||
ASSERT_FALSE(vstorage_.GetFileLocation(999U).IsValid());
|
||||
}
|
||||
|
||||
class VersionStorageInfoTimestampTest : public VersionStorageInfoTestBase {
|
||||
public:
|
||||
VersionStorageInfoTimestampTest()
|
||||
|
||||
+21
-3
@@ -908,7 +908,14 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
PutLengthPrefixedSlice(&b->rep_, key);
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSlice(&b->rep_, key);
|
||||
} else {
|
||||
PutVarint32(&b->rep_,
|
||||
static_cast<uint32_t>(key.size() + b->timestamp_size_));
|
||||
b->rep_.append(key.data(), key.size());
|
||||
b->rep_.append(b->timestamp_size_, '\0');
|
||||
}
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
@@ -930,7 +937,11 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
} else {
|
||||
PutLengthPrefixedSlicePartsWithPadding(&b->rep_, key, b->timestamp_size_);
|
||||
}
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
@@ -1546,7 +1557,14 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
|
||||
auto ret_status = DeleteImpl(column_family_id, key, Slice(), kTypeDeletion);
|
||||
ColumnFamilyData* cfd = cf_mems_->current();
|
||||
assert(!cfd || cfd->user_comparator());
|
||||
const size_t ts_sz = (cfd && cfd->user_comparator())
|
||||
? cfd->user_comparator()->timestamp_size()
|
||||
: 0;
|
||||
const ValueType delete_type =
|
||||
(0 == ts_sz) ? kTypeDeletion : kTypeDeletionWithTimestamp;
|
||||
auto ret_status = DeleteImpl(column_family_id, key, Slice(), delete_type);
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(!ret_status.IsTryAgain() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
|
||||
@@ -10,5 +10,5 @@ add_executable(db_stress${ARTIFACT_SUFFIX}
|
||||
db_stress_gflags.cc
|
||||
db_stress_tool.cc
|
||||
no_batched_ops_stress.cc)
|
||||
target_link_libraries(db_stress${ARTIFACT_SUFFIX} ${ROCKSDB_LIB})
|
||||
target_link_libraries(db_stress${ARTIFACT_SUFFIX} ${ROCKSDB_LIB} ${THIRDPARTY_LIBS})
|
||||
list(APPEND tool_deps db_stress)
|
||||
|
||||
@@ -245,7 +245,7 @@ int db_stress_tool(int argc, char** argv) {
|
||||
}
|
||||
} else {
|
||||
uint64_t keys_per_level = key_gen_ctx.window / levels;
|
||||
for (unsigned int level = 0; level < levels - 1; ++level) {
|
||||
for (unsigned int level = 0; level + 1 < levels; ++level) {
|
||||
key_gen_ctx.weights.emplace_back(keys_per_level);
|
||||
}
|
||||
key_gen_ctx.weights.emplace_back(key_gen_ctx.window -
|
||||
|
||||
@@ -36,8 +36,8 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
if (thread->shared->HasVerificationFailedYet()) {
|
||||
break;
|
||||
}
|
||||
if (!thread->rand.OneIn(2)) {
|
||||
// Use iterator to verify this range
|
||||
if (thread->rand.OneIn(3)) {
|
||||
// 1/3 chance use iterator to verify this range
|
||||
Slice prefix;
|
||||
std::string seek_key = Key(start);
|
||||
std::unique_ptr<Iterator> iter(
|
||||
@@ -82,8 +82,8 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
from_db.data(), from_db.length());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Use Get to verify this range
|
||||
} else if (thread->rand.OneIn(2)) {
|
||||
// 1/3 chance use Get to verify this range
|
||||
for (auto i = start; i < end; i++) {
|
||||
if (thread->shared->HasVerificationFailedYet()) {
|
||||
break;
|
||||
@@ -99,6 +99,38 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
from_db.data(), from_db.length());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 1/3 chance use MultiGet to verify this range
|
||||
for (auto 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> keystrs(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].length());
|
||||
}
|
||||
db_->MultiGet(options, column_families_[cf], batch_size, keys.data(),
|
||||
values.data(), statuses.data());
|
||||
for (size_t j = 0; j < batch_size; ++j) {
|
||||
Status s = statuses[j];
|
||||
std::string from_db = values[j].ToString();
|
||||
VerifyValue(static_cast<int>(cf), i + j, options, shared, from_db,
|
||||
s, true);
|
||||
if (from_db.length()) {
|
||||
PrintKeyValue(static_cast<int>(cf), static_cast<uint32_t>(i + j),
|
||||
from_db.data(), from_db.length());
|
||||
}
|
||||
}
|
||||
|
||||
i += batch_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,6 +241,14 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
std::vector<Status> statuses(num_keys);
|
||||
ColumnFamilyHandle* cfh = column_families_[rand_column_families[0]];
|
||||
int error_count = 0;
|
||||
// Do a consistency check between Get and MultiGet. Don't do it too
|
||||
// often as it will slow db_stress down
|
||||
bool do_consistency_check = thread->rand.OneIn(4);
|
||||
|
||||
ReadOptions readoptionscopy = read_opts;
|
||||
if (do_consistency_check) {
|
||||
readoptionscopy.snapshot = db_->GetSnapshot();
|
||||
}
|
||||
|
||||
// To appease clang analyzer
|
||||
const bool use_txn = FLAGS_use_txn;
|
||||
@@ -275,7 +315,7 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
SharedState::ignore_read_error = false;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
db_->MultiGet(read_opts, cfh, num_keys, keys.data(), values.data(),
|
||||
db_->MultiGet(readoptionscopy, cfh, num_keys, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
#ifndef NDEBUG
|
||||
if (fault_fs_guard) {
|
||||
@@ -284,9 +324,8 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
#endif // NDEBUG
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
txn->MultiGet(read_opts, cfh, num_keys, keys.data(), values.data(),
|
||||
txn->MultiGet(readoptionscopy, cfh, num_keys, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
RollbackTxn(txn);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -309,10 +348,57 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
if (fault_fs_guard) {
|
||||
fault_fs_guard->DisableErrorInjection();
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
for (const auto& s : statuses) {
|
||||
if (s.ok()) {
|
||||
for (size_t i = 0; i < statuses.size(); ++i) {
|
||||
Status s = statuses[i];
|
||||
bool is_consistent = true;
|
||||
// Only do the consistency check if no error was injected and MultiGet
|
||||
// didn't return an unexpected error
|
||||
if (do_consistency_check && !error_count && (s.ok() || s.IsNotFound())) {
|
||||
Status tmp_s;
|
||||
std::string value;
|
||||
|
||||
if (use_txn) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
tmp_s = txn->Get(readoptionscopy, cfh, keys[i], &value);
|
||||
#endif // ROCKSDB_LITE
|
||||
} else {
|
||||
tmp_s = db_->Get(readoptionscopy, cfh, keys[i], &value);
|
||||
}
|
||||
if (!tmp_s.ok() && !tmp_s.IsNotFound()) {
|
||||
fprintf(stderr, "Get error: %s\n", s.ToString().c_str());
|
||||
is_consistent = false;
|
||||
} else if (!s.ok() && tmp_s.ok()) {
|
||||
fprintf(stderr, "MultiGet returned different results with key %s\n",
|
||||
keys[i].ToString(true).c_str());
|
||||
fprintf(stderr, "Get returned ok, MultiGet returned not found\n");
|
||||
is_consistent = false;
|
||||
} else if (s.ok() && tmp_s.IsNotFound()) {
|
||||
fprintf(stderr, "MultiGet returned different results with key %s\n",
|
||||
keys[i].ToString(true).c_str());
|
||||
fprintf(stderr, "MultiGet returned ok, Get returned not found\n");
|
||||
is_consistent = false;
|
||||
} else if (s.ok() && value != values[i].ToString()) {
|
||||
fprintf(stderr, "MultiGet returned different results with key %s\n",
|
||||
keys[i].ToString(true).c_str());
|
||||
fprintf(stderr, "MultiGet returned value %s\n",
|
||||
values[i].ToString(true).c_str());
|
||||
fprintf(stderr, "Get returned value %s\n", value.c_str());
|
||||
is_consistent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_consistent) {
|
||||
fprintf(stderr, "TestMultiGet error: is_consistent is false\n");
|
||||
thread->stats.AddErrors(1);
|
||||
// Fail fast to preserve the DB state
|
||||
thread->shared->SetVerificationFailure();
|
||||
break;
|
||||
} else if (s.ok()) {
|
||||
// found case
|
||||
thread->stats.AddGets(1, 1);
|
||||
} else if (s.IsNotFound()) {
|
||||
@@ -331,11 +417,15 @@ class NonBatchedOpsStressTest : public StressTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
if (fault_fs_guard) {
|
||||
fault_fs_guard->DisableErrorInjection();
|
||||
|
||||
if (readoptionscopy.snapshot) {
|
||||
db_->ReleaseSnapshot(readoptionscopy.snapshot);
|
||||
}
|
||||
if (use_txn) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
RollbackTxn(txn);
|
||||
#endif
|
||||
}
|
||||
#endif // NDEBUG
|
||||
return statuses;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+3
-1
@@ -523,7 +523,9 @@ class CompositeEnvWrapper : public Env {
|
||||
return env_target_->GetThreadPoolQueueLen(pri);
|
||||
}
|
||||
Status GetTestDirectory(std::string* path) override {
|
||||
return env_target_->GetTestDirectory(path);
|
||||
IOOptions io_opts;
|
||||
IODebugContext dbg;
|
||||
return file_system_->GetTestDirectory(io_opts, path, &dbg);
|
||||
}
|
||||
uint64_t NowMicros() override { return env_target_->NowMicros(); }
|
||||
uint64_t NowNanos() override { return env_target_->NowNanos(); }
|
||||
|
||||
Vendored
+1
-1
@@ -613,7 +613,7 @@ Status HdfsEnv::IsDirectory(const std::string& path, bool* is_dir) {
|
||||
hdfsFileInfo* pFileInfo = hdfsGetPathInfo(fileSys_, path.c_str());
|
||||
if (pFileInfo != nullptr) {
|
||||
if (is_dir != nullptr) {
|
||||
*is_dir = (pFileInfo->mKind == tObjectKindDirectory);
|
||||
*is_dir = (pFileInfo->mKind == kObjectKindDirectory);
|
||||
}
|
||||
hdfsFreeFileInfo(pFileInfo, 1);
|
||||
return Status::OK();
|
||||
|
||||
Vendored
-14
@@ -224,20 +224,6 @@ class PosixEnv : public CompositeEnvWrapper {
|
||||
|
||||
unsigned int GetThreadPoolQueueLen(Priority pri = LOW) const override;
|
||||
|
||||
Status GetTestDirectory(std::string* result) override {
|
||||
const char* env = getenv("TEST_TMPDIR");
|
||||
if (env && env[0] != '\0') {
|
||||
*result = env;
|
||||
} else {
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof(buf), "/tmp/rocksdbtest-%d", int(geteuid()));
|
||||
*result = buf;
|
||||
}
|
||||
// Directory may already exist
|
||||
CreateDir(*result);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetThreadList(std::vector<ThreadStatus>* thread_list) override {
|
||||
assert(thread_status_updater_);
|
||||
return thread_status_updater_->GetThreadList(thread_list);
|
||||
|
||||
Vendored
+2
-1
@@ -327,6 +327,7 @@ TEST_P(EnvPosixTestWithParam, UnSchedule) {
|
||||
// run in any order. The purpose of the test is unclear.
|
||||
#ifndef OS_WIN
|
||||
TEST_P(EnvPosixTestWithParam, RunMany) {
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
std::atomic<int> last_id(0);
|
||||
|
||||
struct CB {
|
||||
@@ -1242,7 +1243,7 @@ TEST_F(EnvPosixTest, MultiReadNonAlignedLargeNum) {
|
||||
for (int so: start_offsets) {
|
||||
offsets.push_back(so);
|
||||
}
|
||||
for (size_t i = 0; i < offsets.size() - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < offsets.size(); i++) {
|
||||
lens.push_back(static_cast<size_t>(rnd.Uniform(static_cast<int>(offsets[i + 1] - offsets[i])) + 1));
|
||||
}
|
||||
lens.push_back(static_cast<size_t>(rnd.Uniform(static_cast<int>(kTotalSize - offsets.back())) + 1));
|
||||
|
||||
Vendored
+7
-3
@@ -241,6 +241,8 @@ class PosixFileSystem : public FileSystem {
|
||||
s = IOError("while mmap file for read", fname, errno);
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
if (options.use_direct_reads && !options.use_mmap_reads) {
|
||||
@@ -889,14 +891,16 @@ class PosixFileSystem : public FileSystem {
|
||||
if (fd < 0) {
|
||||
return IOError("While open for IsDirectory()", path, errno);
|
||||
}
|
||||
IOStatus io_s;
|
||||
struct stat sbuf;
|
||||
if (fstat(fd, &sbuf) < 0) {
|
||||
return IOError("While doing stat for IsDirectory()", path, errno);
|
||||
io_s = IOError("While doing stat for IsDirectory()", path, errno);
|
||||
}
|
||||
if (nullptr != is_dir) {
|
||||
close(fd);
|
||||
if (io_s.ok() && nullptr != is_dir) {
|
||||
*is_dir = S_ISDIR(sbuf.st_mode);
|
||||
}
|
||||
return IOStatus::OK();
|
||||
return io_s;
|
||||
}
|
||||
|
||||
FileOptions OptimizeForLogWrite(const FileOptions& file_options,
|
||||
|
||||
Vendored
+22
-13
@@ -189,19 +189,19 @@ bool IsSyncFileRangeSupported(int fd) {
|
||||
/*
|
||||
* DirectIOHelper
|
||||
*/
|
||||
#ifndef NDEBUG
|
||||
namespace {
|
||||
|
||||
bool IsSectorAligned(const size_t off, size_t sector_size) {
|
||||
return off % sector_size == 0;
|
||||
assert((sector_size & (sector_size - 1)) == 0);
|
||||
return (off & (sector_size - 1)) == 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool IsSectorAligned(const void* ptr, size_t sector_size) {
|
||||
return uintptr_t(ptr) % sector_size == 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
/*
|
||||
* PosixSequentialFile
|
||||
@@ -276,7 +276,7 @@ IOStatus PosixSequentialFile::PositionedRead(uint64_t offset, size_t n,
|
||||
ptr += r;
|
||||
offset += r;
|
||||
left -= r;
|
||||
if (r % static_cast<ssize_t>(GetRequiredBufferAlignment()) != 0) {
|
||||
if (!IsSectorAligned(r, GetRequiredBufferAlignment())) {
|
||||
// Bytes reads don't fill sectors. Should only happen at the end
|
||||
// of the file.
|
||||
break;
|
||||
@@ -416,7 +416,7 @@ Status LogicalBlockSizeCache::RefAndCacheLogicalBlockSize(
|
||||
v.size = dir_size->second;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
return s;
|
||||
}
|
||||
|
||||
void LogicalBlockSizeCache::UnrefAndTryRemoveCachedLogicalBlockSize(
|
||||
@@ -711,13 +711,22 @@ IOStatus PosixRandomAccessFile::MultiRead(FSReadRequest* reqs,
|
||||
// comment
|
||||
// https://github.com/facebook/rocksdb/pull/6441#issuecomment-589843435
|
||||
// Fall back to pread in this case.
|
||||
Slice tmp_slice;
|
||||
req->status =
|
||||
Read(req->offset + req_wrap->finished_len,
|
||||
req->len - req_wrap->finished_len, options, &tmp_slice,
|
||||
req->scratch + req_wrap->finished_len, dbg);
|
||||
req->result =
|
||||
Slice(req->scratch, req_wrap->finished_len + tmp_slice.size());
|
||||
if (use_direct_io() &&
|
||||
!IsSectorAligned(req_wrap->finished_len,
|
||||
GetRequiredBufferAlignment())) {
|
||||
// Bytes reads don't fill sectors. Should only happen at the end
|
||||
// of the file.
|
||||
req->result = Slice(req->scratch, req_wrap->finished_len);
|
||||
req->status = IOStatus::OK();
|
||||
} else {
|
||||
Slice tmp_slice;
|
||||
req->status =
|
||||
Read(req->offset + req_wrap->finished_len,
|
||||
req->len - req_wrap->finished_len, options, &tmp_slice,
|
||||
req->scratch + req_wrap->finished_len, dbg);
|
||||
req->result =
|
||||
Slice(req->scratch, req_wrap->finished_len + tmp_slice.size());
|
||||
}
|
||||
} else if (bytes_read < req_wrap->iov.iov_len) {
|
||||
assert(bytes_read > 0);
|
||||
assert(bytes_read + req_wrap->finished_len < req->len);
|
||||
|
||||
Vendored
+3
-3
@@ -67,7 +67,7 @@ TEST_F(LogicalBlockSizeCacheTest, Cache) {
|
||||
ASSERT_EQ(3, cache.GetLogicalBlockSize("/db2/sst2", 7));
|
||||
ASSERT_EQ(8, ncall);
|
||||
|
||||
cache.RefAndCacheLogicalBlockSize({"/db"});
|
||||
ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/db"}));
|
||||
ASSERT_EQ(4, cache.Size());
|
||||
ASSERT_TRUE(cache.Contains("/"));
|
||||
ASSERT_TRUE(cache.Contains("/db1"));
|
||||
@@ -104,7 +104,7 @@ TEST_F(LogicalBlockSizeCacheTest, Ref) {
|
||||
ASSERT_EQ(1, cache.GetLogicalBlockSize("/db/sst0", 1));
|
||||
ASSERT_EQ(1, ncall);
|
||||
|
||||
cache.RefAndCacheLogicalBlockSize({"/db"});
|
||||
ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/db"}));
|
||||
ASSERT_EQ(2, ncall);
|
||||
ASSERT_EQ(1, cache.GetRefCount("/db"));
|
||||
// Block size for /db is cached. Ref count = 1.
|
||||
@@ -112,7 +112,7 @@ TEST_F(LogicalBlockSizeCacheTest, Ref) {
|
||||
ASSERT_EQ(2, ncall);
|
||||
|
||||
// Ref count = 2, but won't recompute the cached buffer size.
|
||||
cache.RefAndCacheLogicalBlockSize({"/db"});
|
||||
ASSERT_OK(cache.RefAndCacheLogicalBlockSize({"/db"}));
|
||||
ASSERT_EQ(2, cache.GetRefCount("/db"));
|
||||
ASSERT_EQ(2, ncall);
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ void RunSecondary() {
|
||||
std::string value;
|
||||
db->Get(ropts, key, &value);
|
||||
}
|
||||
fprintf(stdout, "[process %ld] Point lookup thread finished\n");
|
||||
fprintf(stdout, "[process %ld] Point lookup thread finished\n", my_pid);
|
||||
});
|
||||
|
||||
uint64_t curr_key = 0;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "port/port.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/random.h"
|
||||
#include "util/rate_limiter.h"
|
||||
|
||||
|
||||
+29
-28
@@ -17,30 +17,31 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Utility function to copy a file up to a specified length
|
||||
Status CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size, bool use_fsync) {
|
||||
IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size,
|
||||
bool use_fsync) {
|
||||
const FileOptions soptions;
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
std::unique_ptr<SequentialFileReader> src_reader;
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
|
||||
{
|
||||
std::unique_ptr<FSSequentialFile> srcfile;
|
||||
s = fs->NewSequentialFile(source, soptions, &srcfile, nullptr);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = fs->NewSequentialFile(source, soptions, &srcfile, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
std::unique_ptr<FSWritableFile> destfile;
|
||||
s = fs->NewWritableFile(destination, soptions, &destfile, nullptr);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = fs->NewWritableFile(destination, soptions, &destfile, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
// default argument means copy everything
|
||||
s = fs->GetFileSize(source, IOOptions(), &size, nullptr);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = fs->GetFileSize(source, IOOptions(), &size, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
}
|
||||
src_reader.reset(new SequentialFileReader(std::move(srcfile), source));
|
||||
@@ -52,16 +53,16 @@ Status CopyFile(FileSystem* fs, const std::string& source,
|
||||
Slice slice;
|
||||
while (size > 0) {
|
||||
size_t bytes_to_read = std::min(sizeof(buffer), static_cast<size_t>(size));
|
||||
s = src_reader->Read(bytes_to_read, &slice, buffer);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = status_to_io_status(src_reader->Read(bytes_to_read, &slice, buffer));
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
if (slice.size() == 0) {
|
||||
return Status::Corruption("file too small");
|
||||
return IOStatus::Corruption("file too small");
|
||||
}
|
||||
s = dest_writer->Append(slice);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = dest_writer->Append(slice);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
size -= slice.size();
|
||||
}
|
||||
@@ -69,22 +70,22 @@ Status CopyFile(FileSystem* fs, const std::string& source,
|
||||
}
|
||||
|
||||
// Utility function to create a file with the provided contents
|
||||
Status CreateFile(FileSystem* fs, const std::string& destination,
|
||||
const std::string& contents, bool use_fsync) {
|
||||
IOStatus CreateFile(FileSystem* fs, const std::string& destination,
|
||||
const std::string& contents, bool use_fsync) {
|
||||
const EnvOptions soptions;
|
||||
Status s;
|
||||
IOStatus io_s;
|
||||
std::unique_ptr<WritableFileWriter> dest_writer;
|
||||
|
||||
std::unique_ptr<FSWritableFile> destfile;
|
||||
s = fs->NewWritableFile(destination, soptions, &destfile, nullptr);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = fs->NewWritableFile(destination, soptions, &destfile, nullptr);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
dest_writer.reset(
|
||||
new WritableFileWriter(std::move(destfile), destination, soptions));
|
||||
s = dest_writer->Append(Slice(contents));
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
io_s = dest_writer->Append(Slice(contents));
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
return dest_writer->Sync(use_fsync);
|
||||
}
|
||||
|
||||
+5
-5
@@ -16,12 +16,12 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// use_fsync maps to options.use_fsync, which determines the way that
|
||||
// the file is synced after copying.
|
||||
extern Status CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size,
|
||||
bool use_fsync);
|
||||
extern IOStatus CopyFile(FileSystem* fs, const std::string& source,
|
||||
const std::string& destination, uint64_t size,
|
||||
bool use_fsync);
|
||||
|
||||
extern Status CreateFile(FileSystem* fs, const std::string& destination,
|
||||
const std::string& contents, bool use_fsync);
|
||||
extern IOStatus CreateFile(FileSystem* fs, const std::string& destination,
|
||||
const std::string& contents, bool use_fsync);
|
||||
|
||||
extern Status DeleteDBFile(const ImmutableDBOptions* db_options,
|
||||
const std::string& fname,
|
||||
|
||||
@@ -27,6 +27,8 @@ Status RandomAccessFileReader::Read(const IOOptions& opts, uint64_t offset,
|
||||
AlignedBuf* aligned_buf,
|
||||
bool for_compaction) const {
|
||||
(void)aligned_buf;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("RandomAccessFileReader::Read", nullptr);
|
||||
Status s;
|
||||
uint64_t elapsed = 0;
|
||||
{
|
||||
|
||||
@@ -62,13 +62,13 @@ class RandomAccessFileReader {
|
||||
public:
|
||||
explicit RandomAccessFileReader(
|
||||
std::unique_ptr<FSRandomAccessFile>&& raf, const std::string& _file_name,
|
||||
Env* env = nullptr, Statistics* stats = nullptr, uint32_t hist_type = 0,
|
||||
Env* _env = nullptr, Statistics* stats = nullptr, uint32_t hist_type = 0,
|
||||
HistogramImpl* file_read_hist = nullptr,
|
||||
RateLimiter* rate_limiter = nullptr,
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners = {})
|
||||
: file_(std::move(raf)),
|
||||
file_name_(std::move(_file_name)),
|
||||
env_(env),
|
||||
env_(_env),
|
||||
stats_(stats),
|
||||
hist_type_(hist_type),
|
||||
file_read_hist_(file_read_hist),
|
||||
|
||||
@@ -15,27 +15,18 @@ namespace ROCKSDB_NAMESPACE {
|
||||
class RandomAccessFileReaderTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
test::ResetTmpDirForDirectIO();
|
||||
test::SetupSyncPointsToMockDirectIO();
|
||||
env_ = Env::Default();
|
||||
fs_ = FileSystem::Default();
|
||||
test_dir_ = test::PerThreadDBPath("random_access_file_reader_test");
|
||||
ASSERT_OK(fs_->CreateDir(test_dir_, IOOptions(), nullptr));
|
||||
alignment_ = GetAlignment();
|
||||
ComputeAndSetAlignment();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
EXPECT_OK(test::DestroyDir(env_, test_dir_));
|
||||
}
|
||||
|
||||
bool IsDirectIOSupported() {
|
||||
Write(".direct", "");
|
||||
FileOptions opt;
|
||||
opt.use_direct_reads = true;
|
||||
std::unique_ptr<FSRandomAccessFile> f;
|
||||
auto s = fs_->NewRandomAccessFile(Path(".direct"), opt, &f, nullptr);
|
||||
return s.ok();
|
||||
}
|
||||
|
||||
void Write(const std::string& fname, const std::string& content) {
|
||||
std::unique_ptr<FSWritableFile> f;
|
||||
ASSERT_OK(fs_->NewWritableFile(Path(fname), FileOptions(), &f, nullptr));
|
||||
@@ -72,23 +63,20 @@ class RandomAccessFileReaderTest : public testing::Test {
|
||||
return test_dir_ + "/" + fname;
|
||||
}
|
||||
|
||||
size_t GetAlignment() {
|
||||
void ComputeAndSetAlignment() {
|
||||
std::string f = "get_alignment";
|
||||
Write(f, "");
|
||||
std::unique_ptr<RandomAccessFileReader> r;
|
||||
Read(f, FileOptions(), &r);
|
||||
size_t alignment = r->file()->GetRequiredBufferAlignment();
|
||||
alignment_ = r->file()->GetRequiredBufferAlignment();
|
||||
EXPECT_OK(fs_->DeleteFile(Path(f), IOOptions(), nullptr));
|
||||
return alignment;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(RandomAccessFileReaderTest, ReadDirectIO) {
|
||||
if (!IsDirectIOSupported()) {
|
||||
printf("Direct IO is not supported, skip this test\n");
|
||||
return;
|
||||
}
|
||||
// Skip the following tests in lite mode since direct I/O is unsupported.
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(RandomAccessFileReaderTest, ReadDirectIO) {
|
||||
std::string fname = "read-direct-io";
|
||||
Random rand(0);
|
||||
std::string content;
|
||||
@@ -113,11 +101,6 @@ TEST_F(RandomAccessFileReaderTest, ReadDirectIO) {
|
||||
}
|
||||
|
||||
TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
|
||||
if (!IsDirectIOSupported()) {
|
||||
printf("Direct IO is not supported, skip this test\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Creates a file with 3 pages.
|
||||
std::string fname = "multi-read-direct-io";
|
||||
Random rand(0);
|
||||
@@ -266,6 +249,8 @@ TEST_F(RandomAccessFileReaderTest, MultiReadDirectIO) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -30,6 +30,9 @@ IOStatus WritableFileWriter::Append(const Slice& data) {
|
||||
TEST_KILL_RANDOM("WritableFileWriter::Append:0",
|
||||
rocksdb_kill_odds * REDUCE_ODDS2);
|
||||
|
||||
// Calculate the checksum of appended data
|
||||
UpdateFileChecksum(data);
|
||||
|
||||
{
|
||||
IOSTATS_TIMER_GUARD(prepare_write_nanos);
|
||||
TEST_SYNC_POINT("WritableFileWriter::Append:BeforePrepareWrite");
|
||||
@@ -89,7 +92,6 @@ IOStatus WritableFileWriter::Append(const Slice& data) {
|
||||
TEST_KILL_RANDOM("WritableFileWriter::Append:1", rocksdb_kill_odds);
|
||||
if (s.ok()) {
|
||||
filesize_ += data.size();
|
||||
CalculateFileChecksum(data);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -223,6 +225,7 @@ IOStatus WritableFileWriter::Flush() {
|
||||
|
||||
std::string WritableFileWriter::GetFileChecksum() {
|
||||
if (checksum_generator_ != nullptr) {
|
||||
assert(checksum_finalized_);
|
||||
return checksum_generator_->GetChecksum();
|
||||
} else {
|
||||
return kUnknownFileChecksum;
|
||||
@@ -344,7 +347,7 @@ IOStatus WritableFileWriter::WriteBuffered(const char* data, size_t size) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void WritableFileWriter::CalculateFileChecksum(const Slice& data) {
|
||||
void WritableFileWriter::UpdateFileChecksum(const Slice& data) {
|
||||
if (checksum_generator_ != nullptr) {
|
||||
checksum_generator_->Update(data.data(), data.size());
|
||||
}
|
||||
|
||||
@@ -46,11 +46,12 @@ class WritableFileWriter {
|
||||
for (auto& listener : listeners_) {
|
||||
listener->OnFileWriteFinish(info);
|
||||
}
|
||||
info.status.PermitUncheckedError();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
bool ShouldNotifyListeners() const { return !listeners_.empty(); }
|
||||
void CalculateFileChecksum(const Slice& data);
|
||||
void UpdateFileChecksum(const Slice& data);
|
||||
|
||||
std::unique_ptr<FSWritableFile> writable_file_;
|
||||
std::string file_name_;
|
||||
@@ -126,7 +127,10 @@ class WritableFileWriter {
|
||||
|
||||
WritableFileWriter& operator=(const WritableFileWriter&) = delete;
|
||||
|
||||
~WritableFileWriter() { Close(); }
|
||||
~WritableFileWriter() {
|
||||
auto s = Close();
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
std::string file_name() const { return file_name_; }
|
||||
|
||||
|
||||
+23
-23
@@ -238,8 +238,6 @@ class HdfsEnv : public Env {
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
static const Status notsup;
|
||||
|
||||
class HdfsEnv : public Env {
|
||||
|
||||
public:
|
||||
@@ -260,79 +258,81 @@ class HdfsEnv : public Env {
|
||||
const std::string& /*fname*/,
|
||||
std::unique_ptr<RandomAccessFile>* /*result*/,
|
||||
const EnvOptions& /*options*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status NewWritableFile(const std::string& /*fname*/,
|
||||
std::unique_ptr<WritableFile>* /*result*/,
|
||||
const EnvOptions& /*options*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status NewDirectory(const std::string& /*name*/,
|
||||
std::unique_ptr<Directory>* /*result*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status FileExists(const std::string& /*fname*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status GetChildren(const std::string& /*path*/,
|
||||
std::vector<std::string>* /*result*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& /*fname*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status CreateDir(const std::string& /*name*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status CreateDirIfMissing(const std::string& /*name*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status DeleteDir(const std::string& /*name*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status GetFileSize(const std::string& /*fname*/,
|
||||
uint64_t* /*size*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status GetFileModificationTime(const std::string& /*fname*/,
|
||||
uint64_t* /*time*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status RenameFile(const std::string& /*src*/,
|
||||
const std::string& /*target*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status LinkFile(const std::string& /*src*/,
|
||||
const std::string& /*target*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status LockFile(const std::string& /*fname*/,
|
||||
FileLock** /*lock*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status UnlockFile(FileLock* /*lock*/) override { return notsup; }
|
||||
virtual Status UnlockFile(FileLock* /*lock*/) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status NewLogger(const std::string& /*fname*/,
|
||||
std::shared_ptr<Logger>* /*result*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
Status IsDirectory(const std::string& /*path*/, bool* /*is_dir*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual void Schedule(void (* /*function*/)(void* arg), void* /*arg*/,
|
||||
@@ -352,7 +352,7 @@ class HdfsEnv : public Env {
|
||||
}
|
||||
|
||||
virtual Status GetTestDirectory(std::string* /*path*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual uint64_t NowMicros() override { return 0; }
|
||||
@@ -360,16 +360,16 @@ class HdfsEnv : public Env {
|
||||
virtual void SleepForMicroseconds(int /*micros*/) override {}
|
||||
|
||||
virtual Status GetHostName(char* /*name*/, uint64_t /*len*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* /*unix_time*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual Status GetAbsolutePath(const std::string& /*db_path*/,
|
||||
std::string* /*outputpath*/) override {
|
||||
return notsup;
|
||||
return Status::NotSupported();
|
||||
}
|
||||
|
||||
virtual void SetBackgroundThreads(int /*number*/,
|
||||
|
||||
@@ -495,9 +495,14 @@ extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_mergev_cf(
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_delete(rocksdb_writebatch_t*,
|
||||
const char* key,
|
||||
size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_singledelete(
|
||||
rocksdb_writebatch_t* b, const char* key, size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_delete_cf(
|
||||
rocksdb_writebatch_t*, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_singledelete_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_deletev(
|
||||
rocksdb_writebatch_t* b, int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes);
|
||||
@@ -583,9 +588,14 @@ extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_wi_mergev_cf(
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_wi_delete(rocksdb_writebatch_wi_t*,
|
||||
const char* key,
|
||||
size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_wi_singledelete(
|
||||
rocksdb_writebatch_wi_t*, const char* key, size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_wi_delete_cf(
|
||||
rocksdb_writebatch_wi_t*, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_wi_singledelete_cf(
|
||||
rocksdb_writebatch_wi_t*, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_writebatch_wi_deletev(
|
||||
rocksdb_writebatch_wi_t* b, int num_keys, const char* const* keys_list,
|
||||
const size_t* keys_list_sizes);
|
||||
@@ -771,6 +781,8 @@ extern ROCKSDB_LIBRARY_API void rocksdb_set_options_cf(
|
||||
|
||||
extern ROCKSDB_LIBRARY_API rocksdb_options_t* rocksdb_options_create();
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_destroy(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API rocksdb_options_t* rocksdb_options_create_copy(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_increase_parallelism(
|
||||
rocksdb_options_t* opt, int total_threads);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_optimize_for_point_lookup(
|
||||
@@ -783,12 +795,16 @@ rocksdb_options_optimize_universal_style_compaction(
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_allow_ingest_behind(rocksdb_options_t*,
|
||||
unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_allow_ingest_behind(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t*, rocksdb_compactionfilter_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compaction_filter_factory(
|
||||
rocksdb_options_t*, rocksdb_compactionfilterfactory_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_compaction_readahead_size(
|
||||
rocksdb_options_t*, size_t);
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_options_get_compaction_readahead_size(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_comparator(
|
||||
rocksdb_options_t*, rocksdb_comparator_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_merge_operator(
|
||||
@@ -799,13 +815,21 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compression_per_level(
|
||||
rocksdb_options_t* opt, int* level_values, size_t num_levels);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_create_if_missing(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_create_missing_column_families(rocksdb_options_t*,
|
||||
unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_create_missing_column_families(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_error_if_exists(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_error_if_exists(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_paranoid_checks(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_paranoid_checks(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_db_paths(rocksdb_options_t*,
|
||||
const rocksdb_dbpath_t** path_values,
|
||||
size_t num_paths);
|
||||
@@ -815,41 +839,80 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_set_info_log(rocksdb_options_t*,
|
||||
rocksdb_logger_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_info_log_level(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_info_log_level(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_write_buffer_size(
|
||||
rocksdb_options_t*, size_t);
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_options_get_write_buffer_size(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_db_write_buffer_size(
|
||||
rocksdb_options_t*, size_t);
|
||||
extern ROCKSDB_LIBRARY_API size_t
|
||||
rocksdb_options_get_db_write_buffer_size(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_open_files(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_max_open_files(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_file_opening_threads(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_max_file_opening_threads(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_total_wal_size(
|
||||
rocksdb_options_t* opt, uint64_t n);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_max_total_wal_size(rocksdb_options_t* opt);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compression_options(
|
||||
rocksdb_options_t*, int, int, int, int);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_compression_options_zstd_max_train_bytes(rocksdb_options_t*,
|
||||
int);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_bottommost_compression_options(rocksdb_options_t*, int, int,
|
||||
int, int, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_bottommost_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t*, int, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_prefix_extractor(
|
||||
rocksdb_options_t*, rocksdb_slicetransform_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_num_levels(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_num_levels(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_level0_stop_writes_trigger(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_mem_compaction_level(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_target_file_size_base(
|
||||
rocksdb_options_t*, uint64_t);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_target_file_size_base(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_target_file_size_multiplier(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_target_file_size_multiplier(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_bytes_for_level_base(
|
||||
rocksdb_options_t*, uint64_t);
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_options_get_max_bytes_for_level_base(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(rocksdb_options_t*,
|
||||
unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(rocksdb_options_t*, double);
|
||||
extern ROCKSDB_LIBRARY_API double
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier_additional(
|
||||
rocksdb_options_t*, int* level_values, size_t num_levels);
|
||||
@@ -858,9 +921,14 @@ extern ROCKSDB_LIBRARY_API void rocksdb_options_enable_statistics(
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt,
|
||||
unsigned char val);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_skip_stats_update_on_db_open(rocksdb_options_t* opt);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt, unsigned char val);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt);
|
||||
|
||||
/* returns a pointer to a malloc()-ed, null terminated string */
|
||||
extern ROCKSDB_LIBRARY_API char* rocksdb_options_statistics_get_string(
|
||||
@@ -868,20 +936,34 @@ extern ROCKSDB_LIBRARY_API char* rocksdb_options_statistics_get_string(
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_write_buffer_number(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int rocksdb_options_get_max_write_buffer_number(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(rocksdb_options_t*,
|
||||
int);
|
||||
extern ROCKSDB_LIBRARY_API int
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(rocksdb_options_t*,
|
||||
int64_t);
|
||||
extern ROCKSDB_LIBRARY_API int64_t
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_enable_pipelined_write(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char
|
||||
rocksdb_options_get_enable_pipelined_write(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_unordered_write(
|
||||
rocksdb_options_t*, unsigned char);
|
||||
extern ROCKSDB_LIBRARY_API unsigned char rocksdb_options_get_unordered_write(
|
||||
rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_subcompactions(
|
||||
rocksdb_options_t*, uint32_t);
|
||||
extern ROCKSDB_LIBRARY_API uint32_t
|
||||
rocksdb_options_get_max_subcompactions(rocksdb_options_t*);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_background_jobs(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_max_background_compactions(
|
||||
@@ -1029,6 +1111,8 @@ enum {
|
||||
};
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_compression(
|
||||
rocksdb_options_t*, int);
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_options_set_bottommost_compression(
|
||||
rocksdb_options_t*, int);
|
||||
|
||||
enum {
|
||||
rocksdb_level_compaction = 0,
|
||||
@@ -1803,6 +1887,9 @@ extern ROCKSDB_LIBRARY_API void
|
||||
rocksdb_options_set_memtable_whole_key_filtering(rocksdb_options_t*,
|
||||
unsigned char);
|
||||
|
||||
extern ROCKSDB_LIBRARY_API void rocksdb_cancel_all_background_work(
|
||||
rocksdb_t* db, unsigned char wait);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
@@ -1004,32 +1004,33 @@ class DB {
|
||||
};
|
||||
|
||||
// For each i in [0,n-1], store in "sizes[i]", the approximate
|
||||
// file system space used by keys in "[range[i].start .. range[i].limit)".
|
||||
// file system space used by keys in "[range[i].start .. range[i].limit)"
|
||||
// in a single column family.
|
||||
//
|
||||
// Note that the returned sizes measure file system space usage, so
|
||||
// if the user data compresses by a factor of ten, the returned
|
||||
// sizes will be one-tenth the size of the corresponding user data size.
|
||||
virtual Status GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
const Range* ranges, int n,
|
||||
uint64_t* sizes) = 0;
|
||||
|
||||
// Simpler versions of the GetApproximateSizes() method above.
|
||||
// The include_flags argumenbt must of type DB::SizeApproximationFlags
|
||||
// and can not be NONE.
|
||||
virtual void GetApproximateSizes(ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n, uint64_t* sizes,
|
||||
const Range* ranges, int n, uint64_t* sizes,
|
||||
uint8_t include_flags = INCLUDE_FILES) {
|
||||
SizeApproximationOptions options;
|
||||
options.include_memtabtles =
|
||||
(include_flags & SizeApproximationFlags::INCLUDE_MEMTABLES) != 0;
|
||||
options.include_files =
|
||||
(include_flags & SizeApproximationFlags::INCLUDE_FILES) != 0;
|
||||
GetApproximateSizes(options, column_family, range, n, sizes);
|
||||
GetApproximateSizes(options, column_family, ranges, n, sizes);
|
||||
}
|
||||
virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes,
|
||||
virtual void GetApproximateSizes(const Range* ranges, int n, uint64_t* sizes,
|
||||
uint8_t include_flags = INCLUDE_FILES) {
|
||||
GetApproximateSizes(DefaultColumnFamily(), range, n, sizes, include_flags);
|
||||
GetApproximateSizes(DefaultColumnFamily(), ranges, n, sizes, include_flags);
|
||||
}
|
||||
|
||||
// The method is similar to GetApproximateSizes, except it
|
||||
|
||||
@@ -313,6 +313,8 @@ class Env {
|
||||
virtual Status CreateDirIfMissing(const std::string& dirname) = 0;
|
||||
|
||||
// Delete the specified directory.
|
||||
// Many implementations of this function will only delete a directory if it is
|
||||
// empty.
|
||||
virtual Status DeleteDir(const std::string& dirname) = 0;
|
||||
|
||||
// Store the size of fname in *file_size.
|
||||
|
||||
@@ -24,6 +24,10 @@ struct FileChecksumGenContext {
|
||||
|
||||
// FileChecksumGenerator is the class to generates the checksum value
|
||||
// for each file when the file is written to the file system.
|
||||
// Implementations may assume that
|
||||
// * Finalize is called at most once during the life of the object
|
||||
// * All calls to Update come before Finalize
|
||||
// * All calls to GetChecksum come after Finalize
|
||||
class FileChecksumGenerator {
|
||||
public:
|
||||
virtual ~FileChecksumGenerator() {}
|
||||
@@ -36,7 +40,8 @@ class FileChecksumGenerator {
|
||||
// Generate the final results if no further new data will be updated.
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
// Get the checksum
|
||||
// Get the checksum. The result should not be the empty string and may
|
||||
// include arbitrary bytes, including non-printable characters.
|
||||
virtual std::string GetChecksum() const = 0;
|
||||
|
||||
// Returns a name that identifies the current file checksum function.
|
||||
@@ -97,9 +102,13 @@ class FileChecksumList {
|
||||
// Create a new file checksum list.
|
||||
extern FileChecksumList* NewFileChecksumList();
|
||||
|
||||
// Return a shared_ptr of the builtin Crc32 based file checksum generatory
|
||||
// Return a shared_ptr of the builtin Crc32c based file checksum generatory
|
||||
// factory object, which can be shared to create the Crc32c based checksum
|
||||
// generator object.
|
||||
// Note: this implementation is compatible with many other crc32c checksum
|
||||
// implementations and uses big-endian encoding of the result, unlike most
|
||||
// other crc32c checksums in RocksDB, which alter the result with
|
||||
// crc32c::Mask and use little-endian encoding.
|
||||
extern std::shared_ptr<FileChecksumGenFactory>
|
||||
GetFileChecksumGenCrc32cFactory();
|
||||
|
||||
|
||||
@@ -170,6 +170,9 @@ inline IOStatus::IOStatus(Code _code, SubCode _subcode, const Slice& msg,
|
||||
}
|
||||
|
||||
inline IOStatus::IOStatus(const IOStatus& s) : Status(s.code_, s.subcode_) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
retryable_ = s.retryable_;
|
||||
data_loss_ = s.data_loss_;
|
||||
scope_ = s.scope_;
|
||||
@@ -179,6 +182,10 @@ inline IOStatus& IOStatus::operator=(const IOStatus& s) {
|
||||
// The following condition catches both aliasing (when this == &s),
|
||||
// and the common case where both s and *this are ok.
|
||||
if (this != &s) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
checked_ = false;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
code_ = s.code_;
|
||||
subcode_ = s.subcode_;
|
||||
retryable_ = s.retryable_;
|
||||
@@ -204,6 +211,10 @@ inline IOStatus& IOStatus::operator=(IOStatus&& s)
|
||||
#endif
|
||||
{
|
||||
if (this != &s) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
checked_ = false;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
code_ = std::move(s.code_);
|
||||
s.code_ = kOk;
|
||||
subcode_ = std::move(s.subcode_);
|
||||
@@ -211,7 +222,7 @@ inline IOStatus& IOStatus::operator=(IOStatus&& s)
|
||||
retryable_ = s.retryable_;
|
||||
data_loss_ = s.data_loss_;
|
||||
scope_ = s.scope_;
|
||||
scope_ = kIOErrorScopeFileSystem;
|
||||
s.scope_ = kIOErrorScopeFileSystem;
|
||||
delete[] state_;
|
||||
state_ = nullptr;
|
||||
std::swap(state_, s.state_);
|
||||
@@ -220,10 +231,18 @@ inline IOStatus& IOStatus::operator=(IOStatus&& s)
|
||||
}
|
||||
|
||||
inline bool IOStatus::operator==(const IOStatus& rhs) const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
rhs.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code_ == rhs.code_);
|
||||
}
|
||||
|
||||
inline bool IOStatus::operator!=(const IOStatus& rhs) const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
rhs.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
|
||||
@@ -1356,6 +1356,13 @@ struct ReadOptions {
|
||||
// processing a batch
|
||||
std::chrono::microseconds deadline;
|
||||
|
||||
// It limits the maximum cumulative value size of the keys in batch while
|
||||
// reading through MultiGet. Once the cumulative value size exceeds this
|
||||
// soft limit then all the remaining keys are returned with status Aborted.
|
||||
//
|
||||
// Default: std::numeric_limits<uint64_t>::max()
|
||||
uint64_t value_size_soft_limit;
|
||||
|
||||
ReadOptions();
|
||||
ReadOptions(bool cksum, bool cache);
|
||||
};
|
||||
@@ -1564,6 +1571,11 @@ struct IngestExternalFileOptions {
|
||||
// Using a large readahead size (> 2MB) can typically improve the performance
|
||||
// of forward iteration on spinning disks.
|
||||
size_t verify_checksums_readahead_size = 0;
|
||||
// Set to true if you are sure although the key ranges may overlap,
|
||||
// the memtable and the file being ingested
|
||||
// do not have the same keys, or not care about that.
|
||||
// The memtable flushing may block for hundreds of milliseconds.
|
||||
bool skip_memtable_flush = false;
|
||||
};
|
||||
|
||||
enum TraceFilterType : uint64_t {
|
||||
|
||||
@@ -30,10 +30,10 @@ struct PerfContextByLevel {
|
||||
|
||||
// total number of user key returned (only include keys that are found, does
|
||||
// not include keys that are deleted or merged without a final put
|
||||
uint64_t user_key_return_count;
|
||||
uint64_t user_key_return_count = 0;
|
||||
|
||||
// total nanos spent on reading data from SST files
|
||||
uint64_t get_from_table_nanos;
|
||||
uint64_t get_from_table_nanos = 0;
|
||||
|
||||
uint64_t block_cache_hit_count = 0; // total number of block cache hits
|
||||
uint64_t block_cache_miss_count = 0; // total number of block cache misses
|
||||
|
||||
@@ -34,6 +34,8 @@ struct ExternalSstFileInfo {
|
||||
largest_key(""),
|
||||
smallest_range_del_key(""),
|
||||
largest_range_del_key(""),
|
||||
file_checksum(""),
|
||||
file_checksum_func_name(""),
|
||||
sequence_number(0),
|
||||
file_size(0),
|
||||
num_entries(0),
|
||||
@@ -50,6 +52,8 @@ struct ExternalSstFileInfo {
|
||||
largest_key(_largest_key),
|
||||
smallest_range_del_key(""),
|
||||
largest_range_del_key(""),
|
||||
file_checksum(""),
|
||||
file_checksum_func_name(""),
|
||||
sequence_number(_sequence_number),
|
||||
file_size(_file_size),
|
||||
num_entries(_num_entries),
|
||||
@@ -62,6 +66,8 @@ struct ExternalSstFileInfo {
|
||||
std::string
|
||||
smallest_range_del_key; // smallest range deletion user key in file
|
||||
std::string largest_range_del_key; // largest range deletion user key in file
|
||||
std::string file_checksum; // sst file checksum;
|
||||
std::string file_checksum_func_name; // The name of file checksum function
|
||||
SequenceNumber sequence_number; // sequence number of all keys in file
|
||||
uint64_t file_size; // file size in bytes
|
||||
uint64_t num_entries; // number of entries in file
|
||||
|
||||
+204
-24
@@ -16,7 +16,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
#include "port/stack_trace.h"
|
||||
#endif
|
||||
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -25,7 +35,16 @@ class Status {
|
||||
public:
|
||||
// Create a success status.
|
||||
Status() : code_(kOk), subcode_(kNone), sev_(kNoError), state_(nullptr) {}
|
||||
~Status() { delete[] state_; }
|
||||
~Status() {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
if (!checked_) {
|
||||
fprintf(stderr, "Failed to check Status\n");
|
||||
port::PrintStack();
|
||||
abort();
|
||||
}
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
delete[] state_;
|
||||
}
|
||||
|
||||
// Copy the specified status.
|
||||
Status(const Status& s);
|
||||
@@ -43,6 +62,15 @@ class Status {
|
||||
bool operator==(const Status& rhs) const;
|
||||
bool operator!=(const Status& rhs) const;
|
||||
|
||||
// In case of intentionally swallowing an error, user must explicitly call
|
||||
// this function. That way we are easily able to search the code to find where
|
||||
// error swallowing occurs.
|
||||
void PermitUncheckedError() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
}
|
||||
|
||||
enum Code : unsigned char {
|
||||
kOk = 0,
|
||||
kNotFound = 1,
|
||||
@@ -63,7 +91,12 @@ class Status {
|
||||
kMaxCode
|
||||
};
|
||||
|
||||
Code code() const { return code_; }
|
||||
Code code() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code_;
|
||||
}
|
||||
|
||||
enum SubCode : unsigned char {
|
||||
kNone = 0,
|
||||
@@ -83,7 +116,12 @@ class Status {
|
||||
kMaxSubCode
|
||||
};
|
||||
|
||||
SubCode subcode() const { return subcode_; }
|
||||
SubCode subcode() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return subcode_;
|
||||
}
|
||||
|
||||
enum Severity : unsigned char {
|
||||
kNoError = 0,
|
||||
@@ -95,10 +133,20 @@ class Status {
|
||||
};
|
||||
|
||||
Status(const Status& s, Severity sev);
|
||||
Severity severity() const { return sev_; }
|
||||
Severity severity() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return sev_;
|
||||
}
|
||||
|
||||
// Returns a C style string indicating the message of the Status
|
||||
const char* getState() const { return state_; }
|
||||
const char* getState() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return state_;
|
||||
}
|
||||
|
||||
// Return a success status.
|
||||
static Status OK() { return Status(); }
|
||||
@@ -233,65 +281,156 @@ class Status {
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates success.
|
||||
bool ok() const { return code() == kOk; }
|
||||
bool ok() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kOk;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates success *with* something
|
||||
// overwritten
|
||||
bool IsOkOverwritten() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kOk && subcode() == kOverwritten;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates a NotFound error.
|
||||
bool IsNotFound() const { return code() == kNotFound; }
|
||||
bool IsNotFound() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kNotFound;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates a Corruption error.
|
||||
bool IsCorruption() const { return code() == kCorruption; }
|
||||
bool IsCorruption() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kCorruption;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates a NotSupported error.
|
||||
bool IsNotSupported() const { return code() == kNotSupported; }
|
||||
bool IsNotSupported() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kNotSupported;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates an InvalidArgument error.
|
||||
bool IsInvalidArgument() const { return code() == kInvalidArgument; }
|
||||
bool IsInvalidArgument() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kInvalidArgument;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates an IOError.
|
||||
bool IsIOError() const { return code() == kIOError; }
|
||||
bool IsIOError() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kIOError;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates an MergeInProgress.
|
||||
bool IsMergeInProgress() const { return code() == kMergeInProgress; }
|
||||
bool IsMergeInProgress() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kMergeInProgress;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates Incomplete
|
||||
bool IsIncomplete() const { return code() == kIncomplete; }
|
||||
bool IsIncomplete() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kIncomplete;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates Shutdown In progress
|
||||
bool IsShutdownInProgress() const { return code() == kShutdownInProgress; }
|
||||
bool IsShutdownInProgress() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kShutdownInProgress;
|
||||
}
|
||||
|
||||
bool IsTimedOut() const { return code() == kTimedOut; }
|
||||
bool IsTimedOut() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kTimedOut;
|
||||
}
|
||||
|
||||
bool IsAborted() const { return code() == kAborted; }
|
||||
bool IsAborted() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kAborted;
|
||||
}
|
||||
|
||||
bool IsLockLimit() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kAborted && subcode() == kLockLimit;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates that a resource is Busy and
|
||||
// temporarily could not be acquired.
|
||||
bool IsBusy() const { return code() == kBusy; }
|
||||
bool IsBusy() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kBusy;
|
||||
}
|
||||
|
||||
bool IsDeadlock() const { return code() == kBusy && subcode() == kDeadlock; }
|
||||
bool IsDeadlock() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kBusy && subcode() == kDeadlock;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicated that the operation has Expired.
|
||||
bool IsExpired() const { return code() == kExpired; }
|
||||
bool IsExpired() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kExpired;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates a TryAgain error.
|
||||
// This usually means that the operation failed, but may succeed if
|
||||
// re-attempted.
|
||||
bool IsTryAgain() const { return code() == kTryAgain; }
|
||||
bool IsTryAgain() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kTryAgain;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates the proposed compaction is too large
|
||||
bool IsCompactionTooLarge() const { return code() == kCompactionTooLarge; }
|
||||
bool IsCompactionTooLarge() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kCompactionTooLarge;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates Column Family Dropped
|
||||
bool IsColumnFamilyDropped() const { return code() == kColumnFamilyDropped; }
|
||||
bool IsColumnFamilyDropped() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return code() == kColumnFamilyDropped;
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates a NoSpace error
|
||||
// This is caused by an I/O error returning the specific "out of space"
|
||||
@@ -299,6 +438,9 @@ class Status {
|
||||
// with a specific subcode, enabling users to take the appropriate action
|
||||
// if needed
|
||||
bool IsNoSpace() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code() == kIOError) && (subcode() == kNoSpace);
|
||||
}
|
||||
|
||||
@@ -306,6 +448,9 @@ class Status {
|
||||
// cases where we limit the memory used in certain operations (eg. the size
|
||||
// of a write batch) in order to avoid out of memory exceptions.
|
||||
bool IsMemoryLimit() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code() == kAborted) && (subcode() == kMemoryLimit);
|
||||
}
|
||||
|
||||
@@ -314,17 +459,26 @@ class Status {
|
||||
// directory" error condition. A PathNotFound error is an I/O error with
|
||||
// a specific subcode, enabling users to take appropriate action if necessary
|
||||
bool IsPathNotFound() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code() == kIOError) && (subcode() == kPathNotFound);
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates manual compaction paused. This
|
||||
// is caused by a call to PauseManualCompaction
|
||||
bool IsManualCompactionPaused() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code() == kIncomplete) && (subcode() == kManualCompactionPaused);
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates a TxnNotPrepared error.
|
||||
bool IsTxnNotPrepared() const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code() == kInvalidArgument) && (subcode() == kTxnNotPrepared);
|
||||
}
|
||||
|
||||
@@ -342,6 +496,9 @@ class Status {
|
||||
SubCode subcode_;
|
||||
Severity sev_;
|
||||
const char* state_;
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
mutable bool checked_ = false;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
|
||||
explicit Status(Code _code, SubCode _subcode = kNone)
|
||||
: code_(_code), subcode_(_subcode), sev_(kNoError), state_(nullptr) {}
|
||||
@@ -355,16 +512,24 @@ class Status {
|
||||
|
||||
inline Status::Status(const Status& s)
|
||||
: code_(s.code_), subcode_(s.subcode_), sev_(s.sev_) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_);
|
||||
}
|
||||
inline Status::Status(const Status& s, Severity sev)
|
||||
: code_(s.code_), subcode_(s.subcode_), sev_(sev) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
state_ = (s.state_ == nullptr) ? nullptr : CopyState(s.state_);
|
||||
}
|
||||
inline Status& Status::operator=(const Status& s) {
|
||||
// The following condition catches both aliasing (when this == &s),
|
||||
// and the common case where both s and *this are ok.
|
||||
if (this != &s) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
checked_ = false;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
code_ = s.code_;
|
||||
subcode_ = s.subcode_;
|
||||
sev_ = s.sev_;
|
||||
@@ -379,6 +544,9 @@ inline Status::Status(Status&& s)
|
||||
noexcept
|
||||
#endif
|
||||
: Status() {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
*this = std::move(s);
|
||||
}
|
||||
|
||||
@@ -388,6 +556,10 @@ inline Status& Status::operator=(Status&& s)
|
||||
#endif
|
||||
{
|
||||
if (this != &s) {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
s.checked_ = true;
|
||||
checked_ = false;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
code_ = std::move(s.code_);
|
||||
s.code_ = kOk;
|
||||
subcode_ = std::move(s.subcode_);
|
||||
@@ -402,10 +574,18 @@ inline Status& Status::operator=(Status&& s)
|
||||
}
|
||||
|
||||
inline bool Status::operator==(const Status& rhs) const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
rhs.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return (code_ == rhs.code_);
|
||||
}
|
||||
|
||||
inline bool Status::operator!=(const Status& rhs) const {
|
||||
#ifdef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
checked_ = true;
|
||||
rhs.checked_ = true;
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -40,11 +41,8 @@ class TableBuilder;
|
||||
class TableFactory;
|
||||
class TableReader;
|
||||
class WritableFileWriter;
|
||||
struct ColumnFamilyOptions;
|
||||
struct ConfigOptions;
|
||||
struct DBOptions;
|
||||
struct EnvOptions;
|
||||
struct Options;
|
||||
|
||||
enum ChecksumType : char {
|
||||
kNoChecksum = 0x0,
|
||||
|
||||
@@ -89,7 +89,7 @@ struct BackupableDBOptions {
|
||||
|
||||
// Only used if share_table_files is set to true. If true, will consider that
|
||||
// backups can come from different databases, hence a sst is not uniquely
|
||||
// identifed by its name, but by the triple (file name, crc32, file length)
|
||||
// identifed by its name, but by the triple (file name, crc32c, file length)
|
||||
// Default: false
|
||||
// Note: this is an experimental option, and you'll need to set it manually
|
||||
// *turn it on only if you know what you're doing*
|
||||
|
||||
@@ -59,6 +59,7 @@ class LDBCommand {
|
||||
static const std::string ARG_FILE_SIZE;
|
||||
static const std::string ARG_CREATE_IF_MISSING;
|
||||
static const std::string ARG_NO_VALUE;
|
||||
static const std::string ARG_DISABLE_CONSISTENCY_CHECKS;
|
||||
|
||||
struct ParsedParams {
|
||||
std::string cmd;
|
||||
@@ -163,6 +164,9 @@ class LDBCommand {
|
||||
// If true, try to construct options from DB's option files.
|
||||
bool try_load_options_;
|
||||
|
||||
// The value passed to options.force_consistency_checks.
|
||||
bool force_consistency_checks_;
|
||||
|
||||
bool create_if_missing_;
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// A factory of a table property collector that marks a SST
|
||||
// file as need-compaction when it observe at least "D" deletion
|
||||
// entries in any "N" consecutive entires.
|
||||
// entries in any "N" consecutive entires or the ratio of tombstone
|
||||
// entries in the whole file >= the specified deletion ratio.
|
||||
class CompactOnDeletionCollectorFactory
|
||||
: public TablePropertiesCollectorFactory {
|
||||
public:
|
||||
@@ -34,6 +35,13 @@ class CompactOnDeletionCollectorFactory
|
||||
deletion_trigger_.store(deletion_trigger);
|
||||
}
|
||||
|
||||
// Change deletion ratio.
|
||||
// @param deletion_ratio, if <= 0 or > 1, disable triggering compaction
|
||||
// based on deletion ratio.
|
||||
void SetDeletionRatio(double deletion_ratio) {
|
||||
deletion_ratio_.store(deletion_ratio);
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "CompactOnDeletionCollector";
|
||||
}
|
||||
@@ -43,34 +51,45 @@ class CompactOnDeletionCollectorFactory
|
||||
private:
|
||||
friend std::shared_ptr<CompactOnDeletionCollectorFactory>
|
||||
NewCompactOnDeletionCollectorFactory(size_t sliding_window_size,
|
||||
size_t deletion_trigger);
|
||||
size_t deletion_trigger,
|
||||
double deletion_ratio);
|
||||
// A factory of a table property collector that marks a SST
|
||||
// file as need-compaction when it observe at least "D" deletion
|
||||
// entries in any "N" consecutive entires.
|
||||
// entries in any "N" consecutive entires, or the ratio of tombstone
|
||||
// entries >= deletion_ratio.
|
||||
//
|
||||
// @param sliding_window_size "N"
|
||||
// @param deletion_trigger "D"
|
||||
// @param deletion_ratio, if <= 0 or > 1, disable triggering compaction
|
||||
// based on deletion ratio.
|
||||
CompactOnDeletionCollectorFactory(size_t sliding_window_size,
|
||||
size_t deletion_trigger)
|
||||
size_t deletion_trigger,
|
||||
double deletion_ratio)
|
||||
: sliding_window_size_(sliding_window_size),
|
||||
deletion_trigger_(deletion_trigger) {}
|
||||
deletion_trigger_(deletion_trigger),
|
||||
deletion_ratio_(deletion_ratio) {}
|
||||
|
||||
std::atomic<size_t> sliding_window_size_;
|
||||
std::atomic<size_t> deletion_trigger_;
|
||||
std::atomic<double> deletion_ratio_;
|
||||
};
|
||||
|
||||
// Creates a factory of a table property collector that marks a SST
|
||||
// file as need-compaction when it observe at least "D" deletion
|
||||
// entries in any "N" consecutive entires.
|
||||
// entries in any "N" consecutive entires, or the ratio of tombstone
|
||||
// entries >= deletion_ratio.
|
||||
//
|
||||
// @param sliding_window_size "N". Note that this number will be
|
||||
// round up to the smallest multiple of 128 that is no less
|
||||
// than the specified size.
|
||||
// @param deletion_trigger "D". Note that even when "N" is changed,
|
||||
// the specified number for "D" will not be changed.
|
||||
// @param deletion_ratio, if <= 0 or > 1, disable triggering compaction
|
||||
// based on deletion ratio. Disabled by default.
|
||||
extern std::shared_ptr<CompactOnDeletionCollectorFactory>
|
||||
NewCompactOnDeletionCollectorFactory(size_t sliding_window_size,
|
||||
size_t deletion_trigger);
|
||||
size_t deletion_trigger,
|
||||
double deletion_ratio = 0);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
@@ -533,20 +533,24 @@ jlong Java_org_rocksdb_WriteBatchWithIndex_iterator1(JNIEnv* /*env*/,
|
||||
/*
|
||||
* Class: org_rocksdb_WriteBatchWithIndex
|
||||
* Method: iteratorWithBase
|
||||
* Signature: (JJJ)J
|
||||
* Signature: (JJJJ)J
|
||||
*/
|
||||
jlong Java_org_rocksdb_WriteBatchWithIndex_iteratorWithBase(JNIEnv* /*env*/,
|
||||
jobject /*jobj*/,
|
||||
jlong jwbwi_handle,
|
||||
jlong jcf_handle,
|
||||
jlong jbi_handle) {
|
||||
jlong Java_org_rocksdb_WriteBatchWithIndex_iteratorWithBase(
|
||||
JNIEnv*, jobject, jlong jwbwi_handle, jlong jcf_handle,
|
||||
jlong jbase_iterator_handle, jlong jread_opts_handle) {
|
||||
auto* wbwi =
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::WriteBatchWithIndex*>(jwbwi_handle);
|
||||
auto* cf_handle =
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::ColumnFamilyHandle*>(jcf_handle);
|
||||
auto* base_iterator =
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::Iterator*>(jbi_handle);
|
||||
auto* iterator = wbwi->NewIteratorWithBase(cf_handle, base_iterator);
|
||||
reinterpret_cast<ROCKSDB_NAMESPACE::Iterator*>(jbase_iterator_handle);
|
||||
ROCKSDB_NAMESPACE::ReadOptions* read_opts =
|
||||
jread_opts_handle == 0
|
||||
? nullptr
|
||||
: reinterpret_cast<ROCKSDB_NAMESPACE::ReadOptions*>(
|
||||
jread_opts_handle);
|
||||
auto* iterator =
|
||||
wbwi->NewIteratorWithBase(cf_handle, base_iterator, read_opts);
|
||||
return reinterpret_cast<jlong>(iterator);
|
||||
}
|
||||
|
||||
|
||||
@@ -131,11 +131,36 @@ public class WriteBatchWithIndex extends AbstractWriteBatch {
|
||||
public RocksIterator newIteratorWithBase(
|
||||
final ColumnFamilyHandle columnFamilyHandle,
|
||||
final RocksIterator baseIterator) {
|
||||
RocksIterator iterator = new RocksIterator(baseIterator.parent_,
|
||||
iteratorWithBase(
|
||||
nativeHandle_, columnFamilyHandle.nativeHandle_, baseIterator.nativeHandle_));
|
||||
return newIteratorWithBase(columnFamilyHandle, baseIterator, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Read-Your-Own-Writes like functionality by
|
||||
* creating a new Iterator that will use {@link org.rocksdb.WBWIRocksIterator}
|
||||
* as a delta and baseIterator as a base
|
||||
*
|
||||
* Updating write batch with the current key of the iterator is not safe.
|
||||
* We strongly recommand users not to do it. It will invalidate the current
|
||||
* key() and value() of the iterator. This invalidation happens even before
|
||||
* the write batch update finishes. The state may recover after Next() is
|
||||
* called.
|
||||
*
|
||||
* @param columnFamilyHandle The column family to iterate over
|
||||
* @param baseIterator The base iterator,
|
||||
* e.g. {@link org.rocksdb.RocksDB#newIterator()}
|
||||
* @param readOptions the read options, or null
|
||||
* @return An iterator which shows a view comprised of both the database
|
||||
* point-in-time from baseIterator and modifications made in this write batch.
|
||||
*/
|
||||
public RocksIterator newIteratorWithBase(final ColumnFamilyHandle columnFamilyHandle,
|
||||
final RocksIterator baseIterator, /* @Nullable */ final ReadOptions readOptions) {
|
||||
final RocksIterator iterator = new RocksIterator(baseIterator.parent_,
|
||||
iteratorWithBase(nativeHandle_, columnFamilyHandle.nativeHandle_,
|
||||
baseIterator.nativeHandle_, readOptions == null ? 0 : readOptions.nativeHandle_));
|
||||
|
||||
// when the iterator is deleted it will also delete the baseIterator
|
||||
baseIterator.disOwnNativeHandle();
|
||||
|
||||
return iterator;
|
||||
}
|
||||
|
||||
@@ -151,7 +176,25 @@ public class WriteBatchWithIndex extends AbstractWriteBatch {
|
||||
* point-in-timefrom baseIterator and modifications made in this write batch.
|
||||
*/
|
||||
public RocksIterator newIteratorWithBase(final RocksIterator baseIterator) {
|
||||
return newIteratorWithBase(baseIterator.parent_.getDefaultColumnFamily(), baseIterator);
|
||||
return newIteratorWithBase(baseIterator.parent_.getDefaultColumnFamily(), baseIterator, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides Read-Your-Own-Writes like functionality by
|
||||
* creating a new Iterator that will use {@link org.rocksdb.WBWIRocksIterator}
|
||||
* as a delta and baseIterator as a base. Operates on the default column
|
||||
* family.
|
||||
*
|
||||
* @param baseIterator The base iterator,
|
||||
* e.g. {@link org.rocksdb.RocksDB#newIterator()}
|
||||
* @param readOptions the read options, or null
|
||||
* @return An iterator which shows a view comprised of both the database
|
||||
* point-in-timefrom baseIterator and modifications made in this write batch.
|
||||
*/
|
||||
public RocksIterator newIteratorWithBase(final RocksIterator baseIterator,
|
||||
/* @Nullable */ final ReadOptions readOptions) {
|
||||
return newIteratorWithBase(
|
||||
baseIterator.parent_.getDefaultColumnFamily(), baseIterator, readOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +243,7 @@ public class WriteBatchWithIndex extends AbstractWriteBatch {
|
||||
* the results using the DB's merge operator (if the batch contains any
|
||||
* merge requests).
|
||||
*
|
||||
* Setting {@link ReadOptions#setSnapshot(long, long)} will affect what is
|
||||
* Setting {@link ReadOptions#setSnapshot(Snapshot)} will affect what is
|
||||
* read from the DB but will NOT change which keys are read from the batch
|
||||
* (the keys in this batch do not yet belong to any snapshot and will be
|
||||
* fetched regardless).
|
||||
@@ -230,7 +273,7 @@ public class WriteBatchWithIndex extends AbstractWriteBatch {
|
||||
* the results using the DB's merge operator (if the batch contains any
|
||||
* merge requests).
|
||||
*
|
||||
* Setting {@link ReadOptions#setSnapshot(long, long)} will affect what is
|
||||
* Setting {@link ReadOptions#setSnapshot(Snapshot)} will affect what is
|
||||
* read from the DB but will NOT change which keys are read from the batch
|
||||
* (the keys in this batch do not yet belong to any snapshot and will be
|
||||
* fetched regardless).
|
||||
@@ -303,8 +346,8 @@ public class WriteBatchWithIndex extends AbstractWriteBatch {
|
||||
final boolean overwriteKey);
|
||||
private native long iterator0(final long handle);
|
||||
private native long iterator1(final long handle, final long cfHandle);
|
||||
private native long iteratorWithBase(
|
||||
final long handle, final long baseIteratorHandle, final long cfHandle);
|
||||
private native long iteratorWithBase(final long handle, final long baseIteratorHandle,
|
||||
final long cfHandle, final long readOptionsHandle);
|
||||
private native byte[] getFromBatch(final long handle, final long optHandle,
|
||||
final byte[] key, final int keyLen);
|
||||
private native byte[] getFromBatch(final long handle, final long optHandle,
|
||||
|
||||
@@ -13,7 +13,9 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -101,6 +103,95 @@ public class WriteBatchWithIndexTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readYourOwnWritesCf() throws RocksDBException {
|
||||
final List<ColumnFamilyDescriptor> cfNames =
|
||||
Arrays.asList(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY),
|
||||
new ColumnFamilyDescriptor("new_cf".getBytes()));
|
||||
|
||||
final List<ColumnFamilyHandle> columnFamilyHandleList = new ArrayList<>();
|
||||
|
||||
// Test open database with column family names
|
||||
try (final DBOptions options =
|
||||
new DBOptions().setCreateIfMissing(true).setCreateMissingColumnFamilies(true);
|
||||
final RocksDB db = RocksDB.open(
|
||||
options, dbFolder.getRoot().getAbsolutePath(), cfNames, columnFamilyHandleList)) {
|
||||
final ColumnFamilyHandle newCf = columnFamilyHandleList.get(1);
|
||||
|
||||
try {
|
||||
final byte[] k1 = "key1".getBytes();
|
||||
final byte[] v1 = "value1".getBytes();
|
||||
final byte[] k2 = "key2".getBytes();
|
||||
final byte[] v2 = "value2".getBytes();
|
||||
|
||||
db.put(newCf, k1, v1);
|
||||
db.put(newCf, k2, v2);
|
||||
|
||||
try (final WriteBatchWithIndex wbwi = new WriteBatchWithIndex(true);
|
||||
final ReadOptions readOptions = new ReadOptions();
|
||||
final RocksIterator base = db.newIterator(newCf, readOptions);
|
||||
final RocksIterator it = wbwi.newIteratorWithBase(newCf, base, readOptions)) {
|
||||
it.seek(k1);
|
||||
assertThat(it.isValid()).isTrue();
|
||||
assertThat(it.key()).isEqualTo(k1);
|
||||
assertThat(it.value()).isEqualTo(v1);
|
||||
|
||||
it.seek(k2);
|
||||
assertThat(it.isValid()).isTrue();
|
||||
assertThat(it.key()).isEqualTo(k2);
|
||||
assertThat(it.value()).isEqualTo(v2);
|
||||
|
||||
// put data to the write batch and make sure we can read it.
|
||||
final byte[] k3 = "key3".getBytes();
|
||||
final byte[] v3 = "value3".getBytes();
|
||||
wbwi.put(newCf, k3, v3);
|
||||
it.seek(k3);
|
||||
assertThat(it.isValid()).isTrue();
|
||||
assertThat(it.key()).isEqualTo(k3);
|
||||
assertThat(it.value()).isEqualTo(v3);
|
||||
|
||||
// update k2 in the write batch and check the value
|
||||
final byte[] v2Other = "otherValue2".getBytes();
|
||||
wbwi.put(newCf, k2, v2Other);
|
||||
it.seek(k2);
|
||||
assertThat(it.isValid()).isTrue();
|
||||
assertThat(it.key()).isEqualTo(k2);
|
||||
assertThat(it.value()).isEqualTo(v2Other);
|
||||
|
||||
// delete k1 and make sure we can read back the write
|
||||
wbwi.delete(newCf, k1);
|
||||
it.seek(k1);
|
||||
assertThat(it.key()).isNotEqualTo(k1);
|
||||
|
||||
// reinsert k1 and make sure we see the new value
|
||||
final byte[] v1Other = "otherValue1".getBytes();
|
||||
wbwi.put(newCf, k1, v1Other);
|
||||
it.seek(k1);
|
||||
assertThat(it.isValid()).isTrue();
|
||||
assertThat(it.key()).isEqualTo(k1);
|
||||
assertThat(it.value()).isEqualTo(v1Other);
|
||||
|
||||
// single remove k3 and make sure we can read back the write
|
||||
wbwi.singleDelete(newCf, k3);
|
||||
it.seek(k3);
|
||||
assertThat(it.isValid()).isEqualTo(false);
|
||||
|
||||
// reinsert k3 and make sure we see the new value
|
||||
final byte[] v3Other = "otherValue3".getBytes();
|
||||
wbwi.put(newCf, k3, v3Other);
|
||||
it.seek(k3);
|
||||
assertThat(it.isValid()).isTrue();
|
||||
assertThat(it.key()).isEqualTo(k3);
|
||||
assertThat(it.value()).isEqualTo(v3Other);
|
||||
}
|
||||
} finally {
|
||||
for (final ColumnFamilyHandle columnFamilyHandle : columnFamilyHandleList) {
|
||||
columnFamilyHandle.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeBatchWithIndex() throws RocksDBException {
|
||||
try (final Options options = new Options().setCreateIfMissing(true);
|
||||
|
||||
@@ -452,8 +452,8 @@ class Benchmark {
|
||||
MemTableRep* table_;
|
||||
KeyGenerator* key_gen_;
|
||||
uint64_t* sequence_;
|
||||
uint64_t num_write_ops_per_thread_;
|
||||
uint64_t num_read_ops_per_thread_;
|
||||
uint64_t num_write_ops_per_thread_ = 0;
|
||||
uint64_t num_read_ops_per_thread_ = 0;
|
||||
const uint32_t num_threads_;
|
||||
};
|
||||
|
||||
|
||||
+108
-25
@@ -114,6 +114,63 @@ static Status ParseCompressionOptions(const std::string& value,
|
||||
const std::string kOptNameBMCompOpts = "bottommost_compression_opts";
|
||||
const std::string kOptNameCompOpts = "compression_opts";
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
fifo_compaction_options_type_info = {
|
||||
{"max_table_files_size",
|
||||
{offsetof(struct CompactionOptionsFIFO, max_table_files_size),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct CompactionOptionsFIFO, max_table_files_size)}},
|
||||
{"ttl",
|
||||
{0, OptionType::kUInt64T, OptionVerificationType::kDeprecated,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"allow_compaction",
|
||||
{offsetof(struct CompactionOptionsFIFO, allow_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct CompactionOptionsFIFO, allow_compaction)}}};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
universal_compaction_options_type_info = {
|
||||
{"size_ratio",
|
||||
{offsetof(class CompactionOptionsUniversal, size_ratio),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, size_ratio)}},
|
||||
{"min_merge_width",
|
||||
{offsetof(class CompactionOptionsUniversal, min_merge_width),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, min_merge_width)}},
|
||||
{"max_merge_width",
|
||||
{offsetof(class CompactionOptionsUniversal, max_merge_width),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, max_merge_width)}},
|
||||
{"max_size_amplification_percent",
|
||||
{offsetof(class CompactionOptionsUniversal,
|
||||
max_size_amplification_percent),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal,
|
||||
max_size_amplification_percent)}},
|
||||
{"compression_size_percent",
|
||||
{offsetof(class CompactionOptionsUniversal, compression_size_percent),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal,
|
||||
compression_size_percent)}},
|
||||
{"stop_style",
|
||||
{offsetof(class CompactionOptionsUniversal, stop_style),
|
||||
OptionType::kCompactionStopStyle, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, stop_style)}},
|
||||
{"allow_trivial_move",
|
||||
{offsetof(class CompactionOptionsUniversal, allow_trivial_move),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, allow_trivial_move)}}};
|
||||
|
||||
std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionsHelper::cf_options_type_info = {
|
||||
/* not yet supported
|
||||
@@ -310,12 +367,13 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions, max_bytes_for_level_multiplier)}},
|
||||
{"max_bytes_for_level_multiplier_additional",
|
||||
{offset_of(
|
||||
&ColumnFamilyOptions::max_bytes_for_level_multiplier_additional),
|
||||
OptionType::kVectorInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions,
|
||||
max_bytes_for_level_multiplier_additional)}},
|
||||
OptionTypeInfo::Vector<int>(
|
||||
offset_of(&ColumnFamilyOptions::
|
||||
max_bytes_for_level_multiplier_additional),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions,
|
||||
max_bytes_for_level_multiplier_additional),
|
||||
{0, OptionType::kInt, 0})},
|
||||
{"max_sequential_skip_in_iterations",
|
||||
{offset_of(&ColumnFamilyOptions::max_sequential_skip_in_iterations),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
@@ -336,9 +394,10 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions, compression)}},
|
||||
{"compression_per_level",
|
||||
{offset_of(&ColumnFamilyOptions::compression_per_level),
|
||||
OptionType::kVectorCompressionType, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
OptionTypeInfo::Vector<CompressionType>(
|
||||
offset_of(&ColumnFamilyOptions::compression_per_level),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone, 0,
|
||||
{0, OptionType::kCompressionType})},
|
||||
{"bottommost_compression",
|
||||
{offset_of(&ColumnFamilyOptions::bottommost_compression),
|
||||
OptionType::kCompressionType, OptionVerificationType::kNormal,
|
||||
@@ -459,8 +518,9 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
[](const ConfigOptions& /*opts*/, const std::string& /*name*/,
|
||||
const std::string& value, char* addr) {
|
||||
auto mop = reinterpret_cast<std::shared_ptr<MergeOperator>*>(addr);
|
||||
ObjectRegistry::NewInstance()->NewSharedObject<MergeOperator>(value,
|
||||
mop);
|
||||
ObjectRegistry::NewInstance()
|
||||
->NewSharedObject<MergeOperator>(value, mop)
|
||||
.PermitUncheckedError();
|
||||
return Status::OK();
|
||||
}}},
|
||||
{"compaction_style",
|
||||
@@ -472,15 +532,36 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionType::kCompactionPri, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"compaction_options_fifo",
|
||||
{offset_of(&ColumnFamilyOptions::compaction_options_fifo),
|
||||
OptionType::kCompactionOptionsFIFO, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions, compaction_options_fifo)}},
|
||||
OptionTypeInfo::Struct(
|
||||
"compaction_options_fifo", &fifo_compaction_options_type_info,
|
||||
offset_of(&ColumnFamilyOptions::compaction_options_fifo),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions, compaction_options_fifo),
|
||||
[](const ConfigOptions& opts, const std::string& name,
|
||||
const std::string& value, char* addr) {
|
||||
// This is to handle backward compatibility, where
|
||||
// compaction_options_fifo could be assigned a single scalar
|
||||
// value, say, like "23", which would be assigned to
|
||||
// max_table_files_size.
|
||||
if (name == "compaction_options_fifo" &&
|
||||
value.find("=") == std::string::npos) {
|
||||
// Old format. Parse just a single uint64_t value.
|
||||
auto options = reinterpret_cast<CompactionOptionsFIFO*>(addr);
|
||||
options->max_table_files_size = ParseUint64(value);
|
||||
return Status::OK();
|
||||
} else {
|
||||
return OptionTypeInfo::ParseStruct(
|
||||
opts, "compaction_options_fifo",
|
||||
&fifo_compaction_options_type_info, name, value, addr);
|
||||
}
|
||||
})},
|
||||
{"compaction_options_universal",
|
||||
{offset_of(&ColumnFamilyOptions::compaction_options_universal),
|
||||
OptionType::kCompactionOptionsUniversal,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions, compaction_options_universal)}},
|
||||
OptionTypeInfo::Struct(
|
||||
"compaction_options_universal",
|
||||
&universal_compaction_options_type_info,
|
||||
offset_of(&ColumnFamilyOptions::compaction_options_universal),
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct MutableCFOptions, compaction_options_universal))},
|
||||
{"ttl",
|
||||
{offset_of(&ColumnFamilyOptions::ttl), OptionType::kUInt64T,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
@@ -533,14 +614,16 @@ Status ParseColumnFamilyOption(const ConfigOptions& config_options,
|
||||
? UnescapeOptionString(org_value)
|
||||
: org_value;
|
||||
try {
|
||||
auto iter = cf_options_type_info.find(name);
|
||||
if (iter == cf_options_type_info.end()) {
|
||||
std::string elem;
|
||||
const auto opt_info =
|
||||
OptionTypeInfo::Find(name, cf_options_type_info, &elem);
|
||||
if (opt_info != nullptr) {
|
||||
return opt_info->Parse(
|
||||
config_options, elem, value,
|
||||
reinterpret_cast<char*>(new_options) + opt_info->offset_);
|
||||
} else {
|
||||
return Status::InvalidArgument(
|
||||
"Unable to parse the specified CF option " + name);
|
||||
} else {
|
||||
return iter->second.ParseOption(
|
||||
config_options, name, value,
|
||||
reinterpret_cast<char*>(new_options) + iter->second.offset);
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
return Status::InvalidArgument("unable to parse the specified option " +
|
||||
|
||||
+4
-2
@@ -608,7 +608,8 @@ ReadOptions::ReadOptions()
|
||||
iter_start_seqnum(0),
|
||||
timestamp(nullptr),
|
||||
iter_start_ts(nullptr),
|
||||
deadline(std::chrono::microseconds::zero()) {}
|
||||
deadline(std::chrono::microseconds::zero()),
|
||||
value_size_soft_limit(std::numeric_limits<uint64_t>::max()) {}
|
||||
|
||||
ReadOptions::ReadOptions(bool cksum, bool cache)
|
||||
: snapshot(nullptr),
|
||||
@@ -630,6 +631,7 @@ ReadOptions::ReadOptions(bool cksum, bool cache)
|
||||
iter_start_seqnum(0),
|
||||
timestamp(nullptr),
|
||||
iter_start_ts(nullptr),
|
||||
deadline(std::chrono::microseconds::zero()) {}
|
||||
deadline(std::chrono::microseconds::zero()),
|
||||
value_size_soft_limit(std::numeric_limits<uint64_t>::max()) {}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+319
-417
@@ -271,133 +271,6 @@ std::vector<CompressionType> GetSupportedCompressions() {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
namespace {
|
||||
bool SerializeVectorCompressionType(const std::vector<CompressionType>& types,
|
||||
std::string* value) {
|
||||
std::stringstream ss;
|
||||
bool result;
|
||||
for (size_t i = 0; i < types.size(); ++i) {
|
||||
if (i > 0) {
|
||||
ss << ':';
|
||||
}
|
||||
std::string string_type;
|
||||
result = SerializeEnum<CompressionType>(compression_type_string_map,
|
||||
types[i], &string_type);
|
||||
if (result == false) {
|
||||
return result;
|
||||
}
|
||||
ss << string_type;
|
||||
}
|
||||
*value = ss.str();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseVectorCompressionType(
|
||||
const std::string& value,
|
||||
std::vector<CompressionType>* compression_per_level) {
|
||||
compression_per_level->clear();
|
||||
size_t start = 0;
|
||||
while (start < value.size()) {
|
||||
size_t end = value.find(':', start);
|
||||
bool is_ok;
|
||||
CompressionType type;
|
||||
if (end == std::string::npos) {
|
||||
is_ok = ParseEnum<CompressionType>(compression_type_string_map,
|
||||
value.substr(start), &type);
|
||||
if (!is_ok) {
|
||||
return false;
|
||||
}
|
||||
compression_per_level->emplace_back(type);
|
||||
break;
|
||||
} else {
|
||||
is_ok = ParseEnum<CompressionType>(
|
||||
compression_type_string_map, value.substr(start, end - start), &type);
|
||||
if (!is_ok) {
|
||||
return false;
|
||||
}
|
||||
compression_per_level->emplace_back(type);
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is to handle backward compatibility, where compaction_options_fifo
|
||||
// could be assigned a single scalar value, say, like "23", which would be
|
||||
// assigned to max_table_files_size.
|
||||
bool FIFOCompactionOptionsSpecialCase(const std::string& opt_str,
|
||||
CompactionOptionsFIFO* options) {
|
||||
if (opt_str.find("=") != std::string::npos) {
|
||||
// New format. Go do your new parsing using ParseStructOptions.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Old format. Parse just a single uint64_t value.
|
||||
options->max_table_files_size = ParseUint64(opt_str);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool SerializeStruct(
|
||||
const void* const opt_ptr, std::string* value,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>& type_info_map) {
|
||||
ConfigOptions config_options;
|
||||
config_options.delimiter = ";";
|
||||
|
||||
std::string opt_str;
|
||||
Status s =
|
||||
GetStringFromStruct(config_options, opt_ptr, type_info_map, &opt_str);
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
*value = "{" + opt_str + "}";
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ParseSingleStructOption(
|
||||
const ConfigOptions& config_options, const std::string& opt_val_str,
|
||||
void* options,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>& type_info_map) {
|
||||
size_t end = opt_val_str.find('=');
|
||||
std::string key = opt_val_str.substr(0, end);
|
||||
std::string value = opt_val_str.substr(end + 1);
|
||||
auto iter = type_info_map.find(key);
|
||||
if (iter == type_info_map.end()) {
|
||||
return false;
|
||||
}
|
||||
const auto& opt_info = iter->second;
|
||||
Status s = opt_info.ParseOption(
|
||||
config_options, key, value,
|
||||
reinterpret_cast<char*>(options) + opt_info.mutable_offset);
|
||||
return s.ok();
|
||||
}
|
||||
|
||||
static bool ParseStructOptions(
|
||||
const std::string& opt_str, void* options,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>& type_info_map) {
|
||||
assert(!opt_str.empty());
|
||||
ConfigOptions config_options;
|
||||
|
||||
size_t start = 0;
|
||||
if (opt_str[0] == '{') {
|
||||
start++;
|
||||
}
|
||||
while ((start != std::string::npos) && (start < opt_str.size())) {
|
||||
if (opt_str[start] == '}') {
|
||||
break;
|
||||
}
|
||||
size_t end = opt_str.find(';', start);
|
||||
size_t len = (end == std::string::npos) ? end : end - start;
|
||||
if (!ParseSingleStructOption(config_options, opt_str.substr(start, len),
|
||||
options, type_info_map)) {
|
||||
return false;
|
||||
}
|
||||
start = (end == std::string::npos) ? end : end + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // anonymouse namespace
|
||||
|
||||
bool ParseSliceTransformHelper(
|
||||
const std::string& kFixedPrefixName, const std::string& kCappedPrefixName,
|
||||
const std::string& value,
|
||||
@@ -468,9 +341,6 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type,
|
||||
case OptionType::kInt64T:
|
||||
PutUnaligned(reinterpret_cast<int64_t*>(opt_address), ParseInt64(value));
|
||||
break;
|
||||
case OptionType::kVectorInt:
|
||||
*reinterpret_cast<std::vector<int>*>(opt_address) = ParseVectorInt(value);
|
||||
break;
|
||||
case OptionType::kUInt:
|
||||
*reinterpret_cast<unsigned int*>(opt_address) = ParseUint32(value);
|
||||
break;
|
||||
@@ -501,9 +371,6 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type,
|
||||
return ParseEnum<CompressionType>(
|
||||
compression_type_string_map, value,
|
||||
reinterpret_cast<CompressionType*>(opt_address));
|
||||
case OptionType::kVectorCompressionType:
|
||||
return ParseVectorCompressionType(
|
||||
value, reinterpret_cast<std::vector<CompressionType>*>(opt_address));
|
||||
case OptionType::kSliceTransform:
|
||||
return ParseSliceTransform(
|
||||
value, reinterpret_cast<std::shared_ptr<const SliceTransform>*>(
|
||||
@@ -516,21 +383,6 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type,
|
||||
return ParseEnum<EncodingType>(
|
||||
encoding_type_string_map, value,
|
||||
reinterpret_cast<EncodingType*>(opt_address));
|
||||
case OptionType::kCompactionOptionsFIFO: {
|
||||
if (!FIFOCompactionOptionsSpecialCase(
|
||||
value, reinterpret_cast<CompactionOptionsFIFO*>(opt_address))) {
|
||||
return ParseStructOptions(value, opt_address,
|
||||
fifo_compaction_options_type_info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case OptionType::kLRUCacheOptions: {
|
||||
return ParseStructOptions(value, opt_address,
|
||||
lru_cache_options_type_info);
|
||||
}
|
||||
case OptionType::kCompactionOptionsUniversal:
|
||||
return ParseStructOptions(value, opt_address,
|
||||
universal_compaction_options_type_info);
|
||||
case OptionType::kCompactionStopStyle:
|
||||
return ParseEnum<CompactionStopStyle>(
|
||||
compaction_stop_style_string_map, value,
|
||||
@@ -563,9 +415,6 @@ bool SerializeSingleOptionHelper(const char* opt_address,
|
||||
*value = ToString(v);
|
||||
}
|
||||
break;
|
||||
case OptionType::kVectorInt:
|
||||
return SerializeIntVector(
|
||||
*reinterpret_cast<const std::vector<int>*>(opt_address), value);
|
||||
case OptionType::kUInt:
|
||||
*value = ToString(*(reinterpret_cast<const unsigned int*>(opt_address)));
|
||||
break;
|
||||
@@ -605,11 +454,6 @@ bool SerializeSingleOptionHelper(const char* opt_address,
|
||||
return SerializeEnum<CompressionType>(
|
||||
compression_type_string_map,
|
||||
*(reinterpret_cast<const CompressionType*>(opt_address)), value);
|
||||
case OptionType::kVectorCompressionType:
|
||||
return SerializeVectorCompressionType(
|
||||
*(reinterpret_cast<const std::vector<CompressionType>*>(opt_address)),
|
||||
value);
|
||||
break;
|
||||
case OptionType::kSliceTransform: {
|
||||
const auto* slice_transform_ptr =
|
||||
reinterpret_cast<const std::shared_ptr<const SliceTransform>*>(
|
||||
@@ -691,12 +535,6 @@ bool SerializeSingleOptionHelper(const char* opt_address,
|
||||
return SerializeEnum<EncodingType>(
|
||||
encoding_type_string_map,
|
||||
*reinterpret_cast<const EncodingType*>(opt_address), value);
|
||||
case OptionType::kCompactionOptionsFIFO:
|
||||
return SerializeStruct(opt_address, value,
|
||||
fifo_compaction_options_type_info);
|
||||
case OptionType::kCompactionOptionsUniversal:
|
||||
return SerializeStruct(opt_address, value,
|
||||
universal_compaction_options_type_info);
|
||||
case OptionType::kCompactionStopStyle:
|
||||
return SerializeEnum<CompactionStopStyle>(
|
||||
compaction_stop_style_string_map,
|
||||
@@ -715,26 +553,25 @@ Status GetMutableOptionsFromStrings(
|
||||
*new_options = base_options;
|
||||
ConfigOptions config_options;
|
||||
for (const auto& o : options_map) {
|
||||
auto iter = cf_options_type_info.find(o.first);
|
||||
if (iter == cf_options_type_info.end()) {
|
||||
std::string elem;
|
||||
const auto opt_info =
|
||||
OptionTypeInfo::Find(o.first, cf_options_type_info, &elem);
|
||||
if (opt_info == nullptr) {
|
||||
return Status::InvalidArgument("Unrecognized option: " + o.first);
|
||||
}
|
||||
const auto& opt_info = iter->second;
|
||||
if (!opt_info.IsMutable()) {
|
||||
} else if (!opt_info->IsMutable()) {
|
||||
return Status::InvalidArgument("Option not changeable: " + o.first);
|
||||
}
|
||||
if (opt_info.IsDeprecated()) {
|
||||
} else if (opt_info->IsDeprecated()) {
|
||||
// log warning when user tries to set a deprecated option but don't fail
|
||||
// the call for compatibility.
|
||||
ROCKS_LOG_WARN(info_log, "%s is a deprecated option and cannot be set",
|
||||
o.first.c_str());
|
||||
continue;
|
||||
}
|
||||
Status s = opt_info.ParseOption(
|
||||
config_options, o.first, o.second,
|
||||
reinterpret_cast<char*>(new_options) + opt_info.mutable_offset);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
} else {
|
||||
Status s = opt_info->Parse(
|
||||
config_options, elem, o.second,
|
||||
reinterpret_cast<char*>(new_options) + opt_info->mutable_offset_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
@@ -750,19 +587,20 @@ Status GetMutableDBOptionsFromStrings(
|
||||
|
||||
for (const auto& o : options_map) {
|
||||
try {
|
||||
auto iter = db_options_type_info.find(o.first);
|
||||
if (iter == db_options_type_info.end()) {
|
||||
std::string elem;
|
||||
const auto opt_info =
|
||||
OptionTypeInfo::Find(o.first, db_options_type_info, &elem);
|
||||
if (opt_info == nullptr) {
|
||||
return Status::InvalidArgument("Unrecognized option: " + o.first);
|
||||
}
|
||||
const auto& opt_info = iter->second;
|
||||
if (!opt_info.IsMutable()) {
|
||||
} else if (!opt_info->IsMutable()) {
|
||||
return Status::InvalidArgument("Option not changeable: " + o.first);
|
||||
}
|
||||
Status s = opt_info.ParseOption(
|
||||
config_options, o.first, o.second,
|
||||
reinterpret_cast<char*>(new_options) + opt_info.mutable_offset);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
} else {
|
||||
Status s = opt_info->Parse(
|
||||
config_options, elem, o.second,
|
||||
reinterpret_cast<char*>(new_options) + opt_info->mutable_offset_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
return Status::InvalidArgument("Error parsing " + o.first + ":" +
|
||||
@@ -780,6 +618,11 @@ Status StringToMap(const std::string& opts_str,
|
||||
// "nested_opt={opt1=1;opt2=2};max_bytes_for_level_base=100"
|
||||
size_t pos = 0;
|
||||
std::string opts = trim(opts_str);
|
||||
// If the input string starts and ends with "{...}", strip off the brackets
|
||||
while (opts.size() > 2 && opts[0] == '{' && opts[opts.size() - 1] == '}') {
|
||||
opts = trim(opts.substr(1, opts.size() - 2));
|
||||
}
|
||||
|
||||
while (pos < opts.size()) {
|
||||
size_t eq_pos = opts.find('=', pos);
|
||||
if (eq_pos == std::string::npos) {
|
||||
@@ -790,58 +633,17 @@ Status StringToMap(const std::string& opts_str,
|
||||
return Status::InvalidArgument("Empty key found");
|
||||
}
|
||||
|
||||
// skip space after '=' and look for '{' for possible nested options
|
||||
pos = eq_pos + 1;
|
||||
while (pos < opts.size() && isspace(opts[pos])) {
|
||||
++pos;
|
||||
}
|
||||
// Empty value at the end
|
||||
if (pos >= opts.size()) {
|
||||
(*opts_map)[key] = "";
|
||||
break;
|
||||
}
|
||||
if (opts[pos] == '{') {
|
||||
int count = 1;
|
||||
size_t brace_pos = pos + 1;
|
||||
while (brace_pos < opts.size()) {
|
||||
if (opts[brace_pos] == '{') {
|
||||
++count;
|
||||
} else if (opts[brace_pos] == '}') {
|
||||
--count;
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++brace_pos;
|
||||
}
|
||||
// found the matching closing brace
|
||||
if (count == 0) {
|
||||
(*opts_map)[key] = trim(opts.substr(pos + 1, brace_pos - pos - 1));
|
||||
// skip all whitespace and move to the next ';'
|
||||
// brace_pos points to the next position after the matching '}'
|
||||
pos = brace_pos + 1;
|
||||
while (pos < opts.size() && isspace(opts[pos])) {
|
||||
++pos;
|
||||
}
|
||||
if (pos < opts.size() && opts[pos] != ';') {
|
||||
return Status::InvalidArgument(
|
||||
"Unexpected chars after nested options");
|
||||
}
|
||||
++pos;
|
||||
} else {
|
||||
return Status::InvalidArgument(
|
||||
"Mismatched curly braces for nested options");
|
||||
}
|
||||
std::string value;
|
||||
Status s = OptionTypeInfo::NextToken(opts, ';', eq_pos + 1, &pos, &value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
} else {
|
||||
size_t sc_pos = opts.find(';', pos);
|
||||
if (sc_pos == std::string::npos) {
|
||||
(*opts_map)[key] = trim(opts.substr(pos));
|
||||
// It either ends with a trailing semi-colon or the last key-value pair
|
||||
(*opts_map)[key] = value;
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
} else {
|
||||
(*opts_map)[key] = trim(opts.substr(pos, sc_pos - pos));
|
||||
pos++;
|
||||
}
|
||||
pos = sc_pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -860,10 +662,10 @@ Status GetStringFromStruct(
|
||||
// we skip it in the serialization.
|
||||
if (opt_info.ShouldSerialize()) {
|
||||
const char* opt_addr =
|
||||
reinterpret_cast<const char*>(opt_ptr) + opt_info.offset;
|
||||
reinterpret_cast<const char*>(opt_ptr) + opt_info.offset_;
|
||||
std::string value;
|
||||
Status s = opt_info.SerializeOption(config_options, iter.first, opt_addr,
|
||||
&value);
|
||||
Status s =
|
||||
opt_info.Serialize(config_options, iter.first, opt_addr, &value);
|
||||
if (s.ok()) {
|
||||
opt_string->append(iter.first + "=" + value + config_options.delimiter);
|
||||
} else {
|
||||
@@ -923,13 +725,14 @@ static Status ParseDBOption(const ConfigOptions& config_options,
|
||||
const std::string& value = config_options.input_strings_escaped
|
||||
? UnescapeOptionString(org_value)
|
||||
: org_value;
|
||||
auto iter = db_options_type_info.find(name);
|
||||
if (iter == db_options_type_info.end()) {
|
||||
std::string elem;
|
||||
const auto opt_info = OptionTypeInfo::Find(name, db_options_type_info, &elem);
|
||||
if (opt_info == nullptr) {
|
||||
return Status::InvalidArgument("Unrecognized option DBOptions:", name);
|
||||
} else {
|
||||
return iter->second.ParseOption(
|
||||
config_options, name, value,
|
||||
reinterpret_cast<char*>(new_options) + iter->second.offset);
|
||||
return opt_info->Parse(
|
||||
config_options, elem, value,
|
||||
reinterpret_cast<char*>(new_options) + opt_info->offset_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1135,7 +938,7 @@ Status GetTableFactoryFromMap(
|
||||
return s;
|
||||
}
|
||||
table_factory->reset(new BlockBasedTableFactory(bbt_opt));
|
||||
return Status::OK();
|
||||
return s;
|
||||
} else if (factory_name == PlainTableFactory().Name()) {
|
||||
PlainTableOptions pt_opt;
|
||||
s = GetPlainTableOptionsFromMap(config_options, PlainTableOptions(),
|
||||
@@ -1144,12 +947,12 @@ Status GetTableFactoryFromMap(
|
||||
return s;
|
||||
}
|
||||
table_factory->reset(new PlainTableFactory(pt_opt));
|
||||
return Status::OK();
|
||||
return s;
|
||||
}
|
||||
// Return OK for not supported table factories as TableFactory
|
||||
// Deserialization is optional.
|
||||
table_factory->reset();
|
||||
return Status::OK();
|
||||
return s;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, EncodingType>
|
||||
@@ -1170,121 +973,77 @@ std::unordered_map<std::string, CompactionPri>
|
||||
{"kOldestSmallestSeqFirst", kOldestSmallestSeqFirst},
|
||||
{"kMinOverlappingRatio", kMinOverlappingRatio}};
|
||||
|
||||
LRUCacheOptions OptionsHelper::dummy_lru_cache_options;
|
||||
CompactionOptionsUniversal OptionsHelper::dummy_comp_options_universal;
|
||||
CompactionOptionsFIFO OptionsHelper::dummy_comp_options;
|
||||
|
||||
template <typename T1>
|
||||
int offset_of(T1 LRUCacheOptions::*member) {
|
||||
return int(size_t(&(OptionsHelper::dummy_lru_cache_options.*member)) -
|
||||
size_t(&OptionsHelper::dummy_lru_cache_options));
|
||||
}
|
||||
|
||||
template <typename T1>
|
||||
int offset_of(T1 CompactionOptionsFIFO::*member) {
|
||||
return int(size_t(&(OptionsHelper::dummy_comp_options.*member)) -
|
||||
size_t(&OptionsHelper::dummy_comp_options));
|
||||
}
|
||||
template <typename T1>
|
||||
int offset_of(T1 CompactionOptionsUniversal::*member) {
|
||||
return int(size_t(&(OptionsHelper::dummy_comp_options_universal.*member)) -
|
||||
size_t(&OptionsHelper::dummy_comp_options_universal));
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionsHelper::fifo_compaction_options_type_info = {
|
||||
{"max_table_files_size",
|
||||
{offset_of(&CompactionOptionsFIFO::max_table_files_size),
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct CompactionOptionsFIFO, max_table_files_size)}},
|
||||
{"ttl",
|
||||
{0, OptionType::kUInt64T, OptionVerificationType::kDeprecated,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"allow_compaction",
|
||||
{offset_of(&CompactionOptionsFIFO::allow_compaction),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct CompactionOptionsFIFO, allow_compaction)}}};
|
||||
|
||||
std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionsHelper::universal_compaction_options_type_info = {
|
||||
{"size_ratio",
|
||||
{offset_of(&CompactionOptionsUniversal::size_ratio), OptionType::kUInt,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, size_ratio)}},
|
||||
{"min_merge_width",
|
||||
{offset_of(&CompactionOptionsUniversal::min_merge_width),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, min_merge_width)}},
|
||||
{"max_merge_width",
|
||||
{offset_of(&CompactionOptionsUniversal::max_merge_width),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, max_merge_width)}},
|
||||
{"max_size_amplification_percent",
|
||||
{offset_of(
|
||||
&CompactionOptionsUniversal::max_size_amplification_percent),
|
||||
OptionType::kUInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal,
|
||||
max_size_amplification_percent)}},
|
||||
{"compression_size_percent",
|
||||
{offset_of(&CompactionOptionsUniversal::compression_size_percent),
|
||||
OptionType::kInt, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal,
|
||||
compression_size_percent)}},
|
||||
{"stop_style",
|
||||
{offset_of(&CompactionOptionsUniversal::stop_style),
|
||||
OptionType::kCompactionStopStyle, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, stop_style)}},
|
||||
{"allow_trivial_move",
|
||||
{offset_of(&CompactionOptionsUniversal::allow_trivial_move),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(class CompactionOptionsUniversal, allow_trivial_move)}}};
|
||||
|
||||
std::unordered_map<std::string, CompactionStopStyle>
|
||||
OptionsHelper::compaction_stop_style_string_map = {
|
||||
{"kCompactionStopStyleSimilarSize", kCompactionStopStyleSimilarSize},
|
||||
{"kCompactionStopStyleTotalSize", kCompactionStopStyleTotalSize}};
|
||||
|
||||
std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionsHelper::lru_cache_options_type_info = {
|
||||
{"capacity",
|
||||
{offset_of(&LRUCacheOptions::capacity), OptionType::kSizeT,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, capacity)}},
|
||||
{"num_shard_bits",
|
||||
{offset_of(&LRUCacheOptions::num_shard_bits), OptionType::kInt,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, num_shard_bits)}},
|
||||
{"strict_capacity_limit",
|
||||
{offset_of(&LRUCacheOptions::strict_capacity_limit),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, strict_capacity_limit)}},
|
||||
{"high_pri_pool_ratio",
|
||||
{offset_of(&LRUCacheOptions::high_pri_pool_ratio), OptionType::kDouble,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, high_pri_pool_ratio)}}};
|
||||
Status OptionTypeInfo::NextToken(const std::string& opts, char delimiter,
|
||||
size_t pos, size_t* end, std::string* token) {
|
||||
while (pos < opts.size() && isspace(opts[pos])) {
|
||||
++pos;
|
||||
}
|
||||
// Empty value at the end
|
||||
if (pos >= opts.size()) {
|
||||
*token = "";
|
||||
*end = std::string::npos;
|
||||
return Status::OK();
|
||||
} else if (opts[pos] == '{') {
|
||||
int count = 1;
|
||||
size_t brace_pos = pos + 1;
|
||||
while (brace_pos < opts.size()) {
|
||||
if (opts[brace_pos] == '{') {
|
||||
++count;
|
||||
} else if (opts[brace_pos] == '}') {
|
||||
--count;
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++brace_pos;
|
||||
}
|
||||
// found the matching closing brace
|
||||
if (count == 0) {
|
||||
*token = trim(opts.substr(pos + 1, brace_pos - pos - 1));
|
||||
// skip all whitespace and move to the next delimiter
|
||||
// brace_pos points to the next position after the matching '}'
|
||||
pos = brace_pos + 1;
|
||||
while (pos < opts.size() && isspace(opts[pos])) {
|
||||
++pos;
|
||||
}
|
||||
if (pos < opts.size() && opts[pos] != delimiter) {
|
||||
return Status::InvalidArgument("Unexpected chars after nested options");
|
||||
}
|
||||
*end = pos;
|
||||
} else {
|
||||
return Status::InvalidArgument(
|
||||
"Mismatched curly braces for nested options");
|
||||
}
|
||||
} else {
|
||||
*end = opts.find(delimiter, pos);
|
||||
if (*end == std::string::npos) {
|
||||
// It either ends with a trailing semi-colon or the last key-value pair
|
||||
*token = trim(opts.substr(pos));
|
||||
} else {
|
||||
*token = trim(opts.substr(pos, *end - pos));
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status OptionTypeInfo::ParseOption(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const std::string& opt_value,
|
||||
char* opt_addr) const {
|
||||
Status OptionTypeInfo::Parse(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const std::string& opt_value,
|
||||
char* opt_addr) const {
|
||||
if (IsDeprecated()) {
|
||||
return Status::OK();
|
||||
}
|
||||
try {
|
||||
if (opt_addr == nullptr) {
|
||||
return Status::NotFound("Could not find option: ", opt_name);
|
||||
} else if (parser_func != nullptr) {
|
||||
return parser_func(config_options, opt_name, opt_value, opt_addr);
|
||||
} else if (ParseOptionHelper(opt_addr, type, opt_value)) {
|
||||
} else if (parse_func_ != nullptr) {
|
||||
return parse_func_(config_options, opt_name, opt_value, opt_addr);
|
||||
} else if (ParseOptionHelper(opt_addr, type_, opt_value)) {
|
||||
return Status::OK();
|
||||
} else if (IsByName()) {
|
||||
return Status::NotSupported("Deserializing the option " + opt_name +
|
||||
@@ -1298,23 +1057,123 @@ Status OptionTypeInfo::ParseOption(const ConfigOptions& config_options,
|
||||
}
|
||||
}
|
||||
|
||||
Status OptionTypeInfo::SerializeOption(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* opt_addr,
|
||||
std::string* opt_value) const {
|
||||
Status OptionTypeInfo::ParseStruct(
|
||||
const ConfigOptions& config_options, const std::string& struct_name,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
|
||||
const std::string& opt_name, const std::string& opt_value, char* opt_addr) {
|
||||
assert(struct_map);
|
||||
Status status;
|
||||
if (opt_name == struct_name || EndsWith(opt_name, "." + struct_name)) {
|
||||
// This option represents the entire struct
|
||||
std::unordered_map<std::string, std::string> opt_map;
|
||||
status = StringToMap(opt_value, &opt_map);
|
||||
for (const auto& map_iter : opt_map) {
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
const auto iter = struct_map->find(map_iter.first);
|
||||
if (iter != struct_map->end()) {
|
||||
status =
|
||||
iter->second.Parse(config_options, map_iter.first, map_iter.second,
|
||||
opt_addr + iter->second.offset_);
|
||||
} else {
|
||||
status = Status::InvalidArgument("Unrecognized option: ",
|
||||
struct_name + "." + map_iter.first);
|
||||
}
|
||||
}
|
||||
} else if (StartsWith(opt_name, struct_name + ".")) {
|
||||
// This option represents a nested field in the struct (e.g, struct.field)
|
||||
std::string elem_name;
|
||||
const auto opt_info =
|
||||
Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
|
||||
if (opt_info != nullptr) {
|
||||
status = opt_info->Parse(config_options, elem_name, opt_value,
|
||||
opt_addr + opt_info->offset_);
|
||||
} else {
|
||||
status = Status::InvalidArgument("Unrecognized option: ", opt_name);
|
||||
}
|
||||
} else {
|
||||
// This option represents a field in the struct (e.g. field)
|
||||
std::string elem_name;
|
||||
const auto opt_info = Find(opt_name, *struct_map, &elem_name);
|
||||
if (opt_info != nullptr) {
|
||||
status = opt_info->Parse(config_options, elem_name, opt_value,
|
||||
opt_addr + opt_info->offset_);
|
||||
} else {
|
||||
status = Status::InvalidArgument("Unrecognized option: ",
|
||||
struct_name + "." + opt_name);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status OptionTypeInfo::Serialize(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* opt_addr,
|
||||
std::string* opt_value) const {
|
||||
// If the option is no longer used in rocksdb and marked as deprecated,
|
||||
// we skip it in the serialization.
|
||||
Status s;
|
||||
if (opt_addr == nullptr || IsDeprecated()) {
|
||||
return Status::OK();
|
||||
} else if (string_func != nullptr) {
|
||||
return string_func(config_options, opt_name, opt_addr, opt_value);
|
||||
} else if (SerializeSingleOptionHelper(opt_addr, type, opt_value)) {
|
||||
s = Status::OK();
|
||||
} else {
|
||||
s = Status::InvalidArgument("Cannot serialize option: ", opt_name);
|
||||
if (opt_addr != nullptr && ShouldSerialize()) {
|
||||
if (serialize_func_ != nullptr) {
|
||||
return serialize_func_(config_options, opt_name, opt_addr, opt_value);
|
||||
} else if (!SerializeSingleOptionHelper(opt_addr, type_, opt_value)) {
|
||||
return Status::InvalidArgument("Cannot serialize option: ", opt_name);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status OptionTypeInfo::SerializeStruct(
|
||||
const ConfigOptions& config_options, const std::string& struct_name,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
|
||||
const std::string& opt_name, const char* opt_addr, std::string* value) {
|
||||
assert(struct_map);
|
||||
Status status;
|
||||
if (EndsWith(opt_name, struct_name)) {
|
||||
// We are going to write the struct as "{ prop1=value1; prop2=value2;}.
|
||||
// Set the delimiter to ";" so that the everything will be on one line.
|
||||
ConfigOptions embedded = config_options;
|
||||
embedded.delimiter = ";";
|
||||
|
||||
// This option represents the entire struct
|
||||
std::string result;
|
||||
for (const auto& iter : *struct_map) {
|
||||
std::string single;
|
||||
const auto& opt_info = iter.second;
|
||||
if (opt_info.ShouldSerialize()) {
|
||||
status = opt_info.Serialize(embedded, iter.first,
|
||||
opt_addr + opt_info.offset_, &single);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
} else {
|
||||
result.append(iter.first + "=" + single + embedded.delimiter);
|
||||
}
|
||||
}
|
||||
}
|
||||
*value = "{" + result + "}";
|
||||
} else if (StartsWith(opt_name, struct_name + ".")) {
|
||||
// This option represents a nested field in the struct (e.g, struct.field)
|
||||
std::string elem_name;
|
||||
const auto opt_info =
|
||||
Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
|
||||
if (opt_info != nullptr) {
|
||||
status = opt_info->Serialize(config_options, elem_name,
|
||||
opt_addr + opt_info->offset_, value);
|
||||
} else {
|
||||
status = Status::InvalidArgument("Unrecognized option: ", opt_name);
|
||||
}
|
||||
} else {
|
||||
// This option represents a field in the struct (e.g. field)
|
||||
std::string elem_name;
|
||||
const auto opt_info = Find(opt_name, *struct_map, &elem_name);
|
||||
if (opt_info == nullptr) {
|
||||
status = Status::InvalidArgument("Unrecognized option: ", opt_name);
|
||||
} else if (opt_info->ShouldSerialize()) {
|
||||
status = opt_info->Serialize(config_options, opt_name + "." + elem_name,
|
||||
opt_addr + opt_info->offset_, value);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -1344,8 +1203,6 @@ static bool AreOptionsEqual(OptionType type, const char* this_offset,
|
||||
GetUnaligned(reinterpret_cast<const int64_t*>(that_offset), &v2);
|
||||
return (v1 == v2);
|
||||
}
|
||||
case OptionType::kVectorInt:
|
||||
return IsOptionEqual<std::vector<int> >(this_offset, that_offset);
|
||||
case OptionType::kUInt32T:
|
||||
return IsOptionEqual<uint32_t>(this_offset, that_offset);
|
||||
case OptionType::kUInt64T: {
|
||||
@@ -1365,9 +1222,6 @@ static bool AreOptionsEqual(OptionType type, const char* this_offset,
|
||||
case OptionType::kDouble:
|
||||
return AreEqualDoubles(*reinterpret_cast<const double*>(this_offset),
|
||||
*reinterpret_cast<const double*>(that_offset));
|
||||
case OptionType::kVectorCompressionType:
|
||||
return IsOptionEqual<std::vector<CompressionType> >(this_offset,
|
||||
that_offset);
|
||||
case OptionType::kCompactionStyle:
|
||||
return IsOptionEqual<CompactionStyle>(this_offset, that_offset);
|
||||
case OptionType::kCompactionStopStyle:
|
||||
@@ -1380,44 +1234,15 @@ static bool AreOptionsEqual(OptionType type, const char* this_offset,
|
||||
return IsOptionEqual<ChecksumType>(this_offset, that_offset);
|
||||
case OptionType::kEncodingType:
|
||||
return IsOptionEqual<EncodingType>(this_offset, that_offset);
|
||||
case OptionType::kCompactionOptionsFIFO: {
|
||||
CompactionOptionsFIFO lhs =
|
||||
*reinterpret_cast<const CompactionOptionsFIFO*>(this_offset);
|
||||
CompactionOptionsFIFO rhs =
|
||||
*reinterpret_cast<const CompactionOptionsFIFO*>(that_offset);
|
||||
if (lhs.max_table_files_size == rhs.max_table_files_size &&
|
||||
lhs.allow_compaction == rhs.allow_compaction) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case OptionType::kCompactionOptionsUniversal: {
|
||||
CompactionOptionsUniversal lhs =
|
||||
*reinterpret_cast<const CompactionOptionsUniversal*>(this_offset);
|
||||
CompactionOptionsUniversal rhs =
|
||||
*reinterpret_cast<const CompactionOptionsUniversal*>(that_offset);
|
||||
if (lhs.size_ratio == rhs.size_ratio &&
|
||||
lhs.min_merge_width == rhs.min_merge_width &&
|
||||
lhs.max_merge_width == rhs.max_merge_width &&
|
||||
lhs.max_size_amplification_percent ==
|
||||
rhs.max_size_amplification_percent &&
|
||||
lhs.compression_size_percent == rhs.compression_size_percent &&
|
||||
lhs.stop_style == rhs.stop_style &&
|
||||
lhs.allow_trivial_move == rhs.allow_trivial_move) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
} // End switch
|
||||
}
|
||||
|
||||
bool OptionTypeInfo::MatchesOption(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* this_addr, const char* that_addr,
|
||||
|
||||
std::string* mismatch) const {
|
||||
bool OptionTypeInfo::AreEqual(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* this_addr, const char* that_addr,
|
||||
std::string* mismatch) const {
|
||||
if (!config_options.IsCheckEnabled(GetSanityLevel())) {
|
||||
return true; // If the sanity level is not being checked, skip it
|
||||
}
|
||||
@@ -1425,11 +1250,12 @@ bool OptionTypeInfo::MatchesOption(const ConfigOptions& config_options,
|
||||
if (this_addr == that_addr) {
|
||||
return true;
|
||||
}
|
||||
} else if (equals_func != nullptr) {
|
||||
if (equals_func(config_options, opt_name, this_addr, that_addr, mismatch)) {
|
||||
} else if (equals_func_ != nullptr) {
|
||||
if (equals_func_(config_options, opt_name, this_addr, that_addr,
|
||||
mismatch)) {
|
||||
return true;
|
||||
}
|
||||
} else if (AreOptionsEqual(type, this_addr, that_addr)) {
|
||||
} else if (AreOptionsEqual(type_, this_addr, that_addr)) {
|
||||
return true;
|
||||
}
|
||||
if (mismatch->empty()) {
|
||||
@@ -1438,29 +1264,81 @@ bool OptionTypeInfo::MatchesOption(const ConfigOptions& config_options,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OptionTypeInfo::MatchesByName(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* this_addr,
|
||||
const char* that_addr) const {
|
||||
bool OptionTypeInfo::StructsAreEqual(
|
||||
const ConfigOptions& config_options, const std::string& struct_name,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>* struct_map,
|
||||
const std::string& opt_name, const char* this_addr, const char* that_addr,
|
||||
std::string* mismatch) {
|
||||
assert(struct_map);
|
||||
bool matches = true;
|
||||
std::string result;
|
||||
if (EndsWith(opt_name, struct_name)) {
|
||||
// This option represents the entire struct
|
||||
for (const auto& iter : *struct_map) {
|
||||
const auto& opt_info = iter.second;
|
||||
|
||||
matches = opt_info.AreEqual(config_options, iter.first,
|
||||
this_addr + opt_info.offset_,
|
||||
that_addr + opt_info.offset_, &result);
|
||||
if (!matches) {
|
||||
*mismatch = struct_name + "." + result;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (StartsWith(opt_name, struct_name + ".")) {
|
||||
// This option represents a nested field in the struct (e.g, struct.field)
|
||||
std::string elem_name;
|
||||
const auto opt_info =
|
||||
Find(opt_name.substr(struct_name.size() + 1), *struct_map, &elem_name);
|
||||
assert(opt_info);
|
||||
if (opt_info == nullptr) {
|
||||
*mismatch = opt_name;
|
||||
matches = false;
|
||||
} else if (!opt_info->AreEqual(config_options, elem_name,
|
||||
this_addr + opt_info->offset_,
|
||||
that_addr + opt_info->offset_, &result)) {
|
||||
matches = false;
|
||||
*mismatch = struct_name + "." + result;
|
||||
}
|
||||
} else {
|
||||
// This option represents a field in the struct (e.g. field)
|
||||
std::string elem_name;
|
||||
const auto opt_info = Find(opt_name, *struct_map, &elem_name);
|
||||
assert(opt_info);
|
||||
if (opt_info == nullptr) {
|
||||
*mismatch = struct_name + "." + opt_name;
|
||||
matches = false;
|
||||
} else if (!opt_info->AreEqual(config_options, elem_name,
|
||||
this_addr + opt_info->offset_,
|
||||
that_addr + opt_info->offset_, &result)) {
|
||||
matches = false;
|
||||
*mismatch = struct_name + "." + result;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* this_addr,
|
||||
const char* that_addr) const {
|
||||
if (IsByName()) {
|
||||
std::string that_value;
|
||||
if (SerializeOption(config_options, opt_name, that_addr, &that_value)
|
||||
.ok()) {
|
||||
return MatchesByName(config_options, opt_name, this_addr, that_value);
|
||||
if (Serialize(config_options, opt_name, that_addr, &that_value).ok()) {
|
||||
return AreEqualByName(config_options, opt_name, this_addr, that_value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OptionTypeInfo::MatchesByName(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* opt_addr,
|
||||
const std::string& that_value) const {
|
||||
bool OptionTypeInfo::AreEqualByName(const ConfigOptions& config_options,
|
||||
const std::string& opt_name,
|
||||
const char* opt_addr,
|
||||
const std::string& that_value) const {
|
||||
std::string this_value;
|
||||
if (!IsByName()) {
|
||||
return false;
|
||||
} else if (!SerializeOption(config_options, opt_name, opt_addr, &this_value)
|
||||
.ok()) {
|
||||
} else if (!Serialize(config_options, opt_name, opt_addr, &this_value).ok()) {
|
||||
return false;
|
||||
} else if (IsEnabled(OptionVerificationType::kByNameAllowFromNull)) {
|
||||
if (that_value == kNullptrString) {
|
||||
@@ -1473,6 +1351,30 @@ bool OptionTypeInfo::MatchesByName(const ConfigOptions& config_options,
|
||||
}
|
||||
return (this_value == that_value);
|
||||
}
|
||||
|
||||
const OptionTypeInfo* OptionTypeInfo::Find(
|
||||
const std::string& opt_name,
|
||||
const std::unordered_map<std::string, OptionTypeInfo>& opt_map,
|
||||
std::string* elem_name) {
|
||||
const auto iter = opt_map.find(opt_name); // Look up the value in the map
|
||||
if (iter != opt_map.end()) { // Found the option in the map
|
||||
*elem_name = opt_name; // Return the name
|
||||
return &(iter->second); // Return the contents of the iterator
|
||||
} else {
|
||||
auto idx = opt_name.find("."); // Look for a separator
|
||||
if (idx > 0 && idx != std::string::npos) { // We found a separator
|
||||
auto siter =
|
||||
opt_map.find(opt_name.substr(0, idx)); // Look for the short name
|
||||
if (siter != opt_map.end()) { // We found the short name
|
||||
if (siter->second.IsStruct()) { // If the object is a struct
|
||||
*elem_name = opt_name.substr(idx + 1); // Return the rest
|
||||
return &(siter->second); // Return the contents of the iterator
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user