mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
101
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 | ||
|
|
e9ba4ba348 | ||
|
|
ac3ae1df0b | ||
|
|
1f20df2f38 | ||
|
|
c1e1185b7a | ||
|
|
06c3b85b9a | ||
|
|
53f84470d2 | ||
|
|
c21c459771 | ||
|
|
394f2bbd13 | ||
|
|
a96461d169 | ||
|
|
2f1700c8c5 | ||
|
|
91bc0130fa | ||
|
|
5584595f80 | ||
|
|
5a61e7864d | ||
|
|
a00ddf1574 | ||
|
|
680c416348 | ||
|
|
d9e170d82b |
No files matched your search
@@ -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
|
||||
|
||||
+27
@@ -1,5 +1,32 @@
|
||||
# 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
|
||||
* Fix wrong result being read from ingested file. May happen when a key in the file happen to be prefix of another key also in the file. The issue can further cause more data corruption. The issue exists with rocksdb >= 5.0.0 since DB::IngestExternalFile() was introduced.
|
||||
* Finish implementation of BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey. It's now ready for use. Significantly reduces read amplification in some setups, especially for iterator seeks.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -106,3 +106,6 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## Nebula Graph
|
||||
|
||||
[Nebula Graph](https://github.com/vesoft-inc/nebula) is a distributed, scalable, lightning-fast, open source graph database capable of hosting super large scale graphs with dozens of billions of vertices (nodes) and trillions of edges, with milliseconds of latency.
|
||||
|
||||
## YugabyteDB
|
||||
[YugabyteDB](https://www.yugabyte.com/) is an open source, high performance, distributed SQL database that uses RocksDB as its storage layer. For more information, please see https://github.com/yugabyte/yugabyte-db/.
|
||||
@@ -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!
|
||||
|
||||
@@ -93,9 +93,13 @@ class LevelCompactionBuilder {
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
void PickFilesMarkedForPeriodicCompaction();
|
||||
// Picks a file from level_files to compact.
|
||||
// level_files is a vector of (level, file metadata) in ascending order of
|
||||
// level. If compact_to_next_level is true, compact the file to the next
|
||||
// level, otherwise, compact to the same level as the input file.
|
||||
void PickFileToCompact(
|
||||
const autovector<std::pair<int, FileMetaData*>>& level_files,
|
||||
bool compact_to_next_level);
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
@@ -124,72 +128,34 @@ class LevelCompactionBuilder {
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
void LevelCompactionBuilder::PickFileToCompact(
|
||||
const autovector<std::pair<int, FileMetaData*>>& level_files,
|
||||
bool compact_to_next_level) {
|
||||
for (auto& level_file : level_files) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
if ((compact_to_next_level &&
|
||||
start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (compact_to_next_level) {
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
} else {
|
||||
output_level_ = start_level_;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForPeriodicCompaction() {
|
||||
if (vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
output_level_ = start_level_ = level_file.first;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForPeriodicCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
@@ -238,63 +204,46 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Compaction scores are sorted in descending order, no further scores
|
||||
// will be >= 1.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!start_level_inputs_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_,
|
||||
&start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
|
||||
// Bottommost Files Compaction on deleting tombstones
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->BottommostFilesMarkedForCompaction(), false);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
|
||||
// TTL Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->ExpiredTtlFiles(), true);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Periodic Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickFilesMarkedForPeriodicCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->FilesMarkedForPeriodicCompaction(), false);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
+284
-13
@@ -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(
|
||||
@@ -1774,6 +1993,28 @@ TEST_F(DBBasicTest, IncrementalRecoveryNoCorrupt) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryWithVersionBuildingFailure) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.best_efforts_recovery = true;
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
class TableFileListener : public EventListener {
|
||||
@@ -1814,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]));
|
||||
}
|
||||
@@ -1850,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_;
|
||||
@@ -1916,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);
|
||||
@@ -2511,9 +2783,8 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
std::shared_ptr<DBBasicTestMultiGetDeadline::DeadlineFS> fs(
|
||||
new DBBasicTestMultiGetDeadline::DeadlineFS(env_));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_.store(true);
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -5024,10 +5024,11 @@ TEST_P(DBCompactionTestWithParam, FixFileIngestionCompactionDeadlock) {
|
||||
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency", [&](void* arg) {
|
||||
"VersionBuilder::CheckConsistency0", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
@@ -5046,6 +5047,48 @@ TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
options.target_file_size_base = 1000;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 400; // small block size
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency1", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
*(p->first) = *(p->second);
|
||||
*(p->second) = temp;
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
std::string value = RandomString(&rnd, 1000);
|
||||
|
||||
ASSERT_OK(Put("foo1", value));
|
||||
ASSERT_OK(Put("z", ""));
|
||||
Flush();
|
||||
ASSERT_OK(Put("foo2", value));
|
||||
ASSERT_OK(Put("z", ""));
|
||||
Flush();
|
||||
|
||||
// This probably returns non-OK, but we rely on the next Put()
|
||||
// to determine the DB is frozen.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
void IngestOneKeyValue(DBImpl* db, const std::string& key,
|
||||
|
||||
+21
-10
@@ -90,6 +90,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds, FlushOptions(),
|
||||
FlushReason::kGetLiveFiles);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -103,8 +106,10 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok()) {
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,27 +123,33 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
}
|
||||
|
||||
// Make a set of all of the live *.sst files
|
||||
std::vector<FileDescriptor> live;
|
||||
// Make a set of all of the live table and blob files
|
||||
std::vector<uint64_t> live_table_files;
|
||||
std::vector<uint64_t> live_blob_files;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->current()->AddLiveFiles(&live);
|
||||
cfd->current()->AddLiveFiles(&live_table_files, &live_blob_files);
|
||||
}
|
||||
|
||||
ret.clear();
|
||||
ret.reserve(live.size() + 3); // *.sst + CURRENT + MANIFEST + OPTIONS
|
||||
ret.reserve(live_table_files.size() + live_blob_files.size() +
|
||||
3); // for CURRENT + MANIFEST + OPTIONS
|
||||
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
for (const auto& live_file : live) {
|
||||
ret.push_back(MakeTableFileName("", live_file.GetNumber()));
|
||||
for (const auto& table_file_number : live_table_files) {
|
||||
ret.emplace_back(MakeTableFileName("", table_file_number));
|
||||
}
|
||||
|
||||
ret.push_back(CurrentFileName(""));
|
||||
ret.push_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.push_back(OptionsFileName("", versions_->options_file_number()));
|
||||
for (const auto& blob_file_number : live_blob_files) {
|
||||
ret.emplace_back(BlobFileName("", blob_file_number));
|
||||
}
|
||||
|
||||
ret.emplace_back(CurrentFileName(""));
|
||||
ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
|
||||
|
||||
// find length of manifest file while holding the mutex lock
|
||||
*manifest_file_size = versions_->manifest_file_size();
|
||||
|
||||
+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;
|
||||
|
||||
@@ -969,6 +969,12 @@ class DBImpl : public DB {
|
||||
void TEST_WaitForPersistStatsRun(std::function<void()> callback) const;
|
||||
bool TEST_IsPersistentStatsEnabled() const;
|
||||
size_t TEST_EstimateInMemoryStatsHistorySize() const;
|
||||
|
||||
VersionSet* TEST_GetVersionSet() const { return versions_.get(); }
|
||||
|
||||
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
|
||||
return files_grabbed_for_purge_;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
protected:
|
||||
@@ -1935,7 +1941,7 @@ class DBImpl : public DB {
|
||||
std::unordered_map<uint64_t, PurgeFileInfo> purge_files_;
|
||||
|
||||
// A vector to store the file numbers that have been assigned to certain
|
||||
// JobContext. Current implementation tracks ssts only.
|
||||
// JobContext. Current implementation tracks table and blob files only.
|
||||
std::unordered_set<uint64_t> files_grabbed_for_purge_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
|
||||
@@ -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;
|
||||
@@ -1906,7 +1906,7 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
}
|
||||
}
|
||||
if (1 == num_dropped && 1 == num) {
|
||||
return Status::InvalidArgument("Cannot flush a dropped CF");
|
||||
return Status::ColumnFamilyDropped();
|
||||
}
|
||||
// Column families involved in this flush request have either been dropped
|
||||
// or finished flush. Then it's time to finish waiting.
|
||||
|
||||
+47
-26
@@ -36,7 +36,7 @@ uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
// * Returns the list of live files in 'sst_live' and 'blob_live'.
|
||||
// If it's doing full scan:
|
||||
// * Returns the list of all files in the filesystem in
|
||||
// 'full_scan_candidate_files'.
|
||||
@@ -77,12 +77,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// Since job_context->min_pending_output is set, until file scan finishes,
|
||||
// mutex_ cannot be released. Otherwise, we might see no min_pending_output
|
||||
// here but later find newer generated unfinalized files while scanning.
|
||||
if (!pending_outputs_.empty()) {
|
||||
job_context->min_pending_output = *pending_outputs_.begin();
|
||||
} else {
|
||||
// delete all of them
|
||||
job_context->min_pending_output = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
job_context->min_pending_output = MinObsoleteSstNumberToKeep();
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
@@ -90,13 +85,18 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
&job_context->sst_delete_files, &job_context->blob_delete_files,
|
||||
&job_context->manifest_delete_files, job_context->min_pending_output);
|
||||
|
||||
// Mark the elements in job_context->sst_delete_files as grabbedForPurge
|
||||
// so that other threads calling FindObsoleteFiles with full_scan=true
|
||||
// will not add these files to candidate list for purge.
|
||||
// Mark the elements in job_context->sst_delete_files and
|
||||
// job_context->blob_delete_files as "grabbed for purge" so that other threads
|
||||
// calling FindObsoleteFiles with full_scan=true will not add these files to
|
||||
// candidate list for purge.
|
||||
for (const auto& sst_to_del : job_context->sst_delete_files) {
|
||||
MarkAsGrabbedForPurge(sst_to_del.metadata->fd.GetNumber());
|
||||
}
|
||||
|
||||
for (const auto& blob_file : job_context->blob_delete_files) {
|
||||
MarkAsGrabbedForPurge(blob_file.GetBlobFileNumber());
|
||||
}
|
||||
|
||||
// store the current filenum, lognum, etc
|
||||
job_context->manifest_file_number = versions_->manifest_file_number();
|
||||
job_context->pending_manifest_file_number =
|
||||
@@ -104,7 +104,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
job_context->log_number = MinLogNumberToKeep();
|
||||
job_context->prev_log_number = versions_->prev_log_number();
|
||||
|
||||
versions_->AddLiveFiles(&job_context->sst_live);
|
||||
versions_->AddLiveFiles(&job_context->sst_live, &job_context->blob_live);
|
||||
if (doing_the_full_scan) {
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
@@ -257,8 +257,11 @@ bool CompareCandidateFile(const JobContext::CandidateFileInfo& first,
|
||||
void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
const std::string& path_to_sync,
|
||||
FileType type, uint64_t number) {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl::BeforeDeletion",
|
||||
const_cast<std::string*>(&fname));
|
||||
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kLogFile) {
|
||||
if (type == kTableFile || type == kBlobFile || type == kLogFile) {
|
||||
file_deletion_status =
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
@@ -304,19 +307,19 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
// FindObsoleteFiles() should've populated this so nonzero
|
||||
assert(state.manifest_file_number != 0);
|
||||
|
||||
// Now, convert live list to an unordered map, WITHOUT mutex held;
|
||||
// set is slow.
|
||||
std::unordered_map<uint64_t, const FileDescriptor*> sst_live_map;
|
||||
for (const FileDescriptor& fd : state.sst_live) {
|
||||
sst_live_map[fd.GetNumber()] = &fd;
|
||||
}
|
||||
// Now, convert lists to unordered sets, WITHOUT mutex held; set is slow.
|
||||
std::unordered_set<uint64_t> sst_live_set(state.sst_live.begin(),
|
||||
state.sst_live.end());
|
||||
std::unordered_set<uint64_t> blob_live_set(state.blob_live.begin(),
|
||||
state.blob_live.end());
|
||||
std::unordered_set<uint64_t> log_recycle_files_set(
|
||||
state.log_recycle_files.begin(), state.log_recycle_files.end());
|
||||
|
||||
auto candidate_files = state.full_scan_candidate_files;
|
||||
candidate_files.reserve(
|
||||
candidate_files.size() + state.sst_delete_files.size() +
|
||||
state.log_delete_files.size() + state.manifest_delete_files.size());
|
||||
state.blob_delete_files.size() + state.log_delete_files.size() +
|
||||
state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
candidate_files.emplace_back(
|
||||
@@ -327,6 +330,11 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
|
||||
for (const auto& blob_file : state.blob_delete_files) {
|
||||
candidate_files.emplace_back(BlobFileName(blob_file.GetBlobFileNumber()),
|
||||
blob_file.GetPath());
|
||||
}
|
||||
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(file_num),
|
||||
@@ -413,12 +421,19 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kTableFile:
|
||||
// If the second condition is not there, this makes
|
||||
// DontDeletePendingOutputs fail
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
number >= state.min_pending_output;
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kBlobFile:
|
||||
keep = number >= state.min_pending_output ||
|
||||
(blob_live_set.find(number) != blob_live_set.end());
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kTempFile:
|
||||
// Any temp files that are currently being written to must
|
||||
// be recorded in pending_outputs_, which is inserted into "live".
|
||||
@@ -428,7 +443,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
//
|
||||
// TODO(yhchiang): carefully modify the third condition to safely
|
||||
// remove the temp options files.
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
(blob_live_set.find(number) != blob_live_set.end()) ||
|
||||
(number == state.pending_manifest_file_number) ||
|
||||
(to_delete.find(kOptionsFileNamePrefix) != std::string::npos);
|
||||
break;
|
||||
@@ -451,7 +467,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kDBLockFile:
|
||||
case kIdentityFile:
|
||||
case kMetaDatabase:
|
||||
case kBlobFile:
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
@@ -467,6 +482,9 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = MakeTableFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else if (type == kBlobFile) {
|
||||
fname = BlobFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
@@ -668,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
|
||||
@@ -693,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ class DBSecondaryTest : public DBTestBase {
|
||||
|
||||
void OpenSecondary(const Options& options);
|
||||
|
||||
Status TryOpenSecondary(const Options& options);
|
||||
|
||||
void OpenSecondaryWithColumnFamilies(
|
||||
const std::vector<std::string>& column_families, const Options& options);
|
||||
|
||||
@@ -70,9 +72,13 @@ class DBSecondaryTest : public DBTestBase {
|
||||
};
|
||||
|
||||
void DBSecondaryTest::OpenSecondary(const Options& options) {
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
Status DBSecondaryTest::TryOpenSecondary(const Options& options) {
|
||||
Status s =
|
||||
DB::OpenAsSecondary(options, dbname_, secondary_path_, &db_secondary_);
|
||||
ASSERT_OK(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBSecondaryTest::OpenSecondaryWithColumnFamilies(
|
||||
@@ -858,6 +864,56 @@ TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
thread.join();
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
Status s = TryOpenSecondary(options1);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Options options1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
{
|
||||
std::string value;
|
||||
ASSERT_OK(db_secondary_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("value", value);
|
||||
}
|
||||
|
||||
ASSERT_OK(Put("bar", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = db_secondary_->TryCatchUpWithPrimary();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+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.
|
||||
|
||||
+16
-1
@@ -47,6 +47,21 @@ TEST_F(DBRangeDelTest, WriteBatchWithIndexNotSupported) {
|
||||
ASSERT_TRUE(indexedBatch.DeleteRange("dr1", "dr1").IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, EndSameAsStartCoversNothing) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "b", "val"));
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "b", "b"));
|
||||
ASSERT_EQ("val", Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, EndComesBeforeStartInvalidArgument) {
|
||||
db_->Put(WriteOptions(), "b", "val");
|
||||
ASSERT_TRUE(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "b", "a")
|
||||
.IsInvalidArgument());
|
||||
ASSERT_EQ("val", Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushOutputHasOnlyRangeTombstones) {
|
||||
do {
|
||||
DestroyAndReopen(CurrentOptions());
|
||||
@@ -174,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);
|
||||
|
||||
+7
-19
@@ -356,37 +356,25 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
// Turn timed wait into a simulated sleep
|
||||
uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
|
||||
int64_t cur_time = 0;
|
||||
env_->GetCurrentTime(&cur_time);
|
||||
if (*abs_time_us > static_cast<uint64_t>(cur_time)) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us -
|
||||
static_cast<uint64_t>(cur_time));
|
||||
uint64_t cur_time = env_->NowMicros();
|
||||
if (*abs_time_us > cur_time) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us - cur_time);
|
||||
}
|
||||
|
||||
// Randomly sleep shortly
|
||||
env_->addon_time_.fetch_add(
|
||||
static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
|
||||
|
||||
// Set wait until time to before current to force not to sleep.
|
||||
int64_t real_cur_time = 0;
|
||||
Env::Default()->GetCurrentTime(&real_cur_time);
|
||||
*abs_time_us = static_cast<uint64_t>(real_cur_time);
|
||||
// Set wait until time to before (actual) current time to force not
|
||||
// to sleep
|
||||
*abs_time_us = Env::Default()->NowMicros();
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
options.disable_auto_compactions = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, one of whose member variables is of type
|
||||
// InstrumentedCondVar. The callback for
|
||||
// InstrumentedCondVar::TimedWaitInternal can be triggered by stats dumping
|
||||
// and persisting threads and cause time_spent_deleting measurement to become
|
||||
// incorrect.
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
options.env = env_;
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
|
||||
+88
-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]);
|
||||
}
|
||||
@@ -2309,6 +2334,42 @@ TEST_F(DBTest, ReadonlyDBGetLiveManifestSize) {
|
||||
Close();
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetLiveBlobFiles) {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
// Add a live blob file.
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "3d87ff57";
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
Status s = versions->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, dbfull()->mutex());
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Make sure it appears in the results returned by GetLiveFiles.
|
||||
uint64_t manifest_size = 0;
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(dbfull()->GetLiveFiles(files, &manifest_size));
|
||||
|
||||
ASSERT_FALSE(files.empty());
|
||||
ASSERT_EQ(files[0], BlobFileName("", blob_file_number));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DBTest, PurgeInfoLogs) {
|
||||
|
||||
+218
@@ -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;
|
||||
@@ -4328,6 +4528,24 @@ TEST_F(DBTest2, SameSmallestInSameLevel) {
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", Get("key"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, FileConsistencyCheckInOpen) {
|
||||
Put("foo", "bar");
|
||||
Flush();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
Status* ret_s = static_cast<Status*>(arg);
|
||||
*ret_s = Status::Corruption("fcc");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, BlockBasedTablePrefixIndexSeekForPrev) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
@@ -14,10 +14,19 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
int64_t MaybeCurrentTime(Env* env) {
|
||||
int64_t time = 1337346000; // arbitrary fallback default
|
||||
(void)env->GetCurrentTime(&time);
|
||||
return time;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Special Env used to delay background operations
|
||||
|
||||
SpecialEnv::SpecialEnv(Env* base)
|
||||
: EnvWrapper(base),
|
||||
maybe_starting_time_(MaybeCurrentTime(base)),
|
||||
rnd_(301),
|
||||
sleep_counter_(this),
|
||||
addon_time_(0),
|
||||
|
||||
+19
-1
@@ -502,10 +502,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* unix_time) override {
|
||||
Status s;
|
||||
if (!time_elapse_only_sleep_) {
|
||||
if (time_elapse_only_sleep_) {
|
||||
*unix_time = maybe_starting_time_;
|
||||
} else {
|
||||
s = target()->GetCurrentTime(unix_time);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// FIXME: addon_time_ sometimes used to mean seconds (here) and
|
||||
// sometimes microseconds
|
||||
*unix_time += addon_time_.load();
|
||||
}
|
||||
return s;
|
||||
@@ -531,6 +535,20 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
void SetTimeElapseOnlySleep(Options* options) {
|
||||
time_elapse_only_sleep_ = true;
|
||||
no_slowdown_ = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, which uses InstrumentedCondVar::TimedWaitInternal.
|
||||
// With time_elapse_only_sleep_, this can hang on some platforms.
|
||||
// TODO: why? investigate/fix
|
||||
options->stats_dump_period_sec = 0;
|
||||
options->stats_persist_period_sec = 0;
|
||||
}
|
||||
|
||||
// Something to return when mocking current time
|
||||
const int64_t maybe_starting_time_;
|
||||
|
||||
Random rnd_;
|
||||
port::Mutex rnd_mutex_; // Lock to pretect rnd_
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+5
-2
@@ -1422,8 +1422,11 @@ TEST_F(DBWALTest, RecoverFromCorruptedWALWithoutFlush) {
|
||||
delete iter;
|
||||
return data;
|
||||
};
|
||||
for (auto& mode : wal_recovery_mode_string_map) {
|
||||
options.wal_recovery_mode = mode.second;
|
||||
for (auto& mode : {WALRecoveryMode::kTolerateCorruptedTailRecords,
|
||||
WALRecoveryMode::kAbsoluteConsistency,
|
||||
WALRecoveryMode::kPointInTimeRecovery,
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords}) {
|
||||
options.wal_recovery_mode = mode;
|
||||
for (auto trunc : {true, false}) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = jstart; j < jend; j++) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
+5
-2
@@ -141,11 +141,14 @@ struct JobContext {
|
||||
std::vector<CandidateFileInfo> full_scan_candidate_files;
|
||||
|
||||
// the list of all live sst files that cannot be deleted
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
std::vector<uint64_t> sst_live;
|
||||
|
||||
// a list of sst files that we need to delete
|
||||
// the list of sst files that we need to delete
|
||||
std::vector<ObsoleteFileInfo> sst_delete_files;
|
||||
|
||||
// the list of all live blob files that cannot be deleted
|
||||
std::vector<uint64_t> blob_live;
|
||||
|
||||
// the list of blob files that we need to delete
|
||||
std::vector<ObsoleteBlobFileInfo> blob_delete_files;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -193,6 +194,124 @@ TEST_F(ObsoleteFilesTest, DeleteObsoleteOptionsFile) {
|
||||
ASSERT_EQ(2, opts_file_count);
|
||||
}
|
||||
|
||||
TEST_F(ObsoleteFilesTest, BlobFiles) {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
// Add a blob file that consists of nothing but garbage (and is thus obsolete)
|
||||
// and another one that is live.
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t first_blob_file_number = 234;
|
||||
constexpr uint64_t first_total_blob_count = 555;
|
||||
constexpr uint64_t first_total_blob_bytes = 66666;
|
||||
constexpr char first_checksum_method[] = "CRC32";
|
||||
constexpr char first_checksum_value[] = "3d87ff57";
|
||||
|
||||
edit.AddBlobFile(first_blob_file_number, first_total_blob_count,
|
||||
first_total_blob_bytes, first_checksum_method,
|
||||
first_checksum_value);
|
||||
edit.AddBlobFileGarbage(first_blob_file_number, first_total_blob_count,
|
||||
first_total_blob_bytes);
|
||||
|
||||
constexpr uint64_t second_blob_file_number = 456;
|
||||
constexpr uint64_t second_total_blob_count = 100;
|
||||
constexpr uint64_t second_total_blob_bytes = 2000000;
|
||||
constexpr char second_checksum_method[] = "CRC32B";
|
||||
constexpr char second_checksum_value[] = "6dbdf23a";
|
||||
edit.AddBlobFile(second_blob_file_number, second_total_blob_count,
|
||||
second_total_blob_bytes, second_checksum_method,
|
||||
second_checksum_value);
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
Status s = versions->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, dbfull()->mutex());
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Check for obsolete files and make sure the first blob file is picked up
|
||||
// and grabbed for purge. The second blob file should be on the live list.
|
||||
constexpr int job_id = 0;
|
||||
JobContext job_context{job_id};
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
constexpr bool force_full_scan = false;
|
||||
dbfull()->FindObsoleteFiles(&job_context, force_full_scan);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_TRUE(job_context.HaveSomethingToDelete());
|
||||
ASSERT_EQ(job_context.blob_delete_files.size(), 1);
|
||||
ASSERT_EQ(job_context.blob_delete_files[0].GetBlobFileNumber(),
|
||||
first_blob_file_number);
|
||||
|
||||
const auto& files_grabbed_for_purge =
|
||||
dbfull()->TEST_GetFilesGrabbedForPurge();
|
||||
ASSERT_NE(files_grabbed_for_purge.find(first_blob_file_number),
|
||||
files_grabbed_for_purge.end());
|
||||
|
||||
ASSERT_EQ(job_context.blob_live.size(), 1);
|
||||
ASSERT_EQ(job_context.blob_live[0], second_blob_file_number);
|
||||
|
||||
// Hack the job context a bit by adding a few files to the full scan
|
||||
// list and adjusting the pending file number. We add the two files
|
||||
// above as well as two additional ones, where one is old
|
||||
// and should be cleaned up, and the other is still pending.
|
||||
assert(cfd->ioptions());
|
||||
assert(!cfd->ioptions()->cf_paths.empty());
|
||||
const std::string& path = cfd->ioptions()->cf_paths.front().path;
|
||||
|
||||
constexpr uint64_t old_blob_file_number = 123;
|
||||
constexpr uint64_t pending_blob_file_number = 567;
|
||||
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(old_blob_file_number), path);
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(first_blob_file_number), path);
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(second_blob_file_number), path);
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(pending_blob_file_number), path);
|
||||
|
||||
job_context.min_pending_output = pending_blob_file_number;
|
||||
|
||||
// Purge obsolete files and make sure we purge the old file and the first file
|
||||
// (and keep the second file and the pending file).
|
||||
std::vector<std::string> deleted_files;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DeleteObsoleteFileImpl::BeforeDeletion", [&](void* arg) {
|
||||
const std::string* file = static_cast<std::string*>(arg);
|
||||
assert(file);
|
||||
|
||||
constexpr char blob_extension[] = ".blob";
|
||||
|
||||
if (file->find(blob_extension) != std::string::npos) {
|
||||
deleted_files.emplace_back(*file);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->PurgeObsoleteFiles(job_context);
|
||||
job_context.Clean();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_EQ(files_grabbed_for_purge.find(first_blob_file_number),
|
||||
files_grabbed_for_purge.end());
|
||||
|
||||
std::sort(deleted_files.begin(), deleted_files.end());
|
||||
const std::vector<std::string> expected_deleted_files{
|
||||
BlobFileName(path, old_blob_file_number),
|
||||
BlobFileName(path, first_blob_file_number)};
|
||||
|
||||
ASSERT_EQ(deleted_files, expected_deleted_files);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
+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");
|
||||
|
||||
+265
-157
@@ -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";
|
||||
@@ -236,11 +272,11 @@ class VersionBuilder::Rep {
|
||||
|
||||
auto f1 = level_files[i - 1];
|
||||
auto f2 = level_files[i];
|
||||
#ifndef NDEBUG
|
||||
auto pair = std::make_pair(&f1, &f2);
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency", &pair);
|
||||
#endif
|
||||
if (level == 0) {
|
||||
#ifndef NDEBUG
|
||||
auto pair = std::make_pair(&f1, &f2);
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency0", &pair);
|
||||
#endif
|
||||
if (!level_zero_cmp_(f1, f2)) {
|
||||
fprintf(stderr, "L0 files are not sorted properly");
|
||||
return Status::Corruption("L0 files are not sorted properly");
|
||||
@@ -279,6 +315,10 @@ class VersionBuilder::Rep {
|
||||
NumberToString(f2->fd.GetNumber()));
|
||||
}
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
auto pair = std::make_pair(&f1, &f2);
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency1", &pair);
|
||||
#endif
|
||||
if (!level_nonzero_cmp_(f1, f2)) {
|
||||
fprintf(stderr, "L%d files are not sorted properly", level);
|
||||
return Status::Corruption("L" + NumberToString(level) +
|
||||
@@ -316,76 +356,32 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
Status ret_s;
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistencyBeforeReturn",
|
||||
&ret_s);
|
||||
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";
|
||||
|
||||
@@ -416,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();
|
||||
}
|
||||
@@ -429,81 +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);
|
||||
CheckConsistencyForDeletes(edit, number, level);
|
||||
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;
|
||||
}
|
||||
@@ -511,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(
|
||||
@@ -541,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+260
-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;
|
||||
@@ -839,6 +1068,36 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForFileDeletedTwice) {
|
||||
Add(0, 1U, "150", "200", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr,
|
||||
true /* force_consistency_checks */);
|
||||
ASSERT_OK(version_builder.Apply(&version_edit));
|
||||
ASSERT_OK(version_builder.SaveTo(&new_vstorage));
|
||||
|
||||
VersionBuilder version_builder2(env_options, &ioptions_, table_cache,
|
||||
&new_vstorage, version_set);
|
||||
VersionStorageInfo new_vstorage2(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr,
|
||||
true /* force_consistency_checks */);
|
||||
ASSERT_NOK(version_builder2.Apply(&version_edit));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
UnrefFilesInVersion(&new_vstorage2);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, EstimatedActiveKeys) {
|
||||
const uint32_t kTotalSamples = 20;
|
||||
const uint32_t kNumLevels = 5;
|
||||
|
||||
+28
-17
@@ -377,6 +377,7 @@ Status VersionEditHandler::MaybeCreateVersion(const VersionEdit& /*edit*/,
|
||||
ColumnFamilyData* cfd,
|
||||
bool force_create_version) {
|
||||
assert(cfd->initialized());
|
||||
Status s;
|
||||
if (force_create_version) {
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
@@ -384,13 +385,18 @@ Status VersionEditHandler::MaybeCreateVersion(const VersionEdit& /*edit*/,
|
||||
auto* v = new Version(cfd, version_set_, version_set_->file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
version_set_->current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
// Install new version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(version_set_->db_options_->skip_stats_update_on_db_open));
|
||||
version_set_->AppendVersion(cfd, v);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
if (s.ok()) {
|
||||
// Install new version
|
||||
v->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!(version_set_->db_options_->skip_stats_update_on_db_open));
|
||||
version_set_->AppendVersion(cfd, v);
|
||||
} else {
|
||||
delete v;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
@@ -409,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) {
|
||||
@@ -540,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();
|
||||
}
|
||||
@@ -558,16 +565,20 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
auto* version = new Version(cfd, version_set_, version_set_->file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
version_set_->current_version_number_++);
|
||||
builder->SaveTo(version->storage_info());
|
||||
version->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!version_set_->db_options_->skip_stats_update_on_db_open);
|
||||
auto v_iter = versions_.find(cfd->GetID());
|
||||
if (v_iter != versions_.end()) {
|
||||
delete v_iter->second;
|
||||
v_iter->second = version;
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!version_set_->db_options_->skip_stats_update_on_db_open);
|
||||
auto v_iter = versions_.find(cfd->GetID());
|
||||
if (v_iter != versions_.end()) {
|
||||
delete v_iter->second;
|
||||
v_iter->second = version;
|
||||
} else {
|
||||
versions_.emplace(cfd->GetID(), version);
|
||||
}
|
||||
} else {
|
||||
versions_.emplace(cfd->GetID(), version);
|
||||
delete version;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
||||
+166
-57
@@ -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(
|
||||
@@ -3414,13 +3446,27 @@ bool VersionStorageInfo::RangeMightExistAfterSortedRun(
|
||||
return false;
|
||||
}
|
||||
|
||||
void Version::AddLiveFiles(std::vector<FileDescriptor>* live) {
|
||||
for (int level = 0; level < storage_info_.num_levels(); level++) {
|
||||
const std::vector<FileMetaData*>& files = storage_info_.files_[level];
|
||||
for (const auto& file : files) {
|
||||
live->push_back(file->fd);
|
||||
void Version::AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const {
|
||||
assert(live_table_files);
|
||||
assert(live_blob_files);
|
||||
|
||||
for (int level = 0; level < storage_info_.num_levels(); ++level) {
|
||||
const auto& level_files = storage_info_.LevelFiles(level);
|
||||
for (const auto& meta : level_files) {
|
||||
assert(meta);
|
||||
|
||||
live_table_files->emplace_back(meta->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
|
||||
const auto& blob_files = storage_info_.GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const auto& meta = pair.second;
|
||||
assert(meta);
|
||||
|
||||
live_blob_files->emplace_back(meta->GetBlobFileNumber());
|
||||
}
|
||||
}
|
||||
|
||||
std::string Version::DebugString(bool hex, bool print_stats) const {
|
||||
@@ -4594,7 +4640,11 @@ Status VersionSet::Recover(
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
if (!s.ok()) {
|
||||
delete v;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
@@ -4927,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_;
|
||||
@@ -5145,7 +5207,7 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), false);
|
||||
|
||||
printf("--------------- Column family \"%s\" (ID %" PRIu32
|
||||
@@ -5427,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);
|
||||
}
|
||||
@@ -5507,44 +5570,70 @@ uint64_t VersionSet::ApproximateSize(Version* v, const FdWithKeyRange& f,
|
||||
v->GetMutableCFOptions().prefix_extractor.get());
|
||||
}
|
||||
|
||||
void VersionSet::AddLiveFiles(std::vector<FileDescriptor>* live_list) {
|
||||
void VersionSet::AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const {
|
||||
assert(live_table_files);
|
||||
assert(live_blob_files);
|
||||
|
||||
// pre-calculate space requirement
|
||||
int64_t total_files = 0;
|
||||
size_t total_table_files = 0;
|
||||
size_t total_blob_files = 0;
|
||||
|
||||
assert(column_family_set_);
|
||||
for (auto cfd : *column_family_set_) {
|
||||
assert(cfd);
|
||||
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
Version* dummy_versions = cfd->dummy_versions();
|
||||
|
||||
Version* const dummy_versions = cfd->dummy_versions();
|
||||
assert(dummy_versions);
|
||||
|
||||
for (Version* v = dummy_versions->next_; v != dummy_versions;
|
||||
v = v->next_) {
|
||||
assert(v);
|
||||
|
||||
const auto* vstorage = v->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); level++) {
|
||||
total_files += vstorage->LevelFiles(level).size();
|
||||
assert(vstorage);
|
||||
|
||||
for (int level = 0; level < vstorage->num_levels(); ++level) {
|
||||
total_table_files += vstorage->LevelFiles(level).size();
|
||||
}
|
||||
|
||||
total_blob_files += vstorage->GetBlobFiles().size();
|
||||
}
|
||||
}
|
||||
|
||||
// just one time extension to the right size
|
||||
live_list->reserve(live_list->size() + static_cast<size_t>(total_files));
|
||||
live_table_files->reserve(live_table_files->size() + total_table_files);
|
||||
live_blob_files->reserve(live_blob_files->size() + total_blob_files);
|
||||
|
||||
assert(column_family_set_);
|
||||
for (auto cfd : *column_family_set_) {
|
||||
assert(cfd);
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* current = cfd->current();
|
||||
bool found_current = false;
|
||||
Version* dummy_versions = cfd->dummy_versions();
|
||||
|
||||
Version* const dummy_versions = cfd->dummy_versions();
|
||||
assert(dummy_versions);
|
||||
|
||||
for (Version* v = dummy_versions->next_; v != dummy_versions;
|
||||
v = v->next_) {
|
||||
v->AddLiveFiles(live_list);
|
||||
v->AddLiveFiles(live_table_files, live_blob_files);
|
||||
if (v == current) {
|
||||
found_current = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_current && current != nullptr) {
|
||||
// Should never happen unless it is a bug.
|
||||
assert(false);
|
||||
current->AddLiveFiles(live_list);
|
||||
current->AddLiveFiles(live_table_files, live_blob_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5583,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);
|
||||
@@ -5597,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));
|
||||
}
|
||||
}
|
||||
@@ -5966,13 +6055,23 @@ Status ReactiveVersionSet::Recover(
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
if (s.ok()) {
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(db_options_->info_log,
|
||||
"[%s]: inconsistent version: %s\n",
|
||||
cfd->GetName().c_str(), s.ToString().c_str());
|
||||
delete v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
next_file_number_.store(version_edit.next_file_number_ + 1);
|
||||
last_allocated_sequence_ = version_edit.last_sequence_;
|
||||
last_published_sequence_ = version_edit.last_sequence_;
|
||||
@@ -6049,6 +6148,8 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
s = ApplyOneVersionEditToBuilder(edit, cfds_changed, &temp_edit);
|
||||
if (s.ok()) {
|
||||
applied_edits++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6058,13 +6159,14 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
}
|
||||
// It's possible that:
|
||||
// 1) s.IsCorruption(), indicating the current MANIFEST is corrupted.
|
||||
// Or the version(s) rebuilt from tailing the MANIFEST is inconsistent.
|
||||
// 2) we have finished reading the current MANIFEST.
|
||||
// 3) we have encountered an IOError reading the current MANIFEST.
|
||||
// We need to look for the next MANIFEST and start from there. If we cannot
|
||||
// find the next MANIFEST, we should exit the loop.
|
||||
s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
Status tmp_s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
reader = manifest_reader->get();
|
||||
if (s.ok()) {
|
||||
if (tmp_s.ok()) {
|
||||
if (reader->file()->file_name() == old_manifest_path) {
|
||||
// Still processing the same MANIFEST, thus no need to continue this
|
||||
// loop since no record is available if we have reached here.
|
||||
@@ -6094,6 +6196,7 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
number_of_edits_to_skip_ += 2;
|
||||
}
|
||||
}
|
||||
s = tmp_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6186,12 +6289,16 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
auto version = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(version->storage_info());
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
} else {
|
||||
delete version;
|
||||
}
|
||||
} else if (s.IsPathNotFound()) {
|
||||
s = Status::OK();
|
||||
@@ -6199,23 +6306,25 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
// Some other error has occurred during LoadTableHandlers.
|
||||
}
|
||||
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
if (s.ok()) {
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+54
-4
@@ -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_;
|
||||
|
||||
@@ -629,8 +675,10 @@ class Version {
|
||||
// and return true. Otherwise, return false.
|
||||
bool Unref();
|
||||
|
||||
// Add all files listed in the current version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live);
|
||||
// Add all files listed in the current version to *live_table_files and
|
||||
// *live_blob_files.
|
||||
void AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const;
|
||||
|
||||
// Return a human readable string that describes this version's contents.
|
||||
std::string DebugString(bool hex = false, bool print_stats = false) const;
|
||||
@@ -1047,8 +1095,10 @@ class VersionSet {
|
||||
const Compaction* c, RangeDelAggregator* range_del_agg,
|
||||
const FileOptions& file_options_compactions);
|
||||
|
||||
// Add all files listed in any live version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live_list);
|
||||
// Add all files listed in any live version to *live_table_files and
|
||||
// *live_blob_files. Note that these lists may contain duplicates.
|
||||
void AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const;
|
||||
|
||||
// Return the approximate size of data to be scanned for range [start, end)
|
||||
// in levels [start_level, end_level). If end_level == -1 it will search
|
||||
|
||||
+105
-10
@@ -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()
|
||||
@@ -960,6 +977,84 @@ TEST_F(VersionSetTest, PersistBlobFileStateInNewManifest) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTest, AddLiveBlobFiles) {
|
||||
// Initialize the database and add a blob file.
|
||||
NewDB();
|
||||
|
||||
assert(versions_);
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
VersionEdit first;
|
||||
|
||||
constexpr uint64_t first_blob_file_number = 234;
|
||||
constexpr uint64_t first_total_blob_count = 555;
|
||||
constexpr uint64_t first_total_blob_bytes = 66666;
|
||||
constexpr char first_checksum_method[] = "CRC32";
|
||||
constexpr char first_checksum_value[] = "3d87ff57";
|
||||
|
||||
first.AddBlobFile(first_blob_file_number, first_total_blob_count,
|
||||
first_total_blob_bytes, first_checksum_method,
|
||||
first_checksum_value);
|
||||
|
||||
mutex_.Lock();
|
||||
Status s = versions_->LogAndApply(cfd, mutable_cf_options_, &first, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Reference the version so it stays alive even after the following version
|
||||
// edit.
|
||||
Version* const version = cfd->current();
|
||||
assert(version);
|
||||
|
||||
version->Ref();
|
||||
|
||||
// Get live files directly from version.
|
||||
std::vector<uint64_t> version_table_files;
|
||||
std::vector<uint64_t> version_blob_files;
|
||||
|
||||
version->AddLiveFiles(&version_table_files, &version_blob_files);
|
||||
|
||||
ASSERT_EQ(version_blob_files.size(), 1);
|
||||
ASSERT_EQ(version_blob_files[0], first_blob_file_number);
|
||||
|
||||
// Add another blob file.
|
||||
VersionEdit second;
|
||||
|
||||
constexpr uint64_t second_blob_file_number = 456;
|
||||
constexpr uint64_t second_total_blob_count = 100;
|
||||
constexpr uint64_t second_total_blob_bytes = 2000000;
|
||||
constexpr char second_checksum_method[] = "CRC32B";
|
||||
constexpr char second_checksum_value[] = "6dbdf23a";
|
||||
second.AddBlobFile(second_blob_file_number, second_total_blob_count,
|
||||
second_total_blob_bytes, second_checksum_method,
|
||||
second_checksum_value);
|
||||
|
||||
mutex_.Lock();
|
||||
s = versions_->LogAndApply(cfd, mutable_cf_options_, &second, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Get all live files from version set. Note that the result contains
|
||||
// duplicates.
|
||||
std::vector<uint64_t> all_table_files;
|
||||
std::vector<uint64_t> all_blob_files;
|
||||
|
||||
versions_->AddLiveFiles(&all_table_files, &all_blob_files);
|
||||
|
||||
ASSERT_EQ(all_blob_files.size(), 3);
|
||||
ASSERT_EQ(all_blob_files[0], first_blob_file_number);
|
||||
ASSERT_EQ(all_blob_files[1], first_blob_file_number);
|
||||
ASSERT_EQ(all_blob_files[2], second_blob_file_number);
|
||||
|
||||
// Clean up previous version.
|
||||
version->Unref();
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTest, ObsoleteBlobFile) {
|
||||
// Initialize the database and add a blob file (with no garbage just yet).
|
||||
NewDB();
|
||||
@@ -1370,7 +1465,7 @@ TEST_F(VersionSetAtomicGroupTest,
|
||||
// Write the corrupted edits.
|
||||
AddNewEditsToLog(kAtomicGroupSize);
|
||||
mu.Lock();
|
||||
EXPECT_OK(
|
||||
EXPECT_NOK(
|
||||
reactive_versions_->ReadAndApply(&mu, &manifest_reader, &cfds_changed));
|
||||
mu.Unlock();
|
||||
EXPECT_EQ(edits_[kAtomicGroupSize / 2].DebugString(),
|
||||
@@ -1420,7 +1515,7 @@ TEST_F(VersionSetAtomicGroupTest,
|
||||
&manifest_reader_status));
|
||||
AddNewEditsToLog(kAtomicGroupSize);
|
||||
mu.Lock();
|
||||
EXPECT_OK(
|
||||
EXPECT_NOK(
|
||||
reactive_versions_->ReadAndApply(&mu, &manifest_reader, &cfds_changed));
|
||||
mu.Unlock();
|
||||
EXPECT_EQ(edits_[1].DebugString(),
|
||||
|
||||
+30
-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_);
|
||||
@@ -1630,6 +1648,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
cfd->ioptions()->table_factory->Name() + " in CF " +
|
||||
cfd->GetName());
|
||||
}
|
||||
int cmp = cfd->user_comparator()->Compare(begin_key, end_key);
|
||||
if (cmp > 0) {
|
||||
// It's an empty range where endpoints appear mistaken. Don't bother
|
||||
// applying it to the DB, and return an error to the user.
|
||||
return Status::InvalidArgument("end key comes before start key");
|
||||
} else if (cmp == 0) {
|
||||
// It's an empty range. Don't bother applying it to the DB.
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
auto ret_status =
|
||||
|
||||
@@ -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)
|
||||
@@ -112,6 +112,7 @@ DECLARE_bool(memtable_whole_key_filtering);
|
||||
DECLARE_int32(open_files);
|
||||
DECLARE_int64(compressed_cache_size);
|
||||
DECLARE_int32(compaction_style);
|
||||
DECLARE_int32(num_levels);
|
||||
DECLARE_int32(level0_file_num_compaction_trigger);
|
||||
DECLARE_int32(level0_slowdown_writes_trigger);
|
||||
DECLARE_int32(level0_stop_writes_trigger);
|
||||
|
||||
@@ -186,6 +186,9 @@ DEFINE_int64(compressed_cache_size, -1,
|
||||
DEFINE_int32(compaction_style, ROCKSDB_NAMESPACE::Options().compaction_style,
|
||||
"");
|
||||
|
||||
DEFINE_int32(num_levels, ROCKSDB_NAMESPACE::Options().num_levels,
|
||||
"Number of levels in the DB");
|
||||
|
||||
DEFINE_int32(level0_file_num_compaction_trigger,
|
||||
ROCKSDB_NAMESPACE::Options().level0_file_num_compaction_trigger,
|
||||
"Level0 compaction start trigger");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -79,6 +79,11 @@ std::string LogFileName(uint64_t number) {
|
||||
return MakeFileName(number, "log");
|
||||
}
|
||||
|
||||
std::string BlobFileName(uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(number, kRocksDBBlobFileExt.c_str());
|
||||
}
|
||||
|
||||
std::string BlobFileName(const std::string& blobdirname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(blobdirname, number, kRocksDBBlobFileExt.c_str());
|
||||
|
||||
@@ -56,6 +56,8 @@ extern std::string LogFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
extern std::string LogFileName(uint64_t number);
|
||||
|
||||
extern std::string BlobFileName(uint64_t number);
|
||||
|
||||
extern std::string BlobFileName(const std::string& bdirname, uint64_t number);
|
||||
|
||||
extern std::string BlobFileName(const std::string& dbname,
|
||||
|
||||
@@ -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
|
||||
+12
-8
@@ -356,8 +356,11 @@ class DB {
|
||||
|
||||
// Removes the database entries in the range ["begin_key", "end_key"), i.e.,
|
||||
// including "begin_key" and excluding "end_key". Returns OK on success, and
|
||||
// a non-OK status on error. It is not an error if no keys exist in the range
|
||||
// ["begin_key", "end_key").
|
||||
// a non-OK status on error. It is not an error if the database does not
|
||||
// contain any existing data in the range ["begin_key", "end_key").
|
||||
//
|
||||
// If "end_key" comes before "start_key" according to the user's comparator,
|
||||
// a `Status::InvalidArgument` is returned.
|
||||
//
|
||||
// This feature is now usable in production, with the following caveats:
|
||||
// 1) Accumulating many range tombstones in the memtable will degrade read
|
||||
@@ -1001,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();
|
||||
|
||||
|
||||
Loaded 100 of 199 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user