mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 14:47:40 +08:00
Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 849dca7221 | |||
| 1039133f2d | |||
| c7226428dd | |||
| d6fdd59c63 | |||
| 35d8e65a04 | |||
| d46e832e94 | |||
| edc258127e | |||
| ec0167eecb | |||
| 0ea7170d7d | |||
| 7e3d3326ce | |||
| 7291a3f813 | |||
| c615689bb5 | |||
| 6f5ba0bf5b | |||
| f2f034ef3b | |||
| 5568aec421 | |||
| 47ad6b81ff | |||
| f1cb83fcf4 | |||
| b9873162f0 | |||
| 4decff6fa8 | |||
| 46e599fc6b | |||
| 266d85fbec | |||
| dc360df81e | |||
| bf6f03f3cd | |||
| af92d4ad11 | |||
| d0f1b49ab6 | |||
| 68829ed89c | |||
| 43549c7d59 | |||
| ba295cda29 | |||
| e446d14093 | |||
| 6d7e3b9faf | |||
| 45828c7215 | |||
| 0a7ba0e548 | |||
| 9c2f64e148 | |||
| 204af1eccc | |||
| d4da02d147 | |||
| a53c571d2d | |||
| b010116d82 | |||
| 0c6e8be9e2 | |||
| 199405192d | |||
| bafec6bb30 | |||
| a478e85697 | |||
| 677f249d6d | |||
| 6aa95f4d0f | |||
| 0f0d2ab95a | |||
| 46ec52499e | |||
| 00b33c2474 | |||
| 24e2c1640d | |||
| 398d72fa61 | |||
| 30a017feca | |||
| 3e955fad09 | |||
| 84ddbd186a | |||
| 90c1d81975 | |||
| 1c9ada59cc | |||
| ea8ccd2267 | |||
| 00e5e1ef7f | |||
| ccc095a016 | |||
| b5c99cc908 | |||
| f00e176c5b | |||
| 02a2c11732 | |||
| 0ef3fdd732 | |||
| 58b841b356 | |||
| f54d7f5fea | |||
| e763e1b623 | |||
| 48cf8da2bb | |||
| 06149429d9 | |||
| 1563801bce | |||
| 2190e96727 | |||
| 0faa026db6 | |||
| 78c2eedb4f | |||
| aa6509d8e4 | |||
| a6d3c762df | |||
| fccc12f386 | |||
| 95583e1532 | |||
| 237b292515 | |||
| a79c7c05e8 | |||
| 5a7e08468a | |||
| b4d88d7128 | |||
| def6a00740 | |||
| 6b77c07379 | |||
| cd2e5cae7f | |||
| 546a63272f | |||
| 35dfbd58dd | |||
| 51c2ea0feb | |||
| 9089373a01 | |||
| 7393ef779c | |||
| 4bcb7fb148 | |||
| e3a06f12d2 | |||
| a9c8d4ef15 | |||
| bb5ed4b1d1 | |||
| 0d5692e02b | |||
| 2f1a3a4d74 | |||
| 9a27ac5d89 | |||
| fcc8a6574d | |||
| 250a51a3f9 | |||
| 6a183d1ae8 | |||
| e3814a8608 | |||
| fe608e32ab | |||
| 34aa245dd8 | |||
| baff91c1ad | |||
| 2e3a00987e | |||
| e1c569c324 | |||
| bbef8c3884 | |||
| d51fcb21f4 | |||
| 53a516ab58 | |||
| 78d1a5ec72 | |||
| a7d32776f0 | |||
| 7f04af32a5 | |||
| b5798bd324 | |||
| 20995c5729 | |||
| 36911f55dd | |||
| 1b0c58d7e7 | |||
| a37d734596 | |||
| 4634c735a8 | |||
| 57056bb606 | |||
| 63f1c0a57d | |||
| 18dcf7f98d | |||
| 3e40a5e832 | |||
| fad14050ae | |||
| 54095d3389 | |||
| ffacaaa3ea | |||
| ed3af9ef99 | |||
| c85f8ccca3 | |||
| c1ed005a21 | |||
| 81cf262ff5 | |||
| 75d57a5d53 | |||
| b45fbc1175 | |||
| 7b57510a17 | |||
| cf0d6aa007 | |||
| 78279350aa | |||
| 3cf562be31 | |||
| 1bdb44de95 | |||
| 022c598abb | |||
| 4b65cfc723 | |||
| 2f09524762 | |||
| b72b3c6f51 | |||
| f0dde49cda | |||
| e59cb2a19b | |||
| 5fac4729cc | |||
| fe187e7e6c | |||
| 8954f830a0 | |||
| d394a6bb48 | |||
| 578f36e431 | |||
| a6c6b8b38c | |||
| 30285ee31c | |||
| 0996e140bd | |||
| 0213990b3a | |||
| 2d04ed65e4 | |||
| 32e31d49d1 | |||
| e27f60b1c8 | |||
| dd49f89466 | |||
| bbcd3b0bd2 | |||
| 54b43563be | |||
| 53863b76f9 | |||
| 175d5d6a9e | |||
| 9871ea4357 | |||
| 42564ada53 | |||
| 2515266725 | |||
| 2edc92bc28 | |||
| 857adf388f | |||
| 07c2738ffa | |||
| f8e2db0717 | |||
| eefd75a228 | |||
| f1c5eaba56 | |||
| 93f69cb93a | |||
| 5e9e5a4702 | |||
| ca75f0a64a | |||
| 4f9f124347 | |||
| 114896c4e0 | |||
| 65c95d9c59 | |||
| e03377c7fd | |||
| be410dede8 | |||
| a6d8e30c05 | |||
| 4c8f336401 | |||
| cfb120f737 | |||
| d956169563 | |||
| 2581c0a5a1 | |||
| 24ad430600 | |||
| c4c1f961e7 | |||
| 30e4e01e05 | |||
| 602fe9454c | |||
| 62578d80c1 | |||
| 76c3fbd651 | |||
| 7bfa88037e | |||
| f662f8f0b6 | |||
| 167ba599ec | |||
| 25ac1697b4 | |||
| 3c208e7616 | |||
| 25fbd9a996 | |||
| 60d83df23d | |||
| 6778690b51 | |||
| cd124215df | |||
| 02693f64fc | |||
| 7fe3b32896 | |||
| 17731a43a6 | |||
| c1cf94c787 | |||
| f6082d1944 | |||
| f5078dde2d | |||
| 33c7d4ccd9 | |||
| c1be8d86c6 | |||
| 4d43c6a6a4 | |||
| b7bc9cc038 | |||
| 792ef10ca8 | |||
| 6a9335dbbb | |||
| 84a04af9a9 | |||
| 05993155ef | |||
| 3ebb7ba7b9 | |||
| 682db81385 | |||
| d9240b548c | |||
| 50e95a63dd | |||
| 47166baeac | |||
| 67b29e26be | |||
| 95667383db | |||
| 5a2a6483dc | |||
| 9b18cc2363 | |||
| 96e3a600ba | |||
| addfe1ef4b | |||
| 386a57e6ef | |||
| 57fcdc264a | |||
| 66a2c44ef4 | |||
| d2a65c59e1 | |||
| 63822eb761 | |||
| a02ed12638 | |||
| 3ef55d2c7c | |||
| d75793d6b4 | |||
| f8b5bb2fd8 | |||
| 57fd4a823b | |||
| ee2b1ec1e8 | |||
| f0804db7f7 | |||
| ebab2e2d42 | |||
| b74999458f | |||
| 7deed2b43c | |||
| 7e38238981 | |||
| 7891af8b53 | |||
| eaaef91178 | |||
| 0552029b5c | |||
| 966b32b57c | |||
| c0208dffbe | |||
| b8cea7cc27 | |||
| f7843f30a8 | |||
| 8e63cad078 | |||
| 731895214b | |||
| 60c09f5fbb | |||
| 10ba50e9eb | |||
| 31d3e41810 | |||
| cc67b22d6e | |||
| 560e984995 | |||
| 8dd0a7e11a | |||
| fb4ae4d810 | |||
| 019aa7074c | |||
| 38bbc879a5 | |||
| 0f3b36964e | |||
| 70aa942153 | |||
| 1a61ba179e | |||
| e2548366e1 | |||
| 8c392a31d7 | |||
| 5a38e18627 | |||
| 17c6325e8a | |||
| ec6c5383d0 | |||
| da29eba43b | |||
| d1b74b0c82 | |||
| 01542400a8 | |||
| cc20ec3689 | |||
| 5a6ad9d52a | |||
| 4e3c3d8c6a | |||
| 1026e794a3 | |||
| 75f7f42d41 | |||
| 88ed1f6ea6 | |||
| 5b2cb64bfb | |||
| 821887036e | |||
| 283d60761e | |||
| 377e004048 | |||
| 92ccae7123 | |||
| d1cab2b64e | |||
| 880411f54c | |||
| d27258d3a6 | |||
| bb38cd03a9 | |||
| cf51d3eb73 | |||
| 983028f097 | |||
| 2a3363d52e | |||
| 593d3de371 | |||
| 2b22baf304 | |||
| ab0542f5ec | |||
| 5df172da2f | |||
| 385049baf2 | |||
| 8c724f5c7f | |||
| 93c2b91740 | |||
| a48a398e7c | |||
| c70586621c | |||
| c2f6e45aa3 | |||
| 6a541afcc4 | |||
| ec48e5c77f | |||
| 0806801dc8 | |||
| aa67bae6cf | |||
| 4748911357 | |||
| 1d6700f9e6 | |||
| be97dbb15c | |||
| 4708a6875c | |||
| fc7476bec1 | |||
| c8f3606731 | |||
| 96a13b4f4b | |||
| 65a9cd6168 | |||
| 8fc3de3c62 | |||
| 1480e6f7cf | |||
| 3fc08fa88e | |||
| 8ae81684e9 | |||
| b4596c6174 | |||
| 34ebadf930 | |||
| e4234fbdcf | |||
| 60beefd6e0 | |||
| c57050b770 | |||
| c319792059 | |||
| 6b3c71f6ed | |||
| f47b4eeb1e | |||
| 382277d0fe | |||
| edcbb36944 | |||
| 885b1c682e | |||
| ffac68367f | |||
| 9a970c81af | |||
| a843df668b | |||
| 2d6e42122b | |||
| 464fb36de9 | |||
| 09713a64b3 | |||
| 72e4190918 | |||
| 9d115d3689 | |||
| f615f5604b | |||
| 5785b1fcb8 | |||
| 82860bd55c | |||
| 2d30aaae47 | |||
| f5148ade10 | |||
| 3c42807794 | |||
| 025b85b4ac | |||
| 044a71e27e | |||
| 64b6452e0c | |||
| 2dd22e5449 | |||
| 2a5915049e | |||
| f46464d383 | |||
| 9a4df72994 | |||
| fce6c892ab | |||
| dcd36a6aee | |||
| 3cd7ea2e8a | |||
| 0e99323ac2 | |||
| ba3c58cab6 | |||
| 7e19a571e9 | |||
| 10ddd59ba7 | |||
| 79810e2d49 | |||
| ab95e293d2 | |||
| 37ae8cc60f | |||
| 3b23b1d8c6 | |||
| 3b9a000c15 | |||
| 0ec90a7cc2 | |||
| 4a90cbf429 | |||
| b97685aef6 | |||
| 725bc403f8 | |||
| e612e31740 | |||
| 8fa4d108a2 | |||
| 825a22c00c | |||
| 26ac24f199 | |||
| 06b37eef7b | |||
| c10cf166fa | |||
| 8a6708f5f2 | |||
| dc5f29f32f |
+12
-4
@@ -21,6 +21,7 @@ env:
|
||||
- TEST_GROUP=platform_dependent # 16-18 minutes
|
||||
- TEST_GROUP=1 # 33-35 minutes
|
||||
- TEST_GROUP=2 # 30-32 minutes
|
||||
- TEST_GROUP=3 # ? minutes - under development
|
||||
# Run java tests
|
||||
- JOB_NAME=java_test # 4-11 minutes
|
||||
# Build ROCKSDB_LITE
|
||||
@@ -36,6 +37,8 @@ matrix:
|
||||
env: TEST_GROUP=1
|
||||
- os: osx
|
||||
env: TEST_GROUP=2
|
||||
- os: osx
|
||||
env: TEST_GROUP=3
|
||||
- os : osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os : linux
|
||||
@@ -49,6 +52,9 @@ install:
|
||||
brew install ccache;
|
||||
PATH=$PATH:/usr/local/opt/ccache/libexec;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
mkdir cmake-dist && curl -sfSL https://cmake.org/files/v3.8/cmake-3.8.1-Linux-x86_64.tar.gz | tar --strip-components=1 -C cmake-dist -xz && export PATH=$PWD/cmake-dist/bin:$PATH;
|
||||
fi
|
||||
|
||||
before_script:
|
||||
# Increase the maximum number of open file descriptors, since some tests use
|
||||
@@ -57,14 +63,16 @@ before_script:
|
||||
|
||||
script:
|
||||
- ${CXX} --version
|
||||
- if [ "${TEST_GROUP}" == 'platform_dependent' ]; then ccache -C && OPT=-DTRAVIS V=1 ROCKSDBTESTS_END=db_block_cache_test make -j4 all_but_some_tests check_some; fi
|
||||
- if [ `command -v ccache` ]; then ccache -C; fi
|
||||
- if [ "${TEST_GROUP}" == 'platform_dependent' ]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_END=db_block_cache_test make -j4 all_but_some_tests check_some; fi
|
||||
- if [ "${TEST_GROUP}" == '1' ]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=db_block_cache_test ROCKSDBTESTS_END=comparator_db_test make -j4 check_some; fi
|
||||
- if [ "${TEST_GROUP}" == '2' ]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=comparator_db_test make -j4 check_some; fi
|
||||
- if [ "${TEST_GROUP}" == '2' ]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=comparator_db_test ROCKSDBTESTS_END=write_prepared_transaction_test make -j4 check_some; fi
|
||||
- if [ "${TEST_GROUP}" == '3' ]; then OPT=-DTRAVIS V=1 ROCKSDBTESTS_START=write_prepared_transaction_test make -j4 check_some; fi
|
||||
- if [ "${JOB_NAME}" == 'java_test' ]; then OPT=-DTRAVIS V=1 make clean jclean && make rocksdbjava jtest; fi
|
||||
- if [ "${JOB_NAME}" == 'lite_build' ]; then OPT="-DTRAVIS -DROCKSDB_LITE" V=1 make -j4 static_lib tools; fi
|
||||
- if [ "${JOB_NAME}" == 'examples' ]; then OPT=-DTRAVIS V=1 make -j4 static_lib; cd examples; make -j4; fi
|
||||
- if [ "${JOB_NAME}" == 'cmake' ]; then mkdir build && cd build && cmake .. && make -j4 rocksdb; fi
|
||||
- if [ "${JOB_NAME}" == 'cmake-mingw' ]; then mkdir build && cd build && cmake .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb; fi
|
||||
- if [ "${JOB_NAME}" == 'cmake' ]; then mkdir build && cd build && cmake -DJNI=1 .. && make -j4 rocksdb rocksdbjni; fi
|
||||
- if [ "${JOB_NAME}" == 'cmake-mingw' ]; then mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni; fi
|
||||
notifications:
|
||||
email:
|
||||
- leveldb@fb.com
|
||||
|
||||
@@ -9,3 +9,4 @@ Sanjay Ghemawat <sanjay@google.com>
|
||||
# Partial list of contributors:
|
||||
Kevin Regan <kevin.d.regan@gmail.com>
|
||||
Johan Bilien <jobi@litl.com>
|
||||
Matthew Von-Maszewski <https://github.com/matthewvon> (Basho Technologies)
|
||||
|
||||
+137
-52
@@ -14,7 +14,7 @@
|
||||
# cd build
|
||||
# 3. Run cmake to generate project files for Windows, add more options to enable required third-party libraries.
|
||||
# See thirdparty.inc for more information.
|
||||
# sample command: cmake -G "Visual Studio 14 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# sample command: cmake -G "Visual Studio 15 Win64" -DWITH_GFLAGS=1 -DWITH_SNAPPY=1 -DWITH_JEMALLOC=1 -DWITH_JNI=1 ..
|
||||
# 4. Then build the project in debug mode (you may want to add /m[:<N>] flag to run msbuild in <N> parallel threads
|
||||
# or simply /m ot use all avail cores)
|
||||
# msbuild rocksdb.sln
|
||||
@@ -32,17 +32,27 @@
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(rocksdb)
|
||||
enable_language(CXX)
|
||||
enable_language(C)
|
||||
enable_language(ASM)
|
||||
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
option(WITH_ZLIB "build with zlib" OFF)
|
||||
if(MSVC)
|
||||
# Defaults currently different for GFLAGS.
|
||||
# We will address find_package work a little later
|
||||
option(WITH_GFLAGS "build with GFlags" OFF)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
@@ -56,8 +66,18 @@ else()
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# No config file for this
|
||||
option(WITH_GFLAGS "build with GFlags" ON)
|
||||
if(WITH_GFLAGS)
|
||||
find_package(gflags)
|
||||
if(gflags_FOUND)
|
||||
add_definitions(-DGFLAGS=1)
|
||||
include_directories(${gflags_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${gflags_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
add_definitions(-DSNAPPY)
|
||||
@@ -65,7 +85,6 @@ else()
|
||||
list(APPEND THIRDPARTY_LIBS ${SNAPPY_LIBRARIES})
|
||||
endif()
|
||||
|
||||
option(WITH_ZLIB "build with zlib" OFF)
|
||||
if(WITH_ZLIB)
|
||||
find_package(zlib REQUIRED)
|
||||
add_definitions(-DZLIB)
|
||||
@@ -81,7 +100,6 @@ else()
|
||||
list(APPEND THIRDPARTY_LIBS ${BZIP2_LIBRARIES})
|
||||
endif()
|
||||
|
||||
option(WITH_LZ4 "build with lz4" OFF)
|
||||
if(WITH_LZ4)
|
||||
find_package(lz4 REQUIRED)
|
||||
add_definitions(-DLZ4)
|
||||
@@ -98,17 +116,7 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
execute_process(COMMAND powershell -noprofile -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -noprofile -Command "Get-Date -format HH:mm:ss" OUTPUT_VARIABLE TIME)
|
||||
string(REGEX REPLACE "(..)_(..)_..(..).*" "\\1/\\2/\\3" DATE "${DATE}")
|
||||
string(REGEX REPLACE "(..):(.....).*" " \\1:\\2" TIME "${TIME}")
|
||||
set(GIT_DATE_TIME "${DATE} ${TIME}")
|
||||
else()
|
||||
execute_process(COMMAND date "+%Y/%m/%d %H:%M:%S" OUTPUT_VARIABLE DATETIME)
|
||||
string(REGEX REPLACE "\n" "" DATETIME ${DATETIME})
|
||||
set(GIT_DATE_TIME "${DATETIME}")
|
||||
endif()
|
||||
string(TIMESTAMP GIT_DATE_TIME "%Y/%m/%d %H:%M:%S" UTC)
|
||||
|
||||
find_package(Git)
|
||||
|
||||
@@ -124,20 +132,19 @@ endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
if(NOT WIN32)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
"./build_tools/version.sh" "major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
endif()
|
||||
set(SH_CMD "sh")
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh full"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION}" ROCKSDB_VERSION)
|
||||
execute_process(COMMAND
|
||||
${SH_CMD} -c "build_tools/version.sh major"
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ROCKSDB_VERSION_MAJOR
|
||||
)
|
||||
string(STRIP "${ROCKSDB_VERSION_MAJOR}" ROCKSDB_VERSION_MAJOR)
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
@@ -155,10 +162,10 @@ target_include_directories(build_version PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util)
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W3 /wd4127 /wd4800 /wd4996 /wd4351")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing")
|
||||
if(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format")
|
||||
endif()
|
||||
@@ -173,31 +180,53 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
CHECK_C_COMPILER_FLAG("-maltivec" HAS_ALTIVEC)
|
||||
if(HAS_ALTIVEC)
|
||||
message(STATUS " HAS_ALTIVEC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -maltivec")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -maltivec")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=power8")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=power8")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
if(PORTABLE)
|
||||
# MSVC does not need a separate compiler flag to enable SSE4.2; if nmmintrin.h
|
||||
# is available, it is available by default.
|
||||
if(FORCE_SSE42 AND NOT MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.2 -mpclmul")
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
if(NOT HAVE_POWER8)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
include(CheckCXXSourceCompiles)
|
||||
if(NOT MSVC)
|
||||
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <cstdint>
|
||||
#include <nmmintrin.h>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
volatile uint32_t x = _mm_crc32_u32(0, 0);
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
" HAVE_SSE42)
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
if(HAVE_SSE42)
|
||||
add_definitions(-DHAVE_SSE42)
|
||||
elseif(FORCE_SSE42)
|
||||
@@ -256,6 +285,20 @@ if(WITH_UBSAN)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(NUMA)
|
||||
if(NUMA_FOUND)
|
||||
add_definitions(-DNUMA)
|
||||
include_directories(${NUMA_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${NUMA_LIBRARIES})
|
||||
endif()
|
||||
|
||||
find_package(TBB)
|
||||
if(TBB_FOUND)
|
||||
add_definitions(-DTBB)
|
||||
include_directories(${TBB_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${TBB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
# Used to run CI build and tests so we can run faster
|
||||
set(OPTIMIZE_DEBUG_DEFAULT 0) # Debug build is unoptimized by default use -DOPTDBG=1 to optimize
|
||||
|
||||
@@ -323,11 +366,8 @@ if(NOT WIN32)
|
||||
endif()
|
||||
|
||||
option(WITH_FALLOCATE "build with fallocate" ON)
|
||||
|
||||
if(WITH_FALLOCATE)
|
||||
set(CMAKE_REQUIRED_FLAGS ${CMAKE_C_FLAGS})
|
||||
include(CheckCSourceCompiles)
|
||||
CHECK_C_SOURCE_COMPILES("
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
@@ -340,12 +380,38 @@ int main() {
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(malloc_usable_size HAVE_MALLOC_USABLE_SIZE)
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
int fd = open(\"/dev/null\", 0);
|
||||
sync_file_range(fd, 0, 1024, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
" HAVE_SYNC_FILE_RANGE_WRITE)
|
||||
if(HAVE_SYNC_FILE_RANGE_WRITE)
|
||||
add_definitions(-DROCKSDB_RANGESYNC_PRESENT)
|
||||
endif()
|
||||
|
||||
CHECK_CXX_SOURCE_COMPILES("
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
(void) PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
}
|
||||
" HAVE_PTHREAD_MUTEX_ADAPTIVE_NP)
|
||||
if(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP)
|
||||
add_definitions(-DROCKSDB_PTHREAD_ADAPTIVE_MUTEX)
|
||||
endif()
|
||||
|
||||
include(CheckCXXSymbolExists)
|
||||
check_cxx_symbol_exists(malloc_usable_size malloc.h HAVE_MALLOC_USABLE_SIZE)
|
||||
if(HAVE_MALLOC_USABLE_SIZE)
|
||||
add_definitions(-DROCKSDB_MALLOC_USABLE_SIZE)
|
||||
endif()
|
||||
|
||||
check_cxx_symbol_exists(sched_getcpu sched.h HAVE_SCHED_GETCPU)
|
||||
if(HAVE_SCHED_GETCPU)
|
||||
add_definitions(-DROCKSDB_SCHED_GETCPU_PRESENT)
|
||||
endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
@@ -448,6 +514,7 @@ set(SOURCES
|
||||
table/block_based_table_factory.cc
|
||||
table/block_based_table_reader.cc
|
||||
table/block_builder.cc
|
||||
table/block_fetcher.cc
|
||||
table/block_prefix_index.cc
|
||||
table/bloom_block.cc
|
||||
table/cuckoo_table_builder.cc
|
||||
@@ -553,18 +620,34 @@ set(SOURCES
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
utilities/transactions/optimistic_transaction.cc
|
||||
utilities/transactions/transaction_base.cc
|
||||
utilities/transactions/pessimistic_transaction_db.cc
|
||||
utilities/transactions/transaction_db_mutex_impl.cc
|
||||
utilities/transactions/pessimistic_transaction.cc
|
||||
utilities/transactions/pessimistic_transaction_db.cc
|
||||
utilities/transactions/snapshot_checker.cc
|
||||
utilities/transactions/transaction_base.cc
|
||||
utilities/transactions/transaction_db_mutex_impl.cc
|
||||
utilities/transactions/transaction_lock_mgr.cc
|
||||
utilities/transactions/transaction_util.cc
|
||||
utilities/transactions/write_prepared_txn.cc
|
||||
utilities/transactions/write_prepared_txn_db.cc
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
|
||||
if(HAVE_SSE42 AND NOT FORCE_SSE42)
|
||||
if(NOT MSVC)
|
||||
set_source_files_properties(
|
||||
util/crc32c.cc
|
||||
PROPERTIES COMPILE_FLAGS "-msse4.2 -mpclmul")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_POWER8)
|
||||
list(APPEND SOURCES
|
||||
util/crc32c_ppc.c
|
||||
util/crc32c_ppc_asm.S)
|
||||
endif(HAVE_POWER8)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
@@ -574,12 +657,12 @@ if(WIN32)
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc
|
||||
port/win/xpress_win.cc)
|
||||
|
||||
|
||||
if(WITH_JEMALLOC)
|
||||
list(APPEND SOURCES
|
||||
port/win/win_jemalloc.cc)
|
||||
endif()
|
||||
|
||||
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
port/port_posix.cc
|
||||
@@ -659,7 +742,7 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
set(package_config_destination ${CMAKE_INSTALL_LIBDIR}/cmake/rocksdb)
|
||||
|
||||
configure_package_config_file(
|
||||
${CMAKE_SOURCE_DIR}/cmake/RocksDBConfig.cmake.in RocksDBConfig.cmake
|
||||
${CMAKE_CURRENT_LIST_DIR}/cmake/RocksDBConfig.cmake.in RocksDBConfig.cmake
|
||||
INSTALL_DESTINATION ${package_config_destination}
|
||||
)
|
||||
|
||||
@@ -838,6 +921,7 @@ if(WITH_TESTS)
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
)
|
||||
@@ -888,17 +972,18 @@ if(WITH_TESTS)
|
||||
|
||||
foreach(sourcefile ${TEST_EXES})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
add_executable(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
set_target_properties(${exename}${ARTIFACT_SUFFIX}
|
||||
set_target_properties(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
OUTPUT_NAME ${exename}${ARTIFACT_SUFFIX}
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} gtest ${LIBS})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TEST_EXES})
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Code of Conduct
|
||||
|
||||
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
|
||||
@@ -1,5 +1,8 @@
|
||||
# Contributing to RocksDB
|
||||
|
||||
## Code of Conduct
|
||||
The code of conduct is described in [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md)
|
||||
|
||||
## Contributor License Agreement ("CLA")
|
||||
|
||||
In order to accept your pull request, we need you to submit a CLA. You
|
||||
|
||||
+41
-5
@@ -1,15 +1,51 @@
|
||||
# Rocksdb Change Log
|
||||
## 5.8.7 (11/28/2017)
|
||||
## Unreleased
|
||||
### Public API Change
|
||||
* Iterator::SeekForPrev is now a pure virtual method. This is to prevent user who implement the Iterator interface fail to implement SeekForPrev by mistake.
|
||||
### Bug Fixes
|
||||
* Fix `DisableFileDeletions()` followed by `GetSortedWalFiles()` to not return obsolete WAL files that `PurgeObsoleteFiles()` is going to delete.
|
||||
* Fix DB::Flush() keep waiting after flush finish under certain condition.
|
||||
|
||||
## 5.10.0 (12/11/2017)
|
||||
### Public API Change
|
||||
* When running `make` with environment variable `USE_SSE` set and `PORTABLE` unset, will use all machine features available locally. Previously this combination only compiled SSE-related features.
|
||||
|
||||
### New Features
|
||||
* Provide lifetime hints when writing files on Linux. This reduces hardware write-amp on storage devices supporting multiple streams.
|
||||
* Add a DB stat, `NUMBER_ITER_SKIP`, which returns how many internal keys were skipped during iterations (e.g., due to being tombstones or duplicate versions of a key).
|
||||
* Add PerfContext counters, `key_lock_wait_count` and `key_lock_wait_time`, which measure the number of times transactions wait on key locks and total amount of time waiting.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix IOError on WAL write doesn't propagate to write group follower
|
||||
* Make iterator invalid on merge error.
|
||||
* Fix performance issue in `IngestExternalFile()` affecting databases with large number of SST files.
|
||||
* Fix possible corruption to LSM structure when `DeleteFilesInRange()` deletes a subset of files spanned by a `DeleteRange()` marker.
|
||||
|
||||
## 5.8.6 (11/20/2017)
|
||||
### Bug Fixes
|
||||
* Fixed aligned_alloc issues with Windows.
|
||||
## 5.9.0 (11/1/2017)
|
||||
### Public API Change
|
||||
* `BackupableDBOptions::max_valid_backups_to_open == 0` now means no backups will be opened during BackupEngine initialization. Previously this condition disabled limiting backups opened.
|
||||
* `DBOptions::preserve_deletes` is a new option that allows one to specify that DB should not drop tombstones for regular deletes if they have sequence number larger than what was set by the new API call `DB::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum)`. Disabled by default.
|
||||
* API call `DB::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum)` was added, users who wish to preserve deletes are expected to periodically call this function to advance the cutoff seqnum (all deletes made before this seqnum can be dropped by DB). It's user responsibility to figure out how to advance the seqnum in the way so the tombstones are kept for the desired period of time, yet are eventually processed in time and don't eat up too much space.
|
||||
* `ReadOptions::iter_start_seqnum` was added; if set to something > 0 user will see 2 changes in iterators behavior 1) only keys written with sequence larger than this parameter would be returned and 2) the `Slice` returned by iter->key() now points to the the memory that keep User-oriented representation of the internal key, rather than user key. New struct `FullKey` was added to represent internal keys, along with a new helper function `ParseFullKey(const Slice& internal_key, FullKey* result);`.
|
||||
* Deprecate trash_dir param in NewSstFileManager, right now we will rename deleted files to <name>.trash instead of moving them to trash directory
|
||||
* Allow setting a custom trash/DB size ratio limit in the SstFileManager, after which files that are to be scheduled for deletion are deleted immediately, regardless of any delete ratelimit.
|
||||
* Return an error on write if write_options.sync = true and write_options.disableWAL = true to warn user of inconsistent options. Previously we will not write to WAL and not respecting the sync options in this case.
|
||||
|
||||
## 5.8.1 (10/23/2017)
|
||||
### New Features
|
||||
* CRC32C is now using the 3-way pipelined SSE algorithm `crc32c_3way` on supported platforms to improve performance. The system will choose to use this algorithm on supported platforms automatically whenever possible. If PCLMULQDQ is not supported it will fall back to the old Fast_CRC32 algorithm.
|
||||
* `DBOptions::writable_file_max_buffer_size` can now be changed dynamically.
|
||||
* `DBOptions::bytes_per_sync`, `DBOptions::compaction_readahead_size`, and `DBOptions::wal_bytes_per_sync` can now be changed dynamically, `DBOptions::wal_bytes_per_sync` will flush all memtables and switch to a new WAL file.
|
||||
* Support dynamic adjustment of rate limit according to demand for background I/O. It can be enabled by passing `true` to the `auto_tuned` parameter in `NewGenericRateLimiter()`. The value passed as `rate_bytes_per_sec` will still be respected as an upper-bound.
|
||||
* Support dynamically changing `ColumnFamilyOptions::compaction_options_fifo`.
|
||||
* Introduce `EventListener::OnStallConditionsChanged()` callback. Users can implement it to be notified when user writes are stalled, stopped, or resumed.
|
||||
* Add a new db property "rocksdb.estimate-oldest-key-time" to return oldest data timestamp. The property is available only for FIFO compaction with compaction_options_fifo.allow_compaction = false.
|
||||
* Upon snapshot release, recompact bottommost files containing deleted/overwritten keys that previously could not be dropped due to the snapshot. This alleviates space-amp caused by long-held snapshots.
|
||||
* Support lower bound on iterators specified via `ReadOptions::iterate_lower_bound`.
|
||||
* Support for differential snapshots (via iterator emitting the sequence of key-values representing the difference between DB state at two different sequence numbers). Supports preserving and emitting puts and regular deletes, doesn't support SingleDeletes, MergeOperator, Blobs and Range Deletes.
|
||||
|
||||
### Bug Fixes
|
||||
* Fix a potential data inconsistency issue during point-in-time recovery. `DB:Open()` will abort if column family inconsistency is found during PIT recovery.
|
||||
* Fix possible metadata corruption in databases using `DeleteRange()`.
|
||||
|
||||
## 5.8.0 (08/30/2017)
|
||||
### Public API Change
|
||||
|
||||
+36
-1
@@ -107,6 +107,41 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
* run `brew tap homebrew/versions; brew install gcc48 --use-llvm` to install gcc 4.8 (or higher).
|
||||
* run `brew install rocksdb`
|
||||
|
||||
* **FreeBSD** (11.01):
|
||||
|
||||
* You can either install RocksDB from the Ports system using `cd /usr/ports/databases/rocksdb && make install`, or you can follow the details below to install dependencies and compile from source code:
|
||||
|
||||
* Install the dependencies for RocksDB:
|
||||
|
||||
export BATCH=YES
|
||||
cd /usr/ports/devel/gmake && make install
|
||||
cd /usr/ports/devel/gflags && make install
|
||||
|
||||
cd /usr/ports/archivers/snappy && make install
|
||||
cd /usr/ports/archivers/bzip2 && make install
|
||||
cd /usr/ports/archivers/liblz4 && make install
|
||||
cd /usr/ports/archivesrs/zstd && make install
|
||||
|
||||
cd /usr/ports/devel/git && make install
|
||||
|
||||
|
||||
* Install the dependencies for RocksJava (optional):
|
||||
|
||||
export BATCH=yes
|
||||
cd /usr/ports/java/openjdk7 && make install
|
||||
|
||||
* Build RocksDB from source:
|
||||
cd ~
|
||||
git clone https://github.com/facebook/rocksdb.git
|
||||
cd rocksdb
|
||||
gmake static_lib
|
||||
|
||||
* Build RocksJava from source (optional):
|
||||
cd rocksdb
|
||||
export JAVA_HOME=/usr/local/openjdk7
|
||||
gmake rocksdbjava
|
||||
|
||||
|
||||
* **iOS**:
|
||||
* Run: `TARGET_OS=IOS make static_lib`. When building the project which uses rocksdb iOS library, make sure to define two important pre-processing macros: `ROCKSDB_LITE` and `IOS_CROSS_COMPILE`.
|
||||
|
||||
@@ -114,7 +149,7 @@ to build a portable binary, add `PORTABLE=1` before your make commands, like thi
|
||||
* For building with MS Visual Studio 13 you will need Update 4 installed.
|
||||
* Read and follow the instructions at CMakeLists.txt
|
||||
* Or install via [vcpkg](https://github.com/microsoft/vcpkg)
|
||||
* run `vcpkg install rocksdb`
|
||||
* run `vcpkg install rocksdb:x64-windows`
|
||||
|
||||
* **AIX 6.1**
|
||||
* Install AIX Toolbox rpms with gcc
|
||||
|
||||
@@ -14,3 +14,4 @@ This is the list of all known third-party language bindings for RocksDB. If some
|
||||
* https://github.com/bh1xuw/rust-rocks
|
||||
* D programming language - https://github.com/b1naryth1ef/rocksdb
|
||||
* Erlang - https://gitlab.com/barrel-db/erlang-rocksdb
|
||||
* Elixir - https://github.com/urbint/rox
|
||||
|
||||
@@ -96,11 +96,21 @@ OPT += -momit-leaf-frame-pointer
|
||||
endif
|
||||
endif
|
||||
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG) and
|
||||
# don't treat warnings as errors
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -maltivec -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -DHAS_ALTIVEC
|
||||
CFLAGS += -DHAS_ALTIVEC
|
||||
HAS_ALTIVEC=1
|
||||
endif
|
||||
|
||||
ifeq (,$(shell $(CXX) -fsyntax-only -mcpu=power8 -xc /dev/null 2>&1))
|
||||
CXXFLAGS += -DHAVE_POWER8
|
||||
CFLAGS += -DHAVE_POWER8
|
||||
HAVE_POWER8=1
|
||||
endif
|
||||
|
||||
# if we're compiling for release, compile without debug code (-DNDEBUG)
|
||||
ifeq ($(DEBUG_LEVEL),0)
|
||||
OPT += -DNDEBUG
|
||||
DISABLE_WARNING_AS_ERROR=1
|
||||
|
||||
ifneq ($(USE_RTTI), 1)
|
||||
CXXFLAGS += -fno-rtti
|
||||
@@ -227,6 +237,11 @@ ifdef COMPILE_WITH_UBSAN
|
||||
PLATFORM_CXXFLAGS += -fsanitize=undefined -DROCKSDB_UBSAN_RUN
|
||||
endif
|
||||
|
||||
ifdef ROCKSDB_VALGRIND_RUN
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_VALGRIND_RUN
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_VALGRIND_RUN
|
||||
endif
|
||||
|
||||
ifndef DISABLE_JEMALLOC
|
||||
ifdef JEMALLOC
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_JEMALLOC -DJEMALLOC_NO_DEMANGLE
|
||||
@@ -290,6 +305,9 @@ LDFLAGS += $(LUA_LIB)
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(NO_THREEWAY_CRC32C), 1)
|
||||
CXXFLAGS += -DNO_THREEWAY_CRC32C
|
||||
endif
|
||||
|
||||
CFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)
|
||||
CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers
|
||||
@@ -322,6 +340,14 @@ util/build_version.cc: FORCE
|
||||
endif
|
||||
|
||||
LIBOBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
LIB_CC_OBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
LIBOBJECTS += $(LIB_SOURCES_C:.c=.o)
|
||||
LIBOBJECTS += $(LIB_SOURCES_ASM:.S=.o)
|
||||
else
|
||||
LIB_CC_OBJECTS = $(LIB_SOURCES:.cc=.o)
|
||||
endif
|
||||
|
||||
LIBOBJECTS += $(TOOL_LIB_SOURCES:.cc=.o)
|
||||
MOCKOBJECTS = $(MOCK_LIB_SOURCES:.cc=.o)
|
||||
|
||||
@@ -477,11 +503,13 @@ TESTS = \
|
||||
object_registry_test \
|
||||
repair_test \
|
||||
env_timed_test \
|
||||
write_prepared_transaction_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_merge_operator_test \
|
||||
db_sst_test \
|
||||
db_test \
|
||||
db_universal_compaction_test \
|
||||
@@ -492,7 +520,8 @@ PARALLEL_TEST = \
|
||||
manual_compaction_test \
|
||||
persistent_cache_test \
|
||||
table_test \
|
||||
transaction_test
|
||||
transaction_test \
|
||||
write_prepared_transaction_test \
|
||||
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
@@ -572,14 +601,35 @@ $(SHARED2): $(SHARED4)
|
||||
$(SHARED3): $(SHARED4)
|
||||
ln -fs $(SHARED4) $(SHARED3)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
SHARED_C_OBJECTS = $(LIB_SOURCES_C:.c=.o)
|
||||
SHARED_ASM_OBJECTS = $(LIB_SOURCES_ASM:.S=.o)
|
||||
SHARED_C_LIBOBJECTS = $(patsubst %.o,shared-objects/%.o,$(SHARED_C_OBJECTS))
|
||||
SHARED_ASM_LIBOBJECTS = $(patsubst %.o,shared-objects/%.o,$(SHARED_ASM_OBJECTS))
|
||||
shared_libobjects = $(patsubst %,shared-objects/%,$(LIB_CC_OBJECTS))
|
||||
else
|
||||
shared_libobjects = $(patsubst %,shared-objects/%,$(LIBOBJECTS))
|
||||
CLEAN_FILES += shared-objects
|
||||
endif
|
||||
|
||||
CLEAN_FILES += shared-objects
|
||||
shared_all_libobjects = $(shared_libobjects)
|
||||
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
shared-ppc-objects = $(SHARED_C_LIBOBJECTS) $(SHARED_ASM_LIBOBJECTS)
|
||||
|
||||
shared-objects/util/crc32c_ppc.o: util/crc32c_ppc.c
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
shared-objects/util/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
endif
|
||||
$(shared_libobjects): shared-objects/%.o: %.cc
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) -c $< -o $@
|
||||
|
||||
$(SHARED4): $(shared_libobjects)
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
shared_all_libobjects = $(shared_libobjects) $(shared-ppc-objects)
|
||||
endif
|
||||
$(SHARED4): $(shared_all_libobjects)
|
||||
$(CXX) $(PLATFORM_SHARED_LDFLAGS)$(SHARED3) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(shared_libobjects) $(LDFLAGS) -o $@
|
||||
|
||||
endif # PLATFORM_SHARED_EXT
|
||||
@@ -701,7 +751,7 @@ gen_parallel_tests:
|
||||
# 107.816 PASS t/DBTest.EncodeDecompressedBlockSizeTest
|
||||
#
|
||||
slow_test_regexp = \
|
||||
^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
^.*SnapshotConcurrentAccessTest.*$$|^t/run-table_test-HarnessTest.Randomized$$|^t/run-db_test-.*(?:FileCreationRandomFailure|EncodeDecompressedBlockSizeTest)$$|^.*RecoverFromCorruptedWALWithoutFlush$$
|
||||
prioritize_long_running_tests = \
|
||||
perl -pe 's,($(slow_test_regexp)),100 $$1,' \
|
||||
| sort -k1,1gr \
|
||||
@@ -831,7 +881,7 @@ ubsan_crash_test:
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_test:
|
||||
DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
@@ -1231,7 +1281,7 @@ env_test: env/env_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
fault_injection_test: db/fault_injection_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
rate_limiter_test: util/rate_limiter_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
rate_limiter_test: util/rate_limiter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
delete_scheduler_test: util/delete_scheduler_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -1396,6 +1446,9 @@ heap_test: util/heap_test.o $(GTEST)
|
||||
transaction_test: utilities/transactions/transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_prepared_transaction_test: utilities/transactions/write_prepared_transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1503,11 +1556,11 @@ BZIP2_DOWNLOAD_BASE ?= http://www.bzip.org
|
||||
SNAPPY_VER ?= 1.1.4
|
||||
SNAPPY_SHA256 ?= 134bfe122fd25599bb807bb8130e7ba6d9bdb851e0b16efcb83ac4f5d0b70057
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/releases/download
|
||||
LZ4_VER ?= 1.7.5
|
||||
LZ4_SHA256 ?= 0190cacd63022ccb86f44fa5041dc6c3804407ad61550ca21c382827319e7e7e
|
||||
LZ4_VER ?= 1.8.0
|
||||
LZ4_SHA256 ?= 2ca482ea7a9bb103603108b5a7510b7592b90158c151ff50a28f1ca8389fccf6
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.2.0
|
||||
ZSTD_SHA256 ?= 4a7e4593a3638276ca7f2a09dc4f38e674d8317bbea51626393ca73fc047cbfb
|
||||
ZSTD_VER ?= 1.3.3
|
||||
ZSTD_SHA256 ?= a77c47153ee7de02626c5b2a097005786b71688be61e9fb81806a011f90b297b
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
|
||||
ifeq ($(PLATFORM), OS_MACOSX)
|
||||
@@ -1521,7 +1574,7 @@ else
|
||||
endif
|
||||
endif
|
||||
ifeq ($(PLATFORM), OS_FREEBSD)
|
||||
JAVA_INCLUDE += -I$(JAVA_HOME)/include/freebsd
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/freebsd
|
||||
ROCKSDBJNILIB = librocksdbjni-freebsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH)-freebsd$(ARCH).jar
|
||||
endif
|
||||
@@ -1547,7 +1600,7 @@ libz.a:
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf zlib-$(ZLIB_VER).tar.gz
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${EXTRA_CFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --static && make
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${EXTRA_CFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --static && $(MAKE)
|
||||
cp zlib-$(ZLIB_VER)/libz.a .
|
||||
|
||||
libbz2.a:
|
||||
@@ -1559,7 +1612,7 @@ libbz2.a:
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf bzip2-$(BZIP2_VER).tar.gz
|
||||
cd bzip2-$(BZIP2_VER) && make CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 ${EXTRA_CFLAGS}' AR='ar ${EXTRA_ARFLAGS}'
|
||||
cd bzip2-$(BZIP2_VER) && $(MAKE) CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 ${EXTRA_CFLAGS}' AR='ar ${EXTRA_ARFLAGS}'
|
||||
cp bzip2-$(BZIP2_VER)/libbz2.a .
|
||||
|
||||
libsnappy.a:
|
||||
@@ -1572,7 +1625,7 @@ libsnappy.a:
|
||||
fi
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
cd snappy-$(SNAPPY_VER) && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --with-pic --enable-static --disable-shared
|
||||
cd snappy-$(SNAPPY_VER) && make ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER) && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/.libs/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
@@ -1585,7 +1638,7 @@ liblz4.a:
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf lz4-$(LZ4_VER).tar.gz
|
||||
cd lz4-$(LZ4_VER)/lib && make CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' all
|
||||
cd lz4-$(LZ4_VER)/lib && $(MAKE) CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' all
|
||||
cp lz4-$(LZ4_VER)/lib/liblz4.a .
|
||||
|
||||
libzstd.a:
|
||||
@@ -1598,29 +1651,45 @@ libzstd.a:
|
||||
exit 1; \
|
||||
fi
|
||||
tar xvzf zstd-$(ZSTD_VER).tar.gz
|
||||
cd zstd-$(ZSTD_VER)/lib && make CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' all
|
||||
cd zstd-$(ZSTD_VER)/lib && DESTDIR=. PREFIX= $(MAKE) CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' install
|
||||
cp zstd-$(ZSTD_VER)/lib/libzstd.a .
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC and a fixed set of static compression libraries
|
||||
java_static_libobjects = $(patsubst %,jls/%,$(LIBOBJECTS))
|
||||
java_static_libobjects = $(patsubst %,jls/%,$(LIB_CC_OBJECTS))
|
||||
CLEAN_FILES += jls
|
||||
java_static_all_libobjects = $(java_static_libobjects)
|
||||
|
||||
ifneq ($(ROCKSDB_JAVA_NO_COMPRESSION), 1)
|
||||
JAVA_COMPRESSIONS = libz.a libbz2.a libsnappy.a liblz4.a libzstd.a
|
||||
endif
|
||||
|
||||
JAVA_STATIC_FLAGS = -DZLIB -DBZIP2 -DSNAPPY -DLZ4 -DZSTD
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-$(ZLIB_VER) -I./bzip2-$(BZIP2_VER) -I./snappy-$(SNAPPY_VER) -I./lz4-$(LZ4_VER)/lib -I./zstd-$(ZSTD_VER)/lib
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-$(ZLIB_VER) -I./bzip2-$(BZIP2_VER) -I./snappy-$(SNAPPY_VER) -I./lz4-$(LZ4_VER)/lib -I./zstd-$(ZSTD_VER)/lib/include
|
||||
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
JAVA_STATIC_C_LIBOBJECTS = $(patsubst %.c.o,jls/%.c.o,$(LIB_SOURCES_C:.c=.o))
|
||||
JAVA_STATIC_ASM_LIBOBJECTS = $(patsubst %.S.o,jls/%.S.o,$(LIB_SOURCES_ASM:.S=.o))
|
||||
|
||||
java_static_ppc_libobjects = $(JAVA_STATIC_C_LIBOBJECTS) $(JAVA_STATIC_ASM_LIBOBJECTS)
|
||||
|
||||
jls/util/crc32c_ppc.o: util/crc32c_ppc.c
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES) -c $< -o $@
|
||||
|
||||
jls/util/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES) -c $< -o $@
|
||||
|
||||
java_static_all_libobjects += $(java_static_ppc_libobjects)
|
||||
endif
|
||||
|
||||
$(java_static_libobjects): jls/%.o: %.cc $(JAVA_COMPRESSIONS)
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
rocksdbjavastatic: $(java_static_libobjects)
|
||||
rocksdbjavastatic: $(java_static_all_libobjects)
|
||||
cd java;$(MAKE) javalib;
|
||||
rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC \
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) \
|
||||
$(java_static_libobjects) $(COVERAGEFLAGS) \
|
||||
$(java_static_all_libobjects) $(COVERAGEFLAGS) \
|
||||
$(JAVA_COMPRESSIONS) $(JAVA_STATIC_LDFLAGS)
|
||||
cd java/target;strip $(STRIPFLAGS) $(ROCKSDBJNILIB)
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
@@ -1650,6 +1719,14 @@ rocksdbjavastaticreleasedocker: rocksdbjavastatic
|
||||
cd java/target;jar -uf $(ROCKSDB_JAR_ALL) librocksdbjni-*.so librocksdbjni-*.jnilib
|
||||
cd java/target/classes;jar -uf ../$(ROCKSDB_JAR_ALL) org/rocksdb/*.class org/rocksdb/util/*.class
|
||||
|
||||
rocksdbjavastaticdockerppc64le:
|
||||
mkdir -p java/target
|
||||
DOCKER_LINUX_PPC64LE_CONTAINER=`docker ps -aqf name=rocksdb_linux_ppc64le-be`; \
|
||||
if [ -z "$$DOCKER_LINUX_PPC64LE_CONTAINER" ]; then \
|
||||
docker container create --attach stdin --attach stdout --attach stderr --volume `pwd`:/rocksdb-host --name rocksdb_linux_ppc64le-be evolvedbinary/rocksjava:centos7_ppc64le-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh; \
|
||||
fi
|
||||
docker start -a rocksdb_linux_ppc64le-be
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
rocksdbjavastaticpublishdocker: rocksdbjavastaticreleasedocker rocksdbjavastaticpublishcentral
|
||||
@@ -1664,13 +1741,36 @@ rocksdbjavastaticpublishcentral:
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/rocksjni.pom -Dfile=java/target/rocksdbjni-$(ROCKSDB_MAJOR).$(ROCKSDB_MINOR).$(ROCKSDB_PATCH).jar
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC
|
||||
java_libobjects = $(patsubst %,jl/%,$(LIBOBJECTS))
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
JAVA_CC_OBJECTS = $(SHARED_CC_OBJECTS)
|
||||
JAVA_C_OBJECTS = $(SHARED_C_OBJECTS)
|
||||
JAVA_ASM_OBJECTS = $(SHARED_ASM_OBJECTS)
|
||||
|
||||
JAVA_C_LIBOBJECTS = $(patsubst %.c.o,jl/%.c.o,$(JAVA_C_OBJECTS))
|
||||
JAVA_ASM_LIBOBJECTS = $(patsubst %.S.o,jl/%.S.o,$(JAVA_ASM_OBJECTS))
|
||||
endif
|
||||
|
||||
java_libobjects = $(patsubst %,jl/%,$(LIB_CC_OBJECTS))
|
||||
CLEAN_FILES += jl
|
||||
java_all_libobjects = $(java_libobjects)
|
||||
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
java_ppc_libobjects = $(JAVA_C_LIBOBJECTS) $(JAVA_ASM_LIBOBJECTS)
|
||||
|
||||
jl/crc32c_ppc.o: util/crc32c_ppc.c
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
jl/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
java_all_libobjects += $(java_ppc_libobjects)
|
||||
endif
|
||||
|
||||
$(java_libobjects): jl/%.o: %.cc
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
rocksdbjava: $(java_libobjects)
|
||||
|
||||
|
||||
rocksdbjava: $(java_all_libobjects)
|
||||
$(AM_V_GEN)cd java;$(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(AM_V_at)$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(java_libobjects) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
@@ -1725,30 +1825,54 @@ IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBu
|
||||
lipo ios-x86/$@ ios-arm/$@ -create -output $@
|
||||
|
||||
else
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
util/crc32c_ppc.o: util/crc32c_ppc.c
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
util/crc32c_ppc_asm.o: util/crc32c_ppc_asm.S
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
endif
|
||||
.cc.o:
|
||||
$(AM_V_CC)$(CXX) $(CXXFLAGS) -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
.c.o:
|
||||
$(AM_V_CC)$(CC) $(CFLAGS) -c $< -o $@
|
||||
endif
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(EXP_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.d)
|
||||
DEPFILES = $(all_sources:.cc=.cc.d)
|
||||
|
||||
# Add proper dependency support so changing a .h file forces a .cc file to
|
||||
# rebuild.
|
||||
|
||||
# The .d file indicates .cc file's dependencies on .h files. We generate such
|
||||
# dependency by g++'s -MM option, whose output is a make dependency rule.
|
||||
$(DEPFILES): %.d: %.cc
|
||||
%.cc.d: %.cc
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.cc=.o)' "$<" -o '$@'
|
||||
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
DEPFILES_C = $(LIB_SOURCES_C:.c=.c.d)
|
||||
DEPFILES_ASM = $(LIB_SOURCES_ASM:.S=.S.d)
|
||||
|
||||
%.c.d: %.c
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.c=.o)' "$<" -o '$@'
|
||||
|
||||
%.S.d: %.S
|
||||
@$(CXX) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) \
|
||||
-MM -MT'$@' -MT'$(<:.S=.o)' "$<" -o '$@'
|
||||
|
||||
$(DEPFILES_C): %.c.d
|
||||
|
||||
$(DEPFILES_ASM): %.S.d
|
||||
depend: $(DEPFILES) $(DEPFILES_C) $(DEPFILES_ASM)
|
||||
else
|
||||
depend: $(DEPFILES)
|
||||
endif
|
||||
|
||||
# if the make goal is either "clean" or "format", we shouldn't
|
||||
# try to import the *.d files.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
|
||||
@@ -5,12 +5,14 @@ At Facebook, we use RocksDB as storage engines in multiple data management servi
|
||||
|
||||
1. MyRocks -- https://github.com/MySQLOnRocksDB/mysql-5.6
|
||||
2. MongoRocks -- https://github.com/mongodb-partners/mongo-rocks
|
||||
3. ZippyDB -- Facebook's distributed key-value store with Paxos-style replication, built on top of RocksDB.[*] https://www.youtube.com/watch?v=DfiN7pG0D0khtt
|
||||
4. Laser -- Laser is a high query throughput, low (millisecond) latency, key-value storage service built on top of RocksDB.[*]
|
||||
3. ZippyDB -- Facebook's distributed key-value store with Paxos-style replication, built on top of RocksDB.[1] https://www.youtube.com/watch?v=DfiN7pG0D0khtt
|
||||
4. Laser -- Laser is a high query throughput, low (millisecond) latency, key-value storage service built on top of RocksDB.[1]
|
||||
4. Dragon -- a distributed graph query engine. https://code.facebook.com/posts/1737605303120405/dragon-a-distributed-graph-query-engine/
|
||||
5. Stylus -- a low-level stream processing framework writtenin C++.[*]
|
||||
5. Stylus -- a low-level stream processing framework writtenin C++.[1]
|
||||
6. LogDevice -- a distributed data store for logs [2]
|
||||
|
||||
[*] https://research.facebook.com/publications/realtime-data-processing-at-facebook/
|
||||
[1] https://research.facebook.com/publications/realtime-data-processing-at-facebook/
|
||||
[2] https://code.facebook.com/posts/357056558062811/logdevice-a-distributed-data-store-for-logs/
|
||||
|
||||
## LinkedIn
|
||||
Two different use cases at Linkedin are using RocksDB as a storage engine:
|
||||
@@ -83,3 +85,6 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
|
||||
## LzLabs
|
||||
LzLabs is using RocksDB as a storage engine in their multi-database distributed framework to store application configuration and user data.
|
||||
|
||||
## ProfaneDB
|
||||
[ProfaneDB](https://profanedb.gitlab.io/) is a database for Protocol Buffers, and uses RocksDB for storage. It is accessible via gRPC, and the schema is defined using directly `.proto` files.
|
||||
|
||||
+2
-2
@@ -3,12 +3,12 @@ image: Visual Studio 2015
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DOPTDBG=1 -DXPRESS=1 -DPORTABLE=1 ..
|
||||
- cmake -G "Visual Studio 14 Win64" -DOPTDBG=1 -DWITH_XPRESS=1 -DPORTABLE=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
verbosity: normal
|
||||
test:
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test2,db_test,env_basic_test,env_test -Concurrency 8
|
||||
|
||||
@@ -3,19 +3,17 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
import targets_cfg
|
||||
import pprint
|
||||
|
||||
# TODO(tec): replace this with PrettyPrinter
|
||||
def pretty_list(lst, indent=6):
|
||||
def pretty_list(lst, indent=8):
|
||||
if lst is None or len(lst) == 0:
|
||||
return ""
|
||||
|
||||
if len(lst) == 1:
|
||||
return "\"%s\"" % lst[0]
|
||||
|
||||
|
||||
separator = "\",\n%s\"" % (" " * indent)
|
||||
res = separator.join(lst)
|
||||
res = "\n" + (" " * indent) + "\"" + res + "\",\n" + (" " * (indent - 2))
|
||||
res = separator.join(sorted(lst))
|
||||
res = "\n" + (" " * indent) + "\"" + res + "\",\n" + (" " * (indent - 4))
|
||||
return res
|
||||
|
||||
|
||||
@@ -27,7 +25,7 @@ class TARGETSBuilder:
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
self.tests_cfg = []
|
||||
self.tests_cfg = ""
|
||||
|
||||
def __del__(self):
|
||||
self.targets_file.close()
|
||||
@@ -37,8 +35,8 @@ class TARGETSBuilder:
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
self.targets_file.write(targets_cfg.library_template % (
|
||||
name,
|
||||
headers,
|
||||
pretty_list(srcs),
|
||||
headers,
|
||||
pretty_list(deps)))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
@@ -53,13 +51,13 @@ class TARGETSBuilder:
|
||||
exec_mode = "serial"
|
||||
if is_parallel:
|
||||
exec_mode = "parallel"
|
||||
self.tests_cfg.append([test_name, str(src), str(exec_mode)])
|
||||
self.tests_cfg += targets_cfg.test_cfg_template % (
|
||||
test_name,
|
||||
str(src),
|
||||
str(exec_mode))
|
||||
|
||||
self.total_test = self.total_test + 1
|
||||
|
||||
def flush_tests(self):
|
||||
self.targets_file.write(targets_cfg.unittests_template % (
|
||||
pprint.PrettyPrinter().pformat(self.tests_cfg)
|
||||
))
|
||||
|
||||
self.tests_cfg = []
|
||||
self.targets_file.write(targets_cfg.unittests_template % self.tests_cfg)
|
||||
self.tests_cfg = ""
|
||||
|
||||
+89
-71
@@ -2,123 +2,141 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
rocksdb_target_header = """
|
||||
import os
|
||||
rocksdb_target_header = """REPO_PATH = package_name() + "/"
|
||||
|
||||
TARGETS_PATH = os.path.dirname(__file__)
|
||||
REPO_PATH = "rocksdb/src/"
|
||||
BUCK_BINS = "buck-out/gen/" + REPO_PATH
|
||||
|
||||
TEST_RUNNER = REPO_PATH + "buckifier/rocks_test_runner.sh"
|
||||
|
||||
rocksdb_compiler_flags = [
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
"-fno-builtin-memcmp",
|
||||
"-DROCKSDB_PLATFORM_POSIX",
|
||||
"-DROCKSDB_LIB_IO_POSIX",
|
||||
"-DROCKSDB_FALLOCATE_PRESENT",
|
||||
"-DROCKSDB_MALLOC_USABLE_SIZE",
|
||||
"-DROCKSDB_RANGESYNC_PRESENT",
|
||||
"-DROCKSDB_SCHED_GETCPU_PRESENT",
|
||||
"-DROCKSDB_SUPPORT_THREAD_LOCAL",
|
||||
"-DOS_LINUX",
|
||||
# Flags to enable libs we include
|
||||
"-DSNAPPY",
|
||||
"-DZLIB",
|
||||
"-DBZIP2",
|
||||
"-DLZ4",
|
||||
"-DZSTD",
|
||||
"-DGFLAGS=gflags",
|
||||
"-DNUMA",
|
||||
"-DTBB",
|
||||
# Needed to compile in fbcode
|
||||
"-Wno-expansion-to-defined",
|
||||
]
|
||||
|
||||
rocksdb_external_deps = [
|
||||
('bzip2', None, 'bz2'),
|
||||
('snappy', None, "snappy"),
|
||||
('zlib', None, 'z'),
|
||||
('gflags', None, 'gflags'),
|
||||
('lz4', None, 'lz4'),
|
||||
('zstd', None),
|
||||
('tbb', None),
|
||||
("numa", None, "numa"),
|
||||
("googletest", None, "gtest"),
|
||||
("bzip2", None, "bz2"),
|
||||
("snappy", None, "snappy"),
|
||||
("zlib", None, "z"),
|
||||
("gflags", None, "gflags"),
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("numa", None, "numa"),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
rocksdb_preprocessor_flags = [
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
# Directories with files for #include
|
||||
"-I" + REPO_PATH + "include/",
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
rocksdb_arch_preprocessor_flags = {
|
||||
"x86_64": ["-DHAVE_SSE42"],
|
||||
"x86_64": ["-DHAVE_SSE42"],
|
||||
}
|
||||
|
||||
build_mode = read_config("fbcode", "build_mode")
|
||||
|
||||
is_opt_mode = build_mode.startswith("opt")
|
||||
|
||||
# -DNDEBUG is added by default in opt mode in fbcode. But adding it twice
|
||||
# doesn't harm and avoid forgetting to add it.
|
||||
if is_opt_mode:
|
||||
rocksdb_compiler_flags.append("-DNDEBUG")
|
||||
"""
|
||||
|
||||
|
||||
library_template = """
|
||||
cpp_library(
|
||||
name = "%s",
|
||||
headers = %s,
|
||||
srcs = [%s],
|
||||
deps = [%s],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
headers = %s,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
deps = [%s],
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
"""
|
||||
|
||||
binary_template = """
|
||||
cpp_binary(
|
||||
name = "%s",
|
||||
srcs = [%s],
|
||||
deps = [%s],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
name = "%s",
|
||||
srcs = [%s],
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
deps = [%s],
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
"""
|
||||
|
||||
test_cfg_template = """ [
|
||||
"%s",
|
||||
"%s",
|
||||
"%s",
|
||||
],
|
||||
"""
|
||||
|
||||
unittests_template = """
|
||||
# [test_name, test_src, test_type]
|
||||
ROCKS_TESTS = %s
|
||||
|
||||
ROCKS_TESTS = [
|
||||
%s]
|
||||
|
||||
# Generate a test rule for each entry in ROCKS_TESTS
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
if not is_opt_mode:
|
||||
for test_cfg in ROCKS_TESTS:
|
||||
test_name = test_cfg[0]
|
||||
test_cc = test_cfg[1]
|
||||
ttype = "gtest" if test_cfg[2] == "parallel" else "simple"
|
||||
test_bin = test_name + "_bin"
|
||||
|
||||
cpp_binary (
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
deps = [":rocksdb_test_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
cpp_binary (
|
||||
name = test_bin,
|
||||
srcs = [test_cc],
|
||||
deps = [":rocksdb_test_lib"],
|
||||
preprocessor_flags = rocksdb_preprocessor_flags,
|
||||
arch_preprocessor_flags = rocksdb_arch_preprocessor_flags,
|
||||
compiler_flags = rocksdb_compiler_flags,
|
||||
external_deps = rocksdb_external_deps,
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin]
|
||||
)
|
||||
custom_unittest(
|
||||
name = test_name,
|
||||
type = ttype,
|
||||
deps = [":" + test_bin],
|
||||
command = [TEST_RUNNER, BUCK_BINS + test_bin]
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = "make_rocksdbjavastatic",
|
||||
type = "simple",
|
||||
command = ["internal_repo_rocksdb/make_rocksdbjavastatic.sh"],
|
||||
type = "simple",
|
||||
)
|
||||
|
||||
custom_unittest(
|
||||
name = "make_rocksdb_lite_release",
|
||||
type = "simple",
|
||||
command = ["internal_repo_rocksdb/make_rocksdb_lite_release.sh"],
|
||||
type = "simple",
|
||||
)
|
||||
"""
|
||||
|
||||
+204
-150
@@ -141,6 +141,7 @@ case "$TARGET_OS" in
|
||||
;;
|
||||
FreeBSD)
|
||||
PLATFORM=OS_FREEBSD
|
||||
CXX=clang++
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_FREEBSD"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
|
||||
# PORT_FILES=port/freebsd/freebsd_specific.cc
|
||||
@@ -210,119 +211,137 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether Snappy library is installed
|
||||
# http://code.google.com/p/snappy/
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <snappy.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_SNAPPY; then
|
||||
# Test whether Snappy library is installed
|
||||
# http://code.google.com/p/snappy/
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <snappy.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lsnappy"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lsnappy"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DSNAPPY"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lsnappy"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lsnappy"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether gflags library is installed
|
||||
# http://gflags.github.io/gflags/
|
||||
# check if the namespace is gflags
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace gflags;
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_GFLAGS; then
|
||||
# Test whether gflags library is installed
|
||||
# http://gflags.github.io/gflags/
|
||||
# check if the namespace is gflags
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=gflags"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
else
|
||||
# check if namespace is google
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace google;
|
||||
int main() {}
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=1"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
else
|
||||
# check if namespace is google
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null << EOF
|
||||
#include <gflags/gflags.h>
|
||||
using namespace google;
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DGFLAGS=google"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lgflags"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether zlib library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zlib.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_ZLIB; then
|
||||
# Test whether zlib library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zlib.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DZLIB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lz"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lz"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DZLIB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lz"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lz"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether bzip library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <bzlib.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_BZIP; then
|
||||
# Test whether bzip library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <bzlib.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DBZIP2"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbz2"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lbz2"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DBZIP2"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbz2"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lbz2"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether lz4 library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <lz4.h>
|
||||
#include <lz4hc.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_LZ4; then
|
||||
# Test whether lz4 library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <lz4.h>
|
||||
#include <lz4hc.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DLZ4"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -llz4"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -llz4"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DLZ4"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -llz4"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -llz4"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether zstd library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zstd.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_ZSTD; then
|
||||
# Test whether zstd library is installed
|
||||
$CXX $CFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <zstd.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DZSTD"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lzstd"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lzstd"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DZSTD"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lzstd"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lzstd"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether numa is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_NUMA; then
|
||||
# Test whether numa is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DNUMA"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lnuma"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lnuma"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DNUMA"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lnuma"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lnuma"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether tbb is available
|
||||
$CXX $CFLAGS $LDFLAGS -x c++ - -o /dev/null -ltbb 2>/dev/null <<EOF
|
||||
#include <tbb/tbb.h>
|
||||
int main() {}
|
||||
if ! test $ROCKSDB_DISABLE_TBB; then
|
||||
# Test whether tbb is available
|
||||
$CXX $CFLAGS $LDFLAGS -x c++ - -o /dev/null -ltbb 2>/dev/null <<EOF
|
||||
#include <tbb/tbb.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
# JEMALLOC can be enabled either using the flag (like here) or by
|
||||
# providing direct link to the jemalloc library
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
else
|
||||
if ! test $ROCKSDB_DISABLE_JEMALLOC; then
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
# This will enable some preprocessor identifiers in the Makefile
|
||||
JEMALLOC=1
|
||||
# JEMALLOC can be enabled either using the flag (like here) or by
|
||||
# providing direct link to the jemalloc library
|
||||
WITH_JEMALLOC_FLAG=1
|
||||
fi
|
||||
fi
|
||||
if ! test $JEMALLOC && ! test $ROCKSDB_DISABLE_TCMALLOC; then
|
||||
# jemalloc is not available. Let's try tcmalloc
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null \
|
||||
-ltcmalloc 2>/dev/null; then
|
||||
@@ -331,88 +350,100 @@ EOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether malloc_usable_size is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <malloc.h>
|
||||
int main() {
|
||||
size_t res = malloc_usable_size(0);
|
||||
return 0;
|
||||
}
|
||||
if ! test $ROCKSDB_DISABLE_MALLOC_USABLE_SIZE; then
|
||||
# Test whether malloc_usable_size is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <malloc.h>
|
||||
int main() {
|
||||
size_t res = malloc_usable_size(0);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether PTHREAD_MUTEX_ADAPTIVE_NP mutex type is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
int x = PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
return 0;
|
||||
}
|
||||
if ! test $ROCKSDB_DISABLE_PTHREAD_MUTEX_ADAPTIVE_NP; then
|
||||
# Test whether PTHREAD_MUTEX_ADAPTIVE_NP mutex type is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <pthread.h>
|
||||
int main() {
|
||||
int x = PTHREAD_MUTEX_ADAPTIVE_NP;
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_PTHREAD_ADAPTIVE_MUTEX"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_PTHREAD_ADAPTIVE_MUTEX"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether backtrace is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
backtrace_symbols(frames, backtrace(frames, 1));
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
else
|
||||
# Test whether execinfo library is installed
|
||||
$CXX $CFLAGS -lexecinfo -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>
|
||||
if ! test $ROCKSDB_DISABLE_BACKTRACE; then
|
||||
# Test whether backtrace is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
backtrace_symbols(frames, backtrace(frames, 1));
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lexecinfo"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lexecinfo"
|
||||
else
|
||||
# Test whether execinfo library is installed
|
||||
$CXX $CFLAGS -lexecinfo -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <execinfo.h>
|
||||
int main() {
|
||||
void* frames[1];
|
||||
backtrace_symbols(frames, backtrace(frames, 1));
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_BACKTRACE"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lexecinfo"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lexecinfo"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test if -pg is supported
|
||||
$CXX $CFLAGS -pg -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
if ! test $ROCKSDB_DISABLE_PG; then
|
||||
# Test if -pg is supported
|
||||
$CXX $CFLAGS -pg -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
int main() {
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PROFILING_FLAGS=-pg
|
||||
if [ "$?" = 0 ]; then
|
||||
PROFILING_FLAGS=-pg
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether sync_file_range is supported for compatibility with an old glibc
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
sync_file_range(fd, 0, 1024, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
if ! test $ROCKSDB_DISABLE_SYNC_FILE_RANGE; then
|
||||
# Test whether sync_file_range is supported for compatibility with an old glibc
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <fcntl.h>
|
||||
int main() {
|
||||
int fd = open("/dev/null", 0);
|
||||
sync_file_range(fd, 0, 1024, SYNC_FILE_RANGE_WRITE);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_RANGESYNC_PRESENT"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_RANGESYNC_PRESENT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Test whether sched_getcpu is supported
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
}
|
||||
if ! test $ROCKSDB_DISABLE_SCHED_GETCPU; then
|
||||
# Test whether sched_getcpu is supported
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <sched.h>
|
||||
int main() {
|
||||
int cpuid = sched_getcpu();
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DROCKSDB_SCHED_GETCPU_PRESENT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -444,18 +475,23 @@ if test "$USE_HDFS"; then
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS $HDFS_LDFLAGS"
|
||||
fi
|
||||
|
||||
if test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2"
|
||||
elif test -z "$PORTABLE"; then
|
||||
if test -z "$PORTABLE"; then
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# Tune for this POWER processor, treating '+' models as base models
|
||||
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^arm`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" != AIX ] && [ "$TARGET_OS" != SunOS ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
fi
|
||||
elif test "$USE_SSE"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -msse4.2 -mpclmul"
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
@@ -469,6 +505,24 @@ if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_SSE42"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use SSE intrinsics, disabling"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$CXX $PLATFORM_CXXFLAGS $COMMON_FLAGS -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <cstdint>
|
||||
#include <wmmintrin.h>
|
||||
int main() {
|
||||
const auto a = _mm_set_epi64x(0, 0);
|
||||
const auto b = _mm_set_epi64x(0, 0);
|
||||
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
|
||||
auto d = _mm_cvtsi128_si64(c);
|
||||
}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DHAVE_PCLMUL"
|
||||
elif test "$USE_SSE"; then
|
||||
echo "warning: USE_SSE specified but compiler could not use PCLMUL intrinsics, disabling"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# iOS doesn't support thread-local storage, but this check would erroneously
|
||||
|
||||
+18
-18
@@ -1,18 +1,18 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/2928bb3ed95bf64f5b388ee88c30dc74710c3b35/5.x/centos6-native/f4950a1
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/a5fea028cb7ba43498976e1f8054b0b2e790c295/stable/centos6-native/6aaf4de
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/7a9099f6587ee4378c0b1fa32bb8934019d30ca4/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/3b7c6469854dfc7832a1c3cc5b86919a84e5f865/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/d7861abe6f0e27ab98c9303b95a662f0e4cdedb5/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0815d59804160c96caac5f27ca004f51af893dc6/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/c15a4f5f619a2930478d01e2e34dc1e0652b0873/1.1.4/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f905a5e1032fb30c05db3d3752319857388c0c49/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/8d60633d822a2a55849c73db24e74a25e52b71db/master/gcc-5-glibc-2.23/1c32b4b
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/8db74270cd6d0212ac92d69e7fc7beefe617d772/trunk/gcc-5-glibc-2.23/b1847cb
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/90c9734afc5579c9d1db529fa788d09f97763b85/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/9e829389ef61b92c62de8748c80169aaf25ce1f0/2.26.1/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.11.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/61e4abf5813bbc39bc4f548757ccfcadde175a48/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/8219ec1bcedf8ad9da05e121e193364de2cc4f61/5.x/centos6-native/c447969
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/64d8d58e3d84f8bde7a029763d4f5baf39d0d5b9/stable/centos6-native/6aaf4de
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/ba9be983c81de7299b59fe71950c664a84dcb5f8/5.x/gcc-5-glibc-2.23/339d858
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/f20197cf3d4bd50339c9777aaa0b2ccadad9e2cb/2.23/gcc-5-glibc-2.23/ca1d1c0
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/6427ce8c7496e4ab06c2da81543b94c0de8be3d0/1.1.3/gcc-5-glibc-2.23/9bc6787
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/8f1e8b867d26efef93eac2fabbdb2e1d512665d7/1.2.8/gcc-5-glibc-2.23/9bc6787
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/70471c0571559fe0af7db6d7e8860b93a7eadfe1/1.0.6/gcc-5-glibc-2.23/9bc6787
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/453c89d6f0e68cdf1c151c769197fabedad9cac8/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/00a40fa5f8bd2cd0622f2e868552793aef37ccf4/1.3.0/gcc-5-glibc-2.23/03859b5
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/47eef08f9acb77de982fbda6047c26d330739538/2.2.0/gcc-5-glibc-2.23/9bc6787
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/4414ddc78df8008b35cc4adac23590ad29148584/master/gcc-5-glibc-2.23/d506c82
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/9d7ae2693d05d62f9a579cb21e6b717cf257a75d/2.0.11/gcc-5-glibc-2.23/9bc6787
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/2b2dd58e3a52ccf2c1d827def59e5f740de0ad15/1.2/gcc-5-glibc-2.23/b443de1
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/379addf7ab2468a2b4293b47456cfcd1c9cb318d/4.3/gcc-5-glibc-2.23/9bc6787
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/3f68f5fe65a85b7c2d3e66852268fbd1efdb3151/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b9fab0aec99d9c36408e810b2677e91c12807afd/2.28/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/423431d61786b20bcc3bde8972901130cb29e6b3/3.11.0/gcc-5-glibc-2.23/9bc6787
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/3b0bb3bd9a0f690a069c479fcc0f7424fc7456d2/5.2.3/gcc-5-glibc-2.23/65372bd
|
||||
|
||||
@@ -83,6 +83,7 @@ CFLAGS+=" -DTBB"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
@@ -54,6 +54,7 @@ TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
export PORTABLE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# This script enables you running RocksDB tests by running
|
||||
# All the tests concurrently and utilizing all the cores
|
||||
Param(
|
||||
[switch]$EnableJE = $false, # Look for and use _je executable, append _je to listed exclusions
|
||||
[switch]$EnableJE = $false, # Look for and use test executable, append _je to listed exclusions
|
||||
[switch]$RunAll = $false, # Will attempt discover all *_test[_je].exe binaries and run all
|
||||
# of them as Google suites. I.e. It will run test cases concurrently
|
||||
# except those mentioned as $Run, those will run as individual test cases
|
||||
# And any execlued with $ExcludeExes or $ExcludeCases
|
||||
# It will also not run any individual test cases
|
||||
# excluded but $ExcludeCasese
|
||||
[switch]$RunAllExe = $false, # Look for and use test exdcutables, append _je to exclusions automatically
|
||||
# It will attempt to run them in parallel w/o breaking them up on individual
|
||||
# test cases. Those listed with $ExcludeExes will be excluded
|
||||
[string]$SuiteRun = "", # Split test suites in test cases and run in parallel, not compatible with $RunAll
|
||||
[string]$Run = "", # Run specified executables in parallel but do not split to test cases
|
||||
[string]$ExcludeCases = "", # Exclude test cases, expects a comma separated list, no spaces
|
||||
@@ -39,13 +42,18 @@ $RunOnly.Add("compact_on_deletion_collector_test") | Out-Null
|
||||
$RunOnly.Add("merge_test") | Out-Null
|
||||
$RunOnly.Add("stringappend_test") | Out-Null # Apparently incorrectly written
|
||||
$RunOnly.Add("backupable_db_test") | Out-Null # Disabled
|
||||
|
||||
$RunOnly.Add("timer_queue_test") | Out-Null # Not a gtest
|
||||
|
||||
if($RunAll -and $SuiteRun -ne "") {
|
||||
Write-Error "$RunAll and $SuiteRun are not compatible"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if($RunAllExe -and $Run -ne "") {
|
||||
Write-Error "$RunAllExe and $Run are not compatible"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# If running under Appveyor assume that root
|
||||
[string]$Appveyor = $Env:APPVEYOR_BUILD_FOLDER
|
||||
if($Appveyor -ne "") {
|
||||
@@ -131,12 +139,8 @@ function ExtractTestCases([string]$GTestExe, $HashTable) {
|
||||
|
||||
# Leading whitespace is fine
|
||||
$l = $l -replace '^\s+',''
|
||||
# but no whitespace any other place
|
||||
if($l -match "\s+") {
|
||||
continue
|
||||
}
|
||||
# Trailing dot is a test group but no whitespace
|
||||
elseif ( $l -match "\.$" ) {
|
||||
if ($l -match "\.$" -and $l -notmatch "\s+") {
|
||||
$Group = $l
|
||||
} else {
|
||||
# Otherwise it is a test name, remove leading space
|
||||
@@ -223,13 +227,11 @@ $TestExes = [ordered]@{}
|
||||
if($Run -ne "") {
|
||||
|
||||
$test_list = $Run -split ' '
|
||||
|
||||
ForEach($t in $test_list) {
|
||||
|
||||
if($EnableJE) {
|
||||
$t += "_je"
|
||||
}
|
||||
|
||||
MakeAndAdd -token $t -HashTable $TestExes
|
||||
}
|
||||
|
||||
@@ -237,6 +239,38 @@ if($Run -ne "") {
|
||||
Write-Error "Failed to extract tests from $Run"
|
||||
exit 1
|
||||
}
|
||||
} elseif($RunAllExe) {
|
||||
# Discover all the test binaries
|
||||
if($EnableJE) {
|
||||
$pattern = "*_test_je.exe"
|
||||
} else {
|
||||
$pattern = "*_test.exe"
|
||||
}
|
||||
|
||||
$search_path = -join ($BinariesFolder, $pattern)
|
||||
Write-Host "Binaries Search Path: $search_path"
|
||||
|
||||
$DiscoveredExe = @()
|
||||
dir -Path $search_path | ForEach-Object {
|
||||
$DiscoveredExe += ($_.Name)
|
||||
}
|
||||
|
||||
# Remove exclusions
|
||||
ForEach($e in $DiscoveredExe) {
|
||||
$e = $e -replace '.exe$', ''
|
||||
$bare_name = $e -replace '_je$', ''
|
||||
|
||||
if($ExcludeExesSet.Contains($bare_name)) {
|
||||
Write-Warning "Test $e is excluded"
|
||||
continue
|
||||
}
|
||||
MakeAndAdd -token $e -HashTable $TestExes
|
||||
}
|
||||
|
||||
if($TestExes.Count -lt 1) {
|
||||
Write-Error "Failed to discover test executables"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Ordered by exe @{ Exe = @{ TestCase = LogName }}
|
||||
@@ -245,9 +279,7 @@ $CasesToRun = [ordered]@{}
|
||||
if($SuiteRun -ne "") {
|
||||
$suite_list = $SuiteRun -split ' '
|
||||
ProcessSuites -ListOfSuites $suite_list -HashOfHashes $CasesToRun
|
||||
}
|
||||
|
||||
if($RunAll) {
|
||||
} elseif ($RunAll) {
|
||||
# Discover all the test binaries
|
||||
if($EnableJE) {
|
||||
$pattern = "*_test_je.exe"
|
||||
@@ -255,7 +287,6 @@ if($RunAll) {
|
||||
$pattern = "*_test.exe"
|
||||
}
|
||||
|
||||
|
||||
$search_path = -join ($BinariesFolder, $pattern)
|
||||
Write-Host "Binaries Search Path: $search_path"
|
||||
|
||||
@@ -287,8 +318,6 @@ if($RunAll) {
|
||||
}
|
||||
|
||||
|
||||
Write-Host "Attempting to start: $NumTestsToStart tests"
|
||||
|
||||
# Invoke a test with a filter and redirect all output
|
||||
$InvokeTestCase = {
|
||||
param($exe, $test, $log);
|
||||
@@ -365,6 +394,7 @@ function RunJobs($Suites, $TestCmds, [int]$ConcurrencyVal)
|
||||
break
|
||||
}
|
||||
|
||||
Write-Host "Starting $exe_name"
|
||||
[string]$Exe = -Join ($BinariesFolder, $exe_name)
|
||||
$job = Start-Job -Name $exe_name -ScriptBlock $InvokeTestAsync -ArgumentList @($Exe,$log_path)
|
||||
$JobToLog.Add($job, $log_path)
|
||||
|
||||
@@ -38,6 +38,7 @@ function get_lib_base()
|
||||
# platform is not provided, use latest gcc
|
||||
result=`ls -dr1v $result/gcc-*[^fb]/ | head -n1`
|
||||
else
|
||||
echo $lib_platform
|
||||
result="$result/$lib_platform/"
|
||||
fi
|
||||
|
||||
@@ -71,22 +72,22 @@ log_variable GCC_BASE
|
||||
log_variable CLANG_BASE
|
||||
|
||||
# Libraries locations
|
||||
get_lib_base libgcc 5.x
|
||||
get_lib_base glibc 2.23
|
||||
get_lib_base snappy LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zlib LATEST
|
||||
get_lib_base bzip2 LATEST
|
||||
get_lib_base lz4 LATEST
|
||||
get_lib_base zstd LATEST
|
||||
get_lib_base gflags LATEST
|
||||
get_lib_base jemalloc LATEST
|
||||
get_lib_base numa LATEST
|
||||
get_lib_base libunwind LATEST
|
||||
get_lib_base tbb 4.0_update2 gcc-5-glibc-2.23
|
||||
get_lib_base libgcc 5.x gcc-5-glibc-2.23
|
||||
get_lib_base glibc 2.23 gcc-5-glibc-2.23
|
||||
get_lib_base snappy LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zlib LATEST gcc-5-glibc-2.23
|
||||
get_lib_base bzip2 LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lz4 LATEST gcc-5-glibc-2.23
|
||||
get_lib_base zstd LATEST gcc-5-glibc-2.23
|
||||
get_lib_base gflags LATEST gcc-5-glibc-2.23
|
||||
get_lib_base jemalloc LATEST gcc-5-glibc-2.23
|
||||
get_lib_base numa LATEST gcc-5-glibc-2.23
|
||||
get_lib_base libunwind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base tbb LATEST gcc-5-glibc-2.23
|
||||
|
||||
get_lib_base kernel-headers LATEST
|
||||
get_lib_base kernel-headers 4.0.9-36_fbk5_2933_gd092e3f gcc-5-glibc-2.23
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
get_lib_base valgrind 3.10.0 gcc-5-glibc-2.23
|
||||
get_lib_base valgrind LATEST gcc-5-glibc-2.23
|
||||
get_lib_base lua 5.2.3 gcc-5-glibc-2.23
|
||||
|
||||
git diff $OUTPUT
|
||||
|
||||
Vendored
+5
-5
@@ -17,16 +17,16 @@ int main() {
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
||||
|
||||
static const uint32_t KB = 1024;
|
||||
|
||||
|
||||
Vendored
+23
-1
@@ -100,7 +100,9 @@ void LRUHandleTable::Resize() {
|
||||
}
|
||||
|
||||
LRUCacheShard::LRUCacheShard()
|
||||
: high_pri_pool_usage_(0), usage_(0), lru_usage_(0) {
|
||||
: capacity_(0), high_pri_pool_usage_(0), strict_capacity_limit_(false),
|
||||
high_pri_pool_ratio_(0), high_pri_pool_capacity_(0), usage_(0),
|
||||
lru_usage_(0) {
|
||||
// Make empty circular linked list
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
@@ -167,6 +169,11 @@ size_t LRUCacheShard::TEST_GetLRUSize() {
|
||||
return lru_size;
|
||||
}
|
||||
|
||||
double LRUCacheShard::GetHighPriPoolRatio() {
|
||||
MutexLock l(&mutex_);
|
||||
return high_pri_pool_ratio_;
|
||||
}
|
||||
|
||||
void LRUCacheShard::LRU_Remove(LRUHandle* e) {
|
||||
assert(e->next != nullptr);
|
||||
assert(e->prev != nullptr);
|
||||
@@ -353,6 +360,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
e->deleter = deleter;
|
||||
e->charge = charge;
|
||||
e->key_length = key.size();
|
||||
e->flags = 0;
|
||||
e->hash = hash;
|
||||
e->refs = (handle == nullptr
|
||||
? 1
|
||||
@@ -510,6 +518,20 @@ size_t LRUCache::TEST_GetLRUSize() {
|
||||
return lru_size_of_all_shards;
|
||||
}
|
||||
|
||||
double LRUCache::GetHighPriPoolRatio() {
|
||||
double result = 0.0;
|
||||
if (num_shards_ > 0) {
|
||||
result = shards_[0].GetHighPriPoolRatio();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(const LRUCacheOptions& cache_opts) {
|
||||
return NewLRUCache(cache_opts.capacity, cache_opts.num_shard_bits,
|
||||
cache_opts.strict_capacity_limit,
|
||||
cache_opts.high_pri_pool_ratio);
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(size_t capacity, int num_shard_bits,
|
||||
bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio) {
|
||||
|
||||
Vendored
+5
@@ -202,6 +202,9 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard : public CacheShard {
|
||||
// not threadsafe
|
||||
size_t TEST_GetLRUSize();
|
||||
|
||||
// Retrives high pri pool ratio
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
// Overloading to aligned it to cache line size
|
||||
void* operator new(size_t);
|
||||
|
||||
@@ -293,6 +296,8 @@ class LRUCache : public ShardedCache {
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
size_t TEST_GetLRUSize();
|
||||
// Retrives high pri pool ratio
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
private:
|
||||
LRUCacheShard* shards_;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# - Find NUMA
|
||||
# Find the NUMA library and includes
|
||||
#
|
||||
# NUMA_INCLUDE_DIR - where to find numa.h, etc.
|
||||
# NUMA_LIBRARIES - List of libraries when using NUMA.
|
||||
# NUMA_FOUND - True if NUMA found.
|
||||
|
||||
find_path(NUMA_INCLUDE_DIR
|
||||
NAMES numa.h numaif.h
|
||||
HINTS ${NUMA_ROOT_DIR}/include)
|
||||
|
||||
find_library(NUMA_LIBRARIES
|
||||
NAMES numa
|
||||
HINTS ${NUMA_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(NUMA DEFAULT_MSG NUMA_LIBRARIES NUMA_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
NUMA_LIBRARIES
|
||||
NUMA_INCLUDE_DIR)
|
||||
@@ -0,0 +1,21 @@
|
||||
# - Find TBB
|
||||
# Find the Thread Building Blocks library and includes
|
||||
#
|
||||
# TBB_INCLUDE_DIR - where to find tbb.h, etc.
|
||||
# TBB_LIBRARIES - List of libraries when using TBB.
|
||||
# TBB_FOUND - True if TBB found.
|
||||
|
||||
find_path(TBB_INCLUDE_DIR
|
||||
NAMES tbb/tbb.h
|
||||
HINTS ${TBB_ROOT_DIR}/include)
|
||||
|
||||
find_library(TBB_LIBRARIES
|
||||
NAMES tbb
|
||||
HINTS ${TBB_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(TBB DEFAULT_MSG TBB_LIBRARIES TBB_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
TBB_LIBRARIES
|
||||
TBB_INCLUDE_DIR)
|
||||
+4
-3
@@ -70,12 +70,12 @@ Status BuildTable(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const CompressionType compression,
|
||||
SnapshotChecker* snapshot_checker, const CompressionType compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time) {
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -117,6 +117,7 @@ Status BuildTable(
|
||||
return s;
|
||||
}
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
|
||||
file_writer.reset(new WritableFileWriter(std::move(file), env_options,
|
||||
ioptions.statistics));
|
||||
@@ -134,7 +135,7 @@ Status BuildTable(
|
||||
|
||||
CompactionIterator c_iter(
|
||||
iter, internal_comparator.user_comparator(), &merge, kMaxSequenceNumber,
|
||||
&snapshots, earliest_write_conflict_snapshot, env,
|
||||
&snapshots, earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
true /* internal key corruption is not ok */, range_del_agg.get());
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
|
||||
+4
-2
@@ -29,6 +29,7 @@ struct FileMetaData;
|
||||
class Env;
|
||||
struct EnvOptions;
|
||||
class Iterator;
|
||||
class SnapshotChecker;
|
||||
class TableCache;
|
||||
class VersionEdit;
|
||||
class TableBuilder;
|
||||
@@ -72,12 +73,13 @@ extern Status BuildTable(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
std::vector<SequenceNumber> snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const CompressionType compression,
|
||||
SnapshotChecker* snapshot_checker, const CompressionType compression,
|
||||
const CompressionOptions& compression_opts, bool paranoid_file_checks,
|
||||
InternalStats* internal_stats, TableFileCreationReason reason,
|
||||
EventLogger* event_logger = nullptr, int job_id = 0,
|
||||
const Env::IOPriority io_priority = Env::IO_HIGH,
|
||||
TableProperties* table_properties = nullptr, int level = -1,
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0);
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET);
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -21,23 +21,23 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/universal_compaction.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/rate_limiter.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/universal_compaction.h"
|
||||
#include "rocksdb/utilities/backupable_db.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
#include "rocksdb/utilities/transaction.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "rocksdb/utilities/optimistic_transaction_db.h"
|
||||
#include "rocksdb/utilities/checkpoint.h"
|
||||
#include "rocksdb/utilities/write_batch_with_index.h"
|
||||
#include "rocksdb/write_batch.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using rocksdb::BytewiseComparator;
|
||||
using rocksdb::Cache;
|
||||
@@ -117,7 +117,9 @@ struct rocksdb_flushoptions_t { FlushOptions rep; };
|
||||
struct rocksdb_fifo_compaction_options_t { CompactionOptionsFIFO rep; };
|
||||
struct rocksdb_readoptions_t {
|
||||
ReadOptions rep;
|
||||
Slice upper_bound; // stack variable to set pointer to in ReadOptions
|
||||
// stack variables to set pointers to in ReadOptions
|
||||
Slice upper_bound;
|
||||
Slice lower_bound;
|
||||
};
|
||||
struct rocksdb_writeoptions_t { WriteOptions rep; };
|
||||
struct rocksdb_options_t { Options rep; };
|
||||
@@ -1824,6 +1826,26 @@ void rocksdb_block_based_options_set_block_restart_interval(
|
||||
options->rep.block_restart_interval = block_restart_interval;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_index_block_restart_interval(
|
||||
rocksdb_block_based_table_options_t* options, int index_block_restart_interval) {
|
||||
options->rep.index_block_restart_interval = index_block_restart_interval;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_metadata_block_size(
|
||||
rocksdb_block_based_table_options_t* options, uint64_t metadata_block_size) {
|
||||
options->rep.metadata_block_size = metadata_block_size;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_partition_filters(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char partition_filters) {
|
||||
options->rep.partition_filters = partition_filters;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_use_delta_encoding(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char use_delta_encoding) {
|
||||
options->rep.use_delta_encoding = use_delta_encoding;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_filter_policy(
|
||||
rocksdb_block_based_table_options_t* options,
|
||||
rocksdb_filterpolicy_t* filter_policy) {
|
||||
@@ -1877,6 +1899,11 @@ void rocksdb_block_based_options_set_cache_index_and_filter_blocks(
|
||||
options->rep.cache_index_and_filter_blocks = v;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_cache_index_and_filter_blocks_with_high_priority(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char v) {
|
||||
options->rep.cache_index_and_filter_blocks_with_high_priority = v;
|
||||
}
|
||||
|
||||
void rocksdb_block_based_options_set_pin_l0_filter_and_index_blocks_in_cache(
|
||||
rocksdb_block_based_table_options_t* options, unsigned char v) {
|
||||
options->rep.pin_l0_filter_and_index_blocks_in_cache = v;
|
||||
@@ -1891,7 +1918,6 @@ void rocksdb_options_set_block_based_table_factory(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rocksdb_cuckoo_table_options_t*
|
||||
rocksdb_cuckoo_options_create() {
|
||||
return new rocksdb_cuckoo_table_options_t;
|
||||
@@ -1973,6 +1999,11 @@ void rocksdb_options_optimize_universal_style_compaction(
|
||||
opt->rep.OptimizeUniversalStyleCompaction(memtable_memory_budget);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_allow_ingest_behind(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.allow_ingest_behind = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_compactionfilter_t* filter) {
|
||||
@@ -2023,8 +2054,8 @@ void rocksdb_options_set_paranoid_checks(
|
||||
opt->rep.paranoid_checks = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
size_t num_paths) {
|
||||
std::vector<DbPath> db_paths(num_paths);
|
||||
for (size_t i = 0; i < num_paths; ++i) {
|
||||
@@ -2111,7 +2142,8 @@ void rocksdb_options_enable_statistics(rocksdb_options_t* opt) {
|
||||
opt->rep.statistics = rocksdb::CreateDBStatistics();
|
||||
}
|
||||
|
||||
void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt, unsigned char val) {
|
||||
void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt,
|
||||
unsigned char val) {
|
||||
opt->rep.skip_stats_update_on_db_open = val;
|
||||
}
|
||||
|
||||
@@ -2265,11 +2297,21 @@ void rocksdb_options_set_use_adaptive_mutex(
|
||||
opt->rep.use_adaptive_mutex = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_wal_bytes_per_sync(
|
||||
rocksdb_options_t* opt, uint64_t v) {
|
||||
opt->rep.wal_bytes_per_sync = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bytes_per_sync(
|
||||
rocksdb_options_t* opt, uint64_t v) {
|
||||
opt->rep.bytes_per_sync = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_writable_file_max_buffer_size(rocksdb_options_t* opt,
|
||||
uint64_t v) {
|
||||
opt->rep.writable_file_max_buffer_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_allow_concurrent_memtable_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.allow_concurrent_memtable_write = v;
|
||||
@@ -2717,6 +2759,18 @@ void rocksdb_readoptions_set_iterate_upper_bound(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_iterate_lower_bound(
|
||||
rocksdb_readoptions_t *opt,
|
||||
const char* key, size_t keylen) {
|
||||
if (key == nullptr) {
|
||||
opt->lower_bound = Slice();
|
||||
opt->rep.iterate_lower_bound = nullptr;
|
||||
} else {
|
||||
opt->lower_bound = Slice(key, keylen);
|
||||
opt->rep.iterate_lower_bound = &opt->lower_bound;
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_read_tier(
|
||||
rocksdb_readoptions_t* opt, int v) {
|
||||
opt->rep.read_tier = static_cast<rocksdb::ReadTier>(v);
|
||||
@@ -2727,11 +2781,21 @@ void rocksdb_readoptions_set_tailing(
|
||||
opt->rep.tailing = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_managed(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.managed = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_readahead_size(
|
||||
rocksdb_readoptions_t* opt, size_t v) {
|
||||
opt->rep.readahead_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_prefix_same_as_start(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.prefix_same_as_start = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_pin_data(rocksdb_readoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.pin_data = v;
|
||||
@@ -2742,6 +2806,22 @@ void rocksdb_readoptions_set_total_order_seek(rocksdb_readoptions_t* opt,
|
||||
opt->rep.total_order_seek = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_max_skippable_internal_keys(
|
||||
rocksdb_readoptions_t* opt,
|
||||
uint64_t v) {
|
||||
opt->rep.max_skippable_internal_keys = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.background_purge_on_iterator_cleanup = v;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_ignore_range_deletions(
|
||||
rocksdb_readoptions_t* opt, unsigned char v) {
|
||||
opt->rep.ignore_range_deletions = v;
|
||||
}
|
||||
|
||||
rocksdb_writeoptions_t* rocksdb_writeoptions_create() {
|
||||
return new rocksdb_writeoptions_t;
|
||||
}
|
||||
@@ -2759,6 +2839,24 @@ void rocksdb_writeoptions_disable_WAL(rocksdb_writeoptions_t* opt, int disable)
|
||||
opt->rep.disableWAL = disable;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_ignore_missing_column_families(
|
||||
rocksdb_writeoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.ignore_missing_column_families = v;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_no_slowdown(
|
||||
rocksdb_writeoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.no_slowdown = v;
|
||||
}
|
||||
|
||||
void rocksdb_writeoptions_set_low_pri(
|
||||
rocksdb_writeoptions_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.low_pri = v;
|
||||
}
|
||||
|
||||
rocksdb_compactoptions_t* rocksdb_compactoptions_create() {
|
||||
return new rocksdb_compactoptions_t;
|
||||
}
|
||||
@@ -2951,6 +3049,12 @@ void rocksdb_ingestexternalfileoptions_set_allow_blocking_flush(
|
||||
opt->rep.allow_blocking_flush = allow_blocking_flush;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_set_ingest_behind(
|
||||
rocksdb_ingestexternalfileoptions_t* opt,
|
||||
unsigned char ingest_behind) {
|
||||
opt->rep.ingest_behind = ingest_behind;
|
||||
}
|
||||
|
||||
void rocksdb_ingestexternalfileoptions_destroy(
|
||||
rocksdb_ingestexternalfileoptions_t* opt) {
|
||||
delete opt;
|
||||
@@ -3339,6 +3443,14 @@ void rocksdb_transaction_rollback(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->Rollback());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_set_savepoint(rocksdb_transaction_t* txn) {
|
||||
txn->rep->SetSavePoint();
|
||||
}
|
||||
|
||||
void rocksdb_transaction_rollback_to_savepoint(rocksdb_transaction_t* txn, char** errptr) {
|
||||
SaveError(errptr, txn->rep->RollbackToSavePoint());
|
||||
}
|
||||
|
||||
void rocksdb_transaction_destroy(rocksdb_transaction_t* txn) {
|
||||
delete txn->rep;
|
||||
delete txn;
|
||||
@@ -3476,8 +3588,8 @@ void rocksdb_transactiondb_put(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr, txn_db->rep->Put(options->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
SaveError(errptr,
|
||||
txn_db->rep->Put(options->rep, Slice(key, klen), Slice(val, vlen)));
|
||||
}
|
||||
|
||||
void rocksdb_transactiondb_put_cf(rocksdb_transactiondb_t* txn_db,
|
||||
@@ -3511,8 +3623,8 @@ void rocksdb_transactiondb_merge(rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_writeoptions_t* options,
|
||||
const char* key, size_t klen, const char* val,
|
||||
size_t vlen, char** errptr) {
|
||||
SaveError(errptr,
|
||||
txn_db->rep->Merge(options->rep, Slice(key, klen), Slice(val, vlen)));
|
||||
SaveError(errptr, txn_db->rep->Merge(options->rep, Slice(key, klen),
|
||||
Slice(val, vlen)));
|
||||
}
|
||||
|
||||
// Delete a key inside a transaction
|
||||
@@ -3550,6 +3662,15 @@ rocksdb_iterator_t* rocksdb_transaction_create_iterator(
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create an iterator inside a transaction with column family
|
||||
rocksdb_iterator_t* rocksdb_transaction_create_iterator_cf(
|
||||
rocksdb_transaction_t* txn, const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family) {
|
||||
rocksdb_iterator_t* result = new rocksdb_iterator_t;
|
||||
result->rep = txn->rep->GetIterator(options->rep, column_family->rep);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create an iterator outside a transaction
|
||||
rocksdb_iterator_t* rocksdb_transactiondb_create_iterator(
|
||||
rocksdb_transactiondb_t* txn_db, const rocksdb_readoptions_t* options) {
|
||||
@@ -3575,8 +3696,7 @@ rocksdb_checkpoint_t* rocksdb_transactiondb_checkpoint_object_create(
|
||||
}
|
||||
|
||||
rocksdb_optimistictransactiondb_t* rocksdb_optimistictransactiondb_open(
|
||||
const rocksdb_options_t* options, const char* name,
|
||||
char** errptr) {
|
||||
const rocksdb_options_t* options, const char* name, char** errptr) {
|
||||
OptimisticTransactionDB* otxn_db;
|
||||
if (SaveError(errptr, OptimisticTransactionDB::Open(
|
||||
options->rep, std::string(name), &otxn_db))) {
|
||||
@@ -3588,6 +3708,56 @@ rocksdb_optimistictransactiondb_t* rocksdb_optimistictransactiondb_open(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_optimistictransactiondb_t*
|
||||
rocksdb_optimistictransactiondb_open_column_families(
|
||||
const rocksdb_options_t* db_options, const char* name,
|
||||
int num_column_families, const char** column_family_names,
|
||||
const rocksdb_options_t** column_family_options,
|
||||
rocksdb_column_family_handle_t** column_family_handles, char** errptr) {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
for (int i = 0; i < num_column_families; i++) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
std::string(column_family_names[i]),
|
||||
ColumnFamilyOptions(column_family_options[i]->rep)));
|
||||
}
|
||||
|
||||
OptimisticTransactionDB* otxn_db;
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
if (SaveError(errptr, OptimisticTransactionDB::Open(
|
||||
DBOptions(db_options->rep), std::string(name),
|
||||
column_families, &handles, &otxn_db))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < handles.size(); i++) {
|
||||
rocksdb_column_family_handle_t* c_handle =
|
||||
new rocksdb_column_family_handle_t;
|
||||
c_handle->rep = handles[i];
|
||||
column_family_handles[i] = c_handle;
|
||||
}
|
||||
rocksdb_optimistictransactiondb_t* result =
|
||||
new rocksdb_optimistictransactiondb_t;
|
||||
result->rep = otxn_db;
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_t* rocksdb_optimistictransactiondb_get_base_db(
|
||||
rocksdb_optimistictransactiondb_t* otxn_db) {
|
||||
DB* base_db = otxn_db->rep->GetBaseDB();
|
||||
|
||||
if (base_db != nullptr) {
|
||||
rocksdb_t* result = new rocksdb_t;
|
||||
result->rep = base_db;
|
||||
return result;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void rocksdb_optimistictransactiondb_close_base_db(rocksdb_t* base_db) {
|
||||
delete base_db;
|
||||
}
|
||||
|
||||
rocksdb_transaction_t* rocksdb_optimistictransaction_begin(
|
||||
rocksdb_optimistictransactiondb_t* otxn_db,
|
||||
const rocksdb_writeoptions_t* write_options,
|
||||
|
||||
+134
@@ -334,6 +334,20 @@ static void CheckTxnGet(
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnGetCF(rocksdb_transaction_t* txn,
|
||||
const rocksdb_readoptions_t* options,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, const char* expected) {
|
||||
char* err = NULL;
|
||||
size_t val_len;
|
||||
char* val;
|
||||
val = rocksdb_transaction_get_cf(txn, options, column_family, key,
|
||||
strlen(key), &val_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual(expected, val, val_len);
|
||||
Free(&val);
|
||||
}
|
||||
|
||||
static void CheckTxnDBGet(
|
||||
rocksdb_transactiondb_t* txn_db,
|
||||
const rocksdb_readoptions_t* options,
|
||||
@@ -378,6 +392,8 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transactiondb_options_t* txn_db_options;
|
||||
rocksdb_transaction_t* txn;
|
||||
rocksdb_transaction_options_t* txn_options;
|
||||
rocksdb_optimistictransactiondb_t* otxn_db;
|
||||
rocksdb_optimistictransaction_options_t* otxn_options;
|
||||
char* err = NULL;
|
||||
int run = -1;
|
||||
|
||||
@@ -1447,6 +1463,25 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "bar", NULL);
|
||||
|
||||
// save point
|
||||
rocksdb_transaction_put(txn, "foo1", 4, "hi1", 3, &err);
|
||||
rocksdb_transaction_set_savepoint(txn);
|
||||
CheckTxnGet(txn, roptions, "foo1", "hi1");
|
||||
rocksdb_transaction_put(txn, "foo2", 4, "hi2", 3, &err);
|
||||
CheckTxnGet(txn, roptions, "foo2", "hi2");
|
||||
|
||||
// rollback to savepoint
|
||||
rocksdb_transaction_rollback_to_savepoint(txn, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn, roptions, "foo2", NULL);
|
||||
CheckTxnGet(txn, roptions, "foo1", "hi1");
|
||||
CheckTxnDBGet(txn_db, roptions, "foo1", NULL);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo2", NULL);
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnDBGet(txn_db, roptions, "foo1", "hi1");
|
||||
CheckTxnDBGet(txn_db, roptions, "foo2", NULL);
|
||||
|
||||
// Column families.
|
||||
rocksdb_column_family_handle_t* cfh;
|
||||
cfh = rocksdb_transactiondb_create_column_family(txn_db, options,
|
||||
@@ -1473,6 +1508,105 @@ int main(int argc, char** argv) {
|
||||
rocksdb_transactiondb_options_destroy(txn_db_options);
|
||||
}
|
||||
|
||||
StartPhase("optimistic_transactions");
|
||||
{
|
||||
rocksdb_options_t* db_options = rocksdb_options_create();
|
||||
rocksdb_options_set_create_if_missing(db_options, 1);
|
||||
rocksdb_options_set_allow_concurrent_memtable_write(db_options, 1);
|
||||
otxn_db = rocksdb_optimistictransactiondb_open(db_options, dbname, &err);
|
||||
otxn_options = rocksdb_optimistictransaction_options_create();
|
||||
rocksdb_transaction_t* txn1 = rocksdb_optimistictransaction_begin(
|
||||
otxn_db, woptions, otxn_options, NULL);
|
||||
rocksdb_transaction_t* txn2 = rocksdb_optimistictransaction_begin(
|
||||
otxn_db, woptions, otxn_options, NULL);
|
||||
rocksdb_transaction_put(txn1, "key", 3, "value", 5, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_put(txn2, "key1", 4, "value1", 6, &err);
|
||||
CheckNoError(err);
|
||||
CheckTxnGet(txn1, roptions, "key", "value");
|
||||
rocksdb_transaction_commit(txn1, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_commit(txn2, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_destroy(txn1);
|
||||
rocksdb_transaction_destroy(txn2);
|
||||
|
||||
// Check column family
|
||||
db = rocksdb_optimistictransactiondb_get_base_db(otxn_db);
|
||||
rocksdb_put(db, woptions, "key", 3, "value", 5, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_column_family_handle_t *cfh1, *cfh2;
|
||||
cfh1 = rocksdb_create_column_family(db, db_options, "txn_db_cf1", &err);
|
||||
cfh2 = rocksdb_create_column_family(db, db_options, "txn_db_cf2", &err);
|
||||
txn = rocksdb_optimistictransaction_begin(otxn_db, woptions, otxn_options,
|
||||
NULL);
|
||||
rocksdb_transaction_put_cf(txn, cfh1, "key_cf1", 7, "val_cf1", 7, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_put_cf(txn, cfh2, "key_cf2", 7, "val_cf2", 7, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_commit(txn, &err);
|
||||
CheckNoError(err);
|
||||
txn = rocksdb_optimistictransaction_begin(otxn_db, woptions, otxn_options,
|
||||
txn);
|
||||
CheckGetCF(db, roptions, cfh1, "key_cf1", "val_cf1");
|
||||
CheckTxnGetCF(txn, roptions, cfh1, "key_cf1", "val_cf1");
|
||||
|
||||
// Check iterator with column family
|
||||
rocksdb_transaction_put_cf(txn, cfh1, "key1_cf", 7, "val1_cf", 7, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iterator_t* iter =
|
||||
rocksdb_transaction_create_iterator_cf(txn, roptions, cfh1);
|
||||
CheckCondition(!rocksdb_iter_valid(iter));
|
||||
rocksdb_iter_seek_to_first(iter);
|
||||
CheckCondition(rocksdb_iter_valid(iter));
|
||||
CheckIter(iter, "key1_cf", "val1_cf");
|
||||
rocksdb_iter_get_error(iter, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_iter_destroy(iter);
|
||||
|
||||
rocksdb_transaction_destroy(txn);
|
||||
rocksdb_column_family_handle_destroy(cfh1);
|
||||
rocksdb_column_family_handle_destroy(cfh2);
|
||||
rocksdb_optimistictransactiondb_close_base_db(db);
|
||||
rocksdb_optimistictransactiondb_close(otxn_db);
|
||||
|
||||
// Check open optimistic transaction db with column families
|
||||
size_t cf_len;
|
||||
char** column_fams =
|
||||
rocksdb_list_column_families(db_options, dbname, &cf_len, &err);
|
||||
CheckNoError(err);
|
||||
CheckEqual("default", column_fams[0], 7);
|
||||
CheckEqual("txn_db_cf1", column_fams[1], 10);
|
||||
CheckEqual("txn_db_cf2", column_fams[2], 10);
|
||||
CheckCondition(cf_len == 3);
|
||||
rocksdb_list_column_families_destroy(column_fams, cf_len);
|
||||
|
||||
const char* cf_names[3] = {"default", "txn_db_cf1", "txn_db_cf2"};
|
||||
rocksdb_options_t* cf_options = rocksdb_options_create();
|
||||
const rocksdb_options_t* cf_opts[3] = {cf_options, cf_options, cf_options};
|
||||
|
||||
rocksdb_options_set_error_if_exists(cf_options, 0);
|
||||
rocksdb_column_family_handle_t* cf_handles[3];
|
||||
otxn_db = rocksdb_optimistictransactiondb_open_column_families(
|
||||
db_options, dbname, 3, cf_names, cf_opts, cf_handles, &err);
|
||||
CheckNoError(err);
|
||||
rocksdb_transaction_t* txn_cf = rocksdb_optimistictransaction_begin(
|
||||
otxn_db, woptions, otxn_options, NULL);
|
||||
CheckTxnGetCF(txn_cf, roptions, cf_handles[0], "key", "value");
|
||||
CheckTxnGetCF(txn_cf, roptions, cf_handles[1], "key_cf1", "val_cf1");
|
||||
CheckTxnGetCF(txn_cf, roptions, cf_handles[2], "key_cf2", "val_cf2");
|
||||
rocksdb_transaction_destroy(txn_cf);
|
||||
rocksdb_options_destroy(cf_options);
|
||||
rocksdb_column_family_handle_destroy(cf_handles[0]);
|
||||
rocksdb_column_family_handle_destroy(cf_handles[1]);
|
||||
rocksdb_column_family_handle_destroy(cf_handles[2]);
|
||||
rocksdb_optimistictransactiondb_close(otxn_db);
|
||||
rocksdb_destroy_db(db_options, dbname, &err);
|
||||
rocksdb_options_destroy(db_options);
|
||||
rocksdb_optimistictransaction_options_destroy(otxn_options);
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
// Simple sanity check that setting memtable rep works.
|
||||
StartPhase("memtable_reps");
|
||||
{
|
||||
|
||||
+83
-26
@@ -123,6 +123,22 @@ Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options) {
|
||||
" is not linked with the binary.");
|
||||
}
|
||||
}
|
||||
if (cf_options.compression_opts.zstd_max_train_bytes > 0) {
|
||||
if (!CompressionTypeSupported(CompressionType::kZSTD)) {
|
||||
// Dictionary trainer is available since v0.6.1, but ZSTD was marked
|
||||
// stable only since v0.8.0. For now we enable the feature in stable
|
||||
// versions only.
|
||||
return Status::InvalidArgument(
|
||||
"zstd dictionary trainer cannot be used because " +
|
||||
CompressionTypeToString(CompressionType::kZSTD) +
|
||||
" is not linked with the binary.");
|
||||
}
|
||||
if (cf_options.compression_opts.max_dict_bytes == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"The dictionary size limit (`CompressionOptions::max_dict_bytes`) "
|
||||
"should be nonzero if we're using zstd's dictionary generator.");
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -369,7 +385,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
pending_flush_(false),
|
||||
pending_compaction_(false),
|
||||
prev_compaction_needed_bytes_(0),
|
||||
allow_2pc_(db_options.allow_2pc) {
|
||||
allow_2pc_(db_options.allow_2pc),
|
||||
last_memtable_id_(0) {
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
@@ -463,7 +480,8 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
if (dummy_versions_ != nullptr) {
|
||||
// List must be empty
|
||||
assert(dummy_versions_->TEST_Next() == dummy_versions_);
|
||||
bool deleted __attribute__((unused)) = dummy_versions_->Unref();
|
||||
bool deleted __attribute__((unused));
|
||||
deleted = dummy_versions_->Unref();
|
||||
assert(deleted);
|
||||
}
|
||||
|
||||
@@ -613,8 +631,9 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
WriteStallCondition ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
auto write_stall_condition = WriteStallCondition::kNormal;
|
||||
if (current_ != nullptr) {
|
||||
auto* vstorage = current_->storage_info();
|
||||
auto write_controller = column_family_set_->write_controller_;
|
||||
@@ -626,7 +645,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
|
||||
if (imm()->NumNotFlushed() >= mutable_cf_options.max_write_buffer_number) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_COMPACTION, 1);
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_LIMIT_STOPS, 1);
|
||||
write_stall_condition = WriteStallCondition::kStopped;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d immutable memtables "
|
||||
@@ -637,10 +657,11 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_NUM_FILES_TOTAL, 1);
|
||||
internal_stats_->AddCFStats(InternalStats::L0_FILE_COUNT_LIMIT_STOPS, 1);
|
||||
write_stall_condition = WriteStallCondition::kStopped;
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::LEVEL0_NUM_FILES_WITH_COMPACTION, 1);
|
||||
InternalStats::LOCKED_L0_FILE_COUNT_LIMIT_STOPS, 1);
|
||||
}
|
||||
ROCKS_LOG_WARN(ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
@@ -651,7 +672,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::HARD_PENDING_COMPACTION_BYTES_LIMIT, 1);
|
||||
InternalStats::PENDING_COMPACTION_BYTES_LIMIT_STOPS, 1);
|
||||
write_stall_condition = WriteStallCondition::kStopped;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stopping writes because of estimated pending compaction "
|
||||
@@ -664,7 +686,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
SetupDelay(write_controller, compaction_needed_bytes,
|
||||
prev_compaction_needed_bytes_, was_stopped,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_SLOWDOWN, 1);
|
||||
internal_stats_->AddCFStats(InternalStats::MEMTABLE_LIMIT_SLOWDOWNS, 1);
|
||||
write_stall_condition = WriteStallCondition::kDelayed;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d immutable memtables "
|
||||
@@ -684,10 +707,12 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
SetupDelay(write_controller, compaction_needed_bytes,
|
||||
prev_compaction_needed_bytes_, was_stopped || near_stop,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(InternalStats::LEVEL0_SLOWDOWN_TOTAL, 1);
|
||||
internal_stats_->AddCFStats(InternalStats::L0_FILE_COUNT_LIMIT_SLOWDOWNS,
|
||||
1);
|
||||
write_stall_condition = WriteStallCondition::kDelayed;
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::LEVEL0_SLOWDOWN_WITH_COMPACTION, 1);
|
||||
InternalStats::LOCKED_L0_FILE_COUNT_LIMIT_SLOWDOWNS, 1);
|
||||
}
|
||||
ROCKS_LOG_WARN(ioptions_.info_log,
|
||||
"[%s] Stalling writes because we have %d level-0 files "
|
||||
@@ -714,7 +739,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
prev_compaction_needed_bytes_, was_stopped || near_stop,
|
||||
mutable_cf_options.disable_auto_compactions);
|
||||
internal_stats_->AddCFStats(
|
||||
InternalStats::SOFT_PENDING_COMPACTION_BYTES_LIMIT, 1);
|
||||
InternalStats::PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS, 1);
|
||||
write_stall_condition = WriteStallCondition::kDelayed;
|
||||
ROCKS_LOG_WARN(
|
||||
ioptions_.info_log,
|
||||
"[%s] Stalling writes because of estimated pending compaction "
|
||||
@@ -769,6 +795,7 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
}
|
||||
prev_compaction_needed_bytes_ = compaction_needed_bytes;
|
||||
}
|
||||
return write_stall_condition;
|
||||
}
|
||||
|
||||
const EnvOptions* ColumnFamilyData::soptions() const {
|
||||
@@ -845,6 +872,10 @@ SuperVersion* ColumnFamilyData::GetReferencedSuperVersion(
|
||||
sv = GetThreadLocalSuperVersion(db_mutex);
|
||||
sv->Ref();
|
||||
if (!ReturnThreadLocalSuperVersion(sv)) {
|
||||
// This Unref() corresponds to the Ref() in GetThreadLocalSuperVersion()
|
||||
// when the thread-local pointer was populated. So, the Ref() earlier in
|
||||
// this function still prevents the returned SuperVersion* from being
|
||||
// deleted out from under the caller.
|
||||
sv->Unref();
|
||||
}
|
||||
return sv;
|
||||
@@ -914,15 +945,16 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersion* new_superversion, InstrumentedMutex* db_mutex) {
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
return InstallSuperVersion(new_superversion, db_mutex, mutable_cf_options_);
|
||||
return InstallSuperVersion(sv_context, db_mutex, mutable_cf_options_);
|
||||
}
|
||||
|
||||
SuperVersion* ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersion* new_superversion, InstrumentedMutex* db_mutex,
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->db_mutex = db_mutex;
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->Init(mem_, imm_.current(), current_);
|
||||
@@ -930,23 +962,28 @@ SuperVersion* ColumnFamilyData::InstallSuperVersion(
|
||||
super_version_ = new_superversion;
|
||||
++super_version_number_;
|
||||
super_version_->version_number = super_version_number_;
|
||||
super_version_->write_stall_condition =
|
||||
RecalculateWriteStallConditions(mutable_cf_options);
|
||||
|
||||
if (old_superversion != nullptr) {
|
||||
if (old_superversion->mutable_cf_options.write_buffer_size !=
|
||||
mutable_cf_options.write_buffer_size) {
|
||||
mem_->UpdateWriteBufferSize(mutable_cf_options.write_buffer_size);
|
||||
}
|
||||
if (old_superversion->write_stall_condition !=
|
||||
new_superversion->write_stall_condition) {
|
||||
sv_context->PushWriteStallNotification(
|
||||
old_superversion->write_stall_condition,
|
||||
new_superversion->write_stall_condition, GetName(), ioptions());
|
||||
}
|
||||
if (old_superversion->Unref()) {
|
||||
old_superversion->Cleanup();
|
||||
sv_context->superversions_to_free.push_back(old_superversion);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset SuperVersions cached in thread local storage
|
||||
ResetThreadLocalSuperVersions();
|
||||
|
||||
RecalculateWriteStallConditions(mutable_cf_options);
|
||||
|
||||
if (old_superversion != nullptr && old_superversion->Unref()) {
|
||||
old_superversion->Cleanup();
|
||||
return old_superversion; // will let caller delete outside of mutex
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ColumnFamilyData::ResetThreadLocalSuperVersions() {
|
||||
@@ -979,6 +1016,24 @@ Status ColumnFamilyData::SetOptions(
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
Env::WriteLifeTimeHint ColumnFamilyData::CalculateSSTWriteHint(int level) {
|
||||
if (initial_cf_options_.compaction_style != kCompactionStyleLevel) {
|
||||
return Env::WLTH_NOT_SET;
|
||||
}
|
||||
if (level == 0) {
|
||||
return Env::WLTH_MEDIUM;
|
||||
}
|
||||
int base_level = current_->storage_info()->base_level();
|
||||
|
||||
// L1: medium, L2: long, ...
|
||||
if (level - base_level >= 2) {
|
||||
return Env::WLTH_EXTREME;
|
||||
}
|
||||
return static_cast<Env::WriteLifeTimeHint>(level - base_level +
|
||||
static_cast<int>(Env::WLTH_MEDIUM));
|
||||
}
|
||||
|
||||
ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options,
|
||||
@@ -1005,10 +1060,12 @@ ColumnFamilySet::~ColumnFamilySet() {
|
||||
while (column_family_data_.size() > 0) {
|
||||
// cfd destructor will delete itself from column_family_data_
|
||||
auto cfd = column_family_data_.begin()->second;
|
||||
cfd->Unref();
|
||||
bool last_ref __attribute__((__unused__)) = cfd->Unref();
|
||||
assert(last_ref);
|
||||
delete cfd;
|
||||
}
|
||||
dummy_cfd_->Unref();
|
||||
bool dummy_last_ref __attribute__((__unused__)) = dummy_cfd_->Unref();
|
||||
assert(dummy_last_ref);
|
||||
delete dummy_cfd_;
|
||||
}
|
||||
|
||||
|
||||
+22
-11
@@ -41,6 +41,7 @@ class DBImpl;
|
||||
class LogBuffer;
|
||||
class InstrumentedMutex;
|
||||
class InstrumentedMutexLock;
|
||||
struct SuperVersionContext;
|
||||
|
||||
extern const double kIncSlowdownRatio;
|
||||
|
||||
@@ -76,7 +77,7 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
class ColumnFamilyHandleInternal : public ColumnFamilyHandleImpl {
|
||||
public:
|
||||
ColumnFamilyHandleInternal()
|
||||
: ColumnFamilyHandleImpl(nullptr, nullptr, nullptr) {}
|
||||
: ColumnFamilyHandleImpl(nullptr, nullptr, nullptr), internal_cfd_(nullptr) {}
|
||||
|
||||
void SetCFD(ColumnFamilyData* _cfd) { internal_cfd_ = _cfd; }
|
||||
virtual ColumnFamilyData* cfd() const override { return internal_cfd_; }
|
||||
@@ -95,6 +96,7 @@ struct SuperVersion {
|
||||
MutableCFOptions mutable_cf_options;
|
||||
// Version number of the current SuperVersion
|
||||
uint64_t version_number;
|
||||
WriteStallCondition write_stall_condition;
|
||||
|
||||
InstrumentedMutex* db_mutex;
|
||||
|
||||
@@ -192,7 +194,7 @@ class ColumnFamilyData {
|
||||
// *) delete all memory associated with that column family
|
||||
// *) delete all the files associated with that column family
|
||||
void SetDropped();
|
||||
bool IsDropped() const { return dropped_; }
|
||||
bool IsDropped() const { return dropped_.load(std::memory_order_relaxed); }
|
||||
|
||||
// thread-safe
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
@@ -237,7 +239,11 @@ class ColumnFamilyData {
|
||||
void SetCurrent(Version* _current);
|
||||
uint64_t GetNumLiveVersions() const; // REQUIRE: DB mutex held
|
||||
uint64_t GetTotalSstFilesSize() const; // REQUIRE: DB mutex held
|
||||
void SetMemtable(MemTable* new_mem) { mem_ = new_mem; }
|
||||
void SetMemtable(MemTable* new_mem) {
|
||||
uint64_t memtable_id = last_memtable_id_.fetch_add(1) + 1;
|
||||
new_mem->SetID(memtable_id);
|
||||
mem_ = new_mem;
|
||||
}
|
||||
|
||||
// calculate the oldest log needed for the durability of this column family
|
||||
uint64_t OldestLogToKeep();
|
||||
@@ -264,7 +270,7 @@ class ColumnFamilyData {
|
||||
int level) const;
|
||||
|
||||
// A flag to tell a manual compaction is to compact all levels together
|
||||
// instad of for specific level.
|
||||
// instead of a specific level.
|
||||
static const int kCompactAllLevels;
|
||||
// A flag to tell a manual compaction's output is base level.
|
||||
static const int kCompactToBaseLevel;
|
||||
@@ -311,11 +317,11 @@ class ColumnFamilyData {
|
||||
// As argument takes a pointer to allocated SuperVersion to enable
|
||||
// the clients to allocate SuperVersion outside of mutex.
|
||||
// IMPORTANT: Only call this from DBImpl::InstallSuperVersion()
|
||||
SuperVersion* InstallSuperVersion(SuperVersion* new_superversion,
|
||||
InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
SuperVersion* InstallSuperVersion(SuperVersion* new_superversion,
|
||||
InstrumentedMutex* db_mutex);
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex);
|
||||
|
||||
void ResetThreadLocalSuperVersions();
|
||||
|
||||
@@ -330,13 +336,15 @@ class ColumnFamilyData {
|
||||
// recalculation of compaction score. These values are used in
|
||||
// DBImpl::MakeRoomForWrite function to decide, if it need to make
|
||||
// a write stall
|
||||
void RecalculateWriteStallConditions(
|
||||
WriteStallCondition RecalculateWriteStallConditions(
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
void set_initialized() { initialized_.store(true); }
|
||||
|
||||
bool initialized() const { return initialized_.load(); }
|
||||
|
||||
Env::WriteLifeTimeHint CalculateSSTWriteHint(int level);
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
ColumnFamilyData(uint32_t id, const std::string& name,
|
||||
@@ -354,7 +362,7 @@ class ColumnFamilyData {
|
||||
|
||||
std::atomic<int> refs_; // outstanding references to ColumnFamilyData
|
||||
std::atomic<bool> initialized_;
|
||||
bool dropped_; // true if client dropped it
|
||||
std::atomic<bool> dropped_; // true if client dropped it
|
||||
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
@@ -415,6 +423,9 @@ class ColumnFamilyData {
|
||||
|
||||
// if the database was opened with 2pc enabled
|
||||
bool allow_2pc_;
|
||||
|
||||
// Memtable id to track flush.
|
||||
std::atomic<uint64_t> last_memtable_id_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
|
||||
+16
-136
@@ -933,62 +933,62 @@ TEST_F(ColumnFamilyTest, FlushTest) {
|
||||
TEST_F(ColumnFamilyTest, LogDeletionTest) {
|
||||
db_options_.max_total_wal_size = std::numeric_limits<uint64_t>::max();
|
||||
column_family_options_.arena_block_size = 4 * 1024;
|
||||
column_family_options_.write_buffer_size = 100000; // 100KB
|
||||
column_family_options_.write_buffer_size = 128000; // 128KB
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two", "three", "four"});
|
||||
// Each bracket is one log file. if number is in (), it means
|
||||
// we don't need it anymore (it's been flushed)
|
||||
// []
|
||||
AssertCountLiveLogFiles(0);
|
||||
PutRandomData(0, 1, 100);
|
||||
PutRandomData(0, 1, 128);
|
||||
// [0]
|
||||
PutRandomData(1, 1, 100);
|
||||
PutRandomData(1, 1, 128);
|
||||
// [0, 1]
|
||||
PutRandomData(1, 1000, 100);
|
||||
PutRandomData(1, 1000, 128);
|
||||
WaitForFlush(1);
|
||||
// [0, (1)] [1]
|
||||
AssertCountLiveLogFiles(2);
|
||||
PutRandomData(0, 1, 100);
|
||||
PutRandomData(0, 1, 128);
|
||||
// [0, (1)] [0, 1]
|
||||
AssertCountLiveLogFiles(2);
|
||||
PutRandomData(2, 1, 100);
|
||||
PutRandomData(2, 1, 128);
|
||||
// [0, (1)] [0, 1, 2]
|
||||
PutRandomData(2, 1000, 100);
|
||||
PutRandomData(2, 1000, 128);
|
||||
WaitForFlush(2);
|
||||
// [0, (1)] [0, 1, (2)] [2]
|
||||
AssertCountLiveLogFiles(3);
|
||||
PutRandomData(2, 1000, 100);
|
||||
PutRandomData(2, 1000, 128);
|
||||
WaitForFlush(2);
|
||||
// [0, (1)] [0, 1, (2)] [(2)] [2]
|
||||
AssertCountLiveLogFiles(4);
|
||||
PutRandomData(3, 1, 100);
|
||||
PutRandomData(3, 1, 128);
|
||||
// [0, (1)] [0, 1, (2)] [(2)] [2, 3]
|
||||
PutRandomData(1, 1, 100);
|
||||
PutRandomData(1, 1, 128);
|
||||
// [0, (1)] [0, 1, (2)] [(2)] [1, 2, 3]
|
||||
AssertCountLiveLogFiles(4);
|
||||
PutRandomData(1, 1000, 100);
|
||||
PutRandomData(1, 1000, 128);
|
||||
WaitForFlush(1);
|
||||
// [0, (1)] [0, (1), (2)] [(2)] [(1), 2, 3] [1]
|
||||
AssertCountLiveLogFiles(5);
|
||||
PutRandomData(0, 1000, 100);
|
||||
PutRandomData(0, 1000, 128);
|
||||
WaitForFlush(0);
|
||||
// [(0), (1)] [(0), (1), (2)] [(2)] [(1), 2, 3] [1, (0)] [0]
|
||||
// delete obsolete logs -->
|
||||
// [(1), 2, 3] [1, (0)] [0]
|
||||
AssertCountLiveLogFiles(3);
|
||||
PutRandomData(0, 1000, 100);
|
||||
PutRandomData(0, 1000, 128);
|
||||
WaitForFlush(0);
|
||||
// [(1), 2, 3] [1, (0)], [(0)] [0]
|
||||
AssertCountLiveLogFiles(4);
|
||||
PutRandomData(1, 1000, 100);
|
||||
PutRandomData(1, 1000, 128);
|
||||
WaitForFlush(1);
|
||||
// [(1), 2, 3] [(1), (0)] [(0)] [0, (1)] [1]
|
||||
AssertCountLiveLogFiles(5);
|
||||
PutRandomData(2, 1000, 100);
|
||||
PutRandomData(2, 1000, 128);
|
||||
WaitForFlush(2);
|
||||
// [(1), (2), 3] [(1), (0)] [(0)] [0, (1)] [1, (2)], [2]
|
||||
AssertCountLiveLogFiles(6);
|
||||
PutRandomData(3, 1000, 100);
|
||||
PutRandomData(3, 1000, 128);
|
||||
WaitForFlush(3);
|
||||
// [(1), (2), (3)] [(1), (0)] [(0)] [0, (1)] [1, (2)], [2, (3)] [3]
|
||||
// delete obsolete logs -->
|
||||
@@ -1868,126 +1868,6 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
}
|
||||
}
|
||||
|
||||
// This test checks for automatic getting a conflict if there is a
|
||||
// manual which has not yet been scheduled.
|
||||
// The manual compaction waits in NotScheduled
|
||||
// We generate more files and then trigger an automatic compaction
|
||||
// This will wait because there is an unscheduled manual compaction.
|
||||
// Once the conflict is hit, the manual compaction starts and ends
|
||||
// Then another automatic will start and end.
|
||||
TEST_F(ColumnFamilyTest, SameCFManualAutomaticConflict) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ColumnFamilyOptions default_cf, one;
|
||||
db_options_.max_open_files = 20; // only 10 files in file cache
|
||||
db_options_.max_background_compactions = 3;
|
||||
|
||||
default_cf.compaction_style = kCompactionStyleLevel;
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.max_compaction_bytes = default_cf.target_file_size_base * 1100;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
one.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
one.num_levels = 1;
|
||||
// trigger compaction if there are >= 4 files
|
||||
one.level0_file_num_compaction_trigger = 4;
|
||||
one.write_buffer_size = 120000;
|
||||
|
||||
Reopen({default_cf, one});
|
||||
// make sure all background compaction jobs can be scheduled
|
||||
auto stop_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
// SETUP column family "one" -- universal style
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger - 2; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(ToString(i + 1), 1);
|
||||
}
|
||||
bool cf_1_1 = true;
|
||||
bool cf_1_2 = true;
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction()::Conflict",
|
||||
"ColumnFamilyTest::ManualAutoCon:7"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:9",
|
||||
"ColumnFamilyTest::ManualAutoCon:8"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:2",
|
||||
"ColumnFamilyTest::ManualAutoCon:6"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:4",
|
||||
"ColumnFamilyTest::ManualAutoCon:5"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:1",
|
||||
"ColumnFamilyTest::ManualAutoCon:2"},
|
||||
{"ColumnFamilyTest::ManualAutoCon:1",
|
||||
"ColumnFamilyTest::ManualAutoCon:3"}});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* arg) {
|
||||
if (cf_1_1) {
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:4");
|
||||
cf_1_1 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:3");
|
||||
} else if (cf_1_2) {
|
||||
cf_1_2 = false;
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:2");
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RunManualCompaction:NotScheduled", [&](void* arg) {
|
||||
InstrumentedMutex* mutex = static_cast<InstrumentedMutex*>(arg);
|
||||
mutex->Unlock();
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:9");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:7");
|
||||
mutex->Lock();
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
rocksdb::port::Thread threads([&] {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(
|
||||
db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:6");
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:8");
|
||||
WaitForFlush(1);
|
||||
|
||||
// Add more L0 files and force automatic compaction
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
AssertFilesPerLevel(ToString(one.level0_file_num_compaction_trigger + i),
|
||||
1);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:5");
|
||||
// Add more L0 files and force automatic compaction
|
||||
for (int i = 0; i < one.level0_file_num_compaction_trigger; ++i) {
|
||||
PutRandomData(1, 10, 12000, true);
|
||||
PutRandomData(1, 1, 10, true);
|
||||
WaitForFlush(1);
|
||||
}
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::ManualAutoCon:1");
|
||||
|
||||
threads.join();
|
||||
WaitForCompaction();
|
||||
// VERIFY compaction "one"
|
||||
ASSERT_LE(NumTableFilesAtLevel(0, 1), 3);
|
||||
|
||||
// Compare against saved keys
|
||||
std::set<std::string>::iterator key_iter = keys_.begin();
|
||||
while (key_iter != keys_.end()) {
|
||||
ASSERT_NE("NOT_FOUND", Get(1, *key_iter));
|
||||
key_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
// In this test, we generate enough files to trigger automatic compactions.
|
||||
// The automatic compaction waits in NonTrivial:AfterRun
|
||||
// We generate more files and then trigger an automatic compaction
|
||||
|
||||
@@ -17,7 +17,8 @@ extern bool SaveValue(void* arg, const ParsedInternalKey& parsed_key,
|
||||
|
||||
CompactedDBImpl::CompactedDBImpl(
|
||||
const DBOptions& options, const std::string& dbname)
|
||||
: DBImpl(options, dbname) {
|
||||
: DBImpl(options, dbname), cfd_(nullptr), version_(nullptr),
|
||||
user_comparator_(nullptr) {
|
||||
}
|
||||
|
||||
CompactedDBImpl::~CompactedDBImpl() {
|
||||
@@ -93,6 +94,7 @@ std::vector<Status> CompactedDBImpl::MultiGet(const ReadOptions& options,
|
||||
}
|
||||
|
||||
Status CompactedDBImpl::Init(const Options& options) {
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
mutex_.Lock();
|
||||
ColumnFamilyDescriptor cf(kDefaultColumnFamilyName,
|
||||
ColumnFamilyOptions(options));
|
||||
@@ -100,9 +102,10 @@ Status CompactedDBImpl::Init(const Options& options) {
|
||||
if (s.ok()) {
|
||||
cfd_ = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
DefaultColumnFamily())->cfd();
|
||||
delete cfd_->InstallSuperVersion(new SuperVersion(), &mutex_);
|
||||
cfd_->InstallSuperVersion(&sv_context, &mutex_);
|
||||
}
|
||||
mutex_.Unlock();
|
||||
sv_context.Clean();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
+33
-43
@@ -86,35 +86,23 @@ void Compaction::GetBoundaryKeys(
|
||||
bool Compaction::IsBottommostLevel(
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
const std::vector<CompactionInputFiles>& inputs) {
|
||||
if (inputs[0].level == 0 &&
|
||||
inputs[0].files.back() != vstorage->LevelFiles(0).back()) {
|
||||
return false;
|
||||
int output_l0_idx;
|
||||
if (output_level == 0) {
|
||||
output_l0_idx = 0;
|
||||
for (const auto* file : vstorage->LevelFiles(0)) {
|
||||
if (inputs[0].files.back() == file) {
|
||||
break;
|
||||
}
|
||||
++output_l0_idx;
|
||||
}
|
||||
assert(static_cast<size_t>(output_l0_idx) < vstorage->LevelFiles(0).size());
|
||||
} else {
|
||||
output_l0_idx = -1;
|
||||
}
|
||||
|
||||
Slice smallest_key, largest_key;
|
||||
GetBoundaryKeys(vstorage, inputs, &smallest_key, &largest_key);
|
||||
|
||||
// Checks whether there are files living beyond the output_level.
|
||||
// If lower levels have files, it checks for overlap between files
|
||||
// if the compaction process and those files.
|
||||
// Bottomlevel optimizations can be made if there are no files in
|
||||
// lower levels or if there is no overlap with the files in
|
||||
// the lower levels.
|
||||
for (int i = output_level + 1; i < vstorage->num_levels(); i++) {
|
||||
// It is not the bottommost level if there are files in higher
|
||||
// levels when the output level is 0 or if there are files in
|
||||
// higher levels which overlap with files to be compacted.
|
||||
// output_level == 0 means that we want it to be considered
|
||||
// s the bottommost level only if the last file on the level
|
||||
// is a part of the files to be compacted - this is verified by
|
||||
// the first if condition in this function
|
||||
if (vstorage->NumLevelFiles(i) > 0 &&
|
||||
(output_level == 0 ||
|
||||
vstorage->OverlapInLevel(i, &smallest_key, &largest_key))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return !vstorage->RangeMightExistAfterSortedRun(smallest_key, largest_key,
|
||||
output_level, output_l0_idx);
|
||||
}
|
||||
|
||||
// test function to validate the functionality of IsBottommostLevel()
|
||||
@@ -241,7 +229,7 @@ bool Compaction::IsTrivialMove() const {
|
||||
|
||||
// Used in universal compaction, where trivial move can be done if the
|
||||
// input files are non overlapping
|
||||
if ((immutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
|
||||
if ((mutable_cf_options_.compaction_options_universal.allow_trivial_move) &&
|
||||
(output_level_ != 0)) {
|
||||
return is_trivial_move_;
|
||||
}
|
||||
@@ -284,10 +272,10 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
assert(input_version_ != nullptr);
|
||||
assert(level_ptrs != nullptr);
|
||||
assert(level_ptrs->size() == static_cast<size_t>(number_levels_));
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
if (output_level_ == 0) {
|
||||
return false;
|
||||
}
|
||||
if (bottommost_level_) {
|
||||
return true;
|
||||
} else if (output_level_ != 0 &&
|
||||
cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
// Maybe use binary search to find right entry instead of linear search?
|
||||
const Comparator* user_cmp = cfd_->user_comparator();
|
||||
for (int lvl = output_level_ + 1; lvl < number_levels_; lvl++) {
|
||||
@@ -307,9 +295,8 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return bottommost_level_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mark (or clear) each file that is being compacted
|
||||
@@ -418,20 +405,23 @@ void Compaction::Summary(char* output, int len) {
|
||||
uint64_t Compaction::OutputFilePreallocationSize() const {
|
||||
uint64_t preallocation_size = 0;
|
||||
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(cfd_->ioptions()->compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = max_output_file_size_;
|
||||
} else {
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
}
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
preallocation_size += file->fd.GetFileSize();
|
||||
}
|
||||
}
|
||||
|
||||
if (max_output_file_size_ != port::kMaxUint64 &&
|
||||
(immutable_cf_options_.compaction_style == kCompactionStyleLevel ||
|
||||
output_level() > 0)) {
|
||||
preallocation_size = std::min(max_output_file_size_, preallocation_size);
|
||||
}
|
||||
|
||||
// Over-estimate slightly so we don't end up just barely crossing
|
||||
// the threshold
|
||||
return preallocation_size + (preallocation_size / 10);
|
||||
// No point to prellocate more than 1GB.
|
||||
return std::min(uint64_t{1073741824},
|
||||
preallocation_size + (preallocation_size / 10));
|
||||
}
|
||||
|
||||
std::unique_ptr<CompactionFilter> Compaction::CreateCompactionFilter() const {
|
||||
|
||||
+147
-69
@@ -4,6 +4,9 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "table/internal_iterator.h"
|
||||
|
||||
@@ -37,33 +40,40 @@ CompactionEventListener::CompactionListenerValueType fromInternalValueType(
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
CompactionEventListener* compaction_listener,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, env, expect_valid_internal_key,
|
||||
range_del_agg,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
expect_valid_internal_key, range_del_agg,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction_filter, compaction_listener, shutting_down) {}
|
||||
compaction_filter, compaction_listener, shutting_down,
|
||||
preserve_deletes_seqnum) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter,
|
||||
CompactionEventListener* compaction_listener,
|
||||
const std::atomic<bool>* shutting_down)
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum
|
||||
)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
snapshots_(snapshots),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
env_(env),
|
||||
expect_valid_internal_key_(expect_valid_internal_key),
|
||||
range_del_agg_(range_del_agg),
|
||||
@@ -73,8 +83,12 @@ CompactionIterator::CompactionIterator(
|
||||
compaction_listener_(compaction_listener),
|
||||
#endif // ROCKSDB_LITE
|
||||
shutting_down_(shutting_down),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
ignore_snapshots_(false),
|
||||
merge_out_iter_(merge_helper_) {
|
||||
current_user_key_sequence_(0),
|
||||
current_user_key_snapshot_(0),
|
||||
merge_out_iter_(merge_helper_),
|
||||
current_key_committed_(false) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
bottommost_level_ =
|
||||
compaction_ == nullptr ? false : compaction_->bottommost_level();
|
||||
@@ -131,8 +145,8 @@ void CompactionIterator::Next() {
|
||||
if (merge_out_iter_.Valid()) {
|
||||
key_ = merge_out_iter_.key();
|
||||
value_ = merge_out_iter_.value();
|
||||
bool valid_key __attribute__((__unused__)) =
|
||||
ParseInternalKey(key_, &ikey_);
|
||||
bool valid_key __attribute__((__unused__));
|
||||
valid_key = ParseInternalKey(key_, &ikey_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to be valid.
|
||||
assert(valid_key);
|
||||
@@ -166,6 +180,61 @@ void CompactionIterator::Next() {
|
||||
PrepareOutput();
|
||||
}
|
||||
|
||||
void CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
Slice* skip_until) {
|
||||
if (compaction_filter_ != nullptr &&
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex) &&
|
||||
(visible_at_tip_ || ignore_snapshots_ ||
|
||||
ikey_.sequence > latest_snapshot_ ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
latest_snapshot_))))) {
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
// replace the entry with a deletion marker.
|
||||
CompactionFilter::Decision filter;
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
CompactionFilter::ValueType value_type =
|
||||
ikey_.type == kTypeValue ? CompactionFilter::ValueType::kValue
|
||||
: CompactionFilter::ValueType::kBlobIndex;
|
||||
{
|
||||
StopWatchNano timer(env_, true);
|
||||
filter = compaction_filter_->FilterV2(
|
||||
compaction_->level(), ikey_.user_key, value_type, value_,
|
||||
&compaction_filter_value_, compaction_filter_skip_until_.rep());
|
||||
iter_stats_.total_filter_time +=
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
||||
cmp_->Compare(*compaction_filter_skip_until_.rep(), ikey_.user_key) <=
|
||||
0) {
|
||||
// Can't skip to a key smaller than the current one.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
filter = CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemove) {
|
||||
// convert the current key to a delete; key_ is pointing into
|
||||
// current_key_ at this point, so updating current_key_ updates key()
|
||||
ikey_.type = kTypeDeletion;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeDeletion);
|
||||
// no value associated with delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
*need_skip = true;
|
||||
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
*skip_until = compaction_filter_skip_until_.Encode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::NextFromInput() {
|
||||
at_next_ = false;
|
||||
valid_ = false;
|
||||
@@ -220,6 +289,9 @@ void CompactionIterator::NextFromInput() {
|
||||
has_outputted_key_ = false;
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
current_key_committed_ =
|
||||
(snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber));
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (compaction_listener_) {
|
||||
@@ -227,56 +299,12 @@ void CompactionIterator::NextFromInput() {
|
||||
fromInternalValueType(ikey_.type),
|
||||
value_, ikey_.sequence, true);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// apply the compaction filter to the first occurrence of the user key
|
||||
if (compaction_filter_ != nullptr &&
|
||||
(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex) &&
|
||||
(visible_at_tip_ || ikey_.sequence > latest_snapshot_ ||
|
||||
ignore_snapshots_)) {
|
||||
// If the user has specified a compaction filter and the sequence
|
||||
// number is greater than any external snapshot, then invoke the
|
||||
// filter. If the return value of the compaction filter is true,
|
||||
// replace the entry with a deletion marker.
|
||||
CompactionFilter::Decision filter;
|
||||
compaction_filter_value_.clear();
|
||||
compaction_filter_skip_until_.Clear();
|
||||
CompactionFilter::ValueType value_type =
|
||||
ikey_.type == kTypeValue ? CompactionFilter::ValueType::kValue
|
||||
: CompactionFilter::ValueType::kBlobIndex;
|
||||
{
|
||||
StopWatchNano timer(env_, true);
|
||||
filter = compaction_filter_->FilterV2(
|
||||
compaction_->level(), ikey_.user_key, value_type, value_,
|
||||
&compaction_filter_value_, compaction_filter_skip_until_.rep());
|
||||
iter_stats_.total_filter_time +=
|
||||
env_ != nullptr ? timer.ElapsedNanos() : 0;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil &&
|
||||
cmp_->Compare(*compaction_filter_skip_until_.rep(),
|
||||
ikey_.user_key) <= 0) {
|
||||
// Can't skip to a key smaller than the current one.
|
||||
// Keep the key as per FilterV2 documentation.
|
||||
filter = CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
|
||||
if (filter == CompactionFilter::Decision::kRemove) {
|
||||
// convert the current key to a delete; key_ is pointing into
|
||||
// current_key_ at this point, so updating current_key_ updates key()
|
||||
ikey_.type = kTypeDeletion;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, kTypeDeletion);
|
||||
// no value associated with delete
|
||||
value_.clear();
|
||||
iter_stats_.num_record_drop_user++;
|
||||
} else if (filter == CompactionFilter::Decision::kChangeValue) {
|
||||
value_ = compaction_filter_value_;
|
||||
} else if (filter == CompactionFilter::Decision::kRemoveAndSkipUntil) {
|
||||
need_skip = true;
|
||||
compaction_filter_skip_until_.ConvertFromUserKey(kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
skip_until = compaction_filter_skip_until_.Encode();
|
||||
}
|
||||
// Apply the compaction filter to the first committed version of the user
|
||||
// key.
|
||||
if (current_key_committed_) {
|
||||
InvokeFilterIfNeeded(&need_skip, &skip_until);
|
||||
}
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -295,13 +323,33 @@ void CompactionIterator::NextFromInput() {
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
key_ = current_key_.GetInternalKey();
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
|
||||
// Note that newer version of a key is ordered before older versions. If a
|
||||
// newer version of a key is committed, so as the older version. No need
|
||||
// to query snapshot_checker_ in that case.
|
||||
if (UNLIKELY(!current_key_committed_)) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
current_key_committed_ =
|
||||
snapshot_checker_->IsInSnapshot(ikey_.sequence, kMaxSequenceNumber);
|
||||
// Apply the compaction filter to the first committed version of the
|
||||
// user key.
|
||||
if (current_key_committed_) {
|
||||
InvokeFilterIfNeeded(&need_skip, &skip_until);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (UNLIKELY(!current_key_committed_)) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
valid_ = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If there are no snapshots, then this kv affect visibility at tip.
|
||||
// Otherwise, search though all existing snapshots to find the earliest
|
||||
// snapshot that is affected by this kv.
|
||||
SequenceNumber last_sequence __attribute__((__unused__)) =
|
||||
current_user_key_sequence_;
|
||||
SequenceNumber last_sequence __attribute__((__unused__));
|
||||
last_sequence = current_user_key_sequence_;
|
||||
current_user_key_sequence_ = ikey_.sequence;
|
||||
SequenceNumber last_snapshot = current_user_key_snapshot_;
|
||||
SequenceNumber prev_snapshot = 0; // 0 means no previous snapshot
|
||||
@@ -366,7 +414,10 @@ void CompactionIterator::NextFromInput() {
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot) {
|
||||
if (prev_snapshot == 0 || next_ikey.sequence > prev_snapshot ||
|
||||
(snapshot_checker_ != nullptr &&
|
||||
UNLIKELY(!snapshot_checker_->IsInSnapshot(next_ikey.sequence,
|
||||
prev_snapshot)))) {
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
// We encountered two SingleDeletes in a row. This could be due to
|
||||
// unexpected user input.
|
||||
@@ -377,8 +428,12 @@ void CompactionIterator::NextFromInput() {
|
||||
// input_->Next().
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
} else if ((ikey_.sequence <= earliest_write_conflict_snapshot_) ||
|
||||
has_outputted_key_) {
|
||||
} else if (has_outputted_key_ ||
|
||||
(ikey_.sequence <= earliest_write_conflict_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence,
|
||||
earliest_write_conflict_snapshot_))))) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
// value. It is safe to drop both records since we've already
|
||||
// outputted a key in this snapshot, or there is no earlier
|
||||
@@ -426,6 +481,9 @@ void CompactionIterator::NextFromInput() {
|
||||
// comparison, so the value of has_current_user_key does not matter.
|
||||
has_current_user_key_ = false;
|
||||
if (compaction_ != nullptr && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// Key doesn't exist outside of this range.
|
||||
@@ -459,6 +517,10 @@ void CompactionIterator::NextFromInput() {
|
||||
input_->Next();
|
||||
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
||||
ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(snapshot_checker_->IsInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
&level_ptrs_)) {
|
||||
// TODO(noetzli): This is the only place where we use compaction_
|
||||
@@ -492,6 +554,10 @@ void CompactionIterator::NextFromInput() {
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
// In case snapshot_checker is present, we can probably merge further
|
||||
// beyond prev_snapshot, since there could be more keys with sequence
|
||||
// smaller than prev_snapshot, but reported by snapshot_checker as not
|
||||
// visible by prev_snapshot. But it will make the logic more complicated.
|
||||
Status s = merge_helper_->MergeUntil(input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
@@ -504,8 +570,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// These will be correctly set below.
|
||||
key_ = merge_out_iter_.key();
|
||||
value_ = merge_out_iter_.value();
|
||||
bool valid_key __attribute__((__unused__)) =
|
||||
ParseInternalKey(key_, &ikey_);
|
||||
bool valid_key __attribute__((__unused__));
|
||||
valid_key = ParseInternalKey(key_, &ikey_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to valid.
|
||||
assert(valid_key);
|
||||
@@ -558,8 +624,12 @@ void CompactionIterator::PrepareOutput() {
|
||||
|
||||
// This is safe for TransactionDB write-conflict checking since transactions
|
||||
// only care about sequence number larger than any active snapshots.
|
||||
if ((compaction_ != nullptr && !compaction_->allow_ingest_behind()) &&
|
||||
if ((compaction_ != nullptr &&
|
||||
!compaction_->allow_ingest_behind()) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
bottommost_level_ && valid_ && ikey_.sequence <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr || LIKELY(snapshot_checker_->IsInSnapshot(
|
||||
ikey_.sequence, earliest_snapshot_))) &&
|
||||
ikey_.type != kTypeMerge &&
|
||||
!cmp_->Equal(compaction_->GetLargestUserKey(), ikey_.user_key)) {
|
||||
assert(ikey_.type != kTypeDeletion && ikey_.type != kTypeSingleDeletion);
|
||||
@@ -571,10 +641,11 @@ void CompactionIterator::PrepareOutput() {
|
||||
inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, SequenceNumber* prev_snapshot) {
|
||||
assert(snapshots_->size());
|
||||
SequenceNumber prev __attribute__((__unused__)) = kMaxSequenceNumber;
|
||||
SequenceNumber prev = kMaxSequenceNumber;
|
||||
for (const auto cur : *snapshots_) {
|
||||
assert(prev == kMaxSequenceNumber || prev <= cur);
|
||||
if (cur >= in) {
|
||||
if (cur >= in && (snapshot_checker_ == nullptr ||
|
||||
snapshot_checker_->IsInSnapshot(in, cur))) {
|
||||
*prev_snapshot = prev == kMaxSequenceNumber ? 0 : prev;
|
||||
return cur;
|
||||
}
|
||||
@@ -585,4 +656,11 @@ inline SequenceNumber CompactionIterator::findEarliestVisibleSnapshot(
|
||||
return kMaxSequenceNumber;
|
||||
}
|
||||
|
||||
// used in 2 places - prevents deletion markers to be dropped if they may be
|
||||
// needed and disables seqnum zero-out in PrepareOutput for recent keys.
|
||||
inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
|
||||
return (!compaction_->preserve_deletes()) ||
|
||||
(ikey_.sequence < preserve_deletes_seqnum_);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
|
||||
@@ -48,6 +49,9 @@ class CompactionIterator {
|
||||
virtual bool allow_ingest_behind() const {
|
||||
return compaction_->immutable_cf_options()->allow_ingest_behind;
|
||||
}
|
||||
virtual bool preserve_deletes() const {
|
||||
return compaction_->immutable_cf_options()->preserve_deletes;
|
||||
}
|
||||
|
||||
protected:
|
||||
CompactionProxy() = default;
|
||||
@@ -59,25 +63,29 @@ class CompactionIterator {
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
CompactionEventListener* compaction_listener = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
CompactionEventListener* compaction_listener = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr);
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -111,6 +119,9 @@ class CompactionIterator {
|
||||
// compression.
|
||||
void PrepareOutput();
|
||||
|
||||
// Invoke compaction filter if needed.
|
||||
void InvokeFilterIfNeeded(bool* need_skip, Slice* skip_until);
|
||||
|
||||
// Given a sequence number, return the sequence number of the
|
||||
// earliest snapshot that this sequence number is visible in.
|
||||
// The snapshots themselves are arranged in ascending order of
|
||||
@@ -120,11 +131,17 @@ class CompactionIterator {
|
||||
inline SequenceNumber findEarliestVisibleSnapshot(
|
||||
SequenceNumber in, SequenceNumber* prev_snapshot);
|
||||
|
||||
// Checks whether the currently seen ikey_ is needed for
|
||||
// incremental (differential) snapshot and hence can't be dropped
|
||||
// or seqnum be zero-ed out even if all other conditions for it are met.
|
||||
inline bool ikeyNotNeededForIncrementalSnapshot();
|
||||
|
||||
InternalIterator* input_;
|
||||
const Comparator* cmp_;
|
||||
MergeHelper* merge_helper_;
|
||||
const std::vector<SequenceNumber>* snapshots_;
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
Env* env_;
|
||||
bool expect_valid_internal_key_;
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
@@ -132,8 +149,9 @@ class CompactionIterator {
|
||||
const CompactionFilter* compaction_filter_;
|
||||
#ifndef ROCKSDB_LITE
|
||||
CompactionEventListener* compaction_listener_;
|
||||
#endif // ROCKSDB_LITE
|
||||
#endif // !ROCKSDB_LITE
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
bool visible_at_tip_;
|
||||
@@ -189,6 +207,10 @@ class CompactionIterator {
|
||||
std::vector<size_t> level_ptrs_;
|
||||
CompactionIterationStats iter_stats_;
|
||||
|
||||
// Used to avoid purging uncommitted values. The application can specify
|
||||
// uncommitted values by providing a SnapshotChecker object.
|
||||
bool current_key_committed_;
|
||||
|
||||
bool IsShuttingDown() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return shutting_down_ && shutting_down_->load(std::memory_order_relaxed);
|
||||
|
||||
@@ -156,6 +156,8 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
}
|
||||
virtual bool allow_ingest_behind() const { return false; }
|
||||
|
||||
virtual bool preserve_deletes() const {return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
};
|
||||
|
||||
@@ -181,6 +183,8 @@ class CompactionIteratorTest : public testing::Test {
|
||||
compaction_proxy_ = new FakeCompaction();
|
||||
compaction.reset(compaction_proxy_);
|
||||
}
|
||||
// TODO(yiwu) add a mock snapshot checker and add test for it.
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
|
||||
merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, merge_op, filter,
|
||||
nullptr, false, 0, 0, nullptr,
|
||||
@@ -189,8 +193,9 @@ class CompactionIteratorTest : public testing::Test {
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
kMaxSequenceNumber, Env::Default(), false, range_del_agg_.get(),
|
||||
std::move(compaction), filter, nullptr, &shutting_down_));
|
||||
kMaxSequenceNumber, snapshot_checker, Env::Default(), false,
|
||||
range_del_agg_.get(), std::move(compaction), filter, nullptr,
|
||||
&shutting_down_));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }
|
||||
|
||||
+90
-46
@@ -38,7 +38,6 @@
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/likely.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -264,15 +263,16 @@ void CompactionJob::AggregateStatistics() {
|
||||
|
||||
CompactionJob::CompactionJob(
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, Status* db_bg_error,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats, const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats)
|
||||
const SnapshotChecker* snapshot_checker, std::shared_ptr<Cache> table_cache,
|
||||
EventLogger* event_logger, bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
@@ -281,8 +281,11 @@ CompactionJob::CompactionJob(
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
env_(db_options.env),
|
||||
env_optiosn_for_read_(
|
||||
env_->OptimizeForCompactionTableRead(env_options, db_options_)),
|
||||
versions_(versions),
|
||||
shutting_down_(shutting_down),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
output_directory_(output_directory),
|
||||
@@ -291,10 +294,13 @@ CompactionJob::CompactionJob(
|
||||
db_bg_error_(db_bg_error),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
table_cache_(std::move(table_cache)),
|
||||
event_logger_(event_logger),
|
||||
bottommost_level_(false),
|
||||
paranoid_file_checks_(paranoid_file_checks),
|
||||
measure_io_stats_(measure_io_stats) {
|
||||
measure_io_stats_(measure_io_stats),
|
||||
write_hint_(Env::WLTH_NOT_SET) {
|
||||
assert(log_buffer_ != nullptr);
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
@@ -365,6 +371,8 @@ void CompactionJob::Prepare() {
|
||||
assert(c->column_family_data()->current()->storage_info()
|
||||
->NumLevelFiles(compact_->compaction->level()) > 0);
|
||||
|
||||
write_hint_ = c->column_family_data()->CalculateSSTWriteHint(
|
||||
c->output_level());
|
||||
// Is this compaction producing files at the bottommost level?
|
||||
bottommost_level_ = c->bottommost_level();
|
||||
|
||||
@@ -617,7 +625,8 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
"files in(%d, %d) out(%d) "
|
||||
"MB in(%.1f, %.1f) out(%.1f), read-write-amplify(%.1f) "
|
||||
"write-amplify(%.1f) %s, records in: %d, records dropped: %d\n",
|
||||
"write-amplify(%.1f) %s, records in: %" PRIu64
|
||||
", records dropped: %" PRIu64 " output_compression: %s\n",
|
||||
cfd->GetName().c_str(), vstorage->LevelSummary(&tmp), bytes_read_per_sec,
|
||||
bytes_written_per_sec, compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
@@ -626,20 +635,23 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
stats.bytes_read_output_level / 1048576.0,
|
||||
stats.bytes_written / 1048576.0, read_write_amp, write_amp,
|
||||
status.ToString().c_str(), stats.num_input_records,
|
||||
stats.num_dropped_records);
|
||||
stats.num_dropped_records,
|
||||
CompressionTypeToString(compact_->compaction->output_compression())
|
||||
.c_str());
|
||||
|
||||
UpdateCompactionJobStats(stats);
|
||||
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_);
|
||||
stream << "job" << job_id_
|
||||
<< "event" << "compaction_finished"
|
||||
stream << "job" << job_id_ << "event"
|
||||
<< "compaction_finished"
|
||||
<< "compaction_time_micros" << compaction_stats_.micros
|
||||
<< "output_level" << compact_->compaction->output_level()
|
||||
<< "num_output_files" << compact_->NumOutputFiles()
|
||||
<< "total_output_size" << compact_->total_bytes
|
||||
<< "num_input_records" << compact_->num_input_records
|
||||
<< "num_output_records" << compact_->num_output_records
|
||||
<< "num_subcompactions" << compact_->sub_compact_states.size();
|
||||
<< "total_output_size" << compact_->total_bytes << "num_input_records"
|
||||
<< compact_->num_input_records << "num_output_records"
|
||||
<< compact_->num_output_records << "num_subcompactions"
|
||||
<< compact_->sub_compact_states.size() << "output_compression"
|
||||
<< CompressionTypeToString(compact_->compaction->output_compression());
|
||||
|
||||
if (compaction_job_stats_ != nullptr) {
|
||||
stream << "num_single_delete_mismatches"
|
||||
@@ -674,7 +686,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg(
|
||||
new RangeDelAggregator(cfd->internal_comparator(), existing_snapshots_));
|
||||
std::unique_ptr<InternalIterator> input(versions_->MakeInputIterator(
|
||||
sub_compact->compaction, range_del_agg.get()));
|
||||
sub_compact->compaction, range_del_agg.get(), env_optiosn_for_read_));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -699,15 +711,18 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
|
||||
// To build compression dictionary, we sample the first output file, assuming
|
||||
// it'll reach the maximum length, and then use the dictionary for compressing
|
||||
// subsequent output files. The dictionary may be less than max_dict_bytes if
|
||||
// the first output file's length is less than the maximum.
|
||||
// it'll reach the maximum length. We optionally pass these samples through
|
||||
// zstd's dictionary trainer, or just use them directly. Then, the dictionary
|
||||
// is used for compressing subsequent output files in the same subcompaction.
|
||||
const bool kUseZstdTrainer =
|
||||
cfd->ioptions()->compression_opts.zstd_max_train_bytes > 0;
|
||||
const size_t kSampleBytes =
|
||||
kUseZstdTrainer ? cfd->ioptions()->compression_opts.zstd_max_train_bytes
|
||||
: cfd->ioptions()->compression_opts.max_dict_bytes;
|
||||
const int kSampleLenShift = 6; // 2^6 = 64-byte samples
|
||||
std::set<size_t> sample_begin_offsets;
|
||||
if (bottommost_level_ &&
|
||||
cfd->ioptions()->compression_opts.max_dict_bytes > 0) {
|
||||
const size_t kMaxSamples =
|
||||
cfd->ioptions()->compression_opts.max_dict_bytes >> kSampleLenShift;
|
||||
if (bottommost_level_ && kSampleBytes > 0) {
|
||||
const size_t kMaxSamples = kSampleBytes >> kSampleLenShift;
|
||||
const size_t kOutFileLen = mutable_cf_options->MaxFileSizeForLevel(
|
||||
compact_->compaction->output_level());
|
||||
if (kOutFileLen != port::kMaxSizet) {
|
||||
@@ -761,9 +776,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
Status status;
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, env_, false,
|
||||
range_del_agg.get(), sub_compact->compaction, compaction_filter,
|
||||
comp_event_listener, shutting_down_));
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, false, range_del_agg.get(),
|
||||
sub_compact->compaction, compaction_filter, comp_event_listener,
|
||||
shutting_down_, preserve_deletes_seqnum_));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() &&
|
||||
@@ -776,11 +792,11 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
const auto& c_iter_stats = c_iter->iter_stats();
|
||||
auto sample_begin_offset_iter = sample_begin_offsets.cbegin();
|
||||
// data_begin_offset and compression_dict are only valid while generating
|
||||
// data_begin_offset and dict_sample_data are only valid while generating
|
||||
// dictionary from the first output file.
|
||||
size_t data_begin_offset = 0;
|
||||
std::string compression_dict;
|
||||
compression_dict.reserve(cfd->ioptions()->compression_opts.max_dict_bytes);
|
||||
std::string dict_sample_data;
|
||||
dict_sample_data.reserve(kSampleBytes);
|
||||
|
||||
while (status.ok() && !cfd->IsDropped() && c_iter->Valid()) {
|
||||
// Invariant: c_iter.status() is guaranteed to be OK if c_iter->Valid()
|
||||
@@ -852,7 +868,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
data_elmt_copy_len =
|
||||
data_end_offset - (data_begin_offset + data_elmt_copy_offset);
|
||||
}
|
||||
compression_dict.append(&data_elmt.data()[data_elmt_copy_offset],
|
||||
dict_sample_data.append(&data_elmt.data()[data_elmt_copy_offset],
|
||||
data_elmt_copy_len);
|
||||
if (sample_end_offset > data_end_offset) {
|
||||
// Didn't finish sample. Try to finish it with the next data_elmt.
|
||||
@@ -907,9 +923,15 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(range_del_out_stats,
|
||||
&sub_compact->compaction_job_stats);
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// Use dictionary from first output file for compression of subsequent
|
||||
// files.
|
||||
sub_compact->compression_dict = std::move(compression_dict);
|
||||
// Use samples from first output file to create dictionary for
|
||||
// compression of subsequent files.
|
||||
if (kUseZstdTrainer) {
|
||||
sub_compact->compression_dict = ZSTD_TrainDictionary(
|
||||
dict_sample_data, kSampleLenShift,
|
||||
cfd->ioptions()->compression_opts.max_dict_bytes);
|
||||
} else {
|
||||
sub_compact->compression_dict = std::move(dict_sample_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1040,6 +1062,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
auto meta = &sub_compact->current_output()->meta;
|
||||
if (s.ok()) {
|
||||
Slice lower_bound_guard, upper_bound_guard;
|
||||
std::string smallest_user_key;
|
||||
const Slice *lower_bound, *upper_bound;
|
||||
if (sub_compact->outputs.size() == 1) {
|
||||
// For the first output table, include range tombstones before the min key
|
||||
@@ -1049,7 +1072,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// For subsequent output tables, only include range tombstones from min
|
||||
// key onwards since the previous file was extended to contain range
|
||||
// tombstones falling before min key.
|
||||
lower_bound_guard = meta->smallest.user_key();
|
||||
smallest_user_key = meta->smallest.user_key().ToString(false /*hex*/);
|
||||
lower_bound_guard = Slice(smallest_user_key);
|
||||
lower_bound = &lower_bound_guard;
|
||||
} else {
|
||||
lower_bound = nullptr;
|
||||
@@ -1067,16 +1091,18 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
range_del_agg->AddToBuilder(sub_compact->builder.get(), lower_bound,
|
||||
upper_bound, meta, range_del_out_stats,
|
||||
bottommost_level_);
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
}
|
||||
const uint64_t current_entries = sub_compact->builder->NumEntries();
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
if (s.ok()) {
|
||||
s = sub_compact->builder->Finish();
|
||||
} else {
|
||||
sub_compact->builder->Abandon();
|
||||
}
|
||||
const uint64_t current_bytes = sub_compact->builder->FileSize();
|
||||
meta->fd.file_size = current_bytes;
|
||||
if (s.ok()) {
|
||||
meta->fd.file_size = current_bytes;
|
||||
}
|
||||
sub_compact->current_output()->finished = true;
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
|
||||
@@ -1121,7 +1147,8 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
nullptr /* range_del_agg */, nullptr,
|
||||
cfd->internal_stats()->GetFileReadHist(
|
||||
compact_->compaction->output_level()),
|
||||
false);
|
||||
false, nullptr /* arena */, false /* skip_filters */,
|
||||
compact_->compaction->output_level());
|
||||
s = iter->status();
|
||||
|
||||
if (s.ok() && paranoid_file_checks_) {
|
||||
@@ -1144,17 +1171,24 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
meta->marked_for_compaction ? " (need compaction)" : "");
|
||||
}
|
||||
}
|
||||
std::string fname = TableFileName(db_options_.db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
std::string fname;
|
||||
FileDescriptor output_fd;
|
||||
if (meta != nullptr) {
|
||||
fname = TableFileName(db_options_.db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
output_fd = meta->fd;
|
||||
} else {
|
||||
fname = "(nil)";
|
||||
}
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger_, cfd->ioptions()->listeners, dbname_, cfd->GetName(), fname,
|
||||
job_id_, meta->fd, tp, TableFileCreationReason::kCompaction, s);
|
||||
job_id_, output_fd, tp, TableFileCreationReason::kCompaction, s);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Report new file to SstFileManagerImpl
|
||||
auto sfm =
|
||||
static_cast<SstFileManagerImpl*>(db_options_.sst_file_manager.get());
|
||||
if (sfm && meta->fd.GetPathId() == 0) {
|
||||
if (sfm && meta != nullptr && meta->fd.GetPathId() == 0) {
|
||||
auto fn = TableFileName(cfd->ioptions()->db_paths, meta->fd.GetNumber(),
|
||||
meta->fd.GetPathId());
|
||||
sfm->OnAddFile(fn);
|
||||
@@ -1252,11 +1286,12 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
#endif // !ROCKSDB_LITE
|
||||
// Make the output file
|
||||
unique_ptr<WritableFile> writable_file;
|
||||
EnvOptions opt_env_opts =
|
||||
env_->OptimizeForCompactionTableWrite(env_options_, db_options_);
|
||||
#ifndef NDEBUG
|
||||
bool syncpoint_arg = env_options_.use_direct_writes;
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionJob::OpenCompactionOutputFile",
|
||||
&opt_env_opts.use_direct_writes);
|
||||
Status s = NewWritableFile(env_, fname, &writable_file, opt_env_opts);
|
||||
&syncpoint_arg);
|
||||
#endif
|
||||
Status s = NewWritableFile(env_, fname, &writable_file, env_options_);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(
|
||||
db_options_.info_log,
|
||||
@@ -1279,6 +1314,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
|
||||
sub_compact->outputs.push_back(out);
|
||||
writable_file->SetIOPriority(Env::IO_LOW);
|
||||
writable_file->SetWriteLifeTimeHint(write_hint_);
|
||||
writable_file->SetPreallocationBlockSize(static_cast<size_t>(
|
||||
sub_compact->compaction->OutputFilePreallocationSize()));
|
||||
sub_compact->outfile.reset(new WritableFileWriter(
|
||||
@@ -1294,7 +1330,15 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->MaxInputFileCreationTime();
|
||||
if (output_file_creation_time == 0) {
|
||||
int64_t _current_time = 0;
|
||||
db_options_.env->GetCurrentTime(&_current_time); // ignore error
|
||||
auto status = db_options_.env->GetCurrentTime(&_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Failed to get current time to populate creation_time property. "
|
||||
"Status: %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
output_file_creation_time = static_cast<uint64_t>(_current_time);
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -45,24 +45,28 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class Arena;
|
||||
class MemTable;
|
||||
class SnapshotChecker;
|
||||
class TableCache;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
class Arena;
|
||||
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory,
|
||||
Statistics* stats, InstrumentedMutex* db_mutex,
|
||||
Status* db_bg_error,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
std::shared_ptr<Cache> table_cache, EventLogger* event_logger,
|
||||
bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname,
|
||||
@@ -127,11 +131,14 @@ class CompactionJob {
|
||||
// DBImpl state
|
||||
const std::string& dbname_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const EnvOptions& env_options_;
|
||||
const EnvOptions env_options_;
|
||||
|
||||
Env* env_;
|
||||
// env_option optimized for compaction table reads
|
||||
EnvOptions env_optiosn_for_read_;
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
Directory* output_directory_;
|
||||
@@ -149,6 +156,8 @@ class CompactionJob {
|
||||
// should make sure not to remove evidence that a write occurred.
|
||||
SequenceNumber earliest_write_conflict_snapshot_;
|
||||
|
||||
const SnapshotChecker* const snapshot_checker_;
|
||||
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
EventLogger* event_logger_;
|
||||
@@ -160,6 +169,7 @@ class CompactionJob {
|
||||
std::vector<Slice> boundaries_;
|
||||
// Stores the approx size of keys covered in the range of each subcompaction
|
||||
std::vector<uint64_t> sizes_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -76,6 +76,7 @@ class CompactionJobTest : public testing::Test {
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_)),
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()) {
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
@@ -142,7 +143,8 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
|
||||
void SetLastSequence(const SequenceNumber sequence_number) {
|
||||
versions_->SetLastToBeWrittenSequence(sequence_number + 1);
|
||||
versions_->SetLastAllocatedSequence(sequence_number + 1);
|
||||
versions_->SetLastPublishedSequence(sequence_number + 1);
|
||||
versions_->SetLastSequence(sequence_number + 1);
|
||||
}
|
||||
|
||||
@@ -250,12 +252,15 @@ class CompactionJobTest : public testing::Test {
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
mutex_.Lock();
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
CompactionJob compaction_job(
|
||||
0, &compaction, db_options_, env_options_, versions_.get(),
|
||||
&shutting_down_, &log_buffer, nullptr, nullptr, nullptr, &mutex_,
|
||||
&bg_error_, snapshots, earliest_write_conflict_snapshot, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_);
|
||||
|
||||
// TODO(yiwu) add a mock snapshot checker and add test for it.
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
CompactionJob compaction_job(0, &compaction, db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_, &log_buffer,
|
||||
nullptr, nullptr, nullptr, &mutex_, &bg_error_,
|
||||
snapshots, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, table_cache_, &event_logger,
|
||||
false, false, dbname_, &compaction_job_stats_);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
@@ -291,6 +296,7 @@ class CompactionJobTest : public testing::Test {
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
SequenceNumber preserve_deletes_seqnum_;
|
||||
std::shared_ptr<mock::MockTableFactory> mock_table_factory_;
|
||||
CompactionJobStats compaction_job_stats_;
|
||||
ColumnFamilyData* cfd_;
|
||||
|
||||
+71
-36
@@ -89,7 +89,7 @@ CompressionType GetCompressionType(const ImmutableCFOptions& ioptions,
|
||||
// If bottommost_compression is set and we are compacting to the
|
||||
// bottommost level then we should use it.
|
||||
if (ioptions.bottommost_compression != kDisableCompressionOption &&
|
||||
level > base_level && level >= (vstorage->num_non_empty_levels() - 1)) {
|
||||
level >= (vstorage->num_non_empty_levels() - 1)) {
|
||||
return ioptions.bottommost_compression;
|
||||
}
|
||||
// If the user has specified a different compression level for each level,
|
||||
@@ -292,25 +292,16 @@ Compaction* CompactionPicker::CompactFiles(
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
uint32_t output_path_id) {
|
||||
assert(input_files.size());
|
||||
// This compaction output should not overlap with a running compaction as
|
||||
// `SanitizeCompactionInputFiles` should've checked earlier and db mutex
|
||||
// shouldn't have been released since.
|
||||
assert(!FilesRangeOverlapWithCompaction(input_files, output_level));
|
||||
|
||||
// TODO(rven ): we might be able to run concurrent level 0 compaction
|
||||
// if the key ranges of the two compactions do not overlap, but for now
|
||||
// we do not allow it.
|
||||
if ((input_files[0].level == 0) && !level0_compactions_in_progress_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
// This compaction output could overlap with a running compaction
|
||||
if (FilesRangeOverlapWithCompaction(input_files, output_level)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto c =
|
||||
new Compaction(vstorage, ioptions_, mutable_cf_options, input_files,
|
||||
output_level, compact_options.output_file_size_limit,
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
compact_options.compression, /* grandparents */ {}, true);
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
@@ -396,7 +387,10 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
assert(output_level_inputs->empty());
|
||||
const int input_level = inputs->level;
|
||||
const int output_level = output_level_inputs->level;
|
||||
assert(input_level != output_level);
|
||||
if (input_level == output_level) {
|
||||
// no possibility of conflict
|
||||
return true;
|
||||
}
|
||||
|
||||
// For now, we only support merging two levels, start level and output level.
|
||||
// We need to assert other levels are empty.
|
||||
@@ -730,10 +724,6 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
|
||||
auto& levels = cf_meta.levels;
|
||||
auto comparator = icmp_->user_comparator();
|
||||
|
||||
// TODO(yhchiang): If there is any input files of L1 or up and there
|
||||
// is at least one L0 files. All L0 files older than the L0 file needs
|
||||
// to be included. Otherwise, it is a false conditoin
|
||||
|
||||
// TODO(yhchiang): add is_adjustable to CompactionOptions
|
||||
|
||||
// the smallest and largest key of the current compaction input
|
||||
@@ -794,6 +784,8 @@ Status CompactionPicker::SanitizeCompactionInputFilesForAllLevels(
|
||||
}
|
||||
last_included++;
|
||||
}
|
||||
} else if (output_level > 0) {
|
||||
last_included = static_cast<int>(current_files.size() - 1);
|
||||
}
|
||||
|
||||
// include all files between the first and the last compaction input files.
|
||||
@@ -949,6 +941,9 @@ void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->BottommostFilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
return true;
|
||||
}
|
||||
@@ -1139,7 +1134,28 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
is_manual_ = true;
|
||||
parent_index_ = base_index_ = -1;
|
||||
PickFilesMarkedForCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
}
|
||||
}
|
||||
@@ -1279,6 +1295,8 @@ uint32_t LevelCompactionBuilder::GetPathId(
|
||||
uint64_t level_size;
|
||||
int cur_level = 0;
|
||||
|
||||
// max_bytes_for_level_base denotes L1 size.
|
||||
// We estimate L0 size to be the same as L1.
|
||||
level_size = mutable_cf_options.max_bytes_for_level_base;
|
||||
|
||||
// Last path is the fallback
|
||||
@@ -1289,8 +1307,21 @@ uint32_t LevelCompactionBuilder::GetPathId(
|
||||
return p;
|
||||
} else {
|
||||
current_path_size -= level_size;
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
if (cur_level > 0) {
|
||||
if (ioptions.level_compaction_dynamic_level_bytes) {
|
||||
// Currently, level_compaction_dynamic_level_bytes is ignored when
|
||||
// multiple db paths are specified. https://github.com/facebook/
|
||||
// rocksdb/blob/master/db/column_family.cc.
|
||||
// Still, adding this check to avoid accidentally using
|
||||
// max_bytes_for_level_multiplier_additional
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier);
|
||||
} else {
|
||||
level_size = static_cast<uint64_t>(
|
||||
level_size * mutable_cf_options.max_bytes_for_level_multiplier *
|
||||
mutable_cf_options.MaxBytesMultiplerAdditional(cur_level));
|
||||
}
|
||||
}
|
||||
cur_level++;
|
||||
continue;
|
||||
}
|
||||
@@ -1421,7 +1452,7 @@ uint64_t GetTotalFilesSize(
|
||||
Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
assert(ioptions_.compaction_options_fifo.ttl > 0);
|
||||
assert(mutable_cf_options.compaction_options_fifo.ttl > 0);
|
||||
|
||||
const int kLevel0 = 0;
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
@@ -1443,7 +1474,7 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
inputs[0].level = 0;
|
||||
|
||||
// avoid underflow
|
||||
if (current_time > ioptions_.compaction_options_fifo.ttl) {
|
||||
if (current_time > mutable_cf_options.compaction_options_fifo.ttl) {
|
||||
for (auto ritr = level_files.rbegin(); ritr != level_files.rend(); ++ritr) {
|
||||
auto f = *ritr;
|
||||
if (f->fd.table_reader != nullptr &&
|
||||
@@ -1451,8 +1482,8 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
auto creation_time =
|
||||
f->fd.table_reader->GetTableProperties()->creation_time;
|
||||
if (creation_time == 0 ||
|
||||
creation_time >=
|
||||
(current_time - ioptions_.compaction_options_fifo.ttl)) {
|
||||
creation_time >= (current_time -
|
||||
mutable_cf_options.compaction_options_fifo.ttl)) {
|
||||
break;
|
||||
}
|
||||
total_size -= f->compensated_file_size;
|
||||
@@ -1466,7 +1497,8 @@ Compaction* FIFOCompactionPicker::PickTTLCompaction(
|
||||
// 2. there are a few files older than ttl, but deleting them will not bring
|
||||
// the total size to be less than max_table_files_size threshold.
|
||||
if (inputs[0].files.empty() ||
|
||||
total_size > ioptions_.compaction_options_fifo.max_table_files_size) {
|
||||
total_size >
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1492,10 +1524,11 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
const std::vector<FileMetaData*>& level_files = vstorage->LevelFiles(kLevel0);
|
||||
uint64_t total_size = GetTotalFilesSize(level_files);
|
||||
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size ||
|
||||
if (total_size <=
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size ||
|
||||
level_files.size() == 0) {
|
||||
// total size not exceeded
|
||||
if (ioptions_.compaction_options_fifo.allow_compaction &&
|
||||
if (mutable_cf_options.compaction_options_fifo.allow_compaction &&
|
||||
level_files.size() > 0) {
|
||||
CompactionInputFiles comp_inputs;
|
||||
if (FindIntraL0Compaction(
|
||||
@@ -1515,11 +1548,12 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
}
|
||||
}
|
||||
|
||||
ROCKS_LOG_BUFFER(log_buffer,
|
||||
"[%s] FIFO compaction: nothing to do. Total size %" PRIu64
|
||||
", max size %" PRIu64 "\n",
|
||||
cf_name.c_str(), total_size,
|
||||
ioptions_.compaction_options_fifo.max_table_files_size);
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
"[%s] FIFO compaction: nothing to do. Total size %" PRIu64
|
||||
", max size %" PRIu64 "\n",
|
||||
cf_name.c_str(), total_size,
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1546,7 +1580,8 @@ Compaction* FIFOCompactionPicker::PickSizeCompaction(
|
||||
"[%s] FIFO compaction: picking file %" PRIu64
|
||||
" with size %s for deletion",
|
||||
cf_name.c_str(), f->fd.GetNumber(), tmp_fsize);
|
||||
if (total_size <= ioptions_.compaction_options_fifo.max_table_files_size) {
|
||||
if (total_size <=
|
||||
mutable_cf_options.compaction_options_fifo.max_table_files_size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1564,7 +1599,7 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
assert(vstorage->num_levels() == 1);
|
||||
|
||||
Compaction* c = nullptr;
|
||||
if (ioptions_.compaction_options_fifo.ttl > 0) {
|
||||
if (mutable_cf_options.compaction_options_fifo.ttl > 0) {
|
||||
c = PickTTLCompaction(cf_name, mutable_cf_options, vstorage, log_buffer);
|
||||
}
|
||||
if (c == nullptr) {
|
||||
|
||||
@@ -88,6 +88,10 @@ class CompactionPicker {
|
||||
|
||||
// Takes a list of CompactionInputFiles and returns a (manual) Compaction
|
||||
// object.
|
||||
//
|
||||
// Caller must provide a set of input files that has been passed through
|
||||
// `SanitizeCompactionInputFiles` earlier. The lock should not be released
|
||||
// between that call and this one.
|
||||
Compaction* CompactFiles(const CompactionOptions& compact_options,
|
||||
const std::vector<CompactionInputFiles>& input_files,
|
||||
int output_level, VersionStorageInfo* vstorage,
|
||||
|
||||
@@ -175,6 +175,8 @@ TEST_F(CompactionPickerTest, Level1Trigger) {
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
mutable_cf_options_.target_file_size_base = 10000000000;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
Add(1, 66U, "150", "200", 1000000001U);
|
||||
Add(1, 88U, "201", "300", 1000000000U);
|
||||
@@ -191,13 +193,14 @@ TEST_F(CompactionPickerTest, Level1Trigger2) {
|
||||
ASSERT_EQ(66U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(6U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(uint64_t{1073741824}, compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
Add(0, 1U, "150", "200", 1000000U);
|
||||
// Level 1 score 1.2
|
||||
Add(1, 66U, "150", "200", 6000000U);
|
||||
@@ -218,6 +221,9 @@ TEST_F(CompactionPickerTest, LevelMaxScore) {
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(7U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(mutable_cf_options_.target_file_size_base +
|
||||
mutable_cf_options_.target_file_size_base / 10,
|
||||
compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NeedsCompactionLevel) {
|
||||
@@ -437,7 +443,7 @@ TEST_F(CompactionPickerTest, CompactionUniversalIngestBehindReservedLevel) {
|
||||
TEST_F(CompactionPickerTest, CannotTrivialMoveUniversal) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_options_universal.allow_trivial_move = true;
|
||||
mutable_cf_options_.compaction_options_universal.allow_trivial_move = true;
|
||||
NewVersionStorage(1, kCompactionStyleUniversal);
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
// must return false when there's no files.
|
||||
@@ -468,7 +474,7 @@ TEST_F(CompactionPickerTest, CannotTrivialMoveUniversal) {
|
||||
TEST_F(CompactionPickerTest, AllowsTrivialMoveUniversal) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_options_universal.allow_trivial_move = true;
|
||||
mutable_cf_options_.compaction_options_universal.allow_trivial_move = true;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
NewVersionStorage(3, kCompactionStyleUniversal);
|
||||
@@ -496,7 +502,7 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
const uint64_t kMaxSize = kFileSize * kFileCount / 2;
|
||||
|
||||
fifo_options_.max_table_files_size = kMaxSize;
|
||||
ioptions_.compaction_options_fifo = fifo_options_;
|
||||
mutable_cf_options_.compaction_options_fifo = fifo_options_;
|
||||
FIFOCompactionPicker fifo_compaction_picker(ioptions_, &icmp_);
|
||||
UpdateVersionStorageInfo();
|
||||
// must return false when there's no files.
|
||||
@@ -521,9 +527,10 @@ TEST_F(CompactionPickerTest, NeedsCompactionFIFO) {
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.target_file_size_base = 10000000;
|
||||
mutable_cf_options_.target_file_size_base = 100000000000;
|
||||
mutable_cf_options_.target_file_size_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
|
||||
Add(2, 6U, "150", "179", 50000000U);
|
||||
Add(2, 7U, "180", "220", 50000000U);
|
||||
@@ -543,6 +550,8 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
// Pick file 8 because it overlaps with 0 files on level 3.
|
||||
ASSERT_EQ(8U, compaction->input(0, 0)->fd.GetNumber());
|
||||
// Compaction input size * 1.1
|
||||
ASSERT_GE(uint64_t{55000000}, compaction->OutputFilePreallocationSize());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace {
|
||||
// and the index of the file in that level
|
||||
|
||||
struct InputFileInfo {
|
||||
InputFileInfo() : f(nullptr) {}
|
||||
InputFileInfo() : f(nullptr), level(0), index(0) {}
|
||||
|
||||
FileMetaData* f;
|
||||
size_t level;
|
||||
@@ -204,7 +204,8 @@ void UniversalCompactionPicker::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionPicker::SortedRun>
|
||||
UniversalCompactionPicker::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions) {
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
std::vector<UniversalCompactionPicker::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
@@ -218,7 +219,8 @@ UniversalCompactionPicker::CalculateSortedRuns(
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
if (ioptions.compaction_options_universal.allow_trivial_move == true) {
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
@@ -227,7 +229,8 @@ UniversalCompactionPicker::CalculateSortedRuns(
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// ioptions.compaction_options_universal.allow_trivial_move is false
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move is
|
||||
// false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
@@ -245,14 +248,13 @@ UniversalCompactionPicker::CalculateSortedRuns(
|
||||
|
||||
// Universal style of compaction. Pick files that are contiguous in
|
||||
// time-range to compact.
|
||||
//
|
||||
Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
const std::string& cf_name, const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage, LogBuffer* log_buffer) {
|
||||
const int kLevel0 = 0;
|
||||
double score = vstorage->CompactionScore(kLevel0);
|
||||
std::vector<SortedRun> sorted_runs =
|
||||
CalculateSortedRuns(*vstorage, ioptions_);
|
||||
CalculateSortedRuns(*vstorage, ioptions_, mutable_cf_options);
|
||||
|
||||
if (sorted_runs.size() == 0 ||
|
||||
sorted_runs.size() <
|
||||
@@ -279,7 +281,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
} else {
|
||||
// Size amplification is within limits. Try reducing read
|
||||
// amplification while maintaining file size ratios.
|
||||
unsigned int ratio = ioptions_.compaction_options_universal.size_ratio;
|
||||
unsigned int ratio =
|
||||
mutable_cf_options.compaction_options_universal.size_ratio;
|
||||
|
||||
if ((c = PickCompactionToReduceSortedRuns(
|
||||
cf_name, mutable_cf_options, vstorage, score, ratio, UINT_MAX,
|
||||
@@ -327,7 +330,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ioptions_.compaction_options_universal.allow_trivial_move == true) {
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
c->set_is_trivial_move(IsInputFilesNonOverlapping(c));
|
||||
}
|
||||
|
||||
@@ -381,7 +385,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
}
|
||||
|
||||
uint32_t UniversalCompactionPicker::GetPathId(
|
||||
const ImmutableCFOptions& ioptions, uint64_t file_size) {
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, uint64_t file_size) {
|
||||
// Two conditions need to be satisfied:
|
||||
// (1) the target path needs to be able to hold the file's size
|
||||
// (2) Total size left in this and previous paths need to be not
|
||||
@@ -398,8 +403,8 @@ uint32_t UniversalCompactionPicker::GetPathId(
|
||||
// that case. We need to improve it.
|
||||
uint64_t accumulated_size = 0;
|
||||
uint64_t future_size =
|
||||
file_size * (100 - ioptions.compaction_options_universal.size_ratio) /
|
||||
100;
|
||||
file_size *
|
||||
(100 - mutable_cf_options.compaction_options_universal.size_ratio) / 100;
|
||||
uint32_t p = 0;
|
||||
assert(!ioptions.db_paths.empty());
|
||||
for (; p < ioptions.db_paths.size() - 1; p++) {
|
||||
@@ -423,9 +428,9 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
unsigned int max_number_of_files_to_compact,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer) {
|
||||
unsigned int min_merge_width =
|
||||
ioptions_.compaction_options_universal.min_merge_width;
|
||||
mutable_cf_options.compaction_options_universal.min_merge_width;
|
||||
unsigned int max_merge_width =
|
||||
ioptions_.compaction_options_universal.max_merge_width;
|
||||
mutable_cf_options.compaction_options_universal.max_merge_width;
|
||||
|
||||
const SortedRun* sr = nullptr;
|
||||
bool done = false;
|
||||
@@ -492,7 +497,7 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
if (sz < static_cast<double>(succeeding_sr->size)) {
|
||||
break;
|
||||
}
|
||||
if (ioptions_.compaction_options_universal.stop_style ==
|
||||
if (mutable_cf_options.compaction_options_universal.stop_style ==
|
||||
kCompactionStopStyleSimilarSize) {
|
||||
// Similar-size stopping rule: also check the last picked file isn't
|
||||
// far larger than the next candidate file.
|
||||
@@ -535,7 +540,7 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
// size ratio of compression.
|
||||
bool enable_compression = true;
|
||||
int ratio_to_compress =
|
||||
ioptions_.compaction_options_universal.compression_size_percent;
|
||||
mutable_cf_options.compaction_options_universal.compression_size_percent;
|
||||
if (ratio_to_compress >= 0) {
|
||||
uint64_t total_size = 0;
|
||||
for (auto& sorted_run : sorted_runs) {
|
||||
@@ -556,7 +561,8 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSortedRuns(
|
||||
for (unsigned int i = 0; i < first_index_after; i++) {
|
||||
estimated_total_size += sorted_runs[i].size;
|
||||
}
|
||||
uint32_t path_id = GetPathId(ioptions_, estimated_total_size);
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options, estimated_total_size);
|
||||
int start_level = sorted_runs[start_index].level;
|
||||
int output_level;
|
||||
if (first_index_after == sorted_runs.size()) {
|
||||
@@ -621,8 +627,8 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
VersionStorageInfo* vstorage, double score,
|
||||
const std::vector<SortedRun>& sorted_runs, LogBuffer* log_buffer) {
|
||||
// percentage flexibility while reducing size amplification
|
||||
uint64_t ratio =
|
||||
ioptions_.compaction_options_universal.max_size_amplification_percent;
|
||||
uint64_t ratio = mutable_cf_options.compaction_options_universal
|
||||
.max_size_amplification_percent;
|
||||
|
||||
unsigned int candidate_count = 0;
|
||||
uint64_t candidate_size = 0;
|
||||
@@ -700,7 +706,8 @@ Compaction* UniversalCompactionPicker::PickCompactionToReduceSizeAmp(
|
||||
for (size_t loop = start_index; loop < sorted_runs.size(); loop++) {
|
||||
estimated_total_size += sorted_runs[loop].size;
|
||||
}
|
||||
uint32_t path_id = GetPathId(ioptions_, estimated_total_size);
|
||||
uint32_t path_id =
|
||||
GetPathId(ioptions_, mutable_cf_options, estimated_total_size);
|
||||
int start_level = sorted_runs[start_index].level;
|
||||
|
||||
std::vector<CompactionInputFiles> inputs(vstorage->num_levels());
|
||||
|
||||
@@ -80,11 +80,13 @@ class UniversalCompactionPicker : public CompactionPicker {
|
||||
bool IsInputFilesNonOverlapping(Compaction* c);
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions);
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
static uint32_t GetPathId(const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
uint64_t file_size);
|
||||
};
|
||||
} // namespace rocksdb
|
||||
|
||||
@@ -241,7 +241,7 @@ TEST_F(CuckooTableDBTest, CompactionIntoMultipleFiles) {
|
||||
|
||||
// Write 28 values, each 10016 B ~ 10KB
|
||||
for (int idx = 0; idx < 28; ++idx) {
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + idx)));
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + char(idx))));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("1", FilesPerLevel());
|
||||
@@ -250,7 +250,7 @@ TEST_F(CuckooTableDBTest, CompactionIntoMultipleFiles) {
|
||||
true /* disallow trivial move */);
|
||||
ASSERT_EQ("0,2", FilesPerLevel());
|
||||
for (int idx = 0; idx < 28; ++idx) {
|
||||
ASSERT_EQ(std::string(10000, 'a' + idx), Get(Key(idx)));
|
||||
ASSERT_EQ(std::string(10000, 'a' + char(idx)), Get(Key(idx)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,14 +271,14 @@ TEST_F(CuckooTableDBTest, SameKeyInsertedInTwoDifferentFilesAndCompacted) {
|
||||
|
||||
// Generate one more file in level-0, and should trigger level-0 compaction
|
||||
for (int idx = 0; idx < 11; ++idx) {
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + idx)));
|
||||
ASSERT_OK(Put(Key(idx), std::string(10000, 'a' + char(idx))));
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr);
|
||||
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
for (int idx = 0; idx < 11; ++idx) {
|
||||
ASSERT_EQ(std::string(10000, 'a' + idx), Get(Key(idx)));
|
||||
ASSERT_EQ(std::string(10000, 'a' + char(idx)), Get(Key(idx)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+58
-7
@@ -41,7 +41,7 @@ TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
Close();
|
||||
|
||||
auto options = CurrentOptions();
|
||||
assert(options.env = env_);
|
||||
assert(options.env == env_);
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
ASSERT_EQ("v3", Get("foo"));
|
||||
ASSERT_EQ("v2", Get("bar"));
|
||||
@@ -570,19 +570,19 @@ TEST_F(DBBasicTest, CompactBetweenSnapshots) {
|
||||
|
||||
TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
Options options = CurrentOptions();
|
||||
std::string dbname = test::TmpDir(env_) + "/db_options_test";
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
Close();
|
||||
Destroy(options);
|
||||
|
||||
// Does not exist, and create_if_missing == false: error
|
||||
DB* db = nullptr;
|
||||
options.create_if_missing = false;
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
Status s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != nullptr);
|
||||
ASSERT_TRUE(db == nullptr);
|
||||
|
||||
// Does not exist, and create_if_missing == true: OK
|
||||
options.create_if_missing = true;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
@@ -592,14 +592,14 @@ TEST_F(DBBasicTest, DBOpen_Options) {
|
||||
// Does exist, and error_if_exists == true: error
|
||||
options.create_if_missing = false;
|
||||
options.error_if_exists = true;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != nullptr);
|
||||
ASSERT_TRUE(db == nullptr);
|
||||
|
||||
// Does exist, and error_if_exists == false: OK
|
||||
options.create_if_missing = true;
|
||||
options.error_if_exists = false;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
s = DB::Open(options, dbname_, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
@@ -847,6 +847,57 @@ TEST_F(DBBasicTest, MmapAndBufferOptions) {
|
||||
}
|
||||
#endif
|
||||
|
||||
class TestEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit TestEnv(Env* base) : EnvWrapper(base) { };
|
||||
|
||||
class TestLogger : public Logger {
|
||||
public:
|
||||
using Logger::Logv;
|
||||
virtual void Logv(const char *format, va_list ap) override { };
|
||||
private:
|
||||
virtual Status CloseImpl() override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
};
|
||||
|
||||
virtual Status NewLogger(const std::string& fname,
|
||||
shared_ptr<Logger>* result) {
|
||||
result->reset(new TestLogger());
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTest, DBClose) {
|
||||
Options options = GetDefaultOptions();
|
||||
std::string dbname = test::TmpDir(env_) + "/db_close_test";
|
||||
ASSERT_OK(DestroyDB(dbname, options));
|
||||
|
||||
DB* db = nullptr;
|
||||
options.create_if_missing = true;
|
||||
options.env = new TestEnv(Env::Default());
|
||||
Status s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(s, Status::NotSupported());
|
||||
|
||||
delete db;
|
||||
|
||||
// Provide our own logger and ensure DB::Close() does not close it
|
||||
options.info_log.reset(new TestEnv::TestLogger());
|
||||
options.create_if_missing = false;
|
||||
s = DB::Open(options, dbname, &db);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(db != nullptr);
|
||||
|
||||
s = db->Close();
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
delete db;
|
||||
delete options.env;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -64,7 +64,8 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
read_options.snapshot = snapshot;
|
||||
PinnableSlice value;
|
||||
auto s = dbfull()->GetImpl(read_options, cfh(), key, &value,
|
||||
nullptr /*value_found*/, is_blob_index);
|
||||
nullptr /*value_found*/, nullptr /*callback*/,
|
||||
is_blob_index);
|
||||
if (s.IsNotFound()) {
|
||||
return "NOT_FOUND";
|
||||
}
|
||||
@@ -88,9 +89,9 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* GetBlobIterator() {
|
||||
return dbfull()->NewIteratorImpl(ReadOptions(), cfd(),
|
||||
dbfull()->GetLatestSequenceNumber(),
|
||||
true /*allow_blob*/);
|
||||
return dbfull()->NewIteratorImpl(
|
||||
ReadOptions(), cfd(), dbfull()->GetLatestSequenceNumber(),
|
||||
nullptr /*read_callback*/, true /*allow_blob*/);
|
||||
}
|
||||
|
||||
Options GetTestOptions() {
|
||||
|
||||
@@ -24,6 +24,36 @@ class DBTestCompactionFilter : public DBTestBase {
|
||||
DBTestCompactionFilter() : DBTestBase("/db_compaction_filter_test") {}
|
||||
};
|
||||
|
||||
// Param variant of DBTestBase::ChangeCompactOptions
|
||||
class DBTestCompactionFilterWithCompactParam
|
||||
: public DBTestCompactionFilter,
|
||||
public ::testing::WithParamInterface<DBTestBase::OptionConfig> {
|
||||
public:
|
||||
DBTestCompactionFilterWithCompactParam() : DBTestCompactionFilter() {
|
||||
option_config_ = GetParam();
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
if (option_config_ == kDefault || option_config_ == kUniversalCompaction ||
|
||||
option_config_ == kUniversalCompactionMultiLevel) {
|
||||
options.create_if_missing = true;
|
||||
}
|
||||
if (option_config_ == kLevelSubcompactions ||
|
||||
option_config_ == kUniversalSubcompactions) {
|
||||
assert(options.max_subcompactions > 1);
|
||||
}
|
||||
TryReopen(options);
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTestCompactionFilterWithCompactOption,
|
||||
DBTestCompactionFilterWithCompactParam,
|
||||
::testing::Values(DBTestBase::OptionConfig::kDefault,
|
||||
DBTestBase::OptionConfig::kUniversalCompaction,
|
||||
DBTestBase::OptionConfig::kUniversalCompactionMultiLevel,
|
||||
DBTestBase::OptionConfig::kLevelSubcompactions,
|
||||
DBTestBase::OptionConfig::kUniversalSubcompactions));
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
virtual bool Filter(int level, const Slice& key, const Slice& value,
|
||||
@@ -308,7 +338,6 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
|
||||
ikey.sequence = -1;
|
||||
ASSERT_EQ(ParseInternalKey(iter->key(), &ikey), true);
|
||||
total++;
|
||||
if (ikey.sequence != 0) {
|
||||
@@ -440,65 +469,63 @@ TEST_F(DBTestCompactionFilter, CompactionFilterDeletesAll) {
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterWithValueChange) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 3;
|
||||
options.compaction_filter_factory =
|
||||
std::make_shared<ChangeFilterFactory>();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
TEST_P(DBTestCompactionFilterWithCompactParam,
|
||||
CompactionFilterWithValueChange) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 3;
|
||||
options.compaction_filter_factory = std::make_shared<ChangeFilterFactory>();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
// Write 100K+1 keys, these are written to a few files
|
||||
// in L0. We do this so that the current snapshot points
|
||||
// to the 100001 key.The compaction filter is not invoked
|
||||
// on keys that are visible via a snapshot because we
|
||||
// anyways cannot delete it.
|
||||
const std::string value(10, 'x');
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
}
|
||||
// Write 100K+1 keys, these are written to a few files
|
||||
// in L0. We do this so that the current snapshot points
|
||||
// to the 100001 key.The compaction filter is not invoked
|
||||
// on keys that are visible via a snapshot because we
|
||||
// anyways cannot delete it.
|
||||
const std::string value(10, 'x');
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
}
|
||||
|
||||
// push all files to lower levels
|
||||
ASSERT_OK(Flush(1));
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
}
|
||||
// push all files to lower levels
|
||||
ASSERT_OK(Flush(1));
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// re-write all data again
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
}
|
||||
// re-write all data again
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
}
|
||||
|
||||
// push all files to lower levels. This should
|
||||
// invoke the compaction filter for all 100000 keys.
|
||||
ASSERT_OK(Flush(1));
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
}
|
||||
// push all files to lower levels. This should
|
||||
// invoke the compaction filter for all 100000 keys.
|
||||
ASSERT_OK(Flush(1));
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// verify that all keys now have the new value that
|
||||
// was set by the compaction process.
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
std::string newvalue = Get(1, key);
|
||||
ASSERT_EQ(newvalue.compare(NEW_VALUE), 0);
|
||||
}
|
||||
} while (ChangeCompactOptions());
|
||||
// verify that all keys now have the new value that
|
||||
// was set by the compaction process.
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
std::string newvalue = Get(1, key);
|
||||
ASSERT_EQ(newvalue.compare(NEW_VALUE), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTestCompactionFilter, CompactionFilterWithMergeOperator) {
|
||||
@@ -617,7 +644,6 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
|
||||
ikey.sequence = -1;
|
||||
ASSERT_EQ(ParseInternalKey(iter->key(), &ikey), true);
|
||||
total++;
|
||||
if (ikey.sequence != 0) {
|
||||
|
||||
@@ -218,6 +218,84 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, CompactionsPreserveDeletes) {
|
||||
// For each options type we test following
|
||||
// - Enable preserve_deletes
|
||||
// - write bunch of keys and deletes
|
||||
// - Set start_seqnum to the beginning; compact; check that keys are present
|
||||
// - rewind start_seqnum way forward; compact; check that keys are gone
|
||||
|
||||
for (int tid = 0; tid < 3; ++tid) {
|
||||
Options options = DeletionTriggerOptions(CurrentOptions());
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
options.preserve_deletes=true;
|
||||
options.num_levels = 2;
|
||||
|
||||
if (tid == 1) {
|
||||
options.skip_stats_update_on_db_open = true;
|
||||
} else if (tid == 2) {
|
||||
// third pass with universal compaction
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
// highlight the default; all deletes should be preserved
|
||||
SetPreserveDeletesSequenceNumber(0);
|
||||
|
||||
const int kTestSize = kCDTKeysPerBuffer;
|
||||
std::vector<std::string> values;
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
values.push_back(RandomString(&rnd, kCDTValueSize));
|
||||
ASSERT_OK(Put(Key(k), values[k]));
|
||||
}
|
||||
|
||||
for (int k = 0; k < kTestSize; ++k) {
|
||||
ASSERT_OK(Delete(Key(k)));
|
||||
}
|
||||
// to ensure we tackle all tombstones
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
|
||||
// check that normal user iterator doesn't see anything
|
||||
Iterator* db_iter = dbfull()->NewIterator(ReadOptions());
|
||||
int i = 0;
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
i++;
|
||||
}
|
||||
ASSERT_EQ(i, 0);
|
||||
delete db_iter;
|
||||
|
||||
// check that iterator that sees internal keys sees tombstones
|
||||
ReadOptions ro;
|
||||
ro.iter_start_seqnum=1;
|
||||
db_iter = dbfull()->NewIterator(ro);
|
||||
i = 0;
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
i++;
|
||||
}
|
||||
ASSERT_EQ(i, 4);
|
||||
delete db_iter;
|
||||
|
||||
// now all deletes should be gone
|
||||
SetPreserveDeletesSequenceNumber(100000000);
|
||||
dbfull()->CompactRange(cro, nullptr, nullptr);
|
||||
|
||||
db_iter = dbfull()->NewIterator(ro);
|
||||
i = 0;
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
i++;
|
||||
}
|
||||
ASSERT_EQ(i, 0);
|
||||
delete db_iter;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
|
||||
// This test verify UpdateAccumulatedStats is not on
|
||||
// if options.skip_stats_update_on_db_open = true
|
||||
@@ -1439,6 +1517,60 @@ TEST_F(DBCompactionTest, DeleteFileRange) {
|
||||
ASSERT_GT(old_num_files, new_num_files);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFileRangeFileEndpointsOverlapBug) {
|
||||
// regression test for #2833: groups of files whose user-keys overlap at the
|
||||
// endpoints could be split by `DeleteFilesInRange`. This caused old data to
|
||||
// reappear, either because a new version of the key was removed, or a range
|
||||
// deletion was partially dropped. It could also cause non-overlapping
|
||||
// invariant to be violated if the files dropped by DeleteFilesInRange were
|
||||
// a subset of files that a range deletion spans.
|
||||
const int kNumL0Files = 2;
|
||||
const int kValSize = 8 << 10; // 8KB
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.target_file_size_base = 1 << 10; // 1KB
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// The snapshot prevents key 1 from having its old version dropped. The low
|
||||
// `target_file_size_base` ensures two keys will be in each output file.
|
||||
const Snapshot* snapshot = nullptr;
|
||||
Random rnd(301);
|
||||
// The value indicates which flush the key belonged to, which is enough
|
||||
// for us to determine the keys' relative ages. After L0 flushes finish,
|
||||
// files look like:
|
||||
//
|
||||
// File 0: 0 -> vals[0], 1 -> vals[0]
|
||||
// File 1: 1 -> vals[1], 2 -> vals[1]
|
||||
//
|
||||
// Then L0->L1 compaction happens, which outputs keys as follows:
|
||||
//
|
||||
// File 0: 0 -> vals[0], 1 -> vals[1]
|
||||
// File 1: 1 -> vals[0], 2 -> vals[1]
|
||||
//
|
||||
// DeleteFilesInRange shouldn't be allowed to drop just file 0, as that
|
||||
// would cause `1 -> vals[0]` (an older key) to reappear.
|
||||
std::string vals[kNumL0Files];
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
vals[i] = RandomString(&rnd, kValSize);
|
||||
Put(Key(i), vals[i]);
|
||||
Put(Key(i + 1), vals[i]);
|
||||
Flush();
|
||||
if (i == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Verify `DeleteFilesInRange` can't drop only file 0 which would cause
|
||||
// "1 -> vals[0]" to reappear.
|
||||
std::string begin_str = Key(0), end_str = Key(1);
|
||||
Slice begin = begin_str, end = end_str;
|
||||
ASSERT_OK(DeleteFilesInRange(db_, db_->DefaultColumnFamily(), &begin, &end));
|
||||
ASSERT_EQ(vals[1], Get(Key(1)));
|
||||
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, TrivialMoveToLastLevelWithFiles) {
|
||||
int32_t trivial_move = 0;
|
||||
int32_t non_trivial_move = 0;
|
||||
@@ -2724,6 +2856,139 @@ TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
|
||||
options.statistics->getTickerCount(COMPACTION_KEY_DROP_OBSOLETE));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactFilesPendingL0Bug) {
|
||||
// https://www.facebook.com/groups/rocksdb.dev/permalink/1389452781153232/
|
||||
// CompactFiles() had a bug where it failed to pick a compaction when an L0
|
||||
// compaction existed, but marked it as scheduled anyways. It'd never be
|
||||
// unmarked as scheduled, so future compactions or DB close could hang.
|
||||
const int kNumL0Files = 5;
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files - 1;
|
||||
options.max_background_compactions = 2;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"LevelCompactionPicker::PickCompaction:Return",
|
||||
"DBCompactionTest::CompactFilesPendingL0Bug:Picked"},
|
||||
{"DBCompactionTest::CompactFilesPendingL0Bug:ManualCompacted",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun"}});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
auto schedule_multi_compaction_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
|
||||
// Files 0-3 will be included in an L0->L1 compaction.
|
||||
//
|
||||
// File 4 will be included in a call to CompactFiles() while the first
|
||||
// compaction is running.
|
||||
for (int i = 0; i < kNumL0Files - 1; ++i) {
|
||||
ASSERT_OK(Put(Key(0), "val")); // sentinel to prevent trivial move
|
||||
ASSERT_OK(Put(Key(i + 1), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactFilesPendingL0Bug:Picked");
|
||||
// file 4 flushed after 0-3 picked
|
||||
ASSERT_OK(Put(Key(kNumL0Files), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// previously DB close would hang forever as this situation caused scheduled
|
||||
// compactions count to never decrement to zero.
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
|
||||
ASSERT_EQ(kNumL0Files, cf_meta.levels[0].files.size());
|
||||
std::vector<std::string> input_filenames;
|
||||
input_filenames.push_back(cf_meta.levels[0].files.front().name);
|
||||
ASSERT_OK(dbfull()
|
||||
->CompactFiles(CompactionOptions(), input_filenames,
|
||||
0 /* output_level */));
|
||||
TEST_SYNC_POINT("DBCompactionTest::CompactFilesPendingL0Bug:ManualCompacted");
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactFilesOverlapInL0Bug) {
|
||||
// Regression test for bug of not pulling in L0 files that overlap the user-
|
||||
// specified input files in time- and key-ranges.
|
||||
Put(Key(0), "old_val");
|
||||
Flush();
|
||||
Put(Key(0), "new_val");
|
||||
Flush();
|
||||
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
|
||||
ASSERT_GE(cf_meta.levels.size(), 2);
|
||||
ASSERT_EQ(2, cf_meta.levels[0].files.size());
|
||||
|
||||
// Compacting {new L0 file, L1 file} should pull in the old L0 file since it
|
||||
// overlaps in key-range and time-range.
|
||||
std::vector<std::string> input_filenames;
|
||||
input_filenames.push_back(cf_meta.levels[0].files.front().name);
|
||||
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), input_filenames,
|
||||
1 /* output_level */));
|
||||
ASSERT_EQ("new_val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
// bottom-level files may contain deletions due to snapshots protecting the
|
||||
// deleted keys. Once the snapshot is released, we should see files with many
|
||||
// such deletions undergo single-file compactions.
|
||||
const int kNumKeysPerFile = 1024;
|
||||
const int kNumLevelFiles = 4;
|
||||
const int kValueSize = 128;
|
||||
Options options = CurrentOptions();
|
||||
options.compression = kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = kNumLevelFiles;
|
||||
// inflate it a bit to account for key/metadata overhead
|
||||
options.target_file_size_base = 120 * kNumKeysPerFile * kValueSize / 100;
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), RandomString(&rnd, kValueSize)));
|
||||
}
|
||||
if (i == kNumLevelFiles - 1) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
// delete every other key after grabbing a snapshot, so these deletions
|
||||
// and the keys they cover can't be dropped until after the snapshot is
|
||||
// released.
|
||||
for (int j = 0; j < kNumLevelFiles * kNumKeysPerFile; j += 2) {
|
||||
ASSERT_OK(Delete(Key(j)));
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
if (i < kNumLevelFiles - 1) {
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(kNumLevelFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
std::vector<LiveFileMetaData> pre_release_metadata, post_release_metadata;
|
||||
db_->GetLiveFilesMetaData(&pre_release_metadata);
|
||||
// just need to bump seqnum so ReleaseSnapshot knows the newest key in the SST
|
||||
// files does not need to be preserved in case of a future snapshot.
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
// release snapshot and wait for compactions to finish. Single-file
|
||||
// compactions should be triggered, which reduce the size of each bottom-level
|
||||
// file without changing file count.
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
db_->GetLiveFilesMetaData(&post_release_metadata);
|
||||
ASSERT_EQ(pre_release_metadata.size(), post_release_metadata.size());
|
||||
|
||||
for (size_t i = 0; i < pre_release_metadata.size(); ++i) {
|
||||
const auto& pre_file = pre_release_metadata[i];
|
||||
const auto& post_file = post_release_metadata[i];
|
||||
ASSERT_EQ(1, pre_file.level);
|
||||
ASSERT_EQ(1, post_file.level);
|
||||
// each file is smaller than it was before as it was rewritten without
|
||||
// deletion markers/deleted keys.
|
||||
ASSERT_LT(post_file.size, pre_file.size);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
|
||||
::testing::Values(std::make_tuple(1, true),
|
||||
std::make_tuple(1, false),
|
||||
|
||||
@@ -57,6 +57,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
bg_cv_.SignalAll();
|
||||
} else {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
@@ -141,6 +142,18 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
{
|
||||
// If caller disabled deletions, this function should return files that are
|
||||
// guaranteed not to be deleted until deletions are re-enabled. We need to
|
||||
// wait for pending purges to finish since WalManager doesn't know which
|
||||
// files are going to be purged. Additional purges won't be scheduled as
|
||||
// long as deletions are disabled (so the below loop must terminate).
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (disable_delete_obsolete_files_ > 0 &&
|
||||
pending_purge_obsolete_files_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
return wal_manager_.GetSortedWalFiles(files);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,6 +126,41 @@ TEST_F(DBFlushTest, FlushInLowPriThreadPool) {
|
||||
ASSERT_EQ(1, num_compactions);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, ManualFlushWithMinWriteBufferNumberToMerge) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkFlush",
|
||||
"DBFlushTest::ManualFlushWithMinWriteBufferNumberToMerge:1"},
|
||||
{"DBFlushTest::ManualFlushWithMinWriteBufferNumberToMerge:2",
|
||||
"DBImpl::FlushMemTableToOutputFile:BeforeInstallSV"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
|
||||
port::Thread t([&]() {
|
||||
// The call wait for flush to finish, i.e. with flush_options.wait = true.
|
||||
ASSERT_OK(Flush());
|
||||
});
|
||||
|
||||
// Wait for flush start.
|
||||
TEST_SYNC_POINT("DBFlushTest::ManualFlushWithMinWriteBufferNumberToMerge:1");
|
||||
// Insert a second memtable before the manual flush finish.
|
||||
// At the end of the manual flush job, it will check if further flush
|
||||
// is needed, but it will not trigger flush of the second memtable because
|
||||
// min_write_buffer_number_to_merge is not reached.
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
TEST_SYNC_POINT("DBFlushTest::ManualFlushWithMinWriteBufferNumberToMerge:2");
|
||||
|
||||
// Manual flush should return, without waiting for flush indefinitely.
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST_P(DBFlushDirectIOTest, DirectIO) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
|
||||
+234
-119
@@ -11,14 +11,12 @@
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#ifdef OS_SOLARIS
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
@@ -63,7 +61,6 @@
|
||||
#include "options/cf_options.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "options/options_parser.h"
|
||||
#include "port/likely.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
@@ -74,7 +71,6 @@
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/version.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/block.h"
|
||||
#include "table/block_based_table_factory.h"
|
||||
@@ -110,7 +106,8 @@ CompressionType GetCompressionFlush(
|
||||
// optimization is used for leveled compaction. Otherwise the CPU and
|
||||
// latency overhead is not offset by saving much space.
|
||||
if (ioptions.compaction_style == kCompactionStyleUniversal) {
|
||||
if (ioptions.compaction_options_universal.compression_size_percent < 0) {
|
||||
if (mutable_cf_options.compaction_options_universal
|
||||
.compression_size_percent < 0) {
|
||||
return mutable_cf_options.compression;
|
||||
} else {
|
||||
return kNoCompression;
|
||||
@@ -126,21 +123,25 @@ CompressionType GetCompressionFlush(
|
||||
namespace {
|
||||
void DumpSupportInfo(Logger* logger) {
|
||||
ROCKS_LOG_HEADER(logger, "Compression algorithms supported:");
|
||||
ROCKS_LOG_HEADER(logger, "\tSnappy supported: %d", Snappy_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZlib supported: %d", Zlib_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tBzip supported: %d", BZip2_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tLZ4 supported: %d", LZ4_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "\tZSTD supported: %d", ZSTD_Supported());
|
||||
ROCKS_LOG_HEADER(logger, "Fast CRC32 supported: %d",
|
||||
crc32c::IsFastCrc32Supported());
|
||||
for (auto& compression : OptionsHelper::compression_type_string_map) {
|
||||
if (compression.second != kNoCompression &&
|
||||
compression.second != kDisableCompressionOption) {
|
||||
ROCKS_LOG_HEADER(logger, "\t%s supported: %d", compression.first.c_str(),
|
||||
CompressionTypeSupported(compression.second));
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_HEADER(logger, "Fast CRC32 supported: %s",
|
||||
crc32c::IsFastCrc32Supported().c_str());
|
||||
}
|
||||
|
||||
int64_t kDefaultLowPriThrottledRate = 2 * 1024 * 1024;
|
||||
} // namespace
|
||||
|
||||
DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const bool seq_per_batch)
|
||||
: env_(options.env),
|
||||
dbname_(dbname),
|
||||
own_info_log_(options.info_log == nullptr),
|
||||
initial_db_options_(SanitizeOptions(dbname, options)),
|
||||
immutable_db_options_(initial_db_options_),
|
||||
mutable_db_options_(initial_db_options_),
|
||||
@@ -177,23 +178,44 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
num_running_flushes_(0),
|
||||
bg_purge_scheduled_(0),
|
||||
disable_delete_obsolete_files_(0),
|
||||
pending_purge_obsolete_files_(0),
|
||||
delete_obsolete_files_last_run_(env_->NowMicros()),
|
||||
last_stats_dump_time_microsec_(0),
|
||||
next_job_id_(1),
|
||||
has_unpersisted_data_(false),
|
||||
unable_to_flush_oldest_log_(false),
|
||||
env_options_(BuildDBOptions(immutable_db_options_, mutable_db_options_)),
|
||||
env_options_for_compaction_(env_->OptimizeForCompactionTableWrite(
|
||||
env_options_, immutable_db_options_)),
|
||||
num_running_ingest_file_(0),
|
||||
#ifndef ROCKSDB_LITE
|
||||
wal_manager_(immutable_db_options_, env_options_),
|
||||
wal_manager_(immutable_db_options_, env_options_, seq_per_batch),
|
||||
#endif // ROCKSDB_LITE
|
||||
event_logger_(immutable_db_options_.info_log.get()),
|
||||
bg_work_paused_(0),
|
||||
bg_compaction_paused_(0),
|
||||
refitting_level_(false),
|
||||
opened_successfully_(false),
|
||||
concurrent_prepare_(options.concurrent_prepare),
|
||||
manual_wal_flush_(options.manual_wal_flush) {
|
||||
two_write_queues_(options.two_write_queues),
|
||||
manual_wal_flush_(options.manual_wal_flush),
|
||||
seq_per_batch_(seq_per_batch),
|
||||
// last_sequencee_ is always maintained by the main queue that also writes
|
||||
// to the memtable. When two_write_queues_ is disabled last seq in
|
||||
// memtable is the same as last seq published to the readers. When it is
|
||||
// enabled but seq_per_batch_ is disabled, last seq in memtable still
|
||||
// indicates last published seq since wal-only writes that go to the 2nd
|
||||
// queue do not consume a sequence number. Otherwise writes performed by
|
||||
// the 2nd queue could change what is visible to the readers. In this
|
||||
// cases, last_seq_same_as_publish_seq_==false, the 2nd queue maintains a
|
||||
// separate variable to indicate the last published sequence.
|
||||
last_seq_same_as_publish_seq_(
|
||||
!(seq_per_batch && options.two_write_queues)),
|
||||
// Since seq_per_batch_ is currently set only by WritePreparedTxn which
|
||||
// requires a custom gc for compaction, we use that to set use_custom_gc_
|
||||
// as well.
|
||||
use_custom_gc_(seq_per_batch),
|
||||
preserve_deletes_(options.preserve_deletes),
|
||||
closed_(false) {
|
||||
env_->GetAbsolutePath(dbname, &db_absolute_path_);
|
||||
|
||||
// Reserve ten files or so for other uses and give the rest to TableCache.
|
||||
@@ -215,6 +237,11 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname)
|
||||
immutable_db_options_.Dump(immutable_db_options_.info_log.get());
|
||||
mutable_db_options_.Dump(immutable_db_options_.info_log.get());
|
||||
DumpSupportInfo(immutable_db_options_.info_log.get());
|
||||
|
||||
// always open the DB with 0 here, which means if preserve_deletes_==true
|
||||
// we won't drop any deletion markers until SetPreserveDeletesSequenceNumber()
|
||||
// is called by client and this seqnum is advanced.
|
||||
preserve_deletes_seqnum_.store(0);
|
||||
}
|
||||
|
||||
// Will lock the mutex_, will wait for completion if wait is true
|
||||
@@ -251,7 +278,7 @@ void DBImpl::CancelAllBackgroundWork(bool wait) {
|
||||
}
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() {
|
||||
Status DBImpl::CloseImpl() {
|
||||
// CancelAllBackgroundWork called with false means we just set the shutdown
|
||||
// marker. After this we do a variant of the waiting and unschedule work
|
||||
// (to consider: moving all the waiting into CancelAllBackgroundWork(true))
|
||||
@@ -267,7 +294,8 @@ DBImpl::~DBImpl() {
|
||||
|
||||
// Wait for background work to finish
|
||||
while (bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_) {
|
||||
bg_flush_scheduled_ || bg_purge_scheduled_ ||
|
||||
pending_purge_obsolete_files_) {
|
||||
TEST_SYNC_POINT("DBImpl::~DBImpl:WaitJob");
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
@@ -354,8 +382,16 @@ DBImpl::~DBImpl() {
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Shutdown complete");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
Status s = Status::OK();
|
||||
if (immutable_db_options_.info_log && own_info_log_) {
|
||||
s = immutable_db_options_.info_log->Close();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
DBImpl::~DBImpl() { Close(); }
|
||||
|
||||
void DBImpl::MaybeIgnoreError(Status* s) const {
|
||||
if (s->ok() || immutable_db_options_.paranoid_checks) {
|
||||
// No change needed
|
||||
@@ -482,6 +518,7 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
WriteThread::Writer w;
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
s = cfd->SetOptions(options_map);
|
||||
@@ -494,14 +531,13 @@ Status DBImpl::SetOptions(ColumnFamilyHandle* column_family,
|
||||
// Trigger possible flush/compactions. This has to be before we persist
|
||||
// options to file, otherwise there will be a deadlock with writer
|
||||
// thread.
|
||||
auto* old_sv =
|
||||
InstallSuperVersionAndScheduleWork(cfd, nullptr, new_options);
|
||||
delete old_sv;
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, new_options);
|
||||
|
||||
persist_options_status = WriteOptionsFile(
|
||||
false /*need_mutex_lock*/, true /*need_enter_write_thread*/);
|
||||
}
|
||||
}
|
||||
sv_context.Clean();
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"SetOptions() on column family [%s], inputs:",
|
||||
@@ -540,6 +576,7 @@ Status DBImpl::SetDBOptions(
|
||||
MutableDBOptions new_options;
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
bool wal_changed = false;
|
||||
WriteThread::Writer w;
|
||||
WriteContext write_context;
|
||||
{
|
||||
@@ -558,12 +595,24 @@ Status DBImpl::SetDBOptions(
|
||||
table_cache_.get()->SetCapacity(new_options.max_open_files == -1
|
||||
? TableCache::kInfiniteCapacity
|
||||
: new_options.max_open_files - 10);
|
||||
|
||||
wal_changed = mutable_db_options_.wal_bytes_per_sync !=
|
||||
new_options.wal_bytes_per_sync;
|
||||
if (new_options.bytes_per_sync == 0) {
|
||||
new_options.bytes_per_sync = 1024 * 1024;
|
||||
}
|
||||
mutable_db_options_ = new_options;
|
||||
|
||||
env_options_for_compaction_ = EnvOptions(
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_));
|
||||
env_options_for_compaction_ = env_->OptimizeForCompactionTableWrite(
|
||||
env_options_for_compaction_, immutable_db_options_);
|
||||
versions_->ChangeEnvOptions(mutable_db_options_);
|
||||
env_options_for_compaction_ = env_->OptimizeForCompactionTableRead(
|
||||
env_options_for_compaction_, immutable_db_options_);
|
||||
env_options_for_compaction_.compaction_readahead_size =
|
||||
mutable_db_options_.compaction_readahead_size;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
if (total_log_size_ > GetMaxTotalWalSize()) {
|
||||
Status purge_wal_status = HandleWALFull(&write_context);
|
||||
if (total_log_size_ > GetMaxTotalWalSize() || wal_changed) {
|
||||
Status purge_wal_status = SwitchWAL(&write_context);
|
||||
if (!purge_wal_status.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Unable to purge WAL files in SetDBOptions() -- %s",
|
||||
@@ -730,6 +779,19 @@ SequenceNumber DBImpl::GetLatestSequenceNumber() const {
|
||||
return versions_->LastSequence();
|
||||
}
|
||||
|
||||
void DBImpl::SetLastPublishedSequence(SequenceNumber seq) {
|
||||
versions_->SetLastPublishedSequence(seq);
|
||||
}
|
||||
|
||||
bool DBImpl::SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) {
|
||||
if (seqnum > preserve_deletes_seqnum_.load()) {
|
||||
preserve_deletes_seqnum_.store(seqnum);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
InternalIterator* DBImpl::NewInternalIterator(
|
||||
Arena* arena, RangeDelAggregator* range_del_agg,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
@@ -775,9 +837,7 @@ void DBImpl::BackgroundCallPurge() {
|
||||
purge_queue_.pop_front();
|
||||
|
||||
mutex_.Unlock();
|
||||
Status file_deletion_status;
|
||||
DeleteObsoleteFileImpl(file_deletion_status, job_id, fname, type, number,
|
||||
path_id);
|
||||
DeleteObsoleteFileImpl(job_id, fname, type, number, path_id);
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
assert(!logs_to_free_queue_.empty());
|
||||
@@ -880,6 +940,7 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
range_del_agg);
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::NewInternalIterator:StatusCallback", &s);
|
||||
if (s.ok()) {
|
||||
// Collect iterators for files in L0 - Ln
|
||||
if (read_options.read_tier != kMemtableTier) {
|
||||
@@ -893,8 +954,10 @@ InternalIterator* DBImpl::NewInternalIterator(
|
||||
internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, nullptr);
|
||||
|
||||
return internal_iter;
|
||||
} else {
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
return NewErrorInternalIterator(s);
|
||||
return NewErrorInternalIterator(s, arena);
|
||||
}
|
||||
|
||||
ColumnFamilyHandle* DBImpl::DefaultColumnFamily() const {
|
||||
@@ -910,7 +973,7 @@ Status DBImpl::Get(const ReadOptions& read_options,
|
||||
Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family, const Slice& key,
|
||||
PinnableSlice* pinnable_val, bool* value_found,
|
||||
bool* is_blob_index) {
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
assert(pinnable_val != nullptr);
|
||||
StopWatch sw(env_, stats_, DB_GET);
|
||||
PERF_TIMER_GUARD(get_snapshot_time);
|
||||
@@ -926,6 +989,10 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
|
||||
SequenceNumber snapshot;
|
||||
if (read_options.snapshot != nullptr) {
|
||||
// Note: In WritePrepared txns this is not necessary but not harmful either.
|
||||
// Because prep_seq > snapshot => commit_seq > snapshot so if a snapshot is
|
||||
// specified we should be fine with skipping seq numbers that are greater
|
||||
// than that.
|
||||
snapshot = reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_;
|
||||
} else {
|
||||
@@ -939,7 +1006,9 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
// super versipon because a flush happening in between may compact
|
||||
// away data for the snapshot, but the snapshot is earlier than the
|
||||
// data overwriting it, so users may see wrong results.
|
||||
snapshot = versions_->LastSequence();
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:3");
|
||||
TEST_SYNC_POINT("DBImpl::GetImpl:4");
|
||||
@@ -960,25 +1029,27 @@ Status DBImpl::GetImpl(const ReadOptions& read_options,
|
||||
bool done = false;
|
||||
if (!skip_memtable) {
|
||||
if (sv->mem->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&range_del_agg, read_options, is_blob_index)) {
|
||||
&range_del_agg, read_options, callback, is_blob_index)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
} else if ((s.ok() || s.IsMergeInProgress()) &&
|
||||
sv->imm->Get(lkey, pinnable_val->GetSelf(), &s, &merge_context,
|
||||
&range_del_agg, read_options, is_blob_index)) {
|
||||
&range_del_agg, read_options, callback,
|
||||
is_blob_index)) {
|
||||
done = true;
|
||||
pinnable_val->PinSelf();
|
||||
RecordTick(stats_, MEMTABLE_HIT);
|
||||
}
|
||||
if (!done && !s.ok() && !s.IsMergeInProgress()) {
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
sv->current->Get(read_options, lkey, pinnable_val, &s, &merge_context,
|
||||
&range_del_agg, value_found, nullptr, nullptr,
|
||||
&range_del_agg, value_found, nullptr, nullptr, callback,
|
||||
is_blob_index);
|
||||
RecordTick(stats_, MEMTABLE_MISS);
|
||||
}
|
||||
@@ -1028,7 +1099,9 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
snapshot = reinterpret_cast<const SnapshotImpl*>(
|
||||
read_options.snapshot)->number_;
|
||||
} else {
|
||||
snapshot = versions_->LastSequence();
|
||||
snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
}
|
||||
for (auto mgd_iter : multiget_cf_data) {
|
||||
mgd_iter.second->super_version =
|
||||
@@ -1209,6 +1282,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
return s;
|
||||
}
|
||||
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
@@ -1240,8 +1314,8 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
auto* cfd =
|
||||
versions_->GetColumnFamilySet()->GetColumnFamily(column_family_name);
|
||||
assert(cfd != nullptr);
|
||||
delete InstallSuperVersionAndScheduleWork(
|
||||
cfd, nullptr, *cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &sv_context, *cfd->GetLatestMutableCFOptions());
|
||||
|
||||
if (!cfd->mem()->IsSnapshotSupported()) {
|
||||
is_snapshot_supported_ = false;
|
||||
@@ -1260,6 +1334,7 @@ Status DBImpl::CreateColumnFamilyImpl(const ColumnFamilyOptions& cf_options,
|
||||
}
|
||||
} // InstrumentedMutexLock l(&mutex_)
|
||||
|
||||
sv_context.Clean();
|
||||
// this is outside the mutex
|
||||
if (s.ok()) {
|
||||
NewThreadStatusCfInfo(
|
||||
@@ -1385,55 +1460,70 @@ bool DBImpl::KeyMayExist(const ReadOptions& read_options,
|
||||
|
||||
Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
Iterator* result = nullptr;
|
||||
if (read_options.read_tier == kPersistedTier) {
|
||||
return NewErrorIterator(Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators."));
|
||||
}
|
||||
// if iterator wants internal keys, we can only proceed if
|
||||
// we can guarantee the deletes haven't been processed yet
|
||||
if (immutable_db_options_.preserve_deletes &&
|
||||
read_options.iter_start_seqnum > 0 &&
|
||||
read_options.iter_start_seqnum < preserve_deletes_seqnum_.load()) {
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
"Iterator requested internal keys which are too old and are not"
|
||||
" guaranteed to be preserved, try larger iter_start_seqnum opt."));
|
||||
}
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.managed) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
// not supported in lite version
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
result = NewErrorIterator(Status::InvalidArgument(
|
||||
"Managed Iterators not supported in RocksDBLite."));
|
||||
#else
|
||||
if ((read_options.tailing) || (read_options.snapshot != nullptr) ||
|
||||
(is_snapshot_supported_)) {
|
||||
return new ManagedIterator(this, read_options, cfd);
|
||||
}
|
||||
// Managed iter not supported
|
||||
return NewErrorIterator(Status::InvalidArgument(
|
||||
result = new ManagedIterator(this, read_options, cfd);
|
||||
} else {
|
||||
// Managed iter not supported
|
||||
result = NewErrorIterator(Status::InvalidArgument(
|
||||
"Managed Iterators not supported without snapshots."));
|
||||
}
|
||||
#endif
|
||||
} else if (read_options.tailing) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
// not supported in lite version
|
||||
return nullptr;
|
||||
result = nullptr;
|
||||
|
||||
#else
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
auto iter = new ForwardIterator(this, read_options, cfd, sv);
|
||||
return NewDBIterator(
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), iter,
|
||||
kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations);
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback);
|
||||
#endif
|
||||
} else {
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
auto snapshot =
|
||||
read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
return NewIteratorImpl(read_options, cfd, snapshot);
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
// WritePreparedTxnDB
|
||||
auto snapshot = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
result = NewIteratorImpl(read_options, cfd, snapshot, read_callback);
|
||||
}
|
||||
// To stop compiler from complaining
|
||||
return nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
bool allow_blob) {
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(&mutex_);
|
||||
|
||||
// Try to generate a DB iterator tree in continuous memory area to be
|
||||
@@ -1481,8 +1571,9 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, ((read_options.snapshot != nullptr) ? nullptr : this),
|
||||
cfd, allow_blob);
|
||||
sv->version_number, read_callback,
|
||||
((read_options.snapshot != nullptr) ? nullptr : this), cfd, allow_blob,
|
||||
allow_refresh);
|
||||
|
||||
InternalIterator* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
@@ -1500,6 +1591,7 @@ Status DBImpl::NewIterators(
|
||||
return Status::NotSupported(
|
||||
"ReadTier::kPersistedData is not yet supported in iterators.");
|
||||
}
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
iterators->clear();
|
||||
iterators->reserve(column_families.size());
|
||||
if (read_options.managed) {
|
||||
@@ -1530,21 +1622,22 @@ Status DBImpl::NewIterators(
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), cfd->user_comparator(), iter,
|
||||
kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations));
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback));
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
auto snapshot =
|
||||
read_options.snapshot != nullptr
|
||||
? reinterpret_cast<const SnapshotImpl*>(read_options.snapshot)
|
||||
->number_
|
||||
: latest_snapshot;
|
||||
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterators is overridden in
|
||||
// WritePreparedTxnDB
|
||||
auto snapshot = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
for (size_t i = 0; i < column_families.size(); ++i) {
|
||||
auto* cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(
|
||||
column_families[i])->cfd();
|
||||
iterators->push_back(NewIteratorImpl(read_options, cfd, snapshot));
|
||||
iterators->push_back(
|
||||
NewIteratorImpl(read_options, cfd, snapshot, read_callback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1570,8 +1663,10 @@ const Snapshot* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
return snapshots_.New(s, versions_->LastSequence(), unix_time,
|
||||
is_write_conflict_boundary);
|
||||
auto snapshot_seq = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
return snapshots_.New(s, snapshot_seq, unix_time, is_write_conflict_boundary);
|
||||
}
|
||||
|
||||
void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
@@ -1579,16 +1674,28 @@ void DBImpl::ReleaseSnapshot(const Snapshot* s) {
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
snapshots_.Delete(casted_s);
|
||||
uint64_t oldest_snapshot;
|
||||
if (snapshots_.empty()) {
|
||||
oldest_snapshot = last_seq_same_as_publish_seq_
|
||||
? versions_->LastSequence()
|
||||
: versions_->LastPublishedSequence();
|
||||
} else {
|
||||
oldest_snapshot = snapshots_.oldest()->number_;
|
||||
}
|
||||
for (auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
cfd->current()->storage_info()->UpdateOldestSnapshot(oldest_snapshot);
|
||||
if (!cfd->current()
|
||||
->storage_info()
|
||||
->BottommostFilesMarkedForCompaction()
|
||||
.empty()) {
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
delete casted_s;
|
||||
}
|
||||
|
||||
bool DBImpl::HasActiveSnapshotInRange(SequenceNumber lower_bound,
|
||||
SequenceNumber upper_bound) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return snapshots_.HasSnapshotInRange(lower_bound, upper_bound);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status DBImpl::GetPropertiesOfAllTables(ColumnFamilyHandle* column_family,
|
||||
TablePropertiesCollection* props) {
|
||||
@@ -1683,7 +1790,7 @@ bool DBImpl::GetProperty(ColumnFamilyHandle* column_family,
|
||||
|
||||
bool DBImpl::GetMapProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) {
|
||||
std::map<std::string, std::string>* value) {
|
||||
const DBPropertyInfo* property_info = GetPropertyInfo(property);
|
||||
value->clear();
|
||||
auto cfd = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family)->cfd();
|
||||
@@ -1796,21 +1903,23 @@ SuperVersion* DBImpl::GetAndRefSuperVersion(uint32_t column_family_id) {
|
||||
return GetAndRefSuperVersion(cfd);
|
||||
}
|
||||
|
||||
void DBImpl::CleanupSuperVersion(SuperVersion* sv) {
|
||||
// Release SuperVersion
|
||||
if (sv->Unref()) {
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
sv->Cleanup();
|
||||
}
|
||||
delete sv;
|
||||
RecordTick(stats_, NUMBER_SUPERVERSION_CLEANUPS);
|
||||
}
|
||||
RecordTick(stats_, NUMBER_SUPERVERSION_RELEASES);
|
||||
}
|
||||
|
||||
void DBImpl::ReturnAndCleanupSuperVersion(ColumnFamilyData* cfd,
|
||||
SuperVersion* sv) {
|
||||
bool unref_sv = !cfd->ReturnThreadLocalSuperVersion(sv);
|
||||
|
||||
if (unref_sv) {
|
||||
// Release SuperVersion
|
||||
if (sv->Unref()) {
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
sv->Cleanup();
|
||||
}
|
||||
delete sv;
|
||||
RecordTick(stats_, NUMBER_SUPERVERSION_CLEANUPS);
|
||||
}
|
||||
RecordTick(stats_, NUMBER_SUPERVERSION_RELEASES);
|
||||
if (!cfd->ReturnThreadLocalSuperVersion(sv)) {
|
||||
CleanupSuperVersion(sv);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1950,8 +2059,7 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
name.c_str());
|
||||
return Status::NotSupported("Delete only supported for archived logs");
|
||||
}
|
||||
status =
|
||||
env_->DeleteFile(immutable_db_options_.wal_dir + "/" + name.c_str());
|
||||
status = wal_manager_.DeleteFile(name, number);
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"DeleteFile %s failed -- %s.\n", name.c_str(),
|
||||
@@ -2013,8 +2121,9 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
cfd, &job_context, *cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
FindObsoleteFiles(&job_context, false);
|
||||
} // lock released here
|
||||
@@ -2052,35 +2161,29 @@ Status DBImpl::DeleteFilesInRange(ColumnFamilyHandle* column_family,
|
||||
if (begin == nullptr) {
|
||||
begin_key = nullptr;
|
||||
} else {
|
||||
begin_storage.SetMaxPossibleForUserKey(*begin);
|
||||
begin_storage.SetMinPossibleForUserKey(*begin);
|
||||
begin_key = &begin_storage;
|
||||
}
|
||||
if (end == nullptr) {
|
||||
end_key = nullptr;
|
||||
} else {
|
||||
end_storage.SetMinPossibleForUserKey(*end);
|
||||
end_storage.SetMaxPossibleForUserKey(*end);
|
||||
end_key = &end_storage;
|
||||
}
|
||||
|
||||
vstorage->GetOverlappingInputs(i, begin_key, end_key, &level_files, -1,
|
||||
nullptr, false);
|
||||
vstorage->GetCleanInputsWithinInterval(i, begin_key, end_key,
|
||||
&level_files, -1 /* hint_index */,
|
||||
nullptr /* file_index */);
|
||||
FileMetaData* level_file;
|
||||
for (uint32_t j = 0; j < level_files.size(); j++) {
|
||||
level_file = level_files[j];
|
||||
if (((begin == nullptr) ||
|
||||
(cfd->internal_comparator().user_comparator()->Compare(
|
||||
level_file->smallest.user_key(), *begin) >= 0)) &&
|
||||
((end == nullptr) ||
|
||||
(cfd->internal_comparator().user_comparator()->Compare(
|
||||
level_file->largest.user_key(), *end) <= 0))) {
|
||||
if (level_file->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.DeleteFile(i, level_file->fd.GetNumber());
|
||||
deleted_files.push_back(level_file);
|
||||
level_file->being_compacted = true;
|
||||
if (level_file->being_compacted) {
|
||||
continue;
|
||||
}
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.DeleteFile(i, level_file->fd.GetNumber());
|
||||
deleted_files.push_back(level_file);
|
||||
level_file->being_compacted = true;
|
||||
}
|
||||
}
|
||||
if (edit.GetDeletedFiles().empty()) {
|
||||
@@ -2091,8 +2194,9 @@ Status DBImpl::DeleteFilesInRange(ColumnFamilyHandle* column_family,
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
cfd, &job_context, *cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
for (auto* deleted_file : deleted_files) {
|
||||
deleted_file->being_compacted = false;
|
||||
@@ -2228,7 +2332,15 @@ Status DB::DestroyColumnFamilyHandle(ColumnFamilyHandle* column_family) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
DB::~DB() { }
|
||||
DB::~DB() {}
|
||||
|
||||
Status DBImpl::Close() {
|
||||
if (!closed_) {
|
||||
closed_ = true;
|
||||
return CloseImpl();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DB::ListColumnFamilies(const DBOptions& db_options,
|
||||
const std::string& name,
|
||||
@@ -2542,7 +2654,7 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
|
||||
// Check if there is a record for this key in the latest memtable
|
||||
sv->mem->Get(lkey, nullptr, &s, &merge_context, &range_del_agg, seq,
|
||||
read_options, is_blob_index);
|
||||
read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -2561,7 +2673,7 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->Get(lkey, nullptr, &s, &merge_context, &range_del_agg, seq,
|
||||
read_options, is_blob_index);
|
||||
read_options, nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -2604,7 +2716,8 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
// Check tables
|
||||
sv->current->Get(read_options, lkey, nullptr, &s, &merge_context,
|
||||
&range_del_agg, nullptr /* value_found */,
|
||||
found_record_for_key, seq, is_blob_index);
|
||||
found_record_for_key, seq, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading SST files
|
||||
@@ -2657,6 +2770,7 @@ Status DBImpl::IngestExternalFile(
|
||||
return status;
|
||||
}
|
||||
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:Start");
|
||||
{
|
||||
// Lock db mutex
|
||||
@@ -2667,7 +2781,7 @@ Status DBImpl::IngestExternalFile(
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
}
|
||||
|
||||
@@ -2705,12 +2819,12 @@ Status DBImpl::IngestExternalFile(
|
||||
&mutex_, directories_.GetDbDir());
|
||||
}
|
||||
if (status.ok()) {
|
||||
delete InstallSuperVersionAndScheduleWork(cfd, nullptr,
|
||||
*mutable_cf_options);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context,
|
||||
*mutable_cf_options);
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
@@ -2732,6 +2846,7 @@ Status DBImpl::IngestExternalFile(
|
||||
// mutex_ is unlocked here
|
||||
|
||||
// Cleanup
|
||||
sv_context.Clean();
|
||||
ingestion_job.Cleanup(status);
|
||||
|
||||
if (status.ok()) {
|
||||
|
||||
+145
-44
@@ -28,6 +28,9 @@
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/pre_release_callback.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/wal_manager.h"
|
||||
@@ -52,13 +55,13 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class Arena;
|
||||
class ArenaWrappedDBIter;
|
||||
class MemTable;
|
||||
class TableCache;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
class VersionSet;
|
||||
class Arena;
|
||||
class WriteCallback;
|
||||
struct JobContext;
|
||||
struct ExternalSstFileInfo;
|
||||
@@ -66,7 +69,8 @@ struct MemTableInfo;
|
||||
|
||||
class DBImpl : public DB {
|
||||
public:
|
||||
DBImpl(const DBOptions& options, const std::string& dbname);
|
||||
DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
const bool seq_per_batch = false);
|
||||
virtual ~DBImpl();
|
||||
|
||||
// Implementations of the DB interface
|
||||
@@ -99,7 +103,8 @@ class DBImpl : public DB {
|
||||
// Note: 'value_found' from KeyMayExist propagates here
|
||||
Status GetImpl(const ReadOptions& options, ColumnFamilyHandle* column_family,
|
||||
const Slice& key, PinnableSlice* value,
|
||||
bool* value_found = nullptr, bool* is_blob_index = nullptr);
|
||||
bool* value_found = nullptr, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr);
|
||||
|
||||
using DB::MultiGet;
|
||||
virtual std::vector<Status> MultiGet(
|
||||
@@ -142,7 +147,9 @@ class DBImpl : public DB {
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
bool allow_blob = false);
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
|
||||
virtual const Snapshot* GetSnapshot() override;
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
@@ -150,9 +157,9 @@ class DBImpl : public DB {
|
||||
virtual bool GetProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, std::string* value) override;
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) override;
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override;
|
||||
using DB::GetIntProperty;
|
||||
virtual bool GetIntProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property, uint64_t* value) override;
|
||||
@@ -215,10 +222,13 @@ class DBImpl : public DB {
|
||||
virtual Status SyncWAL() override;
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override;
|
||||
virtual void SetLastPublishedSequence(SequenceNumber seq);
|
||||
// Returns LastSequence in last_seq_same_as_publish_seq_
|
||||
// mode and LastAllocatedSequence otherwise. This is useful when visiblility
|
||||
// depends also on data written to the WAL but not to the memtable.
|
||||
SequenceNumber TEST_GetLastVisibleSequence() const;
|
||||
|
||||
// Whether there is an active snapshot in range [lower_bound, upper_bound).
|
||||
bool HasActiveSnapshotInRange(SequenceNumber lower_bound,
|
||||
SequenceNumber upper_bound);
|
||||
virtual bool SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) override;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::ResetStats;
|
||||
@@ -348,7 +358,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool disallow_trivial_move = false);
|
||||
|
||||
void TEST_HandleWALFull();
|
||||
void TEST_SwitchWAL();
|
||||
|
||||
bool TEST_UnableToFlushOldestLog() {
|
||||
return unable_to_flush_oldest_log_;
|
||||
@@ -378,6 +388,9 @@ class DBImpl : public DB {
|
||||
// Return the current manifest file no.
|
||||
uint64_t TEST_Current_Manifest_FileNo();
|
||||
|
||||
// Returns the number that'll be assigned to the next file that's created.
|
||||
uint64_t TEST_Current_Next_FileNo();
|
||||
|
||||
// get total level0 file size. Only for testing.
|
||||
uint64_t TEST_GetLevel0TotalSize();
|
||||
|
||||
@@ -455,6 +468,8 @@ class DBImpl : public DB {
|
||||
// belong to live files are posibly removed. Also, removes all the
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
// If FindObsoleteFiles() was run, we need to also run
|
||||
// PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
void PurgeObsoleteFiles(const JobContext& background_contet,
|
||||
bool schedule_only = false);
|
||||
|
||||
@@ -481,6 +496,9 @@ class DBImpl : public DB {
|
||||
// mutex is held.
|
||||
SuperVersion* GetAndRefSuperVersion(uint32_t column_family_id);
|
||||
|
||||
// Un-reference the super version and clean it up if it is the last reference.
|
||||
void CleanupSuperVersion(SuperVersion* sv);
|
||||
|
||||
// Un-reference the super version and return it to thread local cache if
|
||||
// needed. If it is the last reference of the super version. Clean it up
|
||||
// after un-referencing it.
|
||||
@@ -528,9 +546,11 @@ class DBImpl : public DB {
|
||||
uint64_t log_number_;
|
||||
std::string name_;
|
||||
WriteBatch* batch_;
|
||||
// The seq number of the first key in the batch
|
||||
SequenceNumber seq_;
|
||||
explicit RecoveredTransaction(const uint64_t log, const std::string& name,
|
||||
WriteBatch* batch)
|
||||
: log_number_(log), name_(name), batch_(batch) {}
|
||||
WriteBatch* batch, SequenceNumber seq)
|
||||
: log_number_(log), name_(name), batch_(batch), seq_(seq) {}
|
||||
|
||||
~RecoveredTransaction() { delete batch_; }
|
||||
};
|
||||
@@ -552,8 +572,9 @@ class DBImpl : public DB {
|
||||
}
|
||||
|
||||
void InsertRecoveredTransaction(const uint64_t log, const std::string& name,
|
||||
WriteBatch* batch) {
|
||||
recovered_transactions_[name] = new RecoveredTransaction(log, name, batch);
|
||||
WriteBatch* batch, SequenceNumber seq) {
|
||||
recovered_transactions_[name] =
|
||||
new RecoveredTransaction(log, name, batch, seq);
|
||||
MarkLogAsContainingPrepSection(log);
|
||||
}
|
||||
|
||||
@@ -579,14 +600,27 @@ class DBImpl : public DB {
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
|
||||
void SetSnapshotChecker(SnapshotChecker* snapshot_checker);
|
||||
|
||||
InstrumentedMutex* mutex() { return &mutex_; }
|
||||
|
||||
Status NewDB();
|
||||
|
||||
// This is to be used only by internal rocksdb classes.
|
||||
static Status Open(const DBOptions& db_options, const std::string& name,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch);
|
||||
|
||||
virtual Status Close() override;
|
||||
|
||||
protected:
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
const DBOptions initial_db_options_;
|
||||
const ImmutableDBOptions immutable_db_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
@@ -632,10 +666,17 @@ class DBImpl : public DB {
|
||||
|
||||
void EraseThreadStatusDbInfo() const;
|
||||
|
||||
// If disable_memtable is set the application logic must guarantee that the
|
||||
// batch will still be skipped from memtable during the recovery. In
|
||||
// WriteCommitted it is guarnateed since disable_memtable is used for prepare
|
||||
// batch which will be written to memtable later during the commit, and in
|
||||
// WritePrepared it is guaranteed since it will be used only for WAL markers
|
||||
// which will never be written to memtable.
|
||||
Status WriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr);
|
||||
bool disable_memtable = false, uint64_t* seq_used = nullptr,
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
Status PipelinedWriteImpl(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
@@ -646,10 +687,14 @@ class DBImpl : public DB {
|
||||
Status WriteImplWALOnly(const WriteOptions& options, WriteBatch* updates,
|
||||
WriteCallback* callback = nullptr,
|
||||
uint64_t* log_used = nullptr, uint64_t log_ref = 0,
|
||||
uint64_t* seq_used = nullptr);
|
||||
uint64_t* seq_used = nullptr,
|
||||
PreReleaseCallback* pre_release_callback = nullptr);
|
||||
|
||||
uint64_t FindMinLogContainingOutstandingPrep();
|
||||
uint64_t FindMinPrepLogReferencedByMemTable();
|
||||
// write cached_recoverable_state_ to memtable if it is not empty
|
||||
// The writer must be the leader in write_thread_ and holding mutex_
|
||||
Status WriteRecoverableState();
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
@@ -657,6 +702,8 @@ class DBImpl : public DB {
|
||||
friend class PessimisticTransaction;
|
||||
friend class WriteCommittedTxn;
|
||||
friend class WritePreparedTxn;
|
||||
friend class WritePreparedTxnDB;
|
||||
friend class WriteBatchWithIndex;
|
||||
#ifndef ROCKSDB_LITE
|
||||
friend class ForwardIterator;
|
||||
#endif
|
||||
@@ -664,19 +711,21 @@ class DBImpl : public DB {
|
||||
friend class CompactedDBImpl;
|
||||
#ifndef NDEBUG
|
||||
friend class DBTest2_ReadCallbackTest_Test;
|
||||
friend class WriteCallbackTest_WriteWithCallbackTest_Test;
|
||||
friend class XFTransactionWriteHandler;
|
||||
friend class DBBlobIndexTest;
|
||||
#endif
|
||||
struct CompactionState;
|
||||
|
||||
struct WriteContext {
|
||||
autovector<SuperVersion*> superversions_to_free_;
|
||||
SuperVersionContext superversion_context;
|
||||
autovector<MemTable*> memtables_to_free_;
|
||||
|
||||
explicit WriteContext(bool create_superversion = false)
|
||||
: superversion_context(create_superversion) {}
|
||||
|
||||
~WriteContext() {
|
||||
for (auto& sv : superversions_to_free_) {
|
||||
delete sv;
|
||||
}
|
||||
superversion_context.Clean();
|
||||
for (auto& m : memtables_to_free_) {
|
||||
delete m;
|
||||
}
|
||||
@@ -706,9 +755,8 @@ class DBImpl : public DB {
|
||||
// Delete any unneeded files and stale in-memory entries.
|
||||
void DeleteObsoleteFiles();
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
void DeleteObsoleteFileImpl(Status file_deletion_status, int job_id,
|
||||
const std::string& fname, FileType type,
|
||||
uint64_t number, uint32_t path_id);
|
||||
void DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
FileType type, uint64_t number, uint32_t path_id);
|
||||
|
||||
// Background process needs to call
|
||||
// auto x = CaptureCurrentFileNumberInPendingOutputs()
|
||||
@@ -765,11 +813,15 @@ class DBImpl : public DB {
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
bool writes_stopped = false);
|
||||
|
||||
// Wait for memtable flushed
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd);
|
||||
// Wait for memtable flushed.
|
||||
// If flush_memtable_id is non-null, wait until the memtable with the ID
|
||||
// gets flush. Otherwise, wait until the column family don't have any
|
||||
// memtable pending flush.
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd,
|
||||
const uint64_t* flush_memtable_id = nullptr);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status HandleWALFull(WriteContext* write_context);
|
||||
Status SwitchWAL(WriteContext* write_context);
|
||||
|
||||
// REQUIRES: mutex locked
|
||||
Status HandleWriteBufferFull(WriteContext* write_context);
|
||||
@@ -779,7 +831,8 @@ class DBImpl : public DB {
|
||||
WriteContext* write_context);
|
||||
|
||||
WriteBatch* MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal);
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state);
|
||||
|
||||
Status WriteToWAL(const WriteBatch& merged_batch, log::Writer* log_writer,
|
||||
uint64_t* log_used, uint64_t* log_size);
|
||||
@@ -791,7 +844,7 @@ class DBImpl : public DB {
|
||||
|
||||
Status ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used, SequenceNumber* last_sequence,
|
||||
int total_count);
|
||||
size_t seq_inc);
|
||||
|
||||
// Used by WriteImpl to update bg_error_ if paranoid check is enabled.
|
||||
void WriteCallbackStatusCheck(const Status& status);
|
||||
@@ -869,6 +922,9 @@ class DBImpl : public DB {
|
||||
|
||||
uint64_t GetMaxTotalWalSize() const;
|
||||
|
||||
// Actual implementation of Close()
|
||||
virtual Status CloseImpl();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
@@ -876,12 +932,12 @@ class DBImpl : public DB {
|
||||
FileLock* db_lock_;
|
||||
|
||||
// In addition to mutex_, log_write_mutex_ protected writes to logs_ and
|
||||
// logfile_number_. With concurrent_prepare it also protects alive_log_files_,
|
||||
// logfile_number_. With two_write_queues it also protects alive_log_files_,
|
||||
// and log_empty_. Refer to the definition of each variable below for more
|
||||
// details.
|
||||
InstrumentedMutex log_write_mutex_;
|
||||
// State below is protected by mutex_
|
||||
// With concurrent_prepare enabled, some of the variables that accessed during
|
||||
// With two_write_queues enabled, some of the variables that accessed during
|
||||
// WriteToWAL need different synchronization: log_empty_, alive_log_files_,
|
||||
// logs_, logfile_number_. Refer to the definition of each variable below for
|
||||
// more description.
|
||||
@@ -897,6 +953,8 @@ class DBImpl : public DB {
|
||||
// (i.e. whenever a flush is done, even if it didn't make any progress)
|
||||
// * whenever there is an error in background purge, flush or compaction
|
||||
// * whenever num_running_ingest_file_ goes to 0.
|
||||
// * whenever pending_purge_obsolete_files_ goes to 0.
|
||||
// * whenever disable_delete_obsolete_files_ goes to 0.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
// Writes are protected by locking both mutex_ and log_write_mutex_, and reads
|
||||
// must be under either mutex_ or log_write_mutex_. Since after ::Open,
|
||||
@@ -904,12 +962,12 @@ class DBImpl : public DB {
|
||||
// from the same write_thread_ without any locks.
|
||||
uint64_t logfile_number_;
|
||||
std::deque<uint64_t>
|
||||
log_recycle_files; // a list of log files that we can recycle
|
||||
log_recycle_files_; // a list of log files that we can recycle
|
||||
bool log_dir_synced_;
|
||||
// Without concurrent_prepare, read and writes to log_empty_ are protected by
|
||||
// Without two_write_queues, read and writes to log_empty_ are protected by
|
||||
// mutex_. Since it is currently updated/read only in write_thread_, it can be
|
||||
// accessed from the same write_thread_ without any locks. With
|
||||
// concurrent_prepare writes, where it can be updated in different threads,
|
||||
// two_write_queues writes, where it can be updated in different threads,
|
||||
// read and writes are protected by log_write_mutex_ instead. This is to avoid
|
||||
// expesnive mutex_ lock during WAL write, which update log_empty_.
|
||||
bool log_empty_;
|
||||
@@ -946,10 +1004,10 @@ class DBImpl : public DB {
|
||||
// true for some prefix of logs_
|
||||
bool getting_synced = false;
|
||||
};
|
||||
// Without concurrent_prepare, read and writes to alive_log_files_ are
|
||||
// Without two_write_queues, read and writes to alive_log_files_ are
|
||||
// protected by mutex_. However since back() is never popped, and push_back()
|
||||
// is done only from write_thread_, the same thread can access the item
|
||||
// reffered by back() without mutex_. With concurrent_prepare_, writes
|
||||
// reffered by back() without mutex_. With two_write_queues_, writes
|
||||
// are protected by locking both mutex_ and log_write_mutex_, and reads must
|
||||
// be under either mutex_ or log_write_mutex_.
|
||||
std::deque<LogFileNumberSize> alive_log_files_;
|
||||
@@ -969,6 +1027,15 @@ class DBImpl : public DB {
|
||||
std::deque<LogWriterNumber> logs_;
|
||||
// Signaled when getting_synced becomes false for some of the logs_.
|
||||
InstrumentedCondVar log_sync_cv_;
|
||||
// This is the app-level state that is written to the WAL but will be used
|
||||
// only during recovery. Using this feature enables not writing the state to
|
||||
// memtable on normal writes and hence improving the throughput. Each new
|
||||
// write of the state will replace the previous state entirely even if the
|
||||
// keys in the two consecuitive states do not overlap.
|
||||
// It is protected by log_write_mutex_ when two_write_queues_ is enabled.
|
||||
// Otherwise only the heaad of write_thread_ can access it.
|
||||
WriteBatch cached_recoverable_state_;
|
||||
std::atomic<bool> cached_recoverable_state_empty_ = {true};
|
||||
std::atomic<uint64_t> total_log_size_;
|
||||
// only used for dynamically adjusting max_total_wal_size. it is a sum of
|
||||
// [write_buffer_size * max_write_buffer_number] over all column families
|
||||
@@ -1154,6 +1221,10 @@ class DBImpl : public DB {
|
||||
// without any synchronization
|
||||
int disable_delete_obsolete_files_;
|
||||
|
||||
// Number of times FindObsoleteFiles has found deletable files and the
|
||||
// corresponding call to PurgeObsoleteFiles has not yet finished.
|
||||
int pending_purge_obsolete_files_;
|
||||
|
||||
// last time when DeleteObsoleteFiles with full scan was executed. Originaly
|
||||
// initialized with startup time.
|
||||
uint64_t delete_obsolete_files_last_run_;
|
||||
@@ -1187,6 +1258,9 @@ class DBImpl : public DB {
|
||||
// The options to access storage files
|
||||
const EnvOptions env_options_;
|
||||
|
||||
// Additonal options for compaction and flush
|
||||
EnvOptions env_options_for_compaction_;
|
||||
|
||||
// Number of running IngestExternalFile() calls.
|
||||
// REQUIRES: mutex held
|
||||
int num_running_ingest_file_;
|
||||
@@ -1232,23 +1306,23 @@ class DBImpl : public DB {
|
||||
std::unordered_map<uint64_t, uint64_t> prepared_section_completed_;
|
||||
std::mutex prep_heap_mutex_;
|
||||
|
||||
// Callback for compaction to check if a key is visible to a snapshot.
|
||||
// REQUIRES: mutex held
|
||||
std::unique_ptr<SnapshotChecker> snapshot_checker_;
|
||||
|
||||
// No copying allowed
|
||||
DBImpl(const DBImpl&);
|
||||
void operator=(const DBImpl&);
|
||||
|
||||
// Background threads call this function, which is just a wrapper around
|
||||
// the InstallSuperVersion() function. Background threads carry
|
||||
// job_context which can have new_superversion already
|
||||
// sv_context which can have new_superversion already
|
||||
// allocated.
|
||||
void InstallSuperVersionAndScheduleWorkWrapper(
|
||||
ColumnFamilyData* cfd, JobContext* job_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
// All ColumnFamily state changes go through this function. Here we analyze
|
||||
// the new state and we schedule background work if we detect that the new
|
||||
// state needs flush or compaction.
|
||||
SuperVersion* InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersion* new_sv,
|
||||
void InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1275,11 +1349,38 @@ class DBImpl : public DB {
|
||||
bool MCOverlap(ManualCompactionState* m, ManualCompactionState* m1);
|
||||
|
||||
size_t GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
Env::WriteLifeTimeHint CalculateWALWriteHint() {
|
||||
return Env::WLTH_SHORT;
|
||||
}
|
||||
|
||||
// When set, we use a seprate queue for writes that dont write to memtable. In
|
||||
// 2PC these are the writes at Prepare phase.
|
||||
const bool concurrent_prepare_;
|
||||
const bool two_write_queues_;
|
||||
const bool manual_wal_flush_;
|
||||
// Increase the sequence number after writing each batch, whether memtable is
|
||||
// disabled for that or not. Otherwise the sequence number is increased after
|
||||
// writing each key into memtable. This implies that when disable_memtable is
|
||||
// set, the seq is not increased at all.
|
||||
//
|
||||
// Default: false
|
||||
const bool seq_per_batch_;
|
||||
// LastSequence also indicates last published sequence visibile to the
|
||||
// readers. Otherwise LastPublishedSequence should be used.
|
||||
const bool last_seq_same_as_publish_seq_;
|
||||
// It indicates that a customized gc algorithm must be used for
|
||||
// flush/compaction and if it is not provided vis SnapshotChecker, we should
|
||||
// disable gc to be safe.
|
||||
const bool use_custom_gc_;
|
||||
|
||||
// Clients must periodically call SetPreserveDeletesSequenceNumber()
|
||||
// to advance this seqnum. Default value is 0 which means ALL deletes are
|
||||
// preserved. Note that this has no effect if DBOptions.preserve_deletes
|
||||
// is set to false.
|
||||
std::atomic<SequenceNumber> preserve_deletes_seqnum_;
|
||||
const bool preserve_deletes_;
|
||||
|
||||
// Flag to check whether Close() has been called on this DB
|
||||
bool closed_;
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
|
||||
+109
-67
@@ -86,11 +86,16 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, env_options_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshot_seqs,
|
||||
earliest_write_conflict_snapshot, job_context, log_buffer,
|
||||
directories_.GetDbDir(), directories_.GetDataDir(0U),
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
env_options_for_compaction_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
job_context, log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(0U),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats);
|
||||
|
||||
@@ -129,8 +134,9 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(cfd, job_context,
|
||||
mutable_cf_options);
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforeInstallSV");
|
||||
InstallSuperVersionAndScheduleWork(cfd, &job_context->superversion_context,
|
||||
mutable_cf_options);
|
||||
if (made_progress) {
|
||||
*made_progress = 1;
|
||||
}
|
||||
@@ -436,7 +442,8 @@ Status DBImpl::CompactFiles(
|
||||
} // release the mutex
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
@@ -515,9 +522,10 @@ Status DBImpl::CompactFilesImpl(
|
||||
c.reset(cfd->compaction_picker()->CompactFiles(
|
||||
compact_options, input_files, output_level, version->storage_info(),
|
||||
*cfd->GetLatestMutableCFOptions(), output_path_id));
|
||||
if (!c) {
|
||||
return Status::Aborted("Another Level 0 compaction is running");
|
||||
}
|
||||
// we already sanitized the set of input files and checked for conflicts
|
||||
// without releasing the lock, so we're guaranteed a compaction can be formed.
|
||||
assert(c != nullptr);
|
||||
|
||||
c->SetInputVersion(version);
|
||||
// deletion compaction currently not allowed in CompactFiles.
|
||||
assert(!c->deletion_compaction());
|
||||
@@ -529,13 +537,19 @@ Status DBImpl::CompactFilesImpl(
|
||||
auto pending_outputs_inserted_elem =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, log_buffer, directories_.GetDbDir(),
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(c->output_path_id()), stats_, &mutex_, &bg_error_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, table_cache_,
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
nullptr); // Here we pass a nullptr for CompactionJobStats because
|
||||
// CompactFiles does not trigger OnCompactionCompleted(),
|
||||
@@ -570,8 +584,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
|
||||
Status status = compaction_job.Install(*c->mutable_cf_options());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
c->column_family_data(), job_context, *c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
c->ReleaseCompactionFiles(s);
|
||||
|
||||
@@ -649,6 +664,8 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
Version* current = cfd->current();
|
||||
current->Ref();
|
||||
// release lock while notifying events
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::NotifyOnCompactionCompleted::UnlockMutex");
|
||||
@@ -671,7 +688,7 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
info.input_files.push_back(fn);
|
||||
if (info.table_properties.count(fn) == 0) {
|
||||
std::shared_ptr<const TableProperties> tp;
|
||||
auto s = cfd->current()->GetTableProperties(&tp, fmd, &fn);
|
||||
auto s = current->GetTableProperties(&tp, fmd, &fn);
|
||||
if (s.ok()) {
|
||||
info.table_properties[fn] = tp;
|
||||
}
|
||||
@@ -688,6 +705,7 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
current->Unref();
|
||||
// no need to signal bg_cv_ as it will be signaled at the end of the
|
||||
// flush process.
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -701,8 +719,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
return Status::InvalidArgument("Target level exceeds number of levels");
|
||||
}
|
||||
|
||||
std::unique_ptr<SuperVersion> superversion_to_free;
|
||||
std::unique_ptr<SuperVersion> new_superversion(new SuperVersion());
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
|
||||
Status status;
|
||||
|
||||
@@ -757,8 +774,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
|
||||
status = versions_->LogAndApply(cfd, mutable_cf_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
superversion_to_free.reset(InstallSuperVersionAndScheduleWork(
|
||||
cfd, new_superversion.release(), mutable_cf_options));
|
||||
InstallSuperVersionAndScheduleWork(cfd, &sv_context, mutable_cf_options);
|
||||
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log, "[%s] LogAndApply: %s\n",
|
||||
cfd->GetName().c_str(), status.ToString().data());
|
||||
@@ -770,6 +786,7 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
}
|
||||
}
|
||||
|
||||
sv_context.Clean();
|
||||
refitting_level_ = false;
|
||||
|
||||
return status;
|
||||
@@ -794,7 +811,13 @@ int DBImpl::Level0StopWriteTrigger(ColumnFamilyHandle* column_family) {
|
||||
Status DBImpl::Flush(const FlushOptions& flush_options,
|
||||
ColumnFamilyHandle* column_family) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
return FlushMemTable(cfh->cfd(), flush_options);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "[%s] Manual flush start.",
|
||||
cfh->GetName().c_str());
|
||||
Status s = FlushMemTable(cfh->cfd(), flush_options);
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[%s] Manual flush finished, status: %s\n",
|
||||
cfh->GetName().c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
@@ -826,7 +849,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
manual.begin = nullptr;
|
||||
} else {
|
||||
begin_storage.SetMaxPossibleForUserKey(*begin);
|
||||
begin_storage.SetMinPossibleForUserKey(*begin);
|
||||
manual.begin = &begin_storage;
|
||||
}
|
||||
if (end == nullptr ||
|
||||
@@ -834,7 +857,7 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
cfd->ioptions()->compaction_style == kCompactionStyleFIFO) {
|
||||
manual.end = nullptr;
|
||||
} else {
|
||||
end_storage.SetMinPossibleForUserKey(*end);
|
||||
end_storage.SetMaxPossibleForUserKey(*end);
|
||||
manual.end = &end_storage;
|
||||
}
|
||||
|
||||
@@ -880,15 +903,15 @@ Status DBImpl::RunManualCompaction(ColumnFamilyData* cfd, int input_level,
|
||||
while (!manual.done) {
|
||||
assert(HasPendingManualCompaction());
|
||||
manual_conflict = false;
|
||||
Compaction* compaction;
|
||||
Compaction* compaction = nullptr;
|
||||
if (ShouldntRunManualCompaction(&manual) || (manual.in_progress == true) ||
|
||||
scheduled ||
|
||||
((manual.manual_end = &manual.tmp_storage1) &&
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), manual.input_level,
|
||||
manual.output_level, manual.output_path_id, manual.begin,
|
||||
manual.end, &manual.manual_end, &manual_conflict)) == nullptr) &&
|
||||
manual_conflict)) {
|
||||
(((manual.manual_end = &manual.tmp_storage1) != nullptr) &&
|
||||
((compaction = manual.cfd->CompactRange(
|
||||
*manual.cfd->GetLatestMutableCFOptions(), manual.input_level,
|
||||
manual.output_level, manual.output_path_id, manual.begin,
|
||||
manual.end, &manual.manual_end, &manual_conflict)) == nullptr &&
|
||||
manual_conflict))) {
|
||||
// exclusive manual compactions should not see a conflict during
|
||||
// CompactRange
|
||||
assert(!exclusive || !manual_conflict);
|
||||
@@ -929,11 +952,13 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
bool writes_stopped) {
|
||||
Status s;
|
||||
uint64_t flush_memtable_id = 0;
|
||||
{
|
||||
WriteContext context;
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
|
||||
if (cfd->imm()->NumNotFlushed() == 0 && cfd->mem()->IsEmpty()) {
|
||||
if (cfd->imm()->NumNotFlushed() == 0 && cfd->mem()->IsEmpty() &&
|
||||
cached_recoverable_state_empty_.load()) {
|
||||
// Nothing to flush
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -943,9 +968,9 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
}
|
||||
|
||||
// SwitchMemtable() will release and reacquire mutex
|
||||
// during execution
|
||||
// SwitchMemtable() will release and reacquire mutex during execution
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
|
||||
if (!writes_stopped) {
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
@@ -960,16 +985,19 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
|
||||
if (s.ok() && flush_options.wait) {
|
||||
// Wait until the compaction completes
|
||||
s = WaitForFlushMemTable(cfd);
|
||||
s = WaitForFlushMemTable(cfd, &flush_memtable_id);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::WaitForFlushMemTable(ColumnFamilyData* cfd) {
|
||||
Status DBImpl::WaitForFlushMemTable(ColumnFamilyData* cfd,
|
||||
const uint64_t* flush_memtable_id) {
|
||||
Status s;
|
||||
// Wait until the compaction completes
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (cfd->imm()->NumNotFlushed() > 0 && bg_error_.ok()) {
|
||||
while (cfd->imm()->NumNotFlushed() > 0 && bg_error_.ok() &&
|
||||
(flush_memtable_id == nullptr ||
|
||||
cfd->imm()->GetEarliestMemTableID() <= *flush_memtable_id)) {
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return Status::ShutdownInProgress();
|
||||
}
|
||||
@@ -1044,6 +1072,7 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
// only manual compactions are allowed to run. don't schedule automatic
|
||||
// compactions
|
||||
TEST_SYNC_POINT("DBImpl::MaybeScheduleFlushOrCompaction:Conflict");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1288,8 +1317,10 @@ void DBImpl::BackgroundCallFlush() {
|
||||
// created. Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallFlush:FilesFound");
|
||||
// Have to flush the info logs before bg_flush_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
// released, the deconstructor of DB can kick in and destroy all the
|
||||
@@ -1369,7 +1400,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress());
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
if (job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
if (job_context.HaveSomethingToClean() ||
|
||||
job_context.HaveSomethingToDelete() || !log_buffer.IsEmpty()) {
|
||||
mutex_.Unlock();
|
||||
// Have to flush the info logs before bg_compaction_scheduled_--
|
||||
// because if bg_flush_scheduled_ becomes 0 and the lock is
|
||||
@@ -1488,7 +1520,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
: m->manual_end->DebugString().c_str()));
|
||||
}
|
||||
} else if (!is_prepicked && !compaction_queue_.empty()) {
|
||||
if (HaveManualCompaction(compaction_queue_.front())) {
|
||||
if (HasExclusiveManualCompaction()) {
|
||||
// Can't compact right now, but try again later
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCompaction()::Conflict");
|
||||
|
||||
@@ -1570,8 +1602,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
status = versions_->LogAndApply(c->column_family_data(),
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
c->column_family_data(), job_context, *c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
ROCKS_LOG_BUFFER(log_buffer, "[%s] Deleted %d files\n",
|
||||
c->column_family_data()->GetName().c_str(),
|
||||
c->num_input_files(0));
|
||||
@@ -1616,8 +1649,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
*c->mutable_cf_options(), c->edit(),
|
||||
&mutex_, directories_.GetDbDir());
|
||||
// Use latest MutableCFOptions
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
c->column_family_data(), job_context, *c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
c->column_family_data()->internal_stats()->IncBytesMoved(c->output_level(),
|
||||
@@ -1662,21 +1696,26 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca, Env::Priority::BOTTOM,
|
||||
this, &DBImpl::UnscheduleCallback);
|
||||
} else {
|
||||
int output_level __attribute__((unused)) = c->output_level();
|
||||
int output_level __attribute__((unused));
|
||||
output_level = c->output_level();
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::BackgroundCompaction:NonTrivial",
|
||||
&output_level);
|
||||
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
assert(is_snapshot_supported_ || snapshots_.empty());
|
||||
CompactionJob compaction_job(
|
||||
job_context->job_id, c.get(), immutable_db_options_, env_options_,
|
||||
versions_.get(), &shutting_down_, log_buffer, directories_.GetDbDir(),
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
env_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
directories_.GetDataDir(c->output_path_id()), stats_, &mutex_,
|
||||
&bg_error_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
table_cache_, &event_logger_,
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats);
|
||||
@@ -1689,8 +1728,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
|
||||
status = compaction_job.Install(*c->mutable_cf_options());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
c->column_family_data(), job_context, *c->mutable_cf_options());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
c->column_family_data(), &job_context->superversion_context,
|
||||
*c->mutable_cf_options());
|
||||
}
|
||||
*made_progress = true;
|
||||
}
|
||||
@@ -1856,30 +1896,21 @@ bool DBImpl::MCOverlap(ManualCompactionState* m, ManualCompactionState* m1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// JobContext gets created and destructed outside of the lock --
|
||||
// SuperVersionContext gets created and destructed outside of the lock --
|
||||
// we
|
||||
// use this convinently to:
|
||||
// * malloc one SuperVersion() outside of the lock -- new_superversion
|
||||
// * delete SuperVersion()s outside of the lock -- superversions_to_free
|
||||
//
|
||||
// However, if InstallSuperVersionAndScheduleWork() gets called twice with the
|
||||
// same job_context, we can't reuse the SuperVersion() that got
|
||||
// same sv_context, we can't reuse the SuperVersion() that got
|
||||
// malloced because
|
||||
// first call already used it. In that rare case, we take a hit and create a
|
||||
// new SuperVersion() inside of the mutex. We do similar thing
|
||||
// for superversion_to_free
|
||||
void DBImpl::InstallSuperVersionAndScheduleWorkWrapper(
|
||||
ColumnFamilyData* cfd, JobContext* job_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
mutex_.AssertHeld();
|
||||
SuperVersion* old_superversion = InstallSuperVersionAndScheduleWork(
|
||||
cfd, job_context->new_superversion, mutable_cf_options);
|
||||
job_context->new_superversion = nullptr;
|
||||
job_context->superversions_to_free.push_back(old_superversion);
|
||||
}
|
||||
|
||||
SuperVersion* DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersion* new_sv,
|
||||
void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
ColumnFamilyData* cfd, SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
@@ -1891,8 +1922,11 @@ SuperVersion* DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
old_sv->mutable_cf_options.max_write_buffer_number;
|
||||
}
|
||||
|
||||
auto* old = cfd->InstallSuperVersion(
|
||||
new_sv ? new_sv : new SuperVersion(), &mutex_, mutable_cf_options);
|
||||
if (sv_context->new_superversion == nullptr) {
|
||||
sv_context->NewSuperVersion();
|
||||
}
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_,
|
||||
mutable_cf_options);
|
||||
|
||||
// Whenever we install new SuperVersion, we might need to issue new flushes or
|
||||
// compactions.
|
||||
@@ -1905,6 +1939,14 @@ SuperVersion* DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
max_total_in_memory_state_ - old_memtable_size +
|
||||
mutable_cf_options.write_buffer_size *
|
||||
mutable_cf_options.max_write_buffer_number;
|
||||
return old;
|
||||
}
|
||||
|
||||
void DBImpl::SetSnapshotChecker(SnapshotChecker* snapshot_checker) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
// snapshot_checker_ should only set once. If we need to set it multiple
|
||||
// times, we need to make sure the old one is not deleted while it is still
|
||||
// using by a compaction job.
|
||||
assert(!snapshot_checker_);
|
||||
snapshot_checker_.reset(snapshot_checker);
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
+14
-2
@@ -19,10 +19,10 @@ uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_HandleWALFull() {
|
||||
void DBImpl::TEST_SwitchWAL() {
|
||||
WriteContext write_context;
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
HandleWALFull(&write_context);
|
||||
SwitchWAL(&write_context);
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
@@ -60,6 +60,10 @@ uint64_t DBImpl::TEST_Current_Manifest_FileNo() {
|
||||
return versions_->manifest_file_number();
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_Current_Next_FileNo() {
|
||||
return versions_->current_next_file_number();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_CompactRange(int level, const Slice* begin,
|
||||
const Slice* end,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -205,5 +209,13 @@ int DBImpl::TEST_BGFlushesAllowed() const {
|
||||
return GetBGJobLimits().max_flushes;
|
||||
}
|
||||
|
||||
SequenceNumber DBImpl::TEST_GetLastVisibleSequence() const {
|
||||
if (last_seq_same_as_publish_seq_) {
|
||||
return versions_->LastSequence();
|
||||
} else {
|
||||
return versions_->LastAllocatedSequence();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
|
||||
@@ -30,10 +30,10 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
auto cfd = cfh->cfd();
|
||||
InternalKey start_key, end_key;
|
||||
if (begin != nullptr) {
|
||||
start_key.SetMaxPossibleForUserKey(*begin);
|
||||
start_key.SetMinPossibleForUserKey(*begin);
|
||||
}
|
||||
if (end != nullptr) {
|
||||
end_key.SetMinPossibleForUserKey(*end);
|
||||
end_key.SetMaxPossibleForUserKey(*end);
|
||||
}
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -138,8 +138,9 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, &mutex_, directories_.GetDbDir());
|
||||
if (status.ok()) {
|
||||
InstallSuperVersionAndScheduleWorkWrapper(
|
||||
cfd, &job_context, *cfd->GetLatestMutableCFOptions());
|
||||
InstallSuperVersionAndScheduleWork(
|
||||
cfd, &job_context.superversion_context,
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
+26
-17
@@ -48,6 +48,7 @@ uint64_t DBImpl::FindMinPrepLogReferencedByMemTable() {
|
||||
return min_log;
|
||||
}
|
||||
|
||||
// TODO(myabandeh): Avoid using locks
|
||||
void DBImpl::MarkLogAsHavingPrepSectionFlushed(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
@@ -56,6 +57,7 @@ void DBImpl::MarkLogAsHavingPrepSectionFlushed(uint64_t log) {
|
||||
it->second += 1;
|
||||
}
|
||||
|
||||
// TODO(myabandeh): Avoid using locks
|
||||
void DBImpl::MarkLogAsContainingPrepSection(uint64_t log) {
|
||||
assert(log != 0);
|
||||
std::lock_guard<std::mutex> lock(prep_heap_mutex_);
|
||||
@@ -236,11 +238,11 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
while (alive_log_files_.begin()->number < min_log_number) {
|
||||
auto& earliest = *alive_log_files_.begin();
|
||||
if (immutable_db_options_.recycle_log_file_num >
|
||||
log_recycle_files.size()) {
|
||||
log_recycle_files_.size()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"adding log %" PRIu64 " to recycle list\n",
|
||||
earliest.number);
|
||||
log_recycle_files.push_back(earliest.number);
|
||||
log_recycle_files_.push_back(earliest.number);
|
||||
} else {
|
||||
job_context->log_delete_files.push_back(earliest.number);
|
||||
}
|
||||
@@ -250,11 +252,11 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
}
|
||||
job_context->size_log_to_delete += earliest.size;
|
||||
total_log_size_ -= earliest.size;
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
alive_log_files_.pop_front();
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
// Current log should always stay alive since it can't have
|
||||
@@ -281,8 +283,11 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// We're just cleaning up for DB::Write().
|
||||
assert(job_context->logs_to_free.empty());
|
||||
job_context->logs_to_free = logs_to_free_;
|
||||
job_context->log_recycle_files.assign(log_recycle_files.begin(),
|
||||
log_recycle_files.end());
|
||||
job_context->log_recycle_files.assign(log_recycle_files_.begin(),
|
||||
log_recycle_files_.end());
|
||||
if (job_context->HaveSomethingToDelete()) {
|
||||
++pending_purge_obsolete_files_;
|
||||
}
|
||||
logs_to_free_.clear();
|
||||
}
|
||||
|
||||
@@ -300,9 +305,10 @@ bool CompareCandidateFile(const JobContext::CandidateFileInfo& first,
|
||||
}; // namespace
|
||||
|
||||
// Delete obsolete files and log status and information of file deletion
|
||||
void DBImpl::DeleteObsoleteFileImpl(Status file_deletion_status, int job_id,
|
||||
const std::string& fname, FileType type,
|
||||
uint64_t number, uint32_t path_id) {
|
||||
void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
FileType type, uint64_t number,
|
||||
uint32_t path_id) {
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile) {
|
||||
file_deletion_status =
|
||||
DeleteSSTFile(&immutable_db_options_, fname, path_id);
|
||||
@@ -339,15 +345,12 @@ void DBImpl::DeleteObsoleteFileImpl(Status file_deletion_status, int job_id,
|
||||
// files in sst_delete_files and log_delete_files.
|
||||
// It is not necessary to hold the mutex when invoking this method.
|
||||
void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:Begin");
|
||||
// we'd better have sth to delete
|
||||
assert(state.HaveSomethingToDelete());
|
||||
|
||||
// this checks if FindObsoleteFiles() was run before. If not, don't do
|
||||
// PurgeObsoleteFiles(). If FindObsoleteFiles() was run, we need to also
|
||||
// run PurgeObsoleteFiles(), even if disable_delete_obsolete_files_ is true
|
||||
if (state.manifest_file_number == 0) {
|
||||
return;
|
||||
}
|
||||
// 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.
|
||||
@@ -488,8 +491,7 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
SchedulePendingPurge(fname, type, number, path_id, state.job_id);
|
||||
} else {
|
||||
DeleteObsoleteFileImpl(file_deletion_status, state.job_id, fname, type,
|
||||
number, path_id);
|
||||
DeleteObsoleteFileImpl(state.job_id, fname, type, number, path_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -531,6 +533,13 @@ void DBImpl::PurgeObsoleteFiles(const JobContext& state, bool schedule_only) {
|
||||
wal_manager_.PurgeObsoleteWALFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
--pending_purge_obsolete_files_;
|
||||
assert(pending_purge_obsolete_files_ >= 0);
|
||||
if (pending_purge_obsolete_files_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::PurgeObsoleteFiles:End");
|
||||
}
|
||||
|
||||
void DBImpl::DeleteObsoleteFiles() {
|
||||
|
||||
+71
-17
@@ -124,6 +124,17 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src) {
|
||||
result.avoid_flush_during_recovery = false;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// When the DB is stopped, it's possible that there are some .trash files that
|
||||
// were not deleted yet, when we open the DB we will find these .trash files
|
||||
// and schedule them to be deleted (or delete immediately if SstFileManager
|
||||
// was not used)
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(result.sst_file_manager.get());
|
||||
for (size_t i = 0; i < result.db_paths.size(); i++) {
|
||||
DeleteScheduler::CleanupDirectory(result.env, sfm, result.db_paths[i].path);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -493,11 +504,12 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
bool stop_replay_by_wal_filter = false;
|
||||
bool stop_replay_for_corruption = false;
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_log_number = kMaxSequenceNumber;
|
||||
for (auto log_number : log_numbers) {
|
||||
// The previous incarnation may not have written any MANIFEST
|
||||
// records after allocating this log number. So we manually
|
||||
// update the file number allocation counter in VersionSet.
|
||||
versions_->MarkFileNumberUsedDuringRecovery(log_number);
|
||||
versions_->MarkFileNumberUsed(log_number);
|
||||
// Open the log file
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, log_number);
|
||||
|
||||
@@ -674,7 +686,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(), &flush_scheduler_, true,
|
||||
log_number, this, false /* concurrent_memtable_writes */,
|
||||
next_sequence, &has_valid_writes);
|
||||
next_sequence, &has_valid_writes, seq_per_batch_);
|
||||
MaybeIgnoreError(&status);
|
||||
if (!status.ok()) {
|
||||
// We are treating this as a failure while reading since we read valid
|
||||
@@ -711,6 +723,12 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
if (status.IsNotSupported()) {
|
||||
// We should not treat NotSupported as corruption. It is rather a clear
|
||||
// sign that we are processing a WAL that is produced by an incompatible
|
||||
// version of the code.
|
||||
return status;
|
||||
}
|
||||
if (immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords) {
|
||||
// We should ignore all errors unconditionally
|
||||
@@ -720,6 +738,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// We should ignore the error but not continue replaying
|
||||
status = Status::OK();
|
||||
stop_replay_for_corruption = true;
|
||||
corrupted_log_number = log_number;
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Point in time recovered to log #%" PRIu64
|
||||
" seq #%" PRIu64,
|
||||
@@ -737,10 +756,30 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
auto last_sequence = *next_sequence - 1;
|
||||
if ((*next_sequence != kMaxSequenceNumber) &&
|
||||
(versions_->LastSequence() <= last_sequence)) {
|
||||
versions_->SetLastToBeWrittenSequence(last_sequence);
|
||||
versions_->SetLastAllocatedSequence(last_sequence);
|
||||
versions_->SetLastPublishedSequence(last_sequence);
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
}
|
||||
// Compare the corrupted log number to all columnfamily's current log number.
|
||||
// Abort Open() if any column family's log number is greater than
|
||||
// the corrupted log number, which means CF contains data beyond the point of
|
||||
// corruption. This could during PIT recovery when the WAL is corrupted and
|
||||
// some (but not all) CFs are flushed
|
||||
if (stop_replay_for_corruption == true &&
|
||||
(immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kPointInTimeRecovery ||
|
||||
immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords)) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->GetLogNumber() > corrupted_log_number) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Column family inconsistency: SST file contains data"
|
||||
" beyond the point of corruption.");
|
||||
return Status::Corruption("SST file is ahead of WALs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// True if there's any data in the WALs; if not, we can skip re-processing
|
||||
// them later
|
||||
@@ -796,7 +835,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// not actually used. that is because VersionSet assumes
|
||||
// VersionSet::next_file_number_ always to be strictly greater than any
|
||||
// log number
|
||||
versions_->MarkFileNumberUsedDuringRecovery(max_log_number + 1);
|
||||
versions_->MarkFileNumberUsed(max_log_number + 1);
|
||||
status = versions_->LogAndApply(
|
||||
cfd, *cfd->GetLatestMutableCFOptions(), edit, &mutex_);
|
||||
if (!status.ok()) {
|
||||
@@ -809,13 +848,13 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
if (data_seen && !flushed) {
|
||||
// Mark these as alive so they'll be considered for deletion later by
|
||||
// FindObsoleteFiles()
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
for (auto log_number : log_numbers) {
|
||||
alive_log_files_.push_back(LogFileNumberSize(log_number));
|
||||
}
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -857,26 +896,28 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
{
|
||||
auto write_hint = cfd->CalculateSSTWriteHint(0);
|
||||
mutex_.Unlock();
|
||||
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
std::vector<SequenceNumber> snapshot_seqs =
|
||||
snapshots_.GetAll(&earliest_write_conflict_snapshot);
|
||||
|
||||
EnvOptions optimized_env_options =
|
||||
env_->OptimizeForCompactionTableWrite(env_options_, immutable_db_options_);
|
||||
auto snapshot_checker = snapshot_checker_.get();
|
||||
if (use_custom_gc_ && snapshot_checker == nullptr) {
|
||||
snapshot_checker = DisableGCSnapshotChecker::Instance();
|
||||
}
|
||||
s = BuildTable(
|
||||
dbname_, env_, *cfd->ioptions(), mutable_cf_options,
|
||||
optimized_env_options, cfd->table_cache(), iter.get(),
|
||||
env_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
std::unique_ptr<InternalIterator>(mem->NewRangeTombstoneIterator(ro)),
|
||||
&meta, cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
cfd->ioptions()->compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery,
|
||||
&event_logger_, job_id, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time);
|
||||
-1 /* level */, current_time, write_hint);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
@@ -929,6 +970,15 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
|
||||
Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr) {
|
||||
const bool seq_per_batch = true;
|
||||
return DBImpl::Open(db_options, dbname, column_families, handles, dbptr,
|
||||
!seq_per_batch);
|
||||
}
|
||||
|
||||
Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
const bool seq_per_batch) {
|
||||
Status s = SanitizeOptionsByTable(db_options, column_families);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -948,7 +998,7 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
std::max(max_write_buffer_size, cf.options.write_buffer_size);
|
||||
}
|
||||
|
||||
DBImpl* impl = new DBImpl(db_options, dbname);
|
||||
DBImpl* impl = new DBImpl(db_options, dbname, seq_per_batch);
|
||||
s = impl->env_->CreateDirIfMissing(impl->immutable_db_options_.wal_dir);
|
||||
if (s.ok()) {
|
||||
for (auto db_path : impl->immutable_db_options_.db_paths) {
|
||||
@@ -970,6 +1020,7 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
return s;
|
||||
}
|
||||
impl->mutex_.Lock();
|
||||
auto write_hint = impl->CalculateWALWriteHint();
|
||||
// Handles create_if_missing, error_if_exists
|
||||
s = impl->Recover(column_families);
|
||||
if (s.ok()) {
|
||||
@@ -985,6 +1036,7 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
LogFileName(impl->immutable_db_options_.wal_dir, new_log_number),
|
||||
&lfile, opt_env_options);
|
||||
if (s.ok()) {
|
||||
lfile->SetWriteLifeTimeHint(write_hint);
|
||||
lfile->SetPreallocationBlockSize(
|
||||
impl->GetWalPreallocateBlockSize(max_write_buffer_size));
|
||||
{
|
||||
@@ -1027,16 +1079,18 @@ Status DB::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
delete impl->InstallSuperVersionAndScheduleWork(
|
||||
cfd, nullptr, *cfd->GetLatestMutableCFOptions());
|
||||
impl->InstallSuperVersionAndScheduleWork(
|
||||
cfd, &sv_context, *cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
if (impl->concurrent_prepare_) {
|
||||
sv_context.Clean();
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Lock();
|
||||
}
|
||||
impl->alive_log_files_.push_back(
|
||||
DBImpl::LogFileNumberSize(impl->logfile_number_));
|
||||
if (impl->concurrent_prepare_) {
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Unlock();
|
||||
}
|
||||
impl->DeleteObsoleteFiles();
|
||||
|
||||
@@ -57,6 +57,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
auto cfd = cfh->cfd();
|
||||
SuperVersion* super_version = cfd->GetSuperVersion()->Ref();
|
||||
SequenceNumber latest_snapshot = versions_->LastSequence();
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(),
|
||||
(read_options.snapshot != nullptr
|
||||
@@ -64,7 +65,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
->number_
|
||||
: latest_snapshot),
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number);
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter =
|
||||
NewInternalIterator(read_options, cfd, super_version, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator());
|
||||
@@ -76,6 +77,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<ColumnFamilyHandle*>& column_families,
|
||||
std::vector<Iterator*>* iterators) {
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (iterators == nullptr) {
|
||||
return Status::InvalidArgument("iterators not allowed to be nullptr");
|
||||
}
|
||||
@@ -93,7 +95,7 @@ Status DBImplReadOnly::NewIterators(
|
||||
->number_
|
||||
: latest_snapshot),
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number);
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter =
|
||||
NewInternalIterator(read_options, cfd, sv, db_iter->GetArena(),
|
||||
db_iter->GetRangeDelAggregator());
|
||||
@@ -140,6 +142,7 @@ Status DB::OpenForReadOnly(
|
||||
*dbptr = nullptr;
|
||||
handles->clear();
|
||||
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
DBImplReadOnly* impl = new DBImplReadOnly(db_options, dbname);
|
||||
impl->mutex_.Lock();
|
||||
Status s = impl->Recover(column_families, true /* read only */,
|
||||
@@ -158,10 +161,12 @@ Status DB::OpenForReadOnly(
|
||||
}
|
||||
if (s.ok()) {
|
||||
for (auto cfd : *impl->versions_->GetColumnFamilySet()) {
|
||||
delete cfd->InstallSuperVersion(new SuperVersion(), &impl->mutex_);
|
||||
sv_context.NewSuperVersion();
|
||||
cfd->InstallSuperVersion(&sv_context, &impl->mutex_);
|
||||
}
|
||||
}
|
||||
impl->mutex_.Unlock();
|
||||
sv_context.Clean();
|
||||
if (s.ok()) {
|
||||
*dbptr = impl;
|
||||
for (auto* h : *handles) {
|
||||
|
||||
+206
-63
@@ -57,17 +57,31 @@ Status DBImpl::WriteWithCallback(const WriteOptions& write_options,
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// The main write queue. This is the only write queue that updates LastSequence.
|
||||
// When using one write queue, the same sequence also indicates the last
|
||||
// published sequence.
|
||||
Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
bool disable_memtable, uint64_t* seq_used) {
|
||||
bool disable_memtable, uint64_t* seq_used,
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
if (my_batch == nullptr) {
|
||||
return Status::Corruption("Batch is nullptr!");
|
||||
}
|
||||
if (concurrent_prepare_ && immutable_db_options_.enable_pipelined_write) {
|
||||
if (write_options.sync && write_options.disableWAL) {
|
||||
return Status::InvalidArgument("Sync writes has to enable WAL.");
|
||||
}
|
||||
if (two_write_queues_ && immutable_db_options_.enable_pipelined_write) {
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with concurrent prepares");
|
||||
}
|
||||
if (seq_per_batch_ && immutable_db_options_.enable_pipelined_write) {
|
||||
return Status::NotSupported(
|
||||
"pipelined_writes is not compatible with seq_per_batch");
|
||||
}
|
||||
// Otherwise IsLatestPersistentState optimization does not make sense
|
||||
assert(!WriteBatchInternal::IsLatestPersistentState(my_batch) ||
|
||||
disable_memtable);
|
||||
|
||||
Status status;
|
||||
if (write_options.low_pri) {
|
||||
@@ -77,9 +91,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
}
|
||||
|
||||
if (concurrent_prepare_ && disable_memtable) {
|
||||
if (two_write_queues_ && disable_memtable) {
|
||||
return WriteImplWALOnly(write_options, my_batch, callback, log_used,
|
||||
log_ref, seq_used);
|
||||
log_ref, seq_used, pre_release_callback);
|
||||
}
|
||||
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
@@ -89,7 +103,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable);
|
||||
disable_memtable, pre_release_callback);
|
||||
|
||||
if (!write_options.disableWAL) {
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
@@ -108,11 +122,22 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/, this,
|
||||
true /*concurrent_memtable_writes*/);
|
||||
true /*concurrent_memtable_writes*/, seq_per_batch_);
|
||||
}
|
||||
|
||||
if (write_thread_.CompleteParallelMemTableWriter(&w)) {
|
||||
// we're responsible for exit batch group
|
||||
for (auto* writer : *(w.write_group)) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(myabandeh): propagate status to write_group
|
||||
auto last_sequence = w.write_group->last_sequence;
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
@@ -144,15 +169,15 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
WriteThread::WriteGroup write_group;
|
||||
bool in_parallel_group = false;
|
||||
uint64_t last_sequence = kMaxSequenceNumber;
|
||||
if (!concurrent_prepare_) {
|
||||
if (!two_write_queues_) {
|
||||
last_sequence = versions_->LastSequence();
|
||||
}
|
||||
|
||||
mutex_.Lock();
|
||||
|
||||
bool need_log_sync = !write_options.disableWAL && write_options.sync;
|
||||
bool need_log_sync = write_options.sync;
|
||||
bool need_log_dir_sync = need_log_sync && !log_dir_synced_;
|
||||
if (!concurrent_prepare_ || !disable_memtable) {
|
||||
if (!two_write_queues_ || !disable_memtable) {
|
||||
// With concurrent writes we do preprocess only in the write thread that
|
||||
// also does write to memtable to avoid sync issue on shared data structure
|
||||
// with the other thread
|
||||
@@ -184,10 +209,12 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
// more than once to a particular key.
|
||||
bool parallel = immutable_db_options_.allow_concurrent_memtable_write &&
|
||||
write_group.size > 1;
|
||||
int total_count = 0;
|
||||
size_t total_count = 0;
|
||||
size_t valid_batches = 0;
|
||||
uint64_t total_byte_size = 0;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
valid_batches++;
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
total_count += WriteBatchInternal::Count(writer->batch);
|
||||
parallel = parallel && !writer->batch->HasMerge();
|
||||
@@ -197,8 +224,15 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
total_byte_size, WriteBatchInternal::ByteSize(writer->batch));
|
||||
}
|
||||
}
|
||||
// Note about seq_per_batch_: either disableWAL is set for the entire write
|
||||
// group or not. In either case we inc seq for each write batch with no
|
||||
// failed callback. This means that there could be a batch with
|
||||
// disalbe_memtable in between; although we do not write this batch to
|
||||
// memtable it still consumes a seq. Otherwise, if !seq_per_batch_, we inc
|
||||
// the seq per valid written key to mem.
|
||||
size_t seq_inc = seq_per_batch_ ? valid_batches : total_count;
|
||||
|
||||
const bool concurrent_update = concurrent_prepare_;
|
||||
const bool concurrent_update = two_write_queues_;
|
||||
// Update stats while we are an exclusive group leader, so we know
|
||||
// that nobody else can be writing to these particular stats.
|
||||
// We're optimistic, updating the stats before we successfully
|
||||
@@ -226,7 +260,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
if (!concurrent_prepare_) {
|
||||
if (!two_write_queues_) {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
status = WriteToWAL(write_group, log_writer, log_used, need_log_sync,
|
||||
@@ -235,32 +269,42 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
} else {
|
||||
if (status.ok() && !write_options.disableWAL) {
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastToBeWrittenSequence is increased inside WriteToWAL under
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
total_count);
|
||||
seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number for memtable writes
|
||||
last_sequence = versions_->FetchAddLastToBeWrittenSequence(total_count);
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
}
|
||||
}
|
||||
assert(last_sequence != kMaxSequenceNumber);
|
||||
const SequenceNumber current_sequence = last_sequence + 1;
|
||||
last_sequence += total_count;
|
||||
last_sequence += seq_inc;
|
||||
|
||||
if (status.ok()) {
|
||||
PERF_TIMER_GUARD(write_memtable_time);
|
||||
|
||||
if (!parallel) {
|
||||
// w.sequence will be set inside InsertInto
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
write_group, current_sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*recovery_log_number*/, this);
|
||||
0 /*recovery_log_number*/, this, parallel, seq_per_batch_);
|
||||
} else {
|
||||
SequenceNumber next_sequence = current_sequence;
|
||||
// Note: the logic for advancing seq here must be consistent with the
|
||||
// logic in WriteBatchInternal::InsertInto(write_group...) as well as
|
||||
// with WriteBatchInternal::InsertInto(write_batch...) that is called on
|
||||
// the merged batch during recovery from the WAL.
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->ShouldWriteToMemtable()) {
|
||||
writer->sequence = next_sequence;
|
||||
if (writer->CallbackFailed()) {
|
||||
continue;
|
||||
}
|
||||
writer->sequence = next_sequence;
|
||||
if (seq_per_batch_) {
|
||||
next_sequence++;
|
||||
} else if (writer->ShouldWriteToMemtable()) {
|
||||
next_sequence += WriteBatchInternal::Count(writer->batch);
|
||||
}
|
||||
}
|
||||
@@ -279,12 +323,12 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
w.status = WriteBatchInternal::InsertInto(
|
||||
&w, w.sequence, &column_family_memtables, &flush_scheduler_,
|
||||
write_options.ignore_missing_column_families, 0 /*log_number*/,
|
||||
this, true /*concurrent_memtable_writes*/);
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
*seq_used = w.sequence;
|
||||
this, true /*concurrent_memtable_writes*/, seq_per_batch_);
|
||||
}
|
||||
}
|
||||
if (seq_used != nullptr) {
|
||||
*seq_used = w.sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
PERF_TIMER_START(write_pre_and_post_process_time);
|
||||
@@ -297,9 +341,9 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
mutex_.Unlock();
|
||||
// Requesting sync with concurrent_prepare_ is expected to be very rare. We
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hance provide a simple implementation that is not necessarily efficient.
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
} else {
|
||||
@@ -316,6 +360,16 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
}
|
||||
if (should_exit_batch_group) {
|
||||
if (status.ok()) {
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
versions_->SetLastSequence(last_sequence);
|
||||
}
|
||||
MemTableInsertStatusCheck(w.status);
|
||||
@@ -385,6 +439,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
RecordTick(stats_, NUMBER_KEYS_WRITTEN, total_count);
|
||||
stats->AddDBStats(InternalStats::BYTES_WRITTEN, total_byte_size);
|
||||
RecordTick(stats_, BYTES_WRITTEN, total_byte_size);
|
||||
MeasureTime(stats_, BYTES_PER_WRITE, total_byte_size);
|
||||
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
@@ -426,7 +481,7 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
memtable_write_group.status = WriteBatchInternal::InsertInto(
|
||||
memtable_write_group, w.sequence, column_family_memtables_.get(),
|
||||
&flush_scheduler_, write_options.ignore_missing_column_families,
|
||||
0 /*log_number*/, this);
|
||||
0 /*log_number*/, this, seq_per_batch_);
|
||||
versions_->SetLastSequence(memtable_write_group.last_sequence);
|
||||
write_thread_.ExitAsMemTableWriter(&w, memtable_write_group);
|
||||
}
|
||||
@@ -454,17 +509,18 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
return w.FinalStatus();
|
||||
}
|
||||
|
||||
// The 2nd write queue. If enabled it will be used only for WAL-only writes.
|
||||
// This is the only queue that updates LastPublishedSequence which is only
|
||||
// applicable in a two-queue setting.
|
||||
Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
WriteBatch* my_batch, WriteCallback* callback,
|
||||
uint64_t* log_used, uint64_t log_ref,
|
||||
uint64_t* seq_used) {
|
||||
uint64_t* seq_used,
|
||||
PreReleaseCallback* pre_release_callback) {
|
||||
Status status;
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
true /* disable_memtable */);
|
||||
if (write_options.disableWAL) {
|
||||
return status;
|
||||
}
|
||||
true /* disable_memtable */, pre_release_callback);
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(env_, immutable_db_options_.statistics.get(), DB_WRITE);
|
||||
|
||||
@@ -518,19 +574,30 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
PERF_TIMER_STOP(write_pre_and_post_process_time);
|
||||
|
||||
PERF_TIMER_GUARD(write_wal_time);
|
||||
// LastToBeWrittenSequence is increased inside WriteToWAL under
|
||||
// LastAllocatedSequence is increased inside WriteToWAL under
|
||||
// wal_write_mutex_ to ensure ordered events in WAL
|
||||
status = ConcurrentWriteToWAL(write_group, log_used, &last_sequence,
|
||||
0 /*total_count*/);
|
||||
size_t seq_inc = seq_per_batch_ ? write_group.size : 0 /*total_count*/;
|
||||
if (!write_options.disableWAL) {
|
||||
status =
|
||||
ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
} else {
|
||||
// Otherwise we inc seq number to do solely the seq allocation
|
||||
last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
}
|
||||
auto curr_seq = last_sequence + 1;
|
||||
for (auto* writer : write_group) {
|
||||
if (writer->CheckCallback(this)) {
|
||||
writer->sequence = curr_seq;
|
||||
curr_seq += WriteBatchInternal::Count(writer->batch);
|
||||
if (writer->CallbackFailed()) {
|
||||
continue;
|
||||
}
|
||||
writer->sequence = curr_seq;
|
||||
if (seq_per_batch_) {
|
||||
curr_seq++;
|
||||
}
|
||||
// else seq advances only by memtable writes
|
||||
}
|
||||
if (status.ok() && write_options.sync) {
|
||||
// Requesting sync with concurrent_prepare_ is expected to be very rare. We
|
||||
assert(!write_options.disableWAL);
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hance provide a simple implementation that is not necessarily efficient.
|
||||
if (manual_wal_flush_) {
|
||||
status = FlushWAL(true);
|
||||
@@ -543,6 +610,18 @@ Status DBImpl::WriteImplWALOnly(const WriteOptions& write_options,
|
||||
if (!w.CallbackFailed()) {
|
||||
WriteCallbackStatusCheck(status);
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (auto* writer : write_group) {
|
||||
if (!writer->CallbackFailed() && writer->pre_release_callback) {
|
||||
assert(writer->sequence != kMaxSequenceNumber);
|
||||
Status ws = writer->pre_release_callback->Callback(writer->sequence);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nonmem_write_thread_.ExitAsBatchGroupLeader(write_group, status);
|
||||
if (status.ok()) {
|
||||
status = w.FinalStatus();
|
||||
@@ -605,7 +684,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() == 1);
|
||||
if (UNLIKELY(status.ok() && !single_column_family_mode_ &&
|
||||
total_log_size_ > GetMaxTotalWalSize())) {
|
||||
status = HandleWALFull(write_context);
|
||||
status = SwitchWAL(write_context);
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && write_buffer_manager_->ShouldFlush())) {
|
||||
@@ -618,7 +697,7 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !bg_error_.ok())) {
|
||||
return bg_error_;
|
||||
status = bg_error_;
|
||||
}
|
||||
|
||||
if (UNLIKELY(status.ok() && !flush_scheduler_.Empty())) {
|
||||
@@ -663,18 +742,24 @@ Status DBImpl::PreprocessWrite(const WriteOptions& write_options,
|
||||
}
|
||||
|
||||
WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal) {
|
||||
WriteBatch* tmp_batch, size_t* write_with_wal,
|
||||
WriteBatch** to_be_cached_state) {
|
||||
assert(write_with_wal != nullptr);
|
||||
assert(tmp_batch != nullptr);
|
||||
assert(*to_be_cached_state == nullptr);
|
||||
WriteBatch* merged_batch = nullptr;
|
||||
*write_with_wal = 0;
|
||||
auto* leader = write_group.leader;
|
||||
if (write_group.size == 1 && leader->ShouldWriteToWAL() &&
|
||||
assert(!leader->disable_wal); // Same holds for all in the batch group
|
||||
if (write_group.size == 1 && !leader->CallbackFailed() &&
|
||||
leader->batch->GetWalTerminationPoint().is_cleared()) {
|
||||
// we simply write the first WriteBatch to WAL if the group only
|
||||
// contains one batch, that batch should be written to the WAL,
|
||||
// and the batch is not wanting to be truncated
|
||||
merged_batch = leader->batch;
|
||||
if (WriteBatchInternal::IsLatestPersistentState(merged_batch)) {
|
||||
*to_be_cached_state = merged_batch;
|
||||
}
|
||||
*write_with_wal = 1;
|
||||
} else {
|
||||
// WAL needs all of the batches flattened into a single batch.
|
||||
@@ -682,9 +767,13 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
// interface
|
||||
merged_batch = tmp_batch;
|
||||
for (auto writer : write_group) {
|
||||
if (writer->ShouldWriteToWAL()) {
|
||||
if (!writer->CallbackFailed()) {
|
||||
WriteBatchInternal::Append(merged_batch, writer->batch,
|
||||
/*WAL_only*/ true);
|
||||
if (WriteBatchInternal::IsLatestPersistentState(writer->batch)) {
|
||||
// We only need to cache the last of such write batch
|
||||
*to_be_cached_state = writer->batch;
|
||||
}
|
||||
(*write_with_wal)++;
|
||||
}
|
||||
}
|
||||
@@ -692,7 +781,7 @@ WriteBatch* DBImpl::MergeBatch(const WriteThread::WriteGroup& write_group,
|
||||
return merged_batch;
|
||||
}
|
||||
|
||||
// When concurrent_prepare_ is disabled, this function is called from the only
|
||||
// When two_write_queues_ is disabled, this function is called from the only
|
||||
// write thread. Otherwise this must be called holding log_write_mutex_.
|
||||
Status DBImpl::WriteToWAL(const WriteBatch& merged_batch,
|
||||
log::Writer* log_writer, uint64_t* log_used,
|
||||
@@ -718,9 +807,12 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
SequenceNumber sequence) {
|
||||
Status status;
|
||||
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
size_t write_with_wal = 0;
|
||||
WriteBatch* merged_batch =
|
||||
MergeBatch(write_group, &tmp_batch_, &write_with_wal);
|
||||
WriteBatch* to_be_cached_state = nullptr;
|
||||
WriteBatch* merged_batch = MergeBatch(write_group, &tmp_batch_,
|
||||
&write_with_wal, &to_be_cached_state);
|
||||
if (merged_batch == write_group.leader->batch) {
|
||||
write_group.leader->log_used = logfile_number_;
|
||||
} else if (write_with_wal > 1) {
|
||||
@@ -733,6 +825,10 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
|
||||
uint64_t log_size;
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
|
||||
if (status.ok() && need_log_sync) {
|
||||
StopWatch sw(env_, stats_, WAL_FILE_SYNC_MICROS);
|
||||
@@ -777,13 +873,16 @@ Status DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
uint64_t* log_used,
|
||||
SequenceNumber* last_sequence,
|
||||
int total_count) {
|
||||
size_t seq_inc) {
|
||||
Status status;
|
||||
|
||||
assert(!write_group.leader->disable_wal);
|
||||
// Same holds for all in the batch group
|
||||
WriteBatch tmp_batch;
|
||||
size_t write_with_wal = 0;
|
||||
WriteBatch* to_be_cached_state = nullptr;
|
||||
WriteBatch* merged_batch =
|
||||
MergeBatch(write_group, &tmp_batch, &write_with_wal);
|
||||
MergeBatch(write_group, &tmp_batch, &write_with_wal, &to_be_cached_state);
|
||||
|
||||
// We need to lock log_write_mutex_ since logs_ and alive_log_files might be
|
||||
// pushed back concurrently
|
||||
@@ -795,13 +894,17 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
writer->log_used = logfile_number_;
|
||||
}
|
||||
}
|
||||
*last_sequence = versions_->FetchAddLastToBeWrittenSequence(total_count);
|
||||
*last_sequence = versions_->FetchAddLastAllocatedSequence(seq_inc);
|
||||
auto sequence = *last_sequence + 1;
|
||||
WriteBatchInternal::SetSequence(merged_batch, sequence);
|
||||
|
||||
log::Writer* log_writer = logs_.back().writer;
|
||||
uint64_t log_size;
|
||||
status = WriteToWAL(*merged_batch, log_writer, log_used, &log_size);
|
||||
if (to_be_cached_state) {
|
||||
cached_recoverable_state_ = *to_be_cached_state;
|
||||
cached_recoverable_state_empty_ = false;
|
||||
}
|
||||
log_write_mutex_.Unlock();
|
||||
|
||||
if (status.ok()) {
|
||||
@@ -816,7 +919,40 @@ Status DBImpl::ConcurrentWriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::HandleWALFull(WriteContext* write_context) {
|
||||
Status DBImpl::WriteRecoverableState() {
|
||||
mutex_.AssertHeld();
|
||||
if (!cached_recoverable_state_empty_) {
|
||||
bool dont_care_bool;
|
||||
SequenceNumber next_seq;
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
WriteBatchInternal::SetSequence(&cached_recoverable_state_, seq + 1);
|
||||
auto status = WriteBatchInternal::InsertInto(
|
||||
&cached_recoverable_state_, column_family_memtables_.get(),
|
||||
&flush_scheduler_, true, 0 /*recovery_log_number*/, this,
|
||||
false /* concurrent_memtable_writes */, &next_seq, &dont_care_bool,
|
||||
seq_per_batch_);
|
||||
auto last_seq = next_seq - 1;
|
||||
if (two_write_queues_) {
|
||||
versions_->FetchAddLastAllocatedSequence(last_seq - seq);
|
||||
}
|
||||
versions_->SetLastSequence(last_seq);
|
||||
versions_->SetLastPublishedSequence(last_seq);
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
if (status.ok()) {
|
||||
cached_recoverable_state_.Clear();
|
||||
cached_recoverable_state_empty_ = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
mutex_.AssertHeld();
|
||||
assert(write_context != nullptr);
|
||||
Status status;
|
||||
@@ -1044,7 +1180,7 @@ void DBImpl::NotifyOnMemTableSealed(ColumnFamilyData* cfd,
|
||||
Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
mutex_.AssertHeld();
|
||||
WriteThread::Writer nonmem_w;
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
// SwitchMemtable is a rare event. To simply the reasoning, we make sure
|
||||
// that there is no concurrent thread writing to WAL.
|
||||
nonmem_write_thread_.EnterUnbatched(&nonmem_w, &mutex_);
|
||||
@@ -1054,6 +1190,13 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
log::Writer* new_log = nullptr;
|
||||
MemTable* new_mem = nullptr;
|
||||
|
||||
// Recoverable state is persisted in WAL. After memtable switch, WAL might
|
||||
// be deleted, so we write the state to memtable to be persisted as well.
|
||||
Status s = WriteRecoverableState();
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// In case of pipelined write is enabled, wait for all pending memtable
|
||||
// writers.
|
||||
if (immutable_db_options_.enable_pipelined_write) {
|
||||
@@ -1063,22 +1206,21 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
// Attempt to switch to a new memtable and trigger flush of old.
|
||||
// Do this without holding the dbmutex lock.
|
||||
assert(versions_->prev_log_number() == 0);
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Lock();
|
||||
}
|
||||
bool creating_new_log = !log_empty_;
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
log_write_mutex_.Unlock();
|
||||
}
|
||||
uint64_t recycle_log_number = 0;
|
||||
if (creating_new_log && immutable_db_options_.recycle_log_file_num &&
|
||||
!log_recycle_files.empty()) {
|
||||
recycle_log_number = log_recycle_files.front();
|
||||
log_recycle_files.pop_front();
|
||||
!log_recycle_files_.empty()) {
|
||||
recycle_log_number = log_recycle_files_.front();
|
||||
log_recycle_files_.pop_front();
|
||||
}
|
||||
uint64_t new_log_number =
|
||||
creating_new_log ? versions_->NewFileNumber() : logfile_number_;
|
||||
SuperVersion* new_superversion = nullptr;
|
||||
const MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
|
||||
// Set memtable_info for memtable sealed callback
|
||||
@@ -1097,8 +1239,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
BuildDBOptions(immutable_db_options_, mutable_db_options_);
|
||||
const auto preallocate_block_size =
|
||||
GetWalPreallocateBlockSize(mutable_cf_options.write_buffer_size);
|
||||
auto write_hint = CalculateWALWriteHint();
|
||||
mutex_.Unlock();
|
||||
Status s;
|
||||
{
|
||||
if (creating_new_log) {
|
||||
EnvOptions opt_env_opt =
|
||||
@@ -1123,6 +1265,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
// use preallocate_block_size instead
|
||||
// of calling GetWalPreallocateBlockSize()
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
lfile->SetWriteLifeTimeHint(write_hint);
|
||||
unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(lfile), opt_env_opt));
|
||||
new_log = new log::Writer(
|
||||
@@ -1134,7 +1277,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
if (s.ok()) {
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
|
||||
new_superversion = new SuperVersion();
|
||||
context->superversion_context.NewSuperVersion();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1154,7 +1297,7 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
assert(creating_new_log);
|
||||
assert(!new_mem);
|
||||
assert(!new_log);
|
||||
if (concurrent_prepare_) {
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
return s;
|
||||
@@ -1192,9 +1335,9 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_);
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
context->superversions_to_free_.push_back(InstallSuperVersionAndScheduleWork(
|
||||
cfd, new_superversion, mutable_cf_options));
|
||||
if (concurrent_prepare_) {
|
||||
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context,
|
||||
mutable_cf_options);
|
||||
if (two_write_queues_) {
|
||||
nonmem_write_thread_.ExitUnbatched(&nonmem_w);
|
||||
}
|
||||
return s;
|
||||
|
||||
+204
-78
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
@@ -47,7 +48,7 @@ static void DumpInternalIter(Iterator* iter) {
|
||||
// combines multiple entries for the same userkey found in the DB
|
||||
// representation into a single entry while accounting for sequence
|
||||
// numbers, deletion markers, overwrites, etc.
|
||||
class DBIter: public Iterator {
|
||||
class DBIter final: public Iterator {
|
||||
public:
|
||||
// The following is grossly complicated. TODO: clean it up
|
||||
// Which direction is the iterator currently moving?
|
||||
@@ -75,6 +76,7 @@ class DBIter: public Iterator {
|
||||
prev_count_ = 0;
|
||||
prev_found_count_ = 0;
|
||||
bytes_read_ = 0;
|
||||
skip_count_ = 0;
|
||||
}
|
||||
|
||||
void BumpGlobalStatistics(Statistics* global_statistics) {
|
||||
@@ -83,6 +85,7 @@ class DBIter: public Iterator {
|
||||
RecordTick(global_statistics, NUMBER_DB_PREV, prev_count_);
|
||||
RecordTick(global_statistics, NUMBER_DB_PREV_FOUND, prev_found_count_);
|
||||
RecordTick(global_statistics, ITER_BYTES_READ, bytes_read_);
|
||||
RecordTick(global_statistics, NUMBER_ITER_SKIP, skip_count_);
|
||||
PERF_COUNTER_ADD(iter_read_bytes, bytes_read_);
|
||||
ResetCounters();
|
||||
}
|
||||
@@ -97,12 +100,15 @@ class DBIter: public Iterator {
|
||||
uint64_t prev_found_count_;
|
||||
// Map to Tickers::ITER_BYTES_READ
|
||||
uint64_t bytes_read_;
|
||||
// Map to Tickers::NUMBER_ITER_SKIP
|
||||
uint64_t skip_count_;
|
||||
};
|
||||
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations, bool allow_blob)
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, bool allow_blob)
|
||||
: arena_mode_(arena_mode),
|
||||
env_(_env),
|
||||
logger_(cf_options.info_log),
|
||||
@@ -114,13 +120,18 @@ class DBIter: public Iterator {
|
||||
valid_(false),
|
||||
current_entry_is_merged_(false),
|
||||
statistics_(cf_options.statistics),
|
||||
num_internal_keys_skipped_(0),
|
||||
iterate_lower_bound_(read_options.iterate_lower_bound),
|
||||
iterate_upper_bound_(read_options.iterate_upper_bound),
|
||||
prefix_same_as_start_(read_options.prefix_same_as_start),
|
||||
pin_thru_lifetime_(read_options.pin_data),
|
||||
total_order_seek_(read_options.total_order_seek),
|
||||
range_del_agg_(cf_options.internal_comparator, s,
|
||||
true /* collapse_deletions */),
|
||||
allow_blob_(allow_blob) {
|
||||
read_callback_(read_callback),
|
||||
allow_blob_(allow_blob),
|
||||
is_blob_(false),
|
||||
start_seqnum_(read_options.iter_start_seqnum) {
|
||||
RecordTick(statistics_, NO_ITERATORS);
|
||||
prefix_extractor_ = cf_options.prefix_extractor;
|
||||
max_skip_ = max_sequential_skip_in_iterations;
|
||||
@@ -137,7 +148,10 @@ class DBIter: public Iterator {
|
||||
if (pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
RecordTick(statistics_, NO_ITERATORS, -1);
|
||||
// Compiler warning issue filed:
|
||||
// https://github.com/facebook/rocksdb/issues/3013
|
||||
RecordTick(statistics_, NO_ITERATORS, uint64_t(-1));
|
||||
ResetInternalKeysSkippedCounter();
|
||||
local_stats_.BumpGlobalStatistics(statistics_);
|
||||
if (!arena_mode_) {
|
||||
delete iter_;
|
||||
@@ -157,7 +171,12 @@ class DBIter: public Iterator {
|
||||
virtual bool Valid() const override { return valid_; }
|
||||
virtual Slice key() const override {
|
||||
assert(valid_);
|
||||
return saved_key_.GetUserKey();
|
||||
if(start_seqnum_ > 0) {
|
||||
return saved_key_.GetInternalKey();
|
||||
} else {
|
||||
return saved_key_.GetUserKey();
|
||||
}
|
||||
|
||||
}
|
||||
virtual Slice value() const override {
|
||||
assert(valid_);
|
||||
@@ -226,6 +245,7 @@ class DBIter: public Iterator {
|
||||
bool ParseKey(ParsedInternalKey* key);
|
||||
void MergeValuesNewToOld();
|
||||
bool TooManyInternalKeysSkipped(bool increment = true);
|
||||
bool IsVisible(SequenceNumber sequence);
|
||||
|
||||
// Temporarily pin the blocks that we encounter until ReleaseTempPinnedData()
|
||||
// is called
|
||||
@@ -252,6 +272,10 @@ class DBIter: public Iterator {
|
||||
}
|
||||
|
||||
inline void ResetInternalKeysSkippedCounter() {
|
||||
local_stats_.skip_count_ += num_internal_keys_skipped_;
|
||||
if (valid_) {
|
||||
local_stats_.skip_count_--;
|
||||
}
|
||||
num_internal_keys_skipped_ = 0;
|
||||
}
|
||||
|
||||
@@ -266,6 +290,10 @@ class DBIter: public Iterator {
|
||||
|
||||
Status status_;
|
||||
IterKey saved_key_;
|
||||
// Reusable internal key data structure. This is only used inside one function
|
||||
// and should not be used across functions. Reusing this object can reduce
|
||||
// overhead of calling construction of the function if creating it each time.
|
||||
ParsedInternalKey ikey_;
|
||||
std::string saved_value_;
|
||||
Slice pinned_value_;
|
||||
Direction direction_;
|
||||
@@ -276,6 +304,7 @@ class DBIter: public Iterator {
|
||||
uint64_t max_skip_;
|
||||
uint64_t max_skippable_internal_keys_;
|
||||
uint64_t num_internal_keys_skipped_;
|
||||
const Slice* iterate_lower_bound_;
|
||||
const Slice* iterate_upper_bound_;
|
||||
IterKey prefix_start_buf_;
|
||||
Slice prefix_start_key_;
|
||||
@@ -289,8 +318,12 @@ class DBIter: public Iterator {
|
||||
RangeDelAggregator range_del_agg_;
|
||||
LocalStatistics local_stats_;
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
ReadCallback* read_callback_;
|
||||
bool allow_blob_;
|
||||
bool is_blob_;
|
||||
// for diff snapshots we want the lower bound on the seqnum;
|
||||
// if this value > 0 iterator will return internal keys
|
||||
SequenceNumber start_seqnum_;
|
||||
|
||||
// No copying allowed
|
||||
DBIter(const DBIter&);
|
||||
@@ -383,22 +416,20 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
is_blob_ = false;
|
||||
|
||||
do {
|
||||
ParsedInternalKey ikey;
|
||||
|
||||
if (!ParseKey(&ikey)) {
|
||||
if (!ParseKey(&ikey_)) {
|
||||
// Skip corrupted keys.
|
||||
iter_->Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
user_comparator_->Compare(ikey.user_key, *iterate_upper_bound_) >= 0) {
|
||||
user_comparator_->Compare(ikey_.user_key, *iterate_upper_bound_) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (prefix_extractor_ && prefix_check &&
|
||||
prefix_extractor_->Transform(ikey.user_key)
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
prefix_extractor_->Transform(ikey_.user_key)
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -406,62 +437,91 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ikey.sequence <= sequence_) {
|
||||
if (skipping &&
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) <=
|
||||
0) {
|
||||
if (IsVisible(ikey_.sequence)) {
|
||||
if (skipping && user_comparator_->Compare(ikey_.user_key,
|
||||
saved_key_.GetUserKey()) <= 0) {
|
||||
num_skipped++; // skip this entry
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
} else {
|
||||
num_skipped = 0;
|
||||
switch (ikey.type) {
|
||||
switch (ikey_.type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
// if iterartor specified start_seqnum we
|
||||
// 1) return internal key, including the type
|
||||
// 2) return ikey only if ikey.seqnum >= start_seqnum_
|
||||
// not that if deletion seqnum is < start_seqnum_ we
|
||||
// just skip it like in normal iterator.
|
||||
if (start_seqnum_ > 0 && ikey_.sequence >= start_seqnum_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_=true;
|
||||
return;
|
||||
} else {
|
||||
saved_key_.SetUserKey(
|
||||
ikey_.user_key,
|
||||
!pin_thru_lifetime_ || !iter_->IsKeyPinned() /* copy */);
|
||||
skipping = true;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
case kTypeBlobIndex:
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
skipping = true;
|
||||
num_skipped = 0;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else if (ikey.type == kTypeBlobIndex) {
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"rocksdb::blob_db::BlobDB instead.");
|
||||
valid_ = false;
|
||||
} else {
|
||||
is_blob_ = true;
|
||||
if (start_seqnum_ > 0) {
|
||||
// we are taking incremental snapshot here
|
||||
// incremental snapshots aren't supported on DB with range deletes
|
||||
assert(!(
|
||||
(ikey_.type == kTypeBlobIndex) && (start_seqnum_ > 0)
|
||||
));
|
||||
if (ikey_.sequence >= start_seqnum_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
valid_ = true;
|
||||
return;
|
||||
} else {
|
||||
// this key and all previous versions shouldn't be included,
|
||||
// skipping
|
||||
saved_key_.SetUserKey(ikey_.user_key,
|
||||
!pin_thru_lifetime_ || !iter_->IsKeyPinned() /* copy */);
|
||||
skipping = true;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
valid_ = true;
|
||||
return;
|
||||
saved_key_.SetUserKey(
|
||||
ikey_.user_key,
|
||||
!pin_thru_lifetime_ || !iter_->IsKeyPinned() /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey_, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
skipping = true;
|
||||
num_skipped = 0;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"rocksdb::blob_db::BlobDB instead.");
|
||||
valid_ = false;
|
||||
} else {
|
||||
is_blob_ = true;
|
||||
valid_ = true;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
valid_ = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kTypeMerge:
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
ikey_.user_key,
|
||||
!pin_thru_lifetime_ || !iter_->IsKeyPinned() /* copy */);
|
||||
if (range_del_agg_.ShouldDelete(
|
||||
ikey, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
ikey_, RangeDelAggregator::RangePositioningMode::
|
||||
kForwardTraversal)) {
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
skipping = true;
|
||||
@@ -487,12 +547,12 @@ void DBIter::FindNextUserEntryInternal(bool skipping, bool prefix_check) {
|
||||
|
||||
// Here saved_key_ may contain some old key, or the default empty key, or
|
||||
// key assigned by some random other method. We don't care.
|
||||
if (user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) <=
|
||||
if (user_comparator_->Compare(ikey_.user_key, saved_key_.GetUserKey()) <=
|
||||
0) {
|
||||
num_skipped++;
|
||||
} else {
|
||||
saved_key_.SetUserKey(
|
||||
ikey.user_key,
|
||||
ikey_.user_key,
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
skipping = false;
|
||||
num_skipped = 0;
|
||||
@@ -580,6 +640,7 @@ void DBIter::MergeValuesNewToOld() {
|
||||
merge_operator_, ikey.user_key, &val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_, &pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
}
|
||||
// iter_ is positioned after put
|
||||
@@ -617,6 +678,7 @@ void DBIter::MergeValuesNewToOld() {
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
}
|
||||
}
|
||||
@@ -673,7 +735,7 @@ void DBIter::ReverseToBackward() {
|
||||
user_comparator_->Compare(ikey.user_key, saved_key_.GetUserKey()) >
|
||||
0) {
|
||||
assert(ikey.sequence != kMaxSequenceNumber);
|
||||
if (ikey.sequence > sequence_) {
|
||||
if (!IsVisible(ikey.sequence)) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
@@ -708,6 +770,22 @@ void DBIter::PrevInternal() {
|
||||
ExtractUserKey(iter_->key()),
|
||||
!iter_->IsKeyPinned() || !pin_thru_lifetime_ /* copy */);
|
||||
|
||||
if (prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetUserKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
// Current key does not have the same prefix as start
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (iterate_lower_bound_ != nullptr &&
|
||||
user_comparator_->Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) < 0) {
|
||||
// We've iterated earlier than the user-specified lower bound.
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (FindValueForCurrentKey()) {
|
||||
if (!iter_->Valid()) {
|
||||
return;
|
||||
@@ -716,11 +794,6 @@ void DBIter::PrevInternal() {
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
FindPrevUserKey();
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetUserKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -761,7 +834,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
size_t num_skipped = 0;
|
||||
while (iter_->Valid() && ikey.sequence <= sequence_ &&
|
||||
while (iter_->Valid() && IsVisible(ikey.sequence) &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey())) {
|
||||
if (TooManyInternalKeysSkipped()) {
|
||||
return false;
|
||||
@@ -875,8 +948,10 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
if (s.ok()) {
|
||||
valid_ = true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
}
|
||||
return true;
|
||||
@@ -897,6 +972,17 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// assume there is at least one parseable key for this user key
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kForward);
|
||||
assert(iter_->Valid());
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
|
||||
// In case read_callback presents, the value we seek to may not be visible.
|
||||
// Seek for the next value that's visible.
|
||||
while (!IsVisible(ikey.sequence)) {
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
assert(iter_->Valid());
|
||||
assert(user_comparator_->Equal(ikey.user_key, saved_key_.GetUserKey()));
|
||||
}
|
||||
|
||||
if (ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion ||
|
||||
range_del_agg_.ShouldDelete(
|
||||
@@ -952,8 +1038,10 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
iter_->Seek(last_key);
|
||||
RecordTick(statistics_, NUMBER_OF_RESEEKS_IN_ITERATION);
|
||||
}
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
if (s.ok()) {
|
||||
valid_ = true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
}
|
||||
return true;
|
||||
@@ -964,8 +1052,10 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
&val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_, true);
|
||||
valid_ = true;
|
||||
if (!s.ok()) {
|
||||
if (s.ok()) {
|
||||
valid_ = true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
}
|
||||
return true;
|
||||
@@ -1000,7 +1090,7 @@ void DBIter::FindPrevUserKey() {
|
||||
while (iter_->Valid() &&
|
||||
((cmp = user_comparator_->Compare(ikey.user_key,
|
||||
saved_key_.GetUserKey())) == 0 ||
|
||||
(cmp > 0 && ikey.sequence > sequence_))) {
|
||||
(cmp > 0 && !IsVisible(ikey.sequence)))) {
|
||||
if (TooManyInternalKeysSkipped()) {
|
||||
return;
|
||||
}
|
||||
@@ -1018,7 +1108,7 @@ void DBIter::FindPrevUserKey() {
|
||||
}
|
||||
}
|
||||
assert(ikey.sequence != kMaxSequenceNumber);
|
||||
if (ikey.sequence > sequence_) {
|
||||
if (!IsVisible(ikey.sequence)) {
|
||||
PERF_COUNTER_ADD(internal_recent_skipped_count, 1);
|
||||
} else {
|
||||
PERF_COUNTER_ADD(internal_key_skipped_count, 1);
|
||||
@@ -1040,6 +1130,11 @@ bool DBIter::TooManyInternalKeysSkipped(bool increment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DBIter::IsVisible(SequenceNumber sequence) {
|
||||
return sequence <= sequence_ &&
|
||||
(read_callback_ == nullptr || read_callback_->IsCommitted(sequence));
|
||||
}
|
||||
|
||||
// Skip all unparseable keys
|
||||
void DBIter::FindParseableKey(ParsedInternalKey* ikey, Direction direction) {
|
||||
while (iter_->Valid() && !ParseKey(ikey)) {
|
||||
@@ -1058,6 +1153,13 @@ void DBIter::Seek(const Slice& target) {
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(target, sequence_);
|
||||
|
||||
if (iterate_lower_bound_ != nullptr &&
|
||||
user_comparator_->Compare(saved_key_.GetUserKey(),
|
||||
*iterate_lower_bound_) < 0) {
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(*iterate_lower_bound_, sequence_);
|
||||
}
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->Seek(saved_key_.GetInternalKey());
|
||||
@@ -1076,6 +1178,7 @@ void DBIter::Seek(const Slice& target) {
|
||||
}
|
||||
if (statistics_ != nullptr) {
|
||||
if (valid_) {
|
||||
// Decrement since we don't want to count this key as skipped
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK_FOUND);
|
||||
RecordTick(statistics_, ITER_BYTES_READ, key().size() + value().size());
|
||||
PERF_COUNTER_ADD(iter_read_bytes, key().size() + value().size());
|
||||
@@ -1100,6 +1203,13 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
saved_key_.SetInternalKey(target, 0 /* sequence_number */,
|
||||
kValueTypeForSeekForPrev);
|
||||
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
user_comparator_->Compare(saved_key_.GetUserKey(),
|
||||
*iterate_upper_bound_) >= 0) {
|
||||
saved_key_.Clear();
|
||||
saved_key_.SetInternalKey(*iterate_upper_bound_, kMaxSequenceNumber);
|
||||
}
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekForPrev(saved_key_.GetInternalKey());
|
||||
@@ -1139,6 +1249,10 @@ void DBIter::SeekToFirst() {
|
||||
if (prefix_extractor_ != nullptr) {
|
||||
max_skip_ = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
if (iterate_lower_bound_ != nullptr) {
|
||||
Seek(*iterate_lower_bound_);
|
||||
return;
|
||||
}
|
||||
direction_ = kForward;
|
||||
ReleaseTempPinnedData();
|
||||
ResetInternalKeysSkippedCounter();
|
||||
@@ -1198,7 +1312,8 @@ void DBIter::SeekToLast() {
|
||||
if (!Valid()) {
|
||||
return;
|
||||
} else if (user_comparator_->Equal(*iterate_upper_bound_, key())) {
|
||||
Prev();
|
||||
ReleaseTempPinnedData();
|
||||
PrevInternal();
|
||||
}
|
||||
} else {
|
||||
PrevInternal();
|
||||
@@ -1224,10 +1339,11 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
bool allow_blob) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, read_options, cf_options, user_key_comparator, internal_iter,
|
||||
sequence, false, max_sequential_skip_in_iterations, allow_blob);
|
||||
ReadCallback* read_callback, bool allow_blob) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, read_options, cf_options, user_key_comparator,
|
||||
internal_iter, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, allow_blob);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
@@ -1272,19 +1388,26 @@ void ArenaWrappedDBIter::Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, bool allow_blob) {
|
||||
uint64_t version_number,
|
||||
ReadCallback* read_callback, bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem)
|
||||
DBIter(env, read_options, cf_options, cf_options.user_comparator, nullptr,
|
||||
sequence, true, max_sequential_skip_in_iteration, allow_blob);
|
||||
sequence, true, max_sequential_skip_in_iteration, read_callback,
|
||||
allow_blob);
|
||||
sv_number_ = version_number;
|
||||
allow_refresh_ = allow_refresh;
|
||||
}
|
||||
|
||||
Status ArenaWrappedDBIter::Refresh() {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr) {
|
||||
if (cfd_ == nullptr || db_impl_ == nullptr || !allow_refresh_) {
|
||||
return Status::NotSupported("Creating renew iterator is not allowed.");
|
||||
}
|
||||
assert(db_iter_ != nullptr);
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
@@ -1296,7 +1419,7 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_->mutex());
|
||||
Init(env, read_options_, *(cfd_->ioptions()), latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, allow_blob_);
|
||||
cur_sv_number, read_callback_, allow_blob_, allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
read_options_, cfd_, sv, &arena_, db_iter_->GetRangeDelAggregator());
|
||||
@@ -1312,12 +1435,15 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
DBImpl* db_impl, ColumnFamilyData* cfd, bool allow_blob) {
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, cf_options, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, allow_blob);
|
||||
if (db_impl != nullptr && cfd != nullptr) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd, allow_blob);
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
allow_blob, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(read_options, db_impl, cfd, read_callback,
|
||||
allow_blob);
|
||||
}
|
||||
|
||||
return iter;
|
||||
|
||||
+10
-5
@@ -34,6 +34,7 @@ extern Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob = false);
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
@@ -72,13 +73,15 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
bool allow_blob);
|
||||
ReadCallback* read_callback, bool allow_blob, bool allow_refresh);
|
||||
|
||||
void StoreRefreshInfo(const ReadOptions& read_options, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob) {
|
||||
ColumnFamilyData* cfd, ReadCallback* read_callback,
|
||||
bool allow_blob) {
|
||||
read_options_ = read_options;
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
read_callback_ = read_callback;
|
||||
allow_blob_ = allow_blob;
|
||||
}
|
||||
|
||||
@@ -89,7 +92,9 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
ColumnFamilyData* cfd_ = nullptr;
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool allow_blob_ = false;
|
||||
bool allow_refresh_ = true;
|
||||
};
|
||||
|
||||
// Generate the arena wrapped iterator class.
|
||||
@@ -99,7 +104,7 @@ extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr,
|
||||
bool allow_blob = false);
|
||||
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
} // namespace rocksdb
|
||||
|
||||
+404
-182
File diff suppressed because it is too large
Load Diff
+327
-13
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_iter.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -1081,7 +1082,8 @@ TEST_F(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
ASSERT_EQ("2", it->key().ToString());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
public:
|
||||
enum TestConfig {
|
||||
NORMAL,
|
||||
CLOSE_AND_OPEN,
|
||||
@@ -1089,19 +1091,19 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
FLUSH_EVERY_1000,
|
||||
MAX
|
||||
};
|
||||
DBIteratorTestForPinnedData() : DBIteratorTest() {}
|
||||
void PinnedDataIteratorRandomized(TestConfig run_config) {
|
||||
// Generate Random data
|
||||
Random rnd(301);
|
||||
|
||||
// Generate Random data
|
||||
Random rnd(301);
|
||||
int puts = 100000;
|
||||
int key_pool = static_cast<int>(puts * 0.7);
|
||||
int key_size = 100;
|
||||
int val_size = 1000;
|
||||
int seeks_percentage = 20; // 20% of keys will be used to test seek()
|
||||
int delete_percentage = 20; // 20% of keys will be deleted
|
||||
int merge_percentage = 20; // 20% of keys will be added using Merge()
|
||||
|
||||
int puts = 100000;
|
||||
int key_pool = static_cast<int>(puts * 0.7);
|
||||
int key_size = 100;
|
||||
int val_size = 1000;
|
||||
int seeks_percentage = 20; // 20% of keys will be used to test seek()
|
||||
int delete_percentage = 20; // 20% of keys will be deleted
|
||||
int merge_percentage = 20; // 20% of keys will be added using Merge()
|
||||
|
||||
for (int run_config = 0; run_config < TestConfig::MAX; run_config++) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.use_delta_encoding = false;
|
||||
@@ -1246,7 +1248,24 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
}
|
||||
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedNormal) {
|
||||
PinnedDataIteratorRandomized(TestConfig::NORMAL);
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedCLoseAndOpen) {
|
||||
PinnedDataIteratorRandomized(TestConfig::CLOSE_AND_OPEN);
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData,
|
||||
PinnedDataIteratorRandomizedCompactBeforeRead) {
|
||||
PinnedDataIteratorRandomized(TestConfig::COMPACT_BEFORE_READ);
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedFlush) {
|
||||
PinnedDataIteratorRandomized(TestConfig::FLUSH_EVERY_1000);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -1979,6 +1998,301 @@ TEST_F(DBIteratorTest, Refresh) {
|
||||
iter.reset();
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, CreationFailure) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::NewInternalIterator:StatusCallback", [](void* arg) {
|
||||
*(reinterpret_cast<Status*>(arg)) = Status::Corruption("test status");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, TableFilter) {
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
ASSERT_OK(Put("c", "3"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("d", "4"));
|
||||
ASSERT_OK(Put("e", "5"));
|
||||
ASSERT_OK(Put("f", "6"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
|
||||
// Ensure the table_filter callback is called once for each table.
|
||||
{
|
||||
std::set<uint64_t> unseen{1, 2, 3};
|
||||
ReadOptions opts;
|
||||
opts.table_filter = [&](const TableProperties& props) {
|
||||
auto it = unseen.find(props.num_entries);
|
||||
if (it == unseen.end()) {
|
||||
ADD_FAILURE() << "saw table properties with an unexpected "
|
||||
<< props.num_entries << " entries";
|
||||
} else {
|
||||
unseen.erase(it);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
auto iter = db_->NewIterator(opts);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->1");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "b->2");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->3");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "d->4");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "e->5");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "f->6");
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(unseen.empty());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
// Ensure returning false in the table_filter hides the keys from that table
|
||||
// during iteration.
|
||||
{
|
||||
ReadOptions opts;
|
||||
opts.table_filter = [](const TableProperties& props) {
|
||||
return props.num_entries != 2;
|
||||
};
|
||||
auto iter = db_->NewIterator(opts);
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(IterStatus(iter), "a->1");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "d->4");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "e->5");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "f->6");
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, SkipStatistics) {
|
||||
Options options = CurrentOptions();
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int skip_count = 0;
|
||||
|
||||
// write a bunch of kvs to the database.
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
ASSERT_OK(Put("b", "1"));
|
||||
ASSERT_OK(Put("c", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("d", "1"));
|
||||
ASSERT_OK(Put("e", "1"));
|
||||
ASSERT_OK(Put("f", "1"));
|
||||
ASSERT_OK(Put("a", "2"));
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Delete("d"));
|
||||
ASSERT_OK(Delete("e"));
|
||||
ASSERT_OK(Delete("f"));
|
||||
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
int count = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQ(count, 3);
|
||||
delete iter;
|
||||
skip_count += 8; // 3 deletes + 3 original keys + 2 lower in sequence
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
count = 0;
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_OK(iter->status());
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQ(count, 3);
|
||||
delete iter;
|
||||
skip_count += 8; // Same as above, but in reverse order
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
ASSERT_OK(Put("aa", "1"));
|
||||
ASSERT_OK(Put("ab", "1"));
|
||||
ASSERT_OK(Put("ac", "1"));
|
||||
ASSERT_OK(Put("ad", "1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Delete("ab"));
|
||||
ASSERT_OK(Delete("ac"));
|
||||
ASSERT_OK(Delete("ad"));
|
||||
|
||||
ReadOptions ro;
|
||||
Slice prefix("b");
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
iter = db_->NewIterator(ro);
|
||||
count = 0;
|
||||
for(iter->Seek("aa"); iter->Valid(); iter->Next()) {
|
||||
ASSERT_OK(iter->status());
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQ(count, 1);
|
||||
delete iter;
|
||||
skip_count += 6; // 3 deletes + 3 original keys
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
|
||||
iter = db_->NewIterator(ro);
|
||||
count = 0;
|
||||
for(iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
ASSERT_OK(iter->status());
|
||||
count++;
|
||||
}
|
||||
ASSERT_EQ(count, 2);
|
||||
delete iter;
|
||||
// 3 deletes + 3 original keys + lower sequence of "a"
|
||||
skip_count += 7;
|
||||
ASSERT_EQ(skip_count, TestGetTickerCount(options, NUMBER_ITER_SKIP));
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, ReadCallback) {
|
||||
class TestReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit TestReadCallback(SequenceNumber last_visible_seq)
|
||||
: last_visible_seq_(last_visible_seq) {}
|
||||
|
||||
bool IsCommitted(SequenceNumber seq) override {
|
||||
return seq <= last_visible_seq_;
|
||||
}
|
||||
|
||||
private:
|
||||
SequenceNumber last_visible_seq_;
|
||||
};
|
||||
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
ASSERT_OK(Put("foo", "v3"));
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
ASSERT_OK(Put("z", "vz"));
|
||||
SequenceNumber seq1 = db_->GetLatestSequenceNumber();
|
||||
TestReadCallback callback1(seq1);
|
||||
ASSERT_OK(Put("foo", "v4"));
|
||||
ASSERT_OK(Put("foo", "v5"));
|
||||
ASSERT_OK(Put("bar", "v7"));
|
||||
|
||||
SequenceNumber seq2 = db_->GetLatestSequenceNumber();
|
||||
auto* cfd =
|
||||
reinterpret_cast<ColumnFamilyHandleImpl*>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
// The iterator are suppose to see data before seq1.
|
||||
Iterator* iter =
|
||||
dbfull()->NewIteratorImpl(ReadOptions(), cfd, seq2, &callback1);
|
||||
|
||||
// Seek
|
||||
// The latest value of "foo" before seq1 is "v3"
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key
|
||||
// "foo".
|
||||
iter->Seek("bar");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
|
||||
// Next
|
||||
// Seek to "a"
|
||||
iter->Seek("a");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("va", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key
|
||||
// "foo".
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
|
||||
// Prev
|
||||
// Seek to "z"
|
||||
iter->Seek("z");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("vz", iter->value());
|
||||
// The previous key is "foo", which is visible to the iterator.
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key "a".
|
||||
iter->Prev(); // skipping "bar"
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("a", iter->key());
|
||||
ASSERT_EQ("va", iter->value());
|
||||
|
||||
// SeekForPrev
|
||||
// The previous key is "foo", which is visible to the iterator.
|
||||
iter->SeekForPrev("y");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v3", iter->value());
|
||||
// "bar" is not visible to the iterator. It will move on to the next key "a".
|
||||
iter->SeekForPrev("bar");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("a", iter->key());
|
||||
ASSERT_EQ("va", iter->value());
|
||||
|
||||
delete iter;
|
||||
|
||||
// Prev beyond max_sequential_skip_in_iterations
|
||||
uint64_t num_versions =
|
||||
CurrentOptions().max_sequential_skip_in_iterations + 10;
|
||||
for (uint64_t i = 0; i < num_versions; i++) {
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
}
|
||||
SequenceNumber seq3 = db_->GetLatestSequenceNumber();
|
||||
TestReadCallback callback2(seq3);
|
||||
ASSERT_OK(Put("bar", "v8"));
|
||||
SequenceNumber seq4 = db_->GetLatestSequenceNumber();
|
||||
|
||||
// The iterator is suppose to see data before seq3.
|
||||
iter = dbfull()->NewIteratorImpl(ReadOptions(), cfd, seq4, &callback2);
|
||||
// Seek to "z", which is visible.
|
||||
iter->Seek("z");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("vz", iter->value());
|
||||
// Previous key is "foo" and the last value "v5" is visible.
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("foo", iter->key());
|
||||
ASSERT_EQ("v5", iter->value());
|
||||
// Since the number of values of "bar" is more than
|
||||
// max_sequential_skip_in_iterations, Prev() will ultimately fallback to
|
||||
// seek in forward direction. Here we test the fallback seek is correct.
|
||||
// The last visible value should be (num_versions - 1), as "v8" is not
|
||||
// visible.
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ("bar", iter->key());
|
||||
ASSERT_EQ(ToString(num_versions - 1), iter->value());
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/forward_iterator.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend2.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -18,6 +20,80 @@ class DBMergeOperatorTest : public DBTestBase {
|
||||
DBMergeOperatorTest() : DBTestBase("/db_merge_operator_test") {}
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
class LimitedStringAppendMergeOp : public StringAppendTESTOperator {
|
||||
public:
|
||||
LimitedStringAppendMergeOp(int limit, char delim)
|
||||
: StringAppendTESTOperator(delim), limit_(limit) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DBMergeOperatorTest::LimitedStringAppendMergeOp";
|
||||
}
|
||||
|
||||
bool ShouldMerge(const std::vector<Slice>& operands) const override {
|
||||
if (operands.size() > 0 && limit_ > 0 && operands.size() >= limit_) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator =
|
||||
std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
// All K1 values are in memtable.
|
||||
ASSERT_OK(Merge("k1", "a"));
|
||||
ASSERT_OK(Merge("k1", "b"));
|
||||
ASSERT_OK(Merge("k1", "c"));
|
||||
ASSERT_OK(Merge("k1", "d"));
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k1", &value).ok());
|
||||
// Make sure that only the latest two merge operands are used. If this was
|
||||
// not the case the value would be "a,b,c,d".
|
||||
ASSERT_EQ(value, "c,d");
|
||||
|
||||
// All K2 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Merge("k2", "a"));
|
||||
ASSERT_OK(Merge("k2", "b"));
|
||||
ASSERT_OK(Merge("k2", "c"));
|
||||
ASSERT_OK(Merge("k2", "d"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k2", &value).ok());
|
||||
ASSERT_EQ(value, "c,d");
|
||||
|
||||
// All K3 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "bc"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "de"));
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k3", &value).ok());
|
||||
ASSERT_EQ(value, "cd,de");
|
||||
|
||||
// All K4 values are in different levels
|
||||
ASSERT_OK(Merge("k4", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
ASSERT_OK(Merge("k4", "bc"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(3);
|
||||
ASSERT_OK(Merge("k4", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Merge("k4", "de"));
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k4", &value).ok());
|
||||
ASSERT_EQ(value, "cd,de");
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperatorTest, MergeErrorOnRead) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
@@ -57,16 +133,33 @@ TEST_F(DBMergeOperatorTest, MergeErrorOnIteration) {
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Merge("k1", "corrupted"));
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
VerifyDBFromMap({{"k1", ""}, {"k2", "v2"}}, nullptr, false,
|
||||
{{"k1", Status::Corruption()}});
|
||||
auto* iter = db_->NewIterator(ReadOptions());
|
||||
iter->Seek("k1");
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
delete iter;
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
iter->Seek("k2");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Prev();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
delete iter;
|
||||
VerifyDBInternal({{"k1", "corrupted"}, {"k1", "v1"}, {"k2", "v2"}});
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Merge("k1", "v1"));
|
||||
ASSERT_OK(Put("k2", "v2"));
|
||||
ASSERT_OK(Merge("k2", "corrupted"));
|
||||
VerifyDBFromMap({{"k1", "v1"}, {"k2", ""}}, nullptr, false,
|
||||
{{"k2", Status::Corruption()}});
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
iter->Seek("k1");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_TRUE(iter->status().IsCorruption());
|
||||
delete iter;
|
||||
VerifyDBInternal({{"k1", "v1"}, {"k2", "corrupted"}, {"k2", "v2"}});
|
||||
}
|
||||
|
||||
|
||||
+283
-1
@@ -117,6 +117,149 @@ TEST_F(DBOptionsTest, GetLatestCFOptions) {
|
||||
GetMutableCFOptionsMap(dbfull()->GetOptions(handles_[1])));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBytesPerSync) {
|
||||
const size_t kValueSize = 1024 * 1024; // 1MB
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.bytes_per_sync = 1024 * 1024;
|
||||
options.use_direct_reads = false;
|
||||
options.write_buffer_size = 400 * kValueSize;
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int counter = 0;
|
||||
int low_bytes_per_sync = 0;
|
||||
int i = 0;
|
||||
const std::string kValue(kValueSize, 'v');
|
||||
ASSERT_EQ(options.bytes_per_sync, dbfull()->GetDBOptions().bytes_per_sync);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::RangeSync:0", [&](void* arg) {
|
||||
counter++;
|
||||
});
|
||||
|
||||
WriteOptions write_opts;
|
||||
// should sync approximately 40MB/1MB ~= 40 times.
|
||||
for (i = 0; i < 40; i++) {
|
||||
Put(Key(i), kValue, write_opts);
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
low_bytes_per_sync = counter;
|
||||
ASSERT_GT(low_bytes_per_sync, 35);
|
||||
ASSERT_LT(low_bytes_per_sync, 45);
|
||||
|
||||
counter = 0;
|
||||
// 8388608 = 8 * 1024 * 1024
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"bytes_per_sync", "8388608"}}));
|
||||
ASSERT_EQ(8388608, dbfull()->GetDBOptions().bytes_per_sync);
|
||||
// should sync approximately 40MB*2/8MB ~= 10 times.
|
||||
// data will be 40*2MB because of previous Puts too.
|
||||
for (i = 0; i < 40; i++) {
|
||||
Put(Key(i), kValue, write_opts);
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_GT(counter, 5);
|
||||
ASSERT_LT(counter, 15);
|
||||
|
||||
// Redundant assert. But leaving it here just to get the point across that
|
||||
// low_bytes_per_sync > counter.
|
||||
ASSERT_GT(low_bytes_per_sync, counter);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetWalBytesPerSync) {
|
||||
const size_t kValueSize = 1024 * 1024 * 3;
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.wal_bytes_per_sync = 512;
|
||||
options.write_buffer_size = 100 * kValueSize;
|
||||
options.disable_auto_compactions = true;
|
||||
options.compression = kNoCompression;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(512, dbfull()->GetDBOptions().wal_bytes_per_sync);
|
||||
int counter = 0;
|
||||
int low_bytes_per_sync = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::RangeSync:0", [&](void* arg) {
|
||||
counter++;
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
const std::string kValue(kValueSize, 'v');
|
||||
int i = 0;
|
||||
for (; i < 10; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
// Do not flush. If we flush here, SwitchWAL will reuse old WAL file since its
|
||||
// empty and will not get the new wal_bytes_per_sync value.
|
||||
low_bytes_per_sync = counter;
|
||||
//5242880 = 1024 * 1024 * 5
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"wal_bytes_per_sync", "5242880"}}));
|
||||
ASSERT_EQ(5242880, dbfull()->GetDBOptions().wal_bytes_per_sync);
|
||||
counter = 0;
|
||||
i = 0;
|
||||
for (; i < 10; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
ASSERT_GT(counter, 0);
|
||||
ASSERT_GT(low_bytes_per_sync, 0);
|
||||
ASSERT_GT(low_bytes_per_sync, counter);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, WritableFileMaxBufferSize) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.writable_file_max_buffer_size = 1024 * 1024;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.max_manifest_file_size = 1;
|
||||
options.env = env_;
|
||||
int buffer_size = 1024 * 1024;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(buffer_size,
|
||||
dbfull()->GetDBOptions().writable_file_max_buffer_size);
|
||||
|
||||
std::atomic<int> match_cnt(0);
|
||||
std::atomic<int> unmatch_cnt(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::WritableFileWriter:0", [&](void* arg) {
|
||||
int value = static_cast<int>(reinterpret_cast<uintptr_t>(arg));
|
||||
if (value == buffer_size) {
|
||||
match_cnt++;
|
||||
} else {
|
||||
unmatch_cnt++;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
int i = 0;
|
||||
for (; i < 3; i++) {
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
Flush();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(unmatch_cnt, 0);
|
||||
ASSERT_GE(match_cnt, 11);
|
||||
|
||||
buffer_size = 512 * 1024;
|
||||
match_cnt = 0;
|
||||
unmatch_cnt = 0;
|
||||
ASSERT_OK(
|
||||
dbfull()->SetDBOptions({{"writable_file_max_buffer_size", "524288"}}));
|
||||
ASSERT_EQ(buffer_size,
|
||||
dbfull()->GetDBOptions().writable_file_max_buffer_size);
|
||||
i = 0;
|
||||
for (; i < 3; i++) {
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
Flush();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(unmatch_cnt, 0);
|
||||
ASSERT_GE(match_cnt, 11);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsAndReopen) {
|
||||
Random rnd(1044);
|
||||
auto rand_opts = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
@@ -375,8 +518,13 @@ static void assert_candidate_files_empty(DBImpl* dbfull, const bool empty) {
|
||||
JobContext job_context(0);
|
||||
dbfull->FindObsoleteFiles(&job_context, false);
|
||||
ASSERT_EQ(empty, job_context.full_scan_candidate_files.empty());
|
||||
job_context.Clean();
|
||||
dbfull->TEST_UnlockMutex();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
// fulfill the contract of FindObsoleteFiles by calling PurgeObsoleteFiles
|
||||
// afterwards; otherwise the test may hang on shutdown
|
||||
dbfull->PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, DeleteObsoleteFilesPeriodChange) {
|
||||
@@ -441,6 +589,140 @@ TEST_F(DBOptionsTest, SanitizeDelayedWriteRate) {
|
||||
ASSERT_EQ(31 * 1024 * 1024, dbfull()->GetDBOptions().delayed_write_rate);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.arena_block_size = 4096;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
env_->time_elapse_only_sleep_ = false;
|
||||
options.env = env_;
|
||||
|
||||
// Test dynamically changing compaction_options_fifo.ttl
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.ttl = 1 * 60 * 60; // 1 hour
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Add 61 seconds to the time.
|
||||
env_->addon_time_.fetch_add(61);
|
||||
|
||||
// No files should be compacted as ttl is set to 1 hour.
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 3600);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Set ttl to 1 minute. So all files should get deleted.
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo", "{ttl=60;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 60);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
|
||||
// Test dynamically changing compaction_options_fifo.max_table_files_size
|
||||
env_->addon_time_.store(0);
|
||||
options.compaction_options_fifo.max_table_files_size = 500 << 10; // 00KB
|
||||
options.compaction_options_fifo.ttl = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// No files should be compacted as max_table_files_size is set to 500 KB.
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
500 << 10);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Set max_table_files_size to 12 KB. So only 1 file should remain now.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo", "{max_table_files_size=12288;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
12 << 10);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
|
||||
|
||||
// Test dynamically changing compaction_options_fifo.allow_compaction
|
||||
options.compaction_options_fifo.max_table_files_size = 500 << 10; // 500KB
|
||||
options.compaction_options_fifo.ttl = 0;
|
||||
options.compaction_options_fifo.allow_compaction = false;
|
||||
options.level0_file_num_compaction_trigger = 6;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// Generate and flush a file about 10KB.
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(ToString(i * 20 + j), RandomString(&rnd, 980)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// No files should be compacted as max_table_files_size is set to 500 KB and
|
||||
// allow_compaction is false
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
|
||||
|
||||
// Set allow_compaction to true. So number of files should be between 1 and 5.
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo", "{allow_compaction=true;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_GE(NumTableFilesAtLevel(0), 1);
|
||||
ASSERT_LE(NumTableFilesAtLevel(0), 5);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, CompactionReadaheadSizeChange) {
|
||||
SpecialEnv env(env_);
|
||||
Options options;
|
||||
options.env = &env;
|
||||
|
||||
options.compaction_readahead_size = 0;
|
||||
options.new_table_reader_for_compaction_inputs = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
const std::string kValue(1024, 'v');
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(0, dbfull()->GetDBOptions().compaction_readahead_size);
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"compaction_readahead_size", "256"}}));
|
||||
ASSERT_EQ(256, dbfull()->GetDBOptions().compaction_readahead_size);
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
Flush();
|
||||
for (int i = 0; i < 1024 * 2; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(256, env_->compaction_readahead_size_);
|
||||
Close();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+32
-10
@@ -327,15 +327,14 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
std::string trash_dir = test::TmpDir(env_) + "/trash";
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
Status s;
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, trash_dir, 0, false, &s));
|
||||
NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
|
||||
ASSERT_OK(s);
|
||||
options.sst_file_manager->SetDeleteRateBytesPerSecond(rate_bytes_per_sec);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->TEST_SetMaxTrashDBRatio(1.1);
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
// Create 4 files in L0
|
||||
@@ -377,6 +376,30 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, "", 1024 * 1024 /* 1 MB/sec */));
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
|
||||
Destroy(last_options_);
|
||||
|
||||
// Add some trash files to the db directory so the DB can clean them up
|
||||
env_->CreateDirIfMissing(dbname_);
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "001.sst.trash"));
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "002.sst.trash"));
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "003.sst.trash"));
|
||||
|
||||
// Reopen the DB and verify that it deletes existing trash files
|
||||
ASSERT_OK(TryReopen(options));
|
||||
sfm->WaitForEmptyTrash();
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "001.sst.trash"));
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "002.sst.trash"));
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "003.sst.trash"));
|
||||
}
|
||||
|
||||
|
||||
// Create a DB with 2 db_paths, and generate multiple files in the 2
|
||||
// db_paths using CompactRangeOptions, make sure that files that were
|
||||
// deleted from first db_path were deleted using DeleteScheduler and
|
||||
@@ -394,14 +417,14 @@ TEST_F(DBSSTTest, DeleteSchedulerMultipleDBPaths) {
|
||||
options.db_paths.emplace_back(dbname_ + "_2", 1024 * 100);
|
||||
options.env = env_;
|
||||
|
||||
std::string trash_dir = test::TmpDir(env_) + "/trash";
|
||||
int64_t rate_bytes_per_sec = 1024 * 1024; // 1 Mb / Sec
|
||||
Status s;
|
||||
options.sst_file_manager.reset(NewSstFileManager(
|
||||
env_, nullptr, trash_dir, rate_bytes_per_sec, false, &s));
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, "", rate_bytes_per_sec, false, &s,
|
||||
/* max_trash_db_ratio= */ 1.1));
|
||||
|
||||
ASSERT_OK(s);
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->TEST_SetMaxTrashDBRatio(1.1);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -460,9 +483,8 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
std::string trash_dir = test::TmpDir(env_) + "/trash";
|
||||
options.sst_file_manager.reset(
|
||||
NewSstFileManager(env_, nullptr, trash_dir, 0, false, &s));
|
||||
NewSstFileManager(env_, nullptr, "", 0, false, &s, 0));
|
||||
ASSERT_OK(s);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -480,7 +502,7 @@ TEST_F(DBSSTTest, DestroyDBWithRateLimitedDelete) {
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
|
||||
sfm->SetDeleteRateBytesPerSecond(1024 * 1024);
|
||||
sfm->delete_scheduler()->TEST_SetMaxTrashDBRatio(1.1);
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(1.1);
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
sfm->WaitForEmptyTrash();
|
||||
// We have deleted the 4 sst files in the delete_scheduler
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
@@ -250,6 +251,37 @@ TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
const int kNumKeys = 1000;
|
||||
const int kWindowSize = 100;
|
||||
const int kNumDelsTrigger = 90;
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.table_properties_collector_factories.emplace_back(
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger));
|
||||
Reopen(opts);
|
||||
|
||||
// add an L1 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
Put(Key(0), "val");
|
||||
Flush();
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
if (i >= kNumKeys - kWindowSize &&
|
||||
i < kNumKeys - kWindowSize + kNumDelsTrigger) {
|
||||
Delete(Key(i));
|
||||
} else {
|
||||
Put(Key(i), "val");
|
||||
}
|
||||
}
|
||||
Flush();
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_GT(NumTableFilesAtLevel(1), 0);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
+239
-36
@@ -60,7 +60,6 @@
|
||||
#include "util/compression.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/filename.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/rate_limiter.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -223,6 +222,10 @@ TEST_F(DBTest, SkipDelay) {
|
||||
|
||||
for (bool sync : {true, false}) {
|
||||
for (bool disableWAL : {true, false}) {
|
||||
if (sync && disableWAL) {
|
||||
// sync and disableWAL is incompatible.
|
||||
continue;
|
||||
}
|
||||
// Use a small number to ensure a large delay that is still effective
|
||||
// when we do Put
|
||||
// TODO(myabandeh): this is time dependent and could potentially make
|
||||
@@ -500,7 +503,9 @@ TEST_F(DBTest, DISABLED_VeryLargeValue) {
|
||||
ASSERT_OK(Put(key2, raw));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::string value;
|
||||
Status s = db_->Get(ReadOptions(), key1, &value);
|
||||
@@ -2188,6 +2193,8 @@ class ModelDB : public DB {
|
||||
batch.Put(cf, k, v);
|
||||
return Write(o, &batch);
|
||||
}
|
||||
using DB::Close;
|
||||
virtual Status Close() override { return Status::OK(); }
|
||||
using DB::Delete;
|
||||
virtual Status Delete(const WriteOptions& o, ColumnFamilyHandle* cf,
|
||||
const Slice& key) override {
|
||||
@@ -2324,9 +2331,9 @@ class ModelDB : public DB {
|
||||
return false;
|
||||
}
|
||||
using DB::GetMapProperty;
|
||||
virtual bool GetMapProperty(ColumnFamilyHandle* column_family,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) override {
|
||||
virtual bool GetMapProperty(
|
||||
ColumnFamilyHandle* column_family, const Slice& property,
|
||||
std::map<std::string, std::string>* value) override {
|
||||
return false;
|
||||
}
|
||||
using DB::GetAggregatedIntProperty;
|
||||
@@ -2459,6 +2466,10 @@ class ModelDB : public DB {
|
||||
|
||||
virtual SequenceNumber GetLatestSequenceNumber() const override { return 0; }
|
||||
|
||||
virtual bool SetPreserveDeletesSequenceNumber(SequenceNumber seqnum) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual ColumnFamilyHandle* DefaultColumnFamily() const override {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -3481,12 +3492,16 @@ TEST_F(DBTest, GetThreadStatus) {
|
||||
const int kTestCount = 3;
|
||||
const unsigned int kHighPriCounts[kTestCount] = {3, 2, 5};
|
||||
const unsigned int kLowPriCounts[kTestCount] = {10, 15, 3};
|
||||
const unsigned int kBottomPriCounts[kTestCount] = {2, 1, 4};
|
||||
for (int test = 0; test < kTestCount; ++test) {
|
||||
// Change the number of threads in high / low priority pool.
|
||||
env_->SetBackgroundThreads(kHighPriCounts[test], Env::HIGH);
|
||||
env_->SetBackgroundThreads(kLowPriCounts[test], Env::LOW);
|
||||
env_->SetBackgroundThreads(kBottomPriCounts[test], Env::BOTTOM);
|
||||
// Wait to ensure the all threads has been registered
|
||||
unsigned int thread_type_counts[ThreadStatus::NUM_THREAD_TYPES];
|
||||
// TODO(ajkr): it'd be better if SetBackgroundThreads returned only after
|
||||
// all threads have been registered.
|
||||
// Try up to 60 seconds.
|
||||
for (int num_try = 0; num_try < 60000; num_try++) {
|
||||
env_->SleepForMicroseconds(1000);
|
||||
@@ -3501,20 +3516,21 @@ TEST_F(DBTest, GetThreadStatus) {
|
||||
if (thread_type_counts[ThreadStatus::HIGH_PRIORITY] ==
|
||||
kHighPriCounts[test] &&
|
||||
thread_type_counts[ThreadStatus::LOW_PRIORITY] ==
|
||||
kLowPriCounts[test]) {
|
||||
kLowPriCounts[test] &&
|
||||
thread_type_counts[ThreadStatus::BOTTOM_PRIORITY] ==
|
||||
kBottomPriCounts[test]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Verify the total number of threades
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::HIGH_PRIORITY] +
|
||||
thread_type_counts[ThreadStatus::LOW_PRIORITY],
|
||||
kHighPriCounts[test] + kLowPriCounts[test]);
|
||||
// Verify the number of high-priority threads
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::HIGH_PRIORITY],
|
||||
kHighPriCounts[test]);
|
||||
// Verify the number of low-priority threads
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::LOW_PRIORITY],
|
||||
kLowPriCounts[test]);
|
||||
// Verify the number of bottom-priority threads
|
||||
ASSERT_EQ(thread_type_counts[ThreadStatus::BOTTOM_PRIORITY],
|
||||
kBottomPriCounts[test]);
|
||||
}
|
||||
if (i == 0) {
|
||||
// repeat the test with multiple column families
|
||||
@@ -4302,6 +4318,140 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_LT(NumTableFilesAtLevel(0), 4);
|
||||
}
|
||||
|
||||
// Test dynamic FIFO copmaction options.
|
||||
// This test covers just option parsing and makes sure that the options are
|
||||
// correctly assigned. Also look at DBOptionsTest.SetFIFOCompactionOptions
|
||||
// test which makes sure that the FIFO compaction funcionality is working
|
||||
// as expected on dynamically changing the options.
|
||||
// Even more FIFOCompactionTests are at DBTest.FIFOCompaction* .
|
||||
TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Initial defaults
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
1024 * 1024 * 1024);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 0);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo", "{max_table_files_size=23;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 0);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo", "{ttl=97}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 97);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions({{"compaction_options_fifo", "{ttl=203;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo", "{allow_compaction=true;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
23);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 203);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo", "{max_table_files_size=31;ttl=19;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
31);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 19);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_fifo",
|
||||
"{max_table_files_size=51;ttl=49;allow_compaction=true;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.max_table_files_size,
|
||||
51);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.ttl, 49);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_fifo.allow_compaction,
|
||||
true);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Initial defaults
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 1);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
2);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_merge_width,
|
||||
UINT_MAX);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.compression_size_percent,
|
||||
-1);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.stop_style,
|
||||
kCompactionStopStyleTotalSize);
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_universal", "{size_ratio=7;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
2);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_merge_width,
|
||||
UINT_MAX);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.compression_size_percent,
|
||||
-1);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.stop_style,
|
||||
kCompactionStopStyleTotalSize);
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
{{"compaction_options_universal", "{min_merge_width=11;}"}}));
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.size_ratio, 7);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.min_merge_width,
|
||||
11);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.max_merge_width,
|
||||
UINT_MAX);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions()
|
||||
.compaction_options_universal.compression_size_percent,
|
||||
-1);
|
||||
ASSERT_EQ(dbfull()->GetOptions().compaction_options_universal.stop_style,
|
||||
kCompactionStopStyleTotalSize);
|
||||
ASSERT_EQ(
|
||||
dbfull()->GetOptions().compaction_options_universal.allow_trivial_move,
|
||||
false);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, FileCreationRandomFailure) {
|
||||
@@ -4515,7 +4665,7 @@ TEST_F(DBTest, EncodeDecompressedBlockSizeTest) {
|
||||
options.compression = comp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
int kNumKeysWritten = 1000;
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
@@ -4908,55 +5058,84 @@ TEST_F(DBTest, PromoteL0Failure) {
|
||||
status = experimental::PromoteL0(db_, db_->DefaultColumnFamily());
|
||||
ASSERT_TRUE(status.IsInvalidArgument());
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Github issue #596
|
||||
TEST_F(DBTest, HugeNumberOfLevels) {
|
||||
TEST_F(DBTest, CompactRangeWithEmptyBottomLevel) {
|
||||
const int kNumLevels = 2;
|
||||
const int kNumL0Files = 2;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 2 * 1024 * 1024; // 2MB
|
||||
options.max_bytes_for_level_base = 2 * 1024 * 1024; // 2MB
|
||||
options.num_levels = 12;
|
||||
options.max_background_compactions = 10;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
ASSERT_OK(Put(Key(0), RandomString(&rnd, 1024)));
|
||||
Flush();
|
||||
}
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), kNumL0Files);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), kNumL0Files);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest, AutomaticConflictsWithManualCompaction) {
|
||||
const int kNumL0Files = 50;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 2 * 1024 * 1024; // 2MB
|
||||
options.max_bytes_for_level_base = 2 * 1024 * 1024; // 2MB
|
||||
options.num_levels = 12;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
// never slowdown / stop
|
||||
options.level0_slowdown_writes_trigger = 999999;
|
||||
options.level0_stop_writes_trigger = 999999;
|
||||
options.max_background_compactions = 10;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
}
|
||||
|
||||
// schedule automatic compactions after the manual one starts, but before it
|
||||
// finishes to ensure conflict.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCompaction:Start",
|
||||
"DBTest::AutomaticConflictsWithManualCompaction:PrePuts"},
|
||||
{"DBTest::AutomaticConflictsWithManualCompaction:PostPuts",
|
||||
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun"}});
|
||||
std::atomic<int> callback_count(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction()::Conflict",
|
||||
"DBImpl::MaybeScheduleFlushOrCompaction:Conflict",
|
||||
[&](void* arg) { callback_count.fetch_add(1); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
std::thread manual_compaction_thread([this]() {
|
||||
CompactRangeOptions croptions;
|
||||
croptions.exclusive_manual_compaction = true;
|
||||
ASSERT_OK(db_->CompactRange(croptions, nullptr, nullptr));
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT("DBTest::AutomaticConflictsWithManualCompaction:PrePuts");
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
// put two keys to ensure no trivial move
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(Key(j), RandomString(&rnd, 1024)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
TEST_SYNC_POINT("DBTest::AutomaticConflictsWithManualCompaction:PostPuts");
|
||||
|
||||
ASSERT_GE(callback_count.load(), 1);
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
for (int i = 0; i < 300000; ++i) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_NE("NOT_FOUND", Get(Key(i)));
|
||||
}
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
manual_compaction_thread.join();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
|
||||
// Github issue #595
|
||||
@@ -5172,6 +5351,22 @@ TEST_F(DBTest, HardLimit) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class WriteStallListener : public EventListener {
|
||||
public:
|
||||
WriteStallListener() : condition_(WriteStallCondition::kNormal) {}
|
||||
void OnStallConditionsChanged(const WriteStallInfo& info) override {
|
||||
MutexLock l(&mutex_);
|
||||
condition_ = info.condition.cur;
|
||||
}
|
||||
bool CheckCondition(WriteStallCondition expected) {
|
||||
MutexLock l(&mutex_);
|
||||
return expected == condition_;
|
||||
}
|
||||
private:
|
||||
port::Mutex mutex_;
|
||||
WriteStallCondition condition_;
|
||||
};
|
||||
|
||||
TEST_F(DBTest, SoftLimit) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -5187,6 +5382,8 @@ TEST_F(DBTest, SoftLimit) {
|
||||
options.max_bytes_for_level_multiplier = 10;
|
||||
options.max_background_compactions = 1;
|
||||
options.compression = kNoCompression;
|
||||
WriteStallListener* listener = new WriteStallListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
Reopen(options);
|
||||
|
||||
@@ -5226,6 +5423,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Flush();
|
||||
}
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kDelayed));
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
@@ -5236,6 +5434,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
// The L1 file size is around 30KB.
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 1);
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kNormal));
|
||||
|
||||
// Only allow one compactin going through.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -5270,6 +5469,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
// doesn't trigger soft_pending_compaction_bytes_limit
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 1);
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kNormal));
|
||||
|
||||
// Create 3 L0 files, making score of L0 to be 3, higher than L0.
|
||||
for (int i = 0; i < 3; i++) {
|
||||
@@ -5290,11 +5490,13 @@ TEST_F(DBTest, SoftLimit) {
|
||||
// triggerring soft_pending_compaction_bytes_limit
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1), 1);
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kDelayed));
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kNormal));
|
||||
|
||||
// shrink level base so L2 will hit soft limit easier.
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
@@ -5304,6 +5506,7 @@ TEST_F(DBTest, SoftLimit) {
|
||||
Put("", "");
|
||||
Flush();
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(listener->CheckCondition(WriteStallCondition::kDelayed));
|
||||
|
||||
sleeping_task_low.WaitUntilSleeping();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
+168
-15
@@ -11,6 +11,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/persistent_cache.h"
|
||||
@@ -1026,6 +1027,7 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
const size_t kL0FileBytes = 128 << 10;
|
||||
const size_t kApproxPerBlockOverheadBytes = 50;
|
||||
const int kNumL0Files = 5;
|
||||
const int kZstdTrainFactor = 16;
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env; // Make sure to use any custom env that the test is configured with.
|
||||
@@ -1058,17 +1060,34 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
for (auto compression_type : compression_types) {
|
||||
options.compression = compression_type;
|
||||
size_t prev_out_bytes;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
// First iteration: compress without preset dictionary
|
||||
// Second iteration: compress with preset dictionary
|
||||
// To make sure the compression dictionary was actually used, we verify
|
||||
// the compressed size is smaller in the second iteration. Also in the
|
||||
// second iteration, verify the data we get out is the same data we put
|
||||
// in.
|
||||
if (i) {
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
} else {
|
||||
options.compression_opts.max_dict_bytes = 0;
|
||||
// Third iteration (zstd only): compress with zstd-trained dictionary
|
||||
//
|
||||
// To make sure the compression dictionary has the intended effect, we
|
||||
// verify the compressed size is smaller in successive iterations. Also in
|
||||
// the non-first iterations, verify the data we get out is the same data
|
||||
// we put in.
|
||||
switch (i) {
|
||||
case 0:
|
||||
options.compression_opts.max_dict_bytes = 0;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case 1:
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes = 0;
|
||||
break;
|
||||
case 2:
|
||||
if (compression_type != kZSTD) {
|
||||
continue;
|
||||
}
|
||||
options.compression_opts.max_dict_bytes = kBlockSizeBytes;
|
||||
options.compression_opts.zstd_max_train_bytes =
|
||||
kZstdTrainFactor * kBlockSizeBytes;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
@@ -1133,7 +1152,7 @@ class CompactionCompressionListener : public EventListener {
|
||||
}
|
||||
|
||||
if (db_options_->bottommost_compression != kDisableCompressionOption &&
|
||||
ci.output_level == bottommost_level && ci.output_level >= 2) {
|
||||
ci.output_level == bottommost_level) {
|
||||
ASSERT_EQ(ci.compression, db_options_->bottommost_compression);
|
||||
} else if (db_options_->compression_per_level.size() != 0) {
|
||||
ASSERT_EQ(ci.compression,
|
||||
@@ -1918,19 +1937,19 @@ TEST_F(DBTest2, AutomaticCompactionOverlapManualCompaction) {
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
auto get_stat = [](std::string level_str, LevelStatType type,
|
||||
std::map<std::string, double> props) {
|
||||
std::map<std::string, std::string> props) {
|
||||
auto prop_str =
|
||||
level_str + "." +
|
||||
"compaction." + level_str + "." +
|
||||
InternalStats::compaction_level_stats.at(type).property_name.c_str();
|
||||
auto prop_item = props.find(prop_str);
|
||||
return prop_item == props.end() ? 0 : prop_item->second;
|
||||
return prop_item == props.end() ? 0 : std::stod(prop_item->second);
|
||||
};
|
||||
|
||||
// Trivial move 2 files to L2
|
||||
ASSERT_EQ("0,0,2", FilesPerLevel());
|
||||
// Also test that the stats GetMapProperty API reporting the same result
|
||||
{
|
||||
std::map<std::string, double> prop;
|
||||
std::map<std::string, std::string> prop;
|
||||
ASSERT_TRUE(dbfull()->GetMapProperty("rocksdb.cfstats", &prop));
|
||||
ASSERT_EQ(0, get_stat("L0", LevelStatType::NUM_FILES, prop));
|
||||
ASSERT_EQ(0, get_stat("L1", LevelStatType::NUM_FILES, prop));
|
||||
@@ -1966,7 +1985,7 @@ TEST_F(DBTest2, AutomaticCompactionOverlapManualCompaction) {
|
||||
|
||||
// Test that the stats GetMapProperty API reporting 1 file in L2
|
||||
{
|
||||
std::map<std::string, double> prop;
|
||||
std::map<std::string, std::string> prop;
|
||||
ASSERT_TRUE(dbfull()->GetMapProperty("rocksdb.cfstats", &prop));
|
||||
ASSERT_EQ(1, get_stat("L2", LevelStatType::NUM_FILES, prop));
|
||||
}
|
||||
@@ -2331,6 +2350,140 @@ TEST_F(DBTest2, ReduceLevel) {
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
// Test that ReadCallback is actually used in both memtbale and sst tables
|
||||
TEST_F(DBTest2, ReadCallbackTest) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
Reopen(options);
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
// Try to create a db with multiple layers and a memtable
|
||||
const std::string key = "foo";
|
||||
const std::string value = "bar";
|
||||
// This test assumes that the seq start with 1 and increased by 1 after each
|
||||
// write batch of size 1. If that behavior changes, the test needs to be
|
||||
// updated as well.
|
||||
// TODO(myabandeh): update this test to use the seq number that is returned by
|
||||
// the DB instead of assuming what seq the DB used.
|
||||
int i = 1;
|
||||
for (; i < 10; i++) {
|
||||
Put(key, value + std::to_string(i));
|
||||
// Take a snapshot to avoid the value being removed during compaction
|
||||
auto snapshot = dbfull()->GetSnapshot();
|
||||
snapshots.push_back(snapshot);
|
||||
}
|
||||
Flush();
|
||||
for (; i < 20; i++) {
|
||||
Put(key, value + std::to_string(i));
|
||||
// Take a snapshot to avoid the value being removed during compaction
|
||||
auto snapshot = dbfull()->GetSnapshot();
|
||||
snapshots.push_back(snapshot);
|
||||
}
|
||||
Flush();
|
||||
MoveFilesToLevel(6);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
for (; i < 30; i++) {
|
||||
Put(key, value + std::to_string(i));
|
||||
auto snapshot = dbfull()->GetSnapshot();
|
||||
snapshots.push_back(snapshot);
|
||||
}
|
||||
Flush();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("1,0,0,0,0,0,2", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
// And also add some values to the memtable
|
||||
for (; i < 40; i++) {
|
||||
Put(key, value + std::to_string(i));
|
||||
auto snapshot = dbfull()->GetSnapshot();
|
||||
snapshots.push_back(snapshot);
|
||||
}
|
||||
|
||||
class TestReadCallback : public ReadCallback {
|
||||
public:
|
||||
explicit TestReadCallback(SequenceNumber snapshot) : snapshot_(snapshot) {}
|
||||
virtual bool IsCommitted(SequenceNumber seq) override {
|
||||
return seq <= snapshot_;
|
||||
}
|
||||
|
||||
private:
|
||||
SequenceNumber snapshot_;
|
||||
};
|
||||
|
||||
for (int seq = 1; seq < i; seq++) {
|
||||
PinnableSlice pinnable_val;
|
||||
ReadOptions roptions;
|
||||
TestReadCallback callback(seq);
|
||||
bool dont_care = true;
|
||||
Status s = dbfull()->GetImpl(roptions, dbfull()->DefaultColumnFamily(), key,
|
||||
&pinnable_val, &dont_care, &callback);
|
||||
ASSERT_TRUE(s.ok());
|
||||
// Assuming that after each Put the DB increased seq by one, the value and
|
||||
// seq number must be equal since we also inc value by 1 after each Put.
|
||||
ASSERT_EQ(value + std::to_string(seq), pinnable_val.ToString());
|
||||
}
|
||||
|
||||
for (auto snapshot : snapshots) {
|
||||
dbfull()->ReleaseSnapshot(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
// Regression test for race condition where an obsolete file is returned to
|
||||
// user as a "live file" but then deleted, all while file deletions are
|
||||
// disabled.
|
||||
//
|
||||
// It happened like this:
|
||||
//
|
||||
// 1. [flush thread] Log file "x.log" found by FindObsoleteFiles
|
||||
// 2. [user thread] DisableFileDeletions, GetSortedWalFiles are called and the
|
||||
// latter returned "x.log"
|
||||
// 3. [flush thread] PurgeObsoleteFiles deleted "x.log"
|
||||
// 4. [user thread] Reading "x.log" failed
|
||||
//
|
||||
// Unfortunately the only regression test I can come up with involves sleep.
|
||||
// We cannot set SyncPoints to repro since, once the fix is applied, the
|
||||
// SyncPoints would cause a deadlock as the repro's sequence of events is now
|
||||
// prohibited.
|
||||
//
|
||||
// Instead, if we sleep for a second between Find and Purge, and ensure the
|
||||
// read attempt happens after purge, then the sequence of events will almost
|
||||
// certainly happen on the old code.
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BackgroundCallFlush:FilesFound",
|
||||
"DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered"},
|
||||
{"DBImpl::PurgeObsoleteFiles:End",
|
||||
"DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::PurgeObsoleteFiles:Begin",
|
||||
[&](void* arg) { env_->SleepForMicroseconds(1000000); });
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Put("key", "val");
|
||||
FlushOptions flush_opts;
|
||||
flush_opts.wait = false;
|
||||
db_->Flush(flush_opts);
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:FlushTriggered");
|
||||
|
||||
db_->DisableFileDeletions();
|
||||
VectorLogPtr log_files;
|
||||
db_->GetSortedWalFiles(log_files);
|
||||
TEST_SYNC_POINT("DBTest2::LiveFilesOmitObsoleteFiles:LiveFilesCaptured");
|
||||
for (const auto& log_file : log_files) {
|
||||
ASSERT_OK(env_->FileExists(LogFileName(dbname_, log_file->LogNumber())));
|
||||
}
|
||||
|
||||
db_->EnableFileDeletions();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+16
-9
@@ -288,12 +288,11 @@ Options DBTestBase::GetOptions(
|
||||
Options options = default_options;
|
||||
BlockBasedTableOptions table_options;
|
||||
bool set_block_based_table_factory = true;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && \
|
||||
!defined(OS_AIX)
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && \
|
||||
!defined(OS_AIX)
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"NewRandomAccessFile:O_DIRECT");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"NewWritableFile:O_DIRECT");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearCallBack("NewWritableFile:O_DIRECT");
|
||||
#endif
|
||||
|
||||
bool can_allow_mmap = IsMemoryMappedAccessSupported();
|
||||
@@ -466,7 +465,7 @@ Options DBTestBase::GetOptions(
|
||||
options.use_direct_io_for_flush_and_compaction = true;
|
||||
options.compaction_readahead_size = 2 * 1024 * 1024;
|
||||
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && \
|
||||
!defined(OS_AIX)
|
||||
!defined(OS_AIX) && !defined(OS_OPENBSD)
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"NewWritableFile:O_DIRECT", [&](void* arg) {
|
||||
int* val = static_cast<int*>(arg);
|
||||
@@ -487,7 +486,7 @@ Options DBTestBase::GetOptions(
|
||||
}
|
||||
case kConcurrentWALWrites: {
|
||||
// This options optimize 2PC commit path
|
||||
options.concurrent_prepare = true;
|
||||
options.two_write_queues = true;
|
||||
options.manual_wal_flush = true;
|
||||
break;
|
||||
}
|
||||
@@ -668,6 +667,10 @@ Status DBTestBase::SingleDelete(int cf, const std::string& k) {
|
||||
return db_->SingleDelete(WriteOptions(), handles_[cf], k);
|
||||
}
|
||||
|
||||
bool DBTestBase::SetPreserveDeletesSequenceNumber(SequenceNumber sn) {
|
||||
return db_->SetPreserveDeletesSequenceNumber(sn);
|
||||
}
|
||||
|
||||
std::string DBTestBase::Get(const std::string& k, const Snapshot* snapshot) {
|
||||
ReadOptions options;
|
||||
options.verify_checksums = true;
|
||||
@@ -864,7 +867,6 @@ size_t DBTestBase::CountLiveFiles() {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
return metadata.size();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int DBTestBase::NumTableFilesAtLevel(int level, int cf) {
|
||||
std::string property;
|
||||
@@ -925,6 +927,7 @@ std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
result.resize(last_non_zero_offset);
|
||||
return result;
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
size_t DBTestBase::CountFiles() {
|
||||
std::vector<std::string> files;
|
||||
@@ -994,6 +997,7 @@ void DBTestBase::MoveFilesToLevel(int level, int cf) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBTestBase::DumpFileCounts(const char* label) {
|
||||
fprintf(stderr, "---\n%s:\n", label);
|
||||
fprintf(stderr, "maxoverlap: %" PRIu64 "\n",
|
||||
@@ -1005,6 +1009,7 @@ void DBTestBase::DumpFileCounts(const char* label) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
std::string DBTestBase::DumpSSTableList() {
|
||||
std::string property;
|
||||
@@ -1154,11 +1159,13 @@ UpdateStatus DBTestBase::updateInPlaceNoAction(char* prevValue,
|
||||
|
||||
// Utility method to test InplaceUpdate
|
||||
void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
ScopedArenaIterator iter;
|
||||
Arena arena;
|
||||
auto options = CurrentOptions();
|
||||
InternalKeyComparator icmp(options.comparator);
|
||||
RangeDelAggregator range_del_agg(icmp, {} /* snapshots */);
|
||||
// This should be defined after range_del_agg so that it destructs the
|
||||
// assigned iterator before it range_del_agg is already destructed.
|
||||
ScopedArenaIterator iter;
|
||||
if (cf != 0) {
|
||||
iter.set(
|
||||
dbfull()->NewInternalIterator(&arena, &range_del_agg, handles_[cf]));
|
||||
@@ -1170,7 +1177,7 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
int seq = numValues;
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey;
|
||||
ikey.sequence = -1;
|
||||
ikey.clear();
|
||||
ASSERT_EQ(ParseInternalKey(iter->key(), &ikey), true);
|
||||
|
||||
// checks sequence number for updates
|
||||
|
||||
+10
-1
@@ -445,6 +445,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
r->reset(new CountingFile(std::move(*r), &random_read_counter_,
|
||||
&random_read_bytes_counter_));
|
||||
}
|
||||
if (s.ok() && soptions.compaction_readahead_size > 0) {
|
||||
compaction_readahead_size_ = soptions.compaction_readahead_size;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -570,6 +573,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
bool no_slowdown_;
|
||||
|
||||
std::atomic<bool> is_wal_sync_thread_safe_{true};
|
||||
|
||||
std::atomic<size_t> compaction_readahead_size_;
|
||||
};
|
||||
|
||||
class MockTimeEnv : public EnvWrapper {
|
||||
@@ -829,6 +834,8 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
Status SingleDelete(int cf, const std::string& k);
|
||||
|
||||
bool SetPreserveDeletesSequenceNumber(SequenceNumber sn);
|
||||
|
||||
std::string Get(const std::string& k, const Snapshot* snapshot = nullptr);
|
||||
|
||||
std::string Get(int cf, const std::string& k,
|
||||
@@ -856,13 +863,13 @@ class DBTestBase : public testing::Test {
|
||||
size_t TotalLiveFiles(int cf = 0);
|
||||
|
||||
size_t CountLiveFiles();
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
int NumTableFilesAtLevel(int level, int cf = 0);
|
||||
|
||||
double CompressionRatioAtLevel(int level, int cf = 0);
|
||||
|
||||
int TotalTableFiles(int cf = 0, int levels = -1);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Return spread of files per level
|
||||
std::string FilesPerLevel(int cf = 0);
|
||||
@@ -890,7 +897,9 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
void MoveFilesToLevel(int level, int cf = 0);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DumpFileCounts(const char* label);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
std::string DumpSSTableList();
|
||||
|
||||
|
||||
@@ -374,6 +374,79 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSizeAmplification) {
|
||||
ASSERT_EQ(NumSortedRuns(1), 1);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, DynamicUniversalCompactionSizeAmplification) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = 1;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
// Initial setup of compaction_options_universal will prevent universal
|
||||
// compaction from happening
|
||||
options.compaction_options_universal.size_ratio = 100;
|
||||
options.compaction_options_universal.min_merge_width = 100;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int total_picked_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
}
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
MutableCFOptions mutable_cf_options;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
Random rnd(301);
|
||||
int key_idx = 0;
|
||||
|
||||
// Generate two files in Level 0. Both files are approx the same size.
|
||||
for (int num = 0; num < options.level0_file_num_compaction_trigger - 1;
|
||||
num++) {
|
||||
// Write 110KB (11 values, each 10K)
|
||||
for (int i = 0; i < 11; i++) {
|
||||
ASSERT_OK(Put(1, Key(key_idx), RandomString(&rnd, 10000)));
|
||||
key_idx++;
|
||||
}
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_EQ(NumSortedRuns(1), num + 1);
|
||||
}
|
||||
ASSERT_EQ(NumSortedRuns(1), 2);
|
||||
|
||||
// Flush whatever is remaining in memtable. This is typically
|
||||
// small, which should not trigger size ratio based compaction
|
||||
// but could instead trigger size amplification if it's set
|
||||
// to 110.
|
||||
ASSERT_OK(Flush(1));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Verify compaction did not happen
|
||||
ASSERT_EQ(NumSortedRuns(1), 3);
|
||||
|
||||
// Trigger compaction if size amplification exceeds 110% without reopening DB
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
200);
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1],
|
||||
{{"compaction_options_universal",
|
||||
"{max_size_amplification_percent=110;}"}}));
|
||||
ASSERT_EQ(dbfull()
|
||||
->GetOptions(handles_[1])
|
||||
.compaction_options_universal.max_size_amplification_percent,
|
||||
110);
|
||||
ASSERT_OK(dbfull()->TEST_GetLatestMutableCFOptions(handles_[1],
|
||||
&mutable_cf_options));
|
||||
ASSERT_EQ(110, mutable_cf_options.compaction_options_universal
|
||||
.max_size_amplification_percent);
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Verify that size amplification did happen
|
||||
ASSERT_EQ(NumSortedRuns(1), 1);
|
||||
ASSERT_EQ(total_picked_compactions, 1);
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, CompactFilesOnUniversalCompaction) {
|
||||
const int kTestKeySize = 16;
|
||||
const int kTestValueSize = 984;
|
||||
|
||||
+36
-2
@@ -730,7 +730,8 @@ class RecoveryTestHelper {
|
||||
batch.Put(key, value);
|
||||
WriteBatchInternal::SetSequence(&batch, seq);
|
||||
current_log_writer->AddRecord(WriteBatchInternal::Contents(&batch));
|
||||
versions->SetLastToBeWrittenSequence(seq);
|
||||
versions->SetLastAllocatedSequence(seq);
|
||||
versions->SetLastPublishedSequence(seq);
|
||||
versions->SetLastSequence(seq);
|
||||
}
|
||||
}
|
||||
@@ -876,6 +877,39 @@ TEST_F(DBWALTest, kAbsoluteConsistency) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test scope:
|
||||
// We don't expect the data store to be opened if there is any inconsistency
|
||||
// between WAL and SST files
|
||||
TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
|
||||
// Create DB with multiple column families.
|
||||
CreateAndReopenWithCF({"one", "two"}, options);
|
||||
ASSERT_OK(Put(1, "key1", "val1"));
|
||||
ASSERT_OK(Put(2, "key2", "val2"));
|
||||
|
||||
// Record the offset at this point
|
||||
Env* env = options.env;
|
||||
int wal_file_id = RecoveryTestHelper::kWALFileOffset + 1;
|
||||
std::string fname = LogFileName(dbname_, wal_file_id);
|
||||
uint64_t offset_to_corrupt;
|
||||
ASSERT_OK(env->GetFileSize(fname, &offset_to_corrupt));
|
||||
ASSERT_GT(offset_to_corrupt, 0);
|
||||
|
||||
ASSERT_OK(Put(1, "key3", "val3"));
|
||||
// Corrupt WAL at location of key3
|
||||
RecoveryTestHelper::InduceCorruption(
|
||||
fname, static_cast<size_t>(offset_to_corrupt), static_cast<size_t>(4));
|
||||
ASSERT_OK(Put(2, "key4", "val4"));
|
||||
ASSERT_OK(Put(1, "key5", "val5"));
|
||||
Flush(2);
|
||||
|
||||
// PIT recovery & verify
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
ASSERT_NOK(TryReopenWithColumnFamilies({"default", "one", "two"}, options));
|
||||
}
|
||||
|
||||
// Test scope:
|
||||
// - We expect to open data store under all circumstances
|
||||
// - We expect only data upto the point where the first error was encountered
|
||||
@@ -1007,7 +1041,7 @@ TEST_F(DBWALTest, AvoidFlushDuringRecovery) {
|
||||
Reopen(options);
|
||||
ASSERT_EQ("v11", Get("foo"));
|
||||
ASSERT_EQ("v12", Get("bar"));
|
||||
ASSERT_EQ(2, TotalTableFiles());
|
||||
ASSERT_EQ(3, TotalTableFiles());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WalCleanupAfterAvoidFlushDuringRecovery) {
|
||||
|
||||
+9
-44
@@ -28,50 +28,15 @@ class DBWriteTest : public DBTestBase, public testing::WithParamInterface<int> {
|
||||
void Open() { DBTestBase::Reopen(GetOptions()); }
|
||||
};
|
||||
|
||||
// Sequence number should be return through input write batch.
|
||||
TEST_P(DBWriteTest, ReturnSeuqneceNumber) {
|
||||
Random rnd(4422);
|
||||
Open();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
WriteBatch batch;
|
||||
batch.Put("key" + ToString(i), test::RandomHumanReadableString(&rnd, 10));
|
||||
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(),
|
||||
WriteBatchInternal::Sequence(&batch));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, ReturnSeuqneceNumberMultiThreaded) {
|
||||
constexpr size_t kThreads = 16;
|
||||
constexpr size_t kNumKeys = 1000;
|
||||
Open();
|
||||
ASSERT_EQ(0, dbfull()->GetLatestSequenceNumber());
|
||||
// Check each sequence is used once and only once.
|
||||
std::vector<std::atomic_flag> flags(kNumKeys * kThreads + 1);
|
||||
for (size_t i = 0; i < flags.size(); i++) {
|
||||
flags[i].clear();
|
||||
}
|
||||
auto writer = [&](size_t id) {
|
||||
Random rnd(4422 + static_cast<uint32_t>(id));
|
||||
for (size_t k = 0; k < kNumKeys; k++) {
|
||||
WriteBatch batch;
|
||||
batch.Put("key" + ToString(id) + "-" + ToString(k),
|
||||
test::RandomHumanReadableString(&rnd, 10));
|
||||
ASSERT_OK(dbfull()->Write(WriteOptions(), &batch));
|
||||
SequenceNumber sequence = WriteBatchInternal::Sequence(&batch);
|
||||
ASSERT_GT(sequence, 0);
|
||||
ASSERT_LE(sequence, kNumKeys * kThreads);
|
||||
// The sequence isn't consumed by someone else.
|
||||
ASSERT_FALSE(flags[sequence].test_and_set());
|
||||
}
|
||||
};
|
||||
std::vector<port::Thread> threads;
|
||||
for (size_t i = 0; i < kThreads; i++) {
|
||||
threads.emplace_back(writer, i);
|
||||
}
|
||||
for (size_t i = 0; i < kThreads; i++) {
|
||||
threads[i].join();
|
||||
}
|
||||
// It is invalid to do sync write while disabling WAL.
|
||||
TEST_P(DBWriteTest, SyncAndDisableWAL) {
|
||||
WriteOptions write_options;
|
||||
write_options.sync = true;
|
||||
write_options.disableWAL = true;
|
||||
ASSERT_TRUE(dbfull()->Put(write_options, "foo", "bar").IsInvalidArgument());
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(batch.Put("foo", "bar"));
|
||||
ASSERT_TRUE(dbfull()->Write(write_options, &batch).IsInvalidArgument());
|
||||
}
|
||||
|
||||
TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
|
||||
@@ -36,6 +36,32 @@ uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {
|
||||
return (seq << 8) | t;
|
||||
}
|
||||
|
||||
EntryType GetEntryType(ValueType value_type) {
|
||||
switch (value_type) {
|
||||
case kTypeValue:
|
||||
return kEntryPut;
|
||||
case kTypeDeletion:
|
||||
return kEntryDelete;
|
||||
case kTypeSingleDeletion:
|
||||
return kEntrySingleDelete;
|
||||
case kTypeMerge:
|
||||
return kEntryMerge;
|
||||
default:
|
||||
return kEntryOther;
|
||||
}
|
||||
}
|
||||
|
||||
bool ParseFullKey(const Slice& internal_key, FullKey* fkey) {
|
||||
ParsedInternalKey ikey;
|
||||
if (!ParseInternalKey(internal_key, &ikey)) {
|
||||
return false;
|
||||
}
|
||||
fkey->user_key = ikey.user_key;
|
||||
fkey->sequence = ikey.sequence;
|
||||
fkey->type = GetEntryType(ikey.type);
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnPackSequenceAndType(uint64_t packed, uint64_t* seq, ValueType* t) {
|
||||
*seq = packed >> 8;
|
||||
*t = static_cast<ValueType>(packed & 0xff);
|
||||
@@ -174,4 +200,13 @@ LookupKey::LookupKey(const Slice& _user_key, SequenceNumber s) {
|
||||
end_ = dst;
|
||||
}
|
||||
|
||||
void IterKey::EnlargeBuffer(size_t key_size) {
|
||||
// If size is smaller than buffer size, continue using current buffer,
|
||||
// or the static allocated one, as default
|
||||
assert(key_size > buf_size_);
|
||||
// Need to enlarge the buffer.
|
||||
ResetBuffer();
|
||||
buf_ = new char[key_size];
|
||||
buf_size_ = key_size;
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
+21
-11
@@ -49,7 +49,12 @@ enum ValueType : unsigned char {
|
||||
kTypeRangeDeletion = 0xF, // meta block
|
||||
kTypeColumnFamilyBlobIndex = 0x10, // Blob DB only
|
||||
kTypeBlobIndex = 0x11, // Blob DB only
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
// When the prepared record is also persisted in db, we use a different
|
||||
// record. This is to ensure that the WAL that is generated by a WritePolicy
|
||||
// is not mistakenly read by another, which would result into data
|
||||
// inconsistency.
|
||||
kTypeBeginPersistedPrepareXID = 0x12, // WAL only.
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
};
|
||||
|
||||
// Defined in dbformat.cc
|
||||
@@ -82,7 +87,7 @@ struct ParsedInternalKey {
|
||||
|
||||
ParsedInternalKey()
|
||||
: sequence(kMaxSequenceNumber) // Make code analyzer happy
|
||||
{} // Intentionally left uninitialized (for speed)
|
||||
{} // Intentionally left uninitialized (for speed)
|
||||
ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
|
||||
: user_key(u), sequence(seq), type(t) { }
|
||||
std::string DebugString(bool hex = false) const;
|
||||
@@ -106,6 +111,8 @@ extern uint64_t PackSequenceAndType(uint64_t seq, ValueType t);
|
||||
// and the ValueType in *t.
|
||||
extern void UnPackSequenceAndType(uint64_t packed, uint64_t* seq, ValueType* t);
|
||||
|
||||
EntryType GetEntryType(ValueType value_type);
|
||||
|
||||
// Append the serialization of "key" to *result.
|
||||
extern void AppendInternalKey(std::string* result,
|
||||
const ParsedInternalKey& key);
|
||||
@@ -138,7 +145,11 @@ inline ValueType ExtractValueType(const Slice& internal_key) {
|
||||
|
||||
// A comparator for internal keys that uses a specified comparator for
|
||||
// the user key portion and breaks ties by decreasing sequence number.
|
||||
class InternalKeyComparator : public Comparator {
|
||||
class InternalKeyComparator
|
||||
#ifdef NDEBUG
|
||||
final
|
||||
#endif
|
||||
: public Comparator {
|
||||
private:
|
||||
const Comparator* user_comparator_;
|
||||
std::string name_;
|
||||
@@ -179,15 +190,15 @@ class InternalKey {
|
||||
// sets the internal key to be bigger or equal to all internal keys with this
|
||||
// user key
|
||||
void SetMaxPossibleForUserKey(const Slice& _user_key) {
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(_user_key, kMaxSequenceNumber,
|
||||
kValueTypeForSeek));
|
||||
AppendInternalKey(
|
||||
&rep_, ParsedInternalKey(_user_key, 0, static_cast<ValueType>(0)));
|
||||
}
|
||||
|
||||
// sets the internal key to be smaller or equal to all internal keys with this
|
||||
// user key
|
||||
void SetMinPossibleForUserKey(const Slice& _user_key) {
|
||||
AppendInternalKey(
|
||||
&rep_, ParsedInternalKey(_user_key, 0, static_cast<ValueType>(0)));
|
||||
AppendInternalKey(&rep_, ParsedInternalKey(_user_key, kMaxSequenceNumber,
|
||||
kValueTypeForSeek));
|
||||
}
|
||||
|
||||
bool Valid() const {
|
||||
@@ -502,13 +513,12 @@ class IterKey {
|
||||
// If size is smaller than buffer size, continue using current buffer,
|
||||
// or the static allocated one, as default
|
||||
if (key_size > buf_size_) {
|
||||
// Need to enlarge the buffer.
|
||||
ResetBuffer();
|
||||
buf_ = new char[key_size];
|
||||
buf_size_ = key_size;
|
||||
EnlargeBuffer(key_size);
|
||||
}
|
||||
}
|
||||
|
||||
void EnlargeBuffer(size_t key_size);
|
||||
|
||||
// No copying allowed
|
||||
IterKey(const IterKey&) = delete;
|
||||
void operator=(const IterKey&) = delete;
|
||||
|
||||
@@ -558,8 +558,10 @@ TEST_F(ExternalSSTFileBasicTest, FadviseTrigger) {
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
int kNumLevels = 7;
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = kNumLevels;
|
||||
Reopen(options);
|
||||
|
||||
std::map<std::string, std::string> true_data;
|
||||
@@ -567,43 +569,65 @@ TEST_F(ExternalSSTFileBasicTest, IngestionWithRangeDeletions) {
|
||||
// prevent range deletions from being dropped due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
|
||||
// range del [0, 50) in L0 file, [50, 100) in memtable
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (i == 1) {
|
||||
// range del [0, 50) in L6 file, [50, 100) in L0 file, [100, 150) in memtable
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (i != 0) {
|
||||
db_->Flush(FlushOptions());
|
||||
if (i == 1) {
|
||||
MoveFilesToLevel(kNumLevels - 1);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(),
|
||||
Key(50 * i), Key(50 * (i + 1))));
|
||||
}
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 1));
|
||||
|
||||
// overlaps with L0 file but not memtable, so flush is skipped
|
||||
// overlaps with L0 file but not memtable, so flush is skipped and file is
|
||||
// ingested into L0
|
||||
SequenceNumber last_seqno = dbfull()->GetLatestSequenceNumber();
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {60, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
|
||||
// overlaps with L6 file but not memtable or L0 file, so flush is skipped and
|
||||
// file is ingested into L5
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {10, 40}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
|
||||
// overlaps with memtable, so flush is triggered (thus file count increases by
|
||||
// two at this step).
|
||||
// ingested file overlaps with memtable, so flush is triggered before the file
|
||||
// is ingested such that the ingested data is considered newest. So L0 file
|
||||
// count increases by two.
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {50, 90}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
options, {100, 140}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), ++last_seqno);
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
|
||||
// snapshot unneeded now that both range deletions are persisted
|
||||
// snapshot unneeded now that all range deletions are persisted
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
// overlaps with nothing, so places at bottom level and skips incrementing
|
||||
// seqnum.
|
||||
ASSERT_OK(GenerateAndAddExternalFile(
|
||||
options, {101, 125}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
options, {151, 175}, {ValueType::kTypeValue, ValueType::kTypeValue},
|
||||
file_id++, &true_data));
|
||||
ASSERT_EQ(dbfull()->GetLatestSequenceNumber(), last_seqno);
|
||||
ASSERT_EQ(4, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(kNumLevels - 2));
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(options.num_levels - 1));
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -164,7 +164,7 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
// if the dont overlap with any ranges since we have snapshots
|
||||
force_global_seqno = true;
|
||||
}
|
||||
// It is safe to use this instead of LastToBeWrittenSequence since we are
|
||||
// It is safe to use this instead of LastAllocatedSequence since we are
|
||||
// the only active writer, and hence they are equal
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
@@ -199,7 +199,8 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
}
|
||||
|
||||
if (consumed_seqno) {
|
||||
versions_->SetLastToBeWrittenSequence(last_seqno + 1);
|
||||
versions_->SetLastAllocatedSequence(last_seqno + 1);
|
||||
versions_->SetLastPublishedSequence(last_seqno + 1);
|
||||
versions_->SetLastSequence(last_seqno + 1);
|
||||
}
|
||||
|
||||
@@ -376,6 +377,7 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
|
||||
Status ExternalSstFileIngestionJob::IngestedFilesOverlapWithMemtables(
|
||||
SuperVersion* sv, bool* overlap) {
|
||||
*overlap = false;
|
||||
// Create an InternalIterator over all memtables
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
@@ -391,26 +393,33 @@ Status ExternalSstFileIngestionJob::IngestedFilesOverlapWithMemtables(
|
||||
memtable_range_del_iters.push_back(active_range_del_iter);
|
||||
}
|
||||
sv->imm->AddRangeTombstoneIterators(ro, &memtable_range_del_iters);
|
||||
std::unique_ptr<InternalIterator> memtable_range_del_iter(NewMergingIterator(
|
||||
&cfd_->internal_comparator(),
|
||||
memtable_range_del_iters.empty() ? nullptr : &memtable_range_del_iters[0],
|
||||
static_cast<int>(memtable_range_del_iters.size())));
|
||||
|
||||
RangeDelAggregator range_del_agg(cfd_->internal_comparator(),
|
||||
{} /* snapshots */,
|
||||
false /* collapse_deletions */);
|
||||
Status status;
|
||||
*overlap = false;
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
status =
|
||||
IngestedFileOverlapWithIteratorRange(&f, memtable_iter.get(), overlap);
|
||||
if (!status.ok() || *overlap == true) {
|
||||
break;
|
||||
}
|
||||
status = IngestedFileOverlapWithRangeDeletions(
|
||||
&f, memtable_range_del_iter.get(), overlap);
|
||||
if (!status.ok() || *overlap == true) {
|
||||
break;
|
||||
{
|
||||
std::unique_ptr<InternalIterator> memtable_range_del_iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(),
|
||||
memtable_range_del_iters.empty()
|
||||
? nullptr
|
||||
: &memtable_range_del_iters[0],
|
||||
static_cast<int>(memtable_range_del_iters.size())));
|
||||
status = range_del_agg.AddTombstones(std::move(memtable_range_del_iter));
|
||||
}
|
||||
if (status.ok()) {
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
status = IngestedFileOverlapWithIteratorRange(&f, memtable_iter.get(),
|
||||
overlap);
|
||||
if (!status.ok() || *overlap == true) {
|
||||
break;
|
||||
}
|
||||
if (range_del_agg.IsRangeOverlapped(f.smallest_user_key,
|
||||
f.largest_user_key)) {
|
||||
*overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -575,34 +584,6 @@ Status ExternalSstFileIngestionJob::IngestedFileOverlapWithIteratorRange(
|
||||
return iter->status();
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::IngestedFileOverlapWithRangeDeletions(
|
||||
const IngestedFileInfo* file_to_ingest, InternalIterator* range_del_iter,
|
||||
bool* overlap) {
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
auto* ucmp = vstorage->InternalComparator()->user_comparator();
|
||||
|
||||
*overlap = false;
|
||||
if (range_del_iter != nullptr) {
|
||||
for (range_del_iter->SeekToFirst(); range_del_iter->Valid();
|
||||
range_del_iter->Next()) {
|
||||
ParsedInternalKey parsed_key;
|
||||
if (!ParseInternalKey(range_del_iter->key(), &parsed_key)) {
|
||||
return Status::Corruption("corrupted range deletion key: " +
|
||||
range_del_iter->key().ToString());
|
||||
}
|
||||
RangeTombstone range_del(parsed_key, range_del_iter->value());
|
||||
if (ucmp->Compare(range_del.start_key_,
|
||||
file_to_ingest->largest_user_key) <= 0 &&
|
||||
ucmp->Compare(file_to_ingest->smallest_user_key,
|
||||
range_del.end_key_) <= 0) {
|
||||
*overlap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
|
||||
const IngestedFileInfo* file_to_ingest, int level) {
|
||||
if (level == 0) {
|
||||
@@ -639,23 +620,26 @@ Status ExternalSstFileIngestionJob::IngestedFileOverlapWithLevel(
|
||||
ro.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&cfd_->internal_comparator(),
|
||||
&arena);
|
||||
// Files are opened lazily when the iterator needs them, thus range deletions
|
||||
// are also added lazily to the aggregator. We need to check for range
|
||||
// deletion overlap only in the case where there's no point-key overlap. Then,
|
||||
// we've already opened the file with range containing the ingested file's
|
||||
// begin key, and iterated through all files until the one containing the
|
||||
// ingested file's end key. So any files maybe containing range deletions
|
||||
// overlapping the ingested file must have been opened and had their range
|
||||
// deletions added to the aggregator.
|
||||
RangeDelAggregator range_del_agg(cfd_->internal_comparator(),
|
||||
{} /* snapshots */,
|
||||
false /* collapse_deletions */);
|
||||
sv->current->AddIteratorsForLevel(ro, env_options_, &merge_iter_builder, lvl,
|
||||
nullptr /* range_del_agg */);
|
||||
&range_del_agg);
|
||||
ScopedArenaIterator level_iter(merge_iter_builder.Finish());
|
||||
|
||||
std::vector<InternalIterator*> level_range_del_iters;
|
||||
sv->current->AddRangeDelIteratorsForLevel(ro, env_options_, lvl,
|
||||
&level_range_del_iters);
|
||||
std::unique_ptr<InternalIterator> level_range_del_iter(NewMergingIterator(
|
||||
&cfd_->internal_comparator(),
|
||||
level_range_del_iters.empty() ? nullptr : &level_range_del_iters[0],
|
||||
static_cast<int>(level_range_del_iters.size())));
|
||||
|
||||
Status status = IngestedFileOverlapWithIteratorRange(
|
||||
file_to_ingest, level_iter.get(), overlap_with_level);
|
||||
if (status.ok() && *overlap_with_level == false) {
|
||||
status = IngestedFileOverlapWithRangeDeletions(
|
||||
file_to_ingest, level_range_del_iter.get(), overlap_with_level);
|
||||
if (status.ok() && *overlap_with_level == false &&
|
||||
range_del_agg.IsRangeOverlapped(file_to_ingest->smallest_user_key,
|
||||
file_to_ingest->largest_user_key)) {
|
||||
*overlap_with_level = true;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -139,13 +139,6 @@ class ExternalSstFileIngestionJob {
|
||||
const IngestedFileInfo* file_to_ingest, InternalIterator* iter,
|
||||
bool* overlap);
|
||||
|
||||
// Check if `file_to_ingest` key range overlaps with any range deletions
|
||||
// specified by `iter`.
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFileOverlapWithRangeDeletions(
|
||||
const IngestedFileInfo* file_to_ingest, InternalIterator* range_del_iter,
|
||||
bool* overlap);
|
||||
|
||||
// Check if `file_to_ingest` key range overlap with level
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFileOverlapWithLevel(SuperVersion* sv,
|
||||
|
||||
@@ -1940,6 +1940,55 @@ TEST_F(ExternalSSTFileTest, IngestBehind) {
|
||||
size_t kcnt = 0;
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileTest, SkipBloomFilter) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
|
||||
// Create external SST file and include bloom filters
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
std::string file_path = sst_files_dir_ + "sst_with_bloom.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("Key1", "Value1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile({file_path}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get("Key1"), "Value1");
|
||||
ASSERT_GE(
|
||||
options.statistics->getTickerCount(Tickers::BLOCK_CACHE_FILTER_ADD), 1);
|
||||
}
|
||||
|
||||
// Create external SST file but skip bloom filters
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
{
|
||||
std::string file_path = sst_files_dir_ + "sst_with_no_bloom.sst";
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, nullptr, true,
|
||||
Env::IOPriority::IO_TOTAL,
|
||||
true /* skip_filters */);
|
||||
ASSERT_OK(sst_file_writer.Open(file_path));
|
||||
ASSERT_OK(sst_file_writer.Put("Key1", "Value1"));
|
||||
ASSERT_OK(sst_file_writer.Finish());
|
||||
|
||||
ASSERT_OK(
|
||||
db_->IngestExternalFile({file_path}, IngestExternalFileOptions()));
|
||||
|
||||
ASSERT_EQ(Get("Key1"), "Value1");
|
||||
ASSERT_EQ(
|
||||
options.statistics->getTickerCount(Tickers::BLOCK_CACHE_FILTER_ADD), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+26
-13
@@ -30,7 +30,6 @@
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "port/likely.h"
|
||||
#include "port/port.h"
|
||||
#include "db/memtable.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -58,13 +57,14 @@ namespace rocksdb {
|
||||
FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex,
|
||||
std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
JobContext* job_context, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_file_directory,
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
Directory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats)
|
||||
: dbname_(dbname),
|
||||
@@ -77,6 +77,7 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
shutting_down_(shutting_down),
|
||||
existing_snapshots_(std::move(existing_snapshots)),
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
snapshot_checker_(snapshot_checker),
|
||||
job_context_(job_context),
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
@@ -85,6 +86,8 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
stats_(stats),
|
||||
event_logger_(event_logger),
|
||||
measure_io_stats_(measure_io_stats),
|
||||
edit_(nullptr),
|
||||
base_(nullptr),
|
||||
pick_memtable_called(false) {
|
||||
// Update the thread status to indicate flush.
|
||||
ReportStartedFlush();
|
||||
@@ -206,6 +209,8 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_);
|
||||
stream << "job" << job_context_->job_id << "event"
|
||||
<< "flush_finished";
|
||||
stream << "output_compression"
|
||||
<< CompressionTypeToString(output_compression_);
|
||||
stream << "lsm_state";
|
||||
stream.StartArray();
|
||||
auto vstorage = cfd_->current()->storage_info();
|
||||
@@ -243,6 +248,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
Status s;
|
||||
{
|
||||
auto write_hint = cfd_->CalculateSSTWriteHint(0);
|
||||
db_mutex_->Unlock();
|
||||
if (log_buffer_) {
|
||||
log_buffer_->FlushBufferToLog();
|
||||
@@ -294,27 +300,33 @@ Status FlushJob::WriteLevel0Table() {
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:output_compression",
|
||||
&output_compression_);
|
||||
EnvOptions optimized_env_options =
|
||||
db_options_.env->OptimizeForCompactionTableWrite(env_options_, db_options_);
|
||||
|
||||
int64_t _current_time = 0;
|
||||
db_options_.env->GetCurrentTime(&_current_time); // ignore error
|
||||
auto status = db_options_.env->GetCurrentTime(&_current_time);
|
||||
// Safe to proceed even if GetCurrentTime fails. So, log and proceed.
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Failed to get current time to populate creation_time property. "
|
||||
"Status: %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
const uint64_t current_time = static_cast<uint64_t>(_current_time);
|
||||
|
||||
uint64_t oldest_key_time = mems_.front()->ApproximateOldestKeyTime();
|
||||
uint64_t oldest_key_time =
|
||||
mems_.front()->ApproximateOldestKeyTime();
|
||||
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
optimized_env_options, cfd_->table_cache(), iter.get(),
|
||||
env_options_, cfd_->table_cache(), iter.get(),
|
||||
std::move(range_del_iter), &meta_, cfd_->internal_comparator(),
|
||||
cfd_->int_tbl_prop_collector_factories(), cfd_->GetID(),
|
||||
cfd_->GetName(), existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, output_compression_,
|
||||
cfd_->ioptions()->compression_opts,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_,
|
||||
output_compression_, cfd_->ioptions()->compression_opts,
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
TableFileCreationReason::kFlush, event_logger_, job_context_->job_id,
|
||||
Env::IO_HIGH, &table_properties_, 0 /* level */, current_time,
|
||||
oldest_key_time);
|
||||
oldest_key_time, write_hint);
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
@@ -352,6 +364,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
MeasureTime(stats_, FLUSH_TIME, stats.micros);
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta_.fd.GetFileSize());
|
||||
|
||||
+8
-6
@@ -43,6 +43,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
class MemTable;
|
||||
class SnapshotChecker;
|
||||
class TableCache;
|
||||
class Version;
|
||||
class VersionEdit;
|
||||
@@ -56,14 +57,14 @@ class FlushJob {
|
||||
FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
const EnvOptions env_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
JobContext* job_context, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_file_directory,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats);
|
||||
SnapshotChecker* snapshot_checker, JobContext* job_context,
|
||||
LogBuffer* log_buffer, Directory* db_directory,
|
||||
Directory* output_file_directory, CompressionType output_compression,
|
||||
Statistics* stats, EventLogger* event_logger, bool measure_io_stats);
|
||||
|
||||
~FlushJob();
|
||||
|
||||
@@ -83,12 +84,13 @@ class FlushJob {
|
||||
ColumnFamilyData* cfd_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const EnvOptions& env_options_;
|
||||
const EnvOptions env_options_;
|
||||
VersionSet* versions_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
std::atomic<bool>* shutting_down_;
|
||||
std::vector<SequenceNumber> existing_snapshots_;
|
||||
SequenceNumber earliest_write_conflict_snapshot_;
|
||||
SnapshotChecker* snapshot_checker_;
|
||||
JobContext* job_context_;
|
||||
LogBuffer* log_buffer_;
|
||||
Directory* db_directory_;
|
||||
|
||||
+26
-12
@@ -40,6 +40,7 @@ class FlushJobTest : public testing::Test {
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
db_options_.statistics = rocksdb::CreateDBStatistics();
|
||||
// TODO(icanadi) Remove this once we mock out VersionSet
|
||||
NewDB();
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
@@ -92,11 +93,12 @@ TEST_F(FlushJobTest, Empty) {
|
||||
JobContext job_context(0);
|
||||
auto cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false);
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, {}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression, nullptr, &event_logger, false);
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
flush_job.PickMemTable();
|
||||
@@ -136,16 +138,23 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
{}, kMaxSequenceNumber, snapshot_checker, &job_context,
|
||||
nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
|
||||
HistogramData hist;
|
||||
FileMetaData fd;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(&fd));
|
||||
mutex_.Unlock();
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
|
||||
ASSERT_EQ(ToString(0), fd.smallest.user_key().ToString());
|
||||
ASSERT_EQ("9999a",
|
||||
fd.largest.user_key().ToString()); // range tombstone end key
|
||||
@@ -202,16 +211,21 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
}
|
||||
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
FlushJob flush_job(
|
||||
dbname_, versions_->GetColumnFamilySet()->GetDefault(), db_options_,
|
||||
*cfd->GetLatestMutableCFOptions(), env_options_, versions_.get(), &mutex_,
|
||||
&shutting_down_, snapshots, kMaxSequenceNumber, &job_context, nullptr,
|
||||
nullptr, nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
SnapshotChecker* snapshot_checker = nullptr; // not relavant
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
snapshots, kMaxSequenceNumber, snapshot_checker,
|
||||
&job_context, nullptr, nullptr, nullptr, kNoCompression,
|
||||
db_options_.statistics.get(), &event_logger, true);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
mutex_.Unlock();
|
||||
mock_table_factory_->AssertSingleFile(inserted_keys);
|
||||
HistogramData hist;
|
||||
db_options_.statistics->histogramData(FLUSH_TIME, &hist);
|
||||
ASSERT_GT(hist.average, 0.0);
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
|
||||
@@ -566,7 +566,7 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
sv_ = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
}
|
||||
RangeDelAggregator range_del_agg(
|
||||
InternalKeyComparator(cfd_->internal_comparator()), {} /* snapshots */);
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
mutable_iter_ = sv_->mem->NewIterator(read_options_, &arena_);
|
||||
sv_->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
@@ -621,13 +621,13 @@ void ForwardIterator::RenewIterators() {
|
||||
mutable_iter_ = svnew->mem->NewIterator(read_options_, &arena_);
|
||||
svnew->imm->AddIterators(read_options_, &imm_iters_, &arena_);
|
||||
RangeDelAggregator range_del_agg(
|
||||
InternalKeyComparator(cfd_->internal_comparator()), {} /* snapshots */);
|
||||
cfd_->internal_comparator(), {} /* snapshots */);
|
||||
if (!read_options_.ignore_range_deletions) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
svnew->mem->NewRangeTombstoneIterator(read_options_));
|
||||
range_del_agg.AddTombstones(std::move(range_del_iter));
|
||||
sv_->imm->AddRangeTombstoneIterators(read_options_, &arena_,
|
||||
&range_del_agg);
|
||||
svnew->imm->AddRangeTombstoneIterators(read_options_, &arena_,
|
||||
&range_del_agg);
|
||||
}
|
||||
|
||||
const auto* vstorage = sv_->current->storage_info();
|
||||
|
||||
@@ -17,7 +17,6 @@ int main() {
|
||||
// Block forward_iterator_bench under MAC and Windows
|
||||
int main() { return 0; }
|
||||
#else
|
||||
#include <gflags/gflags.h>
|
||||
#include <semaphore.h>
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
@@ -30,11 +29,12 @@ int main() { return 0; }
|
||||
#include <random>
|
||||
#include <thread>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "port/port.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/testharness.h"
|
||||
|
||||
const int MAX_SHARDS = 100000;
|
||||
@@ -319,7 +319,7 @@ struct StatsThread {
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
GFLAGS::ParseCommandLineFlags(&argc, &argv, true);
|
||||
GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
std::mt19937 rng{std::random_device()()};
|
||||
rocksdb::Status status;
|
||||
|
||||
+81
-40
@@ -443,7 +443,7 @@ bool InternalStats::GetStringProperty(const DBPropertyInfo& property_info,
|
||||
|
||||
bool InternalStats::GetMapProperty(const DBPropertyInfo& property_info,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) {
|
||||
std::map<std::string, std::string>* value) {
|
||||
assert(value != nullptr);
|
||||
assert(property_info.handle_map != nullptr);
|
||||
return (this->*(property_info.handle_map))(value);
|
||||
@@ -521,7 +521,8 @@ bool InternalStats::HandleStats(std::string* value, Slice suffix) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InternalStats::HandleCFMapStats(std::map<std::string, double>* cf_stats) {
|
||||
bool InternalStats::HandleCFMapStats(
|
||||
std::map<std::string, std::string>* cf_stats) {
|
||||
DumpCFMapStats(cf_stats);
|
||||
return true;
|
||||
}
|
||||
@@ -788,7 +789,8 @@ bool InternalStats::HandleEstimateOldestKeyTime(uint64_t* value, DBImpl* /*db*/,
|
||||
// with allow_compaction = false. This is because we don't propagate
|
||||
// oldest_key_time on compaction.
|
||||
if (cfd_->ioptions()->compaction_style != kCompactionStyleFIFO ||
|
||||
cfd_->ioptions()->compaction_options_fifo.allow_compaction) {
|
||||
cfd_->GetCurrentMutableCFOptions()
|
||||
->compaction_options_fifo.allow_compaction) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -928,12 +930,15 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump Compaction Level stats to a map of stat name to value in double.
|
||||
* The level in stat name is represented with a prefix "Lx" where "x"
|
||||
* is the level number. A special level "Sum" represents the sum of a stat
|
||||
* for all levels.
|
||||
* Dump Compaction Level stats to a map of stat name with "compaction." prefix
|
||||
* to value in double as string. The level in stat name is represented with
|
||||
* a prefix "Lx" where "x" is the level number. A special level "Sum"
|
||||
* represents the sum of a stat for all levels.
|
||||
* The result also contains IO stall counters which keys start with "io_stalls."
|
||||
* and values represent uint64 encoded as strings.
|
||||
*/
|
||||
void InternalStats::DumpCFMapStats(std::map<std::string, double>* cf_stats) {
|
||||
void InternalStats::DumpCFMapStats(
|
||||
std::map<std::string, std::string>* cf_stats) {
|
||||
CompactionStats compaction_stats_sum(0);
|
||||
std::map<int, std::map<LevelStatType, double>> levels_stats;
|
||||
DumpCFMapStats(&levels_stats, &compaction_stats_sum);
|
||||
@@ -943,11 +948,13 @@ void InternalStats::DumpCFMapStats(std::map<std::string, double>* cf_stats) {
|
||||
for (auto const& stat_ent : level_ent.second) {
|
||||
auto stat_type = stat_ent.first;
|
||||
auto key_str =
|
||||
level_str + "." +
|
||||
"compaction." + level_str + "." +
|
||||
InternalStats::compaction_level_stats.at(stat_type).property_name;
|
||||
(*cf_stats)[key_str] = stat_ent.second;
|
||||
(*cf_stats)[key_str] = std::to_string(stat_ent.second);
|
||||
}
|
||||
}
|
||||
|
||||
DumpCFMapStatsIOStalls(cf_stats);
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFMapStats(
|
||||
@@ -1018,6 +1025,38 @@ void InternalStats::DumpCFMapStats(
|
||||
(*levels_stats)[-1] = sum_stats; // -1 is for the Sum level
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFMapStatsIOStalls(
|
||||
std::map<std::string, std::string>* cf_stats) {
|
||||
(*cf_stats)["io_stalls.level0_slowdown"] =
|
||||
std::to_string(cf_stats_count_[L0_FILE_COUNT_LIMIT_SLOWDOWNS]);
|
||||
(*cf_stats)["io_stalls.level0_slowdown_with_compaction"] =
|
||||
std::to_string(cf_stats_count_[LOCKED_L0_FILE_COUNT_LIMIT_SLOWDOWNS]);
|
||||
(*cf_stats)["io_stalls.level0_numfiles"] =
|
||||
std::to_string(cf_stats_count_[L0_FILE_COUNT_LIMIT_STOPS]);
|
||||
(*cf_stats)["io_stalls.level0_numfiles_with_compaction"] =
|
||||
std::to_string(cf_stats_count_[LOCKED_L0_FILE_COUNT_LIMIT_STOPS]);
|
||||
(*cf_stats)["io_stalls.stop_for_pending_compaction_bytes"] =
|
||||
std::to_string(cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_STOPS]);
|
||||
(*cf_stats)["io_stalls.slowdown_for_pending_compaction_bytes"] =
|
||||
std::to_string(cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS]);
|
||||
(*cf_stats)["io_stalls.memtable_compaction"] =
|
||||
std::to_string(cf_stats_count_[MEMTABLE_LIMIT_STOPS]);
|
||||
(*cf_stats)["io_stalls.memtable_slowdown"] =
|
||||
std::to_string(cf_stats_count_[MEMTABLE_LIMIT_SLOWDOWNS]);
|
||||
|
||||
uint64_t total_stop = cf_stats_count_[L0_FILE_COUNT_LIMIT_STOPS] +
|
||||
cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_STOPS] +
|
||||
cf_stats_count_[MEMTABLE_LIMIT_STOPS];
|
||||
|
||||
uint64_t total_slowdown =
|
||||
cf_stats_count_[L0_FILE_COUNT_LIMIT_SLOWDOWNS] +
|
||||
cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS] +
|
||||
cf_stats_count_[MEMTABLE_LIMIT_SLOWDOWNS];
|
||||
|
||||
(*cf_stats)["io_stalls.total_stop"] = std::to_string(total_stop);
|
||||
(*cf_stats)["io_stalls.total_slowdown"] = std::to_string(total_slowdown);
|
||||
}
|
||||
|
||||
void InternalStats::DumpCFStats(std::string* value) {
|
||||
DumpCFStatsNoFileHistogram(value);
|
||||
DumpCFFileHistogram(value);
|
||||
@@ -1052,11 +1091,12 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
uint64_t ingest_keys_addfile = cf_stats_value_[INGESTED_NUM_KEYS_TOTAL];
|
||||
// Cumulative summary
|
||||
uint64_t total_stall_count =
|
||||
cf_stats_count_[LEVEL0_SLOWDOWN_TOTAL] +
|
||||
cf_stats_count_[LEVEL0_NUM_FILES_TOTAL] +
|
||||
cf_stats_count_[SOFT_PENDING_COMPACTION_BYTES_LIMIT] +
|
||||
cf_stats_count_[HARD_PENDING_COMPACTION_BYTES_LIMIT] +
|
||||
cf_stats_count_[MEMTABLE_COMPACTION] + cf_stats_count_[MEMTABLE_SLOWDOWN];
|
||||
cf_stats_count_[L0_FILE_COUNT_LIMIT_SLOWDOWNS] +
|
||||
cf_stats_count_[L0_FILE_COUNT_LIMIT_STOPS] +
|
||||
cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS] +
|
||||
cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_STOPS] +
|
||||
cf_stats_count_[MEMTABLE_LIMIT_STOPS] +
|
||||
cf_stats_count_[MEMTABLE_LIMIT_SLOWDOWNS];
|
||||
// Interval summary
|
||||
uint64_t interval_flush_ingest =
|
||||
flush_ingest - cf_stats_snapshot_.ingest_bytes_flush;
|
||||
@@ -1145,31 +1185,32 @@ void InternalStats::DumpCFStatsNoFileHistogram(std::string* value) {
|
||||
cf_stats_snapshot_.compact_bytes_read = compact_bytes_read;
|
||||
cf_stats_snapshot_.compact_micros = compact_micros;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Stalls(count): %" PRIu64
|
||||
" level0_slowdown, "
|
||||
"%" PRIu64
|
||||
" level0_slowdown_with_compaction, "
|
||||
"%" PRIu64
|
||||
" level0_numfiles, "
|
||||
"%" PRIu64
|
||||
" level0_numfiles_with_compaction, "
|
||||
"%" PRIu64
|
||||
" stop for pending_compaction_bytes, "
|
||||
"%" PRIu64
|
||||
" slowdown for pending_compaction_bytes, "
|
||||
"%" PRIu64
|
||||
" memtable_compaction, "
|
||||
"%" PRIu64
|
||||
" memtable_slowdown, "
|
||||
"interval %" PRIu64 " total count\n",
|
||||
cf_stats_count_[LEVEL0_SLOWDOWN_TOTAL],
|
||||
cf_stats_count_[LEVEL0_SLOWDOWN_WITH_COMPACTION],
|
||||
cf_stats_count_[LEVEL0_NUM_FILES_TOTAL],
|
||||
cf_stats_count_[LEVEL0_NUM_FILES_WITH_COMPACTION],
|
||||
cf_stats_count_[HARD_PENDING_COMPACTION_BYTES_LIMIT],
|
||||
cf_stats_count_[SOFT_PENDING_COMPACTION_BYTES_LIMIT],
|
||||
cf_stats_count_[MEMTABLE_COMPACTION],
|
||||
cf_stats_count_[MEMTABLE_SLOWDOWN],
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Stalls(count): %" PRIu64
|
||||
" level0_slowdown, "
|
||||
"%" PRIu64
|
||||
" level0_slowdown_with_compaction, "
|
||||
"%" PRIu64
|
||||
" level0_numfiles, "
|
||||
"%" PRIu64
|
||||
" level0_numfiles_with_compaction, "
|
||||
"%" PRIu64
|
||||
" stop for pending_compaction_bytes, "
|
||||
"%" PRIu64
|
||||
" slowdown for pending_compaction_bytes, "
|
||||
"%" PRIu64
|
||||
" memtable_compaction, "
|
||||
"%" PRIu64
|
||||
" memtable_slowdown, "
|
||||
"interval %" PRIu64 " total count\n",
|
||||
cf_stats_count_[L0_FILE_COUNT_LIMIT_SLOWDOWNS],
|
||||
cf_stats_count_[LOCKED_L0_FILE_COUNT_LIMIT_SLOWDOWNS],
|
||||
cf_stats_count_[L0_FILE_COUNT_LIMIT_STOPS],
|
||||
cf_stats_count_[LOCKED_L0_FILE_COUNT_LIMIT_STOPS],
|
||||
cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_STOPS],
|
||||
cf_stats_count_[PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS],
|
||||
cf_stats_count_[MEMTABLE_LIMIT_STOPS],
|
||||
cf_stats_count_[MEMTABLE_LIMIT_SLOWDOWNS],
|
||||
total_stall_count - cf_stats_snapshot_.stall_count);
|
||||
value->append(buf);
|
||||
|
||||
|
||||
+24
-22
@@ -42,8 +42,9 @@ struct DBPropertyInfo {
|
||||
// holding db mutex, which is only supported for int properties.
|
||||
bool (InternalStats::*handle_int)(uint64_t* value, DBImpl* db,
|
||||
Version* version);
|
||||
bool (InternalStats::*handle_map)(
|
||||
std::map<std::string, double>* compaction_stats);
|
||||
|
||||
// @param props Map of general properties to populate
|
||||
bool (InternalStats::*handle_map)(std::map<std::string, std::string>* props);
|
||||
};
|
||||
|
||||
extern const DBPropertyInfo* GetPropertyInfo(const Slice& property);
|
||||
@@ -85,14 +86,14 @@ class InternalStats {
|
||||
static const std::map<LevelStatType, LevelStat> compaction_level_stats;
|
||||
|
||||
enum InternalCFStatsType {
|
||||
LEVEL0_SLOWDOWN_TOTAL,
|
||||
LEVEL0_SLOWDOWN_WITH_COMPACTION,
|
||||
MEMTABLE_COMPACTION,
|
||||
MEMTABLE_SLOWDOWN,
|
||||
LEVEL0_NUM_FILES_TOTAL,
|
||||
LEVEL0_NUM_FILES_WITH_COMPACTION,
|
||||
SOFT_PENDING_COMPACTION_BYTES_LIMIT,
|
||||
HARD_PENDING_COMPACTION_BYTES_LIMIT,
|
||||
L0_FILE_COUNT_LIMIT_SLOWDOWNS,
|
||||
LOCKED_L0_FILE_COUNT_LIMIT_SLOWDOWNS,
|
||||
MEMTABLE_LIMIT_STOPS,
|
||||
MEMTABLE_LIMIT_SLOWDOWNS,
|
||||
L0_FILE_COUNT_LIMIT_STOPS,
|
||||
LOCKED_L0_FILE_COUNT_LIMIT_STOPS,
|
||||
PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS,
|
||||
PENDING_COMPACTION_BYTES_LIMIT_STOPS,
|
||||
WRITE_STALLS_ENUM_MAX,
|
||||
BYTES_FLUSHED,
|
||||
BYTES_INGESTED_ADD_FILE,
|
||||
@@ -298,7 +299,7 @@ class InternalStats {
|
||||
|
||||
bool GetMapProperty(const DBPropertyInfo& property_info,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value);
|
||||
std::map<std::string, std::string>* value);
|
||||
|
||||
bool GetIntProperty(const DBPropertyInfo& property_info, uint64_t* value,
|
||||
DBImpl* db);
|
||||
@@ -312,10 +313,11 @@ class InternalStats {
|
||||
|
||||
private:
|
||||
void DumpDBStats(std::string* value);
|
||||
void DumpCFMapStats(std::map<std::string, double>* cf_stats);
|
||||
void DumpCFMapStats(std::map<std::string, std::string>* cf_stats);
|
||||
void DumpCFMapStats(
|
||||
std::map<int, std::map<LevelStatType, double>>* level_stats,
|
||||
CompactionStats* compaction_stats_sum);
|
||||
void DumpCFMapStatsIOStalls(std::map<std::string, std::string>* cf_stats);
|
||||
void DumpCFStats(std::string* value);
|
||||
void DumpCFStatsNoFileHistogram(std::string* value);
|
||||
void DumpCFFileHistogram(std::string* value);
|
||||
@@ -424,7 +426,7 @@ class InternalStats {
|
||||
bool HandleCompressionRatioAtLevelPrefix(std::string* value, Slice suffix);
|
||||
bool HandleLevelStats(std::string* value, Slice suffix);
|
||||
bool HandleStats(std::string* value, Slice suffix);
|
||||
bool HandleCFMapStats(std::map<std::string, double>* compaction_stats);
|
||||
bool HandleCFMapStats(std::map<std::string, std::string>* compaction_stats);
|
||||
bool HandleCFStats(std::string* value, Slice suffix);
|
||||
bool HandleCFStatsNoFileHistogram(std::string* value, Slice suffix);
|
||||
bool HandleCFFileHistogram(std::string* value, Slice suffix);
|
||||
@@ -496,14 +498,14 @@ class InternalStats {
|
||||
class InternalStats {
|
||||
public:
|
||||
enum InternalCFStatsType {
|
||||
LEVEL0_SLOWDOWN_TOTAL,
|
||||
LEVEL0_SLOWDOWN_WITH_COMPACTION,
|
||||
MEMTABLE_COMPACTION,
|
||||
MEMTABLE_SLOWDOWN,
|
||||
LEVEL0_NUM_FILES_TOTAL,
|
||||
LEVEL0_NUM_FILES_WITH_COMPACTION,
|
||||
SOFT_PENDING_COMPACTION_BYTES_LIMIT,
|
||||
HARD_PENDING_COMPACTION_BYTES_LIMIT,
|
||||
L0_FILE_COUNT_LIMIT_SLOWDOWNS,
|
||||
LOCKED_L0_FILE_COUNT_LIMIT_SLOWDOWNS,
|
||||
MEMTABLE_LIMIT_STOPS,
|
||||
MEMTABLE_LIMIT_SLOWDOWNS,
|
||||
L0_FILE_COUNT_LIMIT_STOPS,
|
||||
LOCKED_L0_FILE_COUNT_LIMIT_STOPS,
|
||||
PENDING_COMPACTION_BYTES_LIMIT_SLOWDOWNS,
|
||||
PENDING_COMPACTION_BYTES_LIMIT_STOPS,
|
||||
WRITE_STALLS_ENUM_MAX,
|
||||
BYTES_FLUSHED,
|
||||
BYTES_INGESTED_ADD_FILE,
|
||||
@@ -571,7 +573,7 @@ class InternalStats {
|
||||
|
||||
bool GetMapProperty(const DBPropertyInfo& property_info,
|
||||
const Slice& property,
|
||||
std::map<std::string, double>* value) {
|
||||
std::map<std::string, std::string>* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+71
-19
@@ -13,17 +13,80 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/log_writer.h"
|
||||
#include "db/column_family.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class MemTable;
|
||||
struct SuperVersion;
|
||||
|
||||
struct SuperVersionContext {
|
||||
struct WriteStallNotification {
|
||||
WriteStallInfo write_stall_info;
|
||||
const ImmutableCFOptions* immutable_cf_options;
|
||||
};
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
autovector<WriteStallNotification> write_stall_notifications;
|
||||
unique_ptr<SuperVersion> new_superversion; // if nullptr no new superversion
|
||||
|
||||
explicit SuperVersionContext(bool create_superversion = false)
|
||||
: new_superversion(create_superversion ? new SuperVersion() : nullptr) {}
|
||||
|
||||
void NewSuperVersion() {
|
||||
new_superversion = unique_ptr<SuperVersion>(new SuperVersion());
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return superversions_to_free.size() > 0 ||
|
||||
write_stall_notifications.size() > 0;
|
||||
}
|
||||
|
||||
void PushWriteStallNotification(
|
||||
WriteStallCondition old_cond, WriteStallCondition new_cond,
|
||||
const std::string& name, const ImmutableCFOptions* ioptions) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
WriteStallNotification notif;
|
||||
notif.write_stall_info.cf_name = name;
|
||||
notif.write_stall_info.condition.prev = old_cond;
|
||||
notif.write_stall_info.condition.cur = new_cond;
|
||||
notif.immutable_cf_options = ioptions;
|
||||
write_stall_notifications.push_back(notif);
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void Clean() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// notify listeners on changed write stall conditions
|
||||
for (auto& notif : write_stall_notifications) {
|
||||
for (auto listener : notif.immutable_cf_options->listeners) {
|
||||
listener->OnStallConditionsChanged(notif.write_stall_info);
|
||||
}
|
||||
}
|
||||
write_stall_notifications.clear();
|
||||
#endif // !ROCKSDB_LITE
|
||||
// free superversions
|
||||
for (auto s : superversions_to_free) {
|
||||
delete s;
|
||||
}
|
||||
superversions_to_free.clear();
|
||||
}
|
||||
|
||||
~SuperVersionContext() {
|
||||
assert(write_stall_notifications.size() == 0);
|
||||
assert(superversions_to_free.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct JobContext {
|
||||
inline bool HaveSomethingToDelete() const {
|
||||
return full_scan_candidate_files.size() || sst_delete_files.size() ||
|
||||
log_delete_files.size() || manifest_delete_files.size() ||
|
||||
new_superversion != nullptr || superversions_to_free.size() > 0 ||
|
||||
memtables_to_free.size() > 0 || logs_to_free.size() > 0;
|
||||
log_delete_files.size() || manifest_delete_files.size();
|
||||
}
|
||||
|
||||
inline bool HaveSomethingToClean() const {
|
||||
return memtables_to_free.size() > 0 || logs_to_free.size() > 0 ||
|
||||
superversion_context.HaveSomethingToDelete();
|
||||
}
|
||||
|
||||
// Structure to store information for candidate files to delete.
|
||||
@@ -65,12 +128,10 @@ struct JobContext {
|
||||
// a list of memtables to be free
|
||||
autovector<MemTable*> memtables_to_free;
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
SuperVersionContext superversion_context;
|
||||
|
||||
autovector<log::Writer*> logs_to_free;
|
||||
|
||||
SuperVersion* new_superversion; // if nullptr no new superversion
|
||||
|
||||
// the current manifest_file_number, log_number and prev_log_number
|
||||
// that corresponds to the set of files in 'live'.
|
||||
uint64_t manifest_file_number;
|
||||
@@ -83,13 +144,13 @@ struct JobContext {
|
||||
size_t num_alive_log_files = 0;
|
||||
uint64_t size_log_to_delete = 0;
|
||||
|
||||
explicit JobContext(int _job_id, bool create_superversion = false) {
|
||||
explicit JobContext(int _job_id, bool create_superversion = false)
|
||||
: superversion_context(create_superversion) {
|
||||
job_id = _job_id;
|
||||
manifest_file_number = 0;
|
||||
pending_manifest_file_number = 0;
|
||||
log_number = 0;
|
||||
prev_log_number = 0;
|
||||
new_superversion = create_superversion ? new SuperVersion() : nullptr;
|
||||
}
|
||||
|
||||
// For non-empty JobContext Clean() has to be called at least once before
|
||||
@@ -97,31 +158,22 @@ struct JobContext {
|
||||
// unlocked DB mutex. Destructor doesn't call Clean() to avoid accidentally
|
||||
// doing potentially slow Clean() with locked DB mutex.
|
||||
void Clean() {
|
||||
// free superversions
|
||||
superversion_context.Clean();
|
||||
// free pending memtables
|
||||
for (auto m : memtables_to_free) {
|
||||
delete m;
|
||||
}
|
||||
// free superversions
|
||||
for (auto s : superversions_to_free) {
|
||||
delete s;
|
||||
}
|
||||
for (auto l : logs_to_free) {
|
||||
delete l;
|
||||
}
|
||||
// if new_superversion was not used, it will be non-nullptr and needs
|
||||
// to be freed here
|
||||
delete new_superversion;
|
||||
|
||||
memtables_to_free.clear();
|
||||
superversions_to_free.clear();
|
||||
logs_to_free.clear();
|
||||
new_superversion = nullptr;
|
||||
}
|
||||
|
||||
~JobContext() {
|
||||
assert(memtables_to_free.size() == 0);
|
||||
assert(superversions_to_free.size() == 0);
|
||||
assert(new_superversion == nullptr);
|
||||
assert(logs_to_free.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
+5
-5
@@ -195,7 +195,7 @@ class LogTest : public ::testing::TestWithParam<int> {
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementByte(int offset, int delta) {
|
||||
void IncrementByte(int offset, char delta) {
|
||||
dest_contents()[offset] += delta;
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ TEST_P(LogTest, ChecksumMismatch) {
|
||||
|
||||
TEST_P(LogTest, UnexpectedMiddleType) {
|
||||
Write("foo");
|
||||
SetByte(6, GetParam() ? kRecyclableMiddleType : kMiddleType);
|
||||
SetByte(6, static_cast<char>(GetParam() ? kRecyclableMiddleType : kMiddleType));
|
||||
FixChecksum(0, 3, !!GetParam());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
ASSERT_EQ(3U, DroppedBytes());
|
||||
@@ -496,7 +496,7 @@ TEST_P(LogTest, UnexpectedMiddleType) {
|
||||
|
||||
TEST_P(LogTest, UnexpectedLastType) {
|
||||
Write("foo");
|
||||
SetByte(6, GetParam() ? kRecyclableLastType : kLastType);
|
||||
SetByte(6, static_cast<char>(GetParam() ? kRecyclableLastType : kLastType));
|
||||
FixChecksum(0, 3, !!GetParam());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
ASSERT_EQ(3U, DroppedBytes());
|
||||
@@ -506,7 +506,7 @@ TEST_P(LogTest, UnexpectedLastType) {
|
||||
TEST_P(LogTest, UnexpectedFullType) {
|
||||
Write("foo");
|
||||
Write("bar");
|
||||
SetByte(6, GetParam() ? kRecyclableFirstType : kFirstType);
|
||||
SetByte(6, static_cast<char>(GetParam() ? kRecyclableFirstType : kFirstType));
|
||||
FixChecksum(0, 3, !!GetParam());
|
||||
ASSERT_EQ("bar", Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
@@ -517,7 +517,7 @@ TEST_P(LogTest, UnexpectedFullType) {
|
||||
TEST_P(LogTest, UnexpectedFirstType) {
|
||||
Write("foo");
|
||||
Write(BigString("bar", 100000));
|
||||
SetByte(6, GetParam() ? kRecyclableFirstType : kFirstType);
|
||||
SetByte(6, static_cast<char>(GetParam() ? kRecyclableFirstType : kFirstType));
|
||||
FixChecksum(0, 3, !!GetParam());
|
||||
ASSERT_EQ(BigString("bar", 100000), Read());
|
||||
ASSERT_EQ("EOF", Read());
|
||||
|
||||
@@ -16,7 +16,12 @@
|
||||
namespace rocksdb {
|
||||
|
||||
#ifdef ROCKSDB_JEMALLOC
|
||||
#ifdef __FreeBSD__
|
||||
#include <malloc_np.h>
|
||||
#define je_malloc_stats_print malloc_stats_print
|
||||
#else
|
||||
#include "jemalloc/jemalloc.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
char* cur;
|
||||
|
||||
+50
-11
@@ -17,6 +17,7 @@
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "port/port.h"
|
||||
@@ -235,6 +236,14 @@ int MemTable::KeyComparator::operator()(const char* prefix_len_key,
|
||||
return comparator.Compare(a, key);
|
||||
}
|
||||
|
||||
void MemTableRep::InsertConcurrently(KeyHandle handle) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
throw std::runtime_error("concurrent insert not supported");
|
||||
#else
|
||||
abort();
|
||||
#endif
|
||||
}
|
||||
|
||||
Slice MemTableRep::UserKey(const char* key) const {
|
||||
Slice slice = GetLengthPrefixedSlice(key);
|
||||
return Slice(slice.data(), slice.size() - 8);
|
||||
@@ -492,7 +501,7 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
}
|
||||
|
||||
// The first sequence number inserted into the memtable
|
||||
assert(first_seqno_ == 0 || s > first_seqno_);
|
||||
assert(first_seqno_ == 0 || s >= first_seqno_);
|
||||
if (first_seqno_ == 0) {
|
||||
first_seqno_.store(s, std::memory_order_relaxed);
|
||||
|
||||
@@ -556,7 +565,15 @@ struct Saver {
|
||||
Statistics* statistics;
|
||||
bool inplace_update_support;
|
||||
Env* env_;
|
||||
ReadCallback* callback_;
|
||||
bool* is_blob_index;
|
||||
|
||||
bool CheckCallback(SequenceNumber _seq) {
|
||||
if (callback_) {
|
||||
return callback_->IsCommitted(_seq);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -584,7 +601,14 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
// Correct user key
|
||||
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
|
||||
ValueType type;
|
||||
UnPackSequenceAndType(tag, &s->seq, &type);
|
||||
SequenceNumber seq;
|
||||
UnPackSequenceAndType(tag, &seq, &type);
|
||||
// If the value is not in the snapshot, skip it
|
||||
if (!s->CheckCallback(seq)) {
|
||||
return true; // to continue to the next seq
|
||||
}
|
||||
|
||||
s->seq = seq;
|
||||
|
||||
if ((type == kTypeValue || type == kTypeMerge || type == kTypeBlobIndex) &&
|
||||
range_del_agg->ShouldDelete(Slice(key_ptr, key_length))) {
|
||||
@@ -613,10 +637,12 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
|
||||
*(s->status) = Status::OK();
|
||||
if (*(s->merge_in_progress)) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), &v,
|
||||
merge_context->GetOperands(), s->value, s->logger, s->statistics,
|
||||
s->env_, nullptr /* result_operand */, true);
|
||||
if (s->value != nullptr) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), &v,
|
||||
merge_context->GetOperands(), s->value, s->logger,
|
||||
s->statistics, s->env_, nullptr /* result_operand */, true);
|
||||
}
|
||||
} else if (s->value != nullptr) {
|
||||
s->value->assign(v.data(), v.size());
|
||||
}
|
||||
@@ -633,10 +659,12 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
case kTypeSingleDeletion:
|
||||
case kTypeRangeDeletion: {
|
||||
if (*(s->merge_in_progress)) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger, s->statistics,
|
||||
s->env_, nullptr /* result_operand */, true);
|
||||
if (s->value != nullptr) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger,
|
||||
s->statistics, s->env_, nullptr /* result_operand */, true);
|
||||
}
|
||||
} else {
|
||||
*(s->status) = Status::NotFound();
|
||||
}
|
||||
@@ -658,6 +686,14 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
*(s->merge_in_progress) = true;
|
||||
merge_context->PushOperand(
|
||||
v, s->inplace_update_support == false /* operand_pinned */);
|
||||
if (merge_operator->ShouldMerge(merge_context->GetOperands())) {
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger, s->statistics,
|
||||
s->env_, nullptr /* result_operand */, true);
|
||||
*(s->found_final_value) = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -673,7 +709,8 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context,
|
||||
RangeDelAggregator* range_del_agg, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts, bool* is_blob_index) {
|
||||
const ReadOptions& read_opts, ReadCallback* callback,
|
||||
bool* is_blob_index) {
|
||||
// The sequence number is updated synchronously in version_set.h
|
||||
if (IsEmpty()) {
|
||||
// Avoiding recording stats for speed.
|
||||
@@ -719,6 +756,7 @@ bool MemTable::Get(const LookupKey& key, std::string* value, Status* s,
|
||||
saver.inplace_update_support = moptions_.inplace_update_support;
|
||||
saver.statistics = moptions_.statistics;
|
||||
saver.env_ = env_;
|
||||
saver.callback_ = callback;
|
||||
saver.is_blob_index = is_blob_index;
|
||||
table_->Get(key, &saver, SaveValue);
|
||||
|
||||
@@ -777,6 +815,7 @@ void MemTable::Update(SequenceNumber seq,
|
||||
assert((unsigned)((p + value.size()) - entry) ==
|
||||
(unsigned)(VarintLength(key_length) + key_length +
|
||||
VarintLength(value.size()) + value.size()));
|
||||
RecordTick(moptions_.statistics, NUMBER_KEYS_UPDATED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -17,6 +17,7 @@
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "monitoring/instrumented_mutex.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -186,14 +187,15 @@ class MemTable {
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
bool* is_blob_index = nullptr);
|
||||
ReadCallback* callback = nullptr, bool* is_blob_index = nullptr);
|
||||
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
const ReadOptions& read_opts, bool* is_blob_index = nullptr) {
|
||||
const ReadOptions& read_opts, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr) {
|
||||
SequenceNumber seq;
|
||||
return Get(key, value, s, merge_context, range_del_agg, &seq, read_opts,
|
||||
is_blob_index);
|
||||
callback, is_blob_index);
|
||||
}
|
||||
|
||||
// Attempts to update the new_value inplace, else does normal Add
|
||||
@@ -366,6 +368,11 @@ class MemTable {
|
||||
return oldest_key_time_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// REQUIRES: db_mutex held.
|
||||
void SetID(uint64_t id) { id_ = id; }
|
||||
|
||||
uint64_t GetID() const { return id_; }
|
||||
|
||||
private:
|
||||
enum FlushStateEnum { FLUSH_NOT_REQUESTED, FLUSH_REQUESTED, FLUSH_SCHEDULED };
|
||||
|
||||
@@ -435,6 +442,9 @@ class MemTable {
|
||||
// Timestamp of oldest key
|
||||
std::atomic<uint64_t> oldest_key_time_;
|
||||
|
||||
// Memtable id to track flush.
|
||||
uint64_t id_ = 0;
|
||||
|
||||
// Returns a heuristic flush decision
|
||||
bool ShouldFlushNow() const;
|
||||
|
||||
|
||||
+10
-5
@@ -105,9 +105,9 @@ bool MemTableListVersion::Get(const LookupKey& key, std::string* value,
|
||||
Status* s, MergeContext* merge_context,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
bool* is_blob_index) {
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
return GetFromList(&memlist_, key, value, s, merge_context, range_del_agg,
|
||||
seq, read_opts, is_blob_index);
|
||||
seq, read_opts, callback, is_blob_index);
|
||||
}
|
||||
|
||||
bool MemTableListVersion::GetFromHistory(
|
||||
@@ -115,25 +115,30 @@ bool MemTableListVersion::GetFromHistory(
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts, bool* is_blob_index) {
|
||||
return GetFromList(&memlist_history_, key, value, s, merge_context,
|
||||
range_del_agg, seq, read_opts, is_blob_index);
|
||||
range_del_agg, seq, read_opts, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
}
|
||||
|
||||
bool MemTableListVersion::GetFromList(
|
||||
std::list<MemTable*>* list, const LookupKey& key, std::string* value,
|
||||
Status* s, MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts, bool* is_blob_index) {
|
||||
SequenceNumber* seq, const ReadOptions& read_opts, ReadCallback* callback,
|
||||
bool* is_blob_index) {
|
||||
*seq = kMaxSequenceNumber;
|
||||
|
||||
for (auto& memtable : *list) {
|
||||
SequenceNumber current_seq = kMaxSequenceNumber;
|
||||
|
||||
bool done = memtable->Get(key, value, s, merge_context, range_del_agg,
|
||||
¤t_seq, read_opts, is_blob_index);
|
||||
¤t_seq, read_opts, callback, is_blob_index);
|
||||
if (*seq == kMaxSequenceNumber) {
|
||||
// Store the most recent sequence number of any operation on this key.
|
||||
// Since we only care about the most recent change, we only need to
|
||||
// return the first operation found when searching memtables in
|
||||
// reverse-chronological order.
|
||||
// current_seq would be equal to kMaxSequenceNumber if the value was to be
|
||||
// skipped. This allows seq to be assigned again when the next value is
|
||||
// read.
|
||||
*seq = current_seq;
|
||||
}
|
||||
|
||||
|
||||
+28
-8
@@ -5,11 +5,12 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/memtable.h"
|
||||
@@ -55,14 +56,15 @@ class MemTableListVersion {
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
SequenceNumber* seq, const ReadOptions& read_opts,
|
||||
bool* is_blob_index = nullptr);
|
||||
ReadCallback* callback = nullptr, bool* is_blob_index = nullptr);
|
||||
|
||||
bool Get(const LookupKey& key, std::string* value, Status* s,
|
||||
MergeContext* merge_context, RangeDelAggregator* range_del_agg,
|
||||
const ReadOptions& read_opts, bool* is_blob_index = nullptr) {
|
||||
const ReadOptions& read_opts, ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr) {
|
||||
SequenceNumber seq;
|
||||
return Get(key, value, s, merge_context, range_del_agg, &seq, read_opts,
|
||||
is_blob_index);
|
||||
callback, is_blob_index);
|
||||
}
|
||||
|
||||
// Similar to Get(), but searches the Memtable history of memtables that
|
||||
@@ -121,7 +123,9 @@ class MemTableListVersion {
|
||||
bool GetFromList(std::list<MemTable*>* list, const LookupKey& key,
|
||||
std::string* value, Status* s, MergeContext* merge_context,
|
||||
RangeDelAggregator* range_del_agg, SequenceNumber* seq,
|
||||
const ReadOptions& read_opts, bool* is_blob_index = nullptr);
|
||||
const ReadOptions& read_opts,
|
||||
ReadCallback* callback = nullptr,
|
||||
bool* is_blob_index = nullptr);
|
||||
|
||||
void AddMemTable(MemTable* m);
|
||||
|
||||
@@ -241,6 +245,22 @@ class MemTableList {
|
||||
|
||||
uint64_t GetMinLogContainingPrepSection();
|
||||
|
||||
uint64_t GetEarliestMemTableID() const {
|
||||
auto& memlist = current_->memlist_;
|
||||
if (memlist.empty()) {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
return memlist.back()->GetID();
|
||||
}
|
||||
|
||||
uint64_t GetLatestMemTableID() const {
|
||||
auto& memlist = current_->memlist_;
|
||||
if (memlist.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return memlist.front()->GetID();
|
||||
}
|
||||
|
||||
private:
|
||||
// DB mutex held
|
||||
void InstallNewVersion();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DB;
|
||||
|
||||
class PreReleaseCallback {
|
||||
public:
|
||||
virtual ~PreReleaseCallback() {}
|
||||
|
||||
// Will be called while on the write thread after the write and before the
|
||||
// release of the sequence number. This is useful if any operation needs to be
|
||||
// done before the write gets visible to the readers, or if we want to reduce
|
||||
// the overhead of locking by updating something sequentially while we are on
|
||||
// the write thread. If the callback fails, this function returns a non-OK
|
||||
// status, the sequence number will not be released, and same status will be
|
||||
// propagated to all the writers in the write group.
|
||||
// seq is the sequence number that is used for this write and will be
|
||||
// released.
|
||||
virtual Status Callback(SequenceNumber seq) = 0;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+3
-5
@@ -17,7 +17,6 @@ int main() {
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include "db/db_impl.h"
|
||||
#include "monitoring/histogram.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
@@ -27,14 +26,15 @@ int main() {
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/slice_transform.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/gflags_compat.h"
|
||||
#include "util/random.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
#include "util/testharness.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
using GFLAGS::ParseCommandLineFlags;
|
||||
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
||||
|
||||
DEFINE_bool(trigger_deadlock, false,
|
||||
"issue delete in range scan to trigger PrefixHashMap deadlock");
|
||||
@@ -879,8 +879,6 @@ TEST_F(PrefixTest, PrefixSeekModePrev3) {
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
ParseCommandLineFlags(&argc, &argv, true);
|
||||
std::cout << kDbName << "\n";
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
@@ -43,11 +43,9 @@ void RangeDelAggregator::InitRep(const std::vector<SequenceNumber>& snapshots) {
|
||||
rep_->pinned_iters_mgr_.StartPinning();
|
||||
}
|
||||
|
||||
bool RangeDelAggregator::ShouldDelete(
|
||||
bool RangeDelAggregator::ShouldDeleteImpl(
|
||||
const Slice& internal_key, RangeDelAggregator::RangePositioningMode mode) {
|
||||
if (rep_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
assert(rep_ != nullptr);
|
||||
ParsedInternalKey parsed;
|
||||
if (!ParseInternalKey(internal_key, &parsed)) {
|
||||
assert(false);
|
||||
@@ -55,13 +53,11 @@ bool RangeDelAggregator::ShouldDelete(
|
||||
return ShouldDelete(parsed, mode);
|
||||
}
|
||||
|
||||
bool RangeDelAggregator::ShouldDelete(
|
||||
bool RangeDelAggregator::ShouldDeleteImpl(
|
||||
const ParsedInternalKey& parsed,
|
||||
RangeDelAggregator::RangePositioningMode mode) {
|
||||
assert(IsValueType(parsed.type));
|
||||
if (rep_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
assert(rep_ != nullptr);
|
||||
auto& positional_tombstone_map = GetPositionalTombstoneMap(parsed.sequence);
|
||||
const auto& tombstone_map = positional_tombstone_map.raw_map;
|
||||
if (tombstone_map.empty()) {
|
||||
@@ -144,6 +140,29 @@ bool RangeDelAggregator::ShouldDelete(
|
||||
return parsed.sequence < tombstone_map_iter->second.seq_;
|
||||
}
|
||||
|
||||
bool RangeDelAggregator::IsRangeOverlapped(const Slice& start,
|
||||
const Slice& end) {
|
||||
// so far only implemented for non-collapsed mode since file ingestion (only
|
||||
// client) doesn't use collapsing
|
||||
assert(!collapse_deletions_);
|
||||
if (rep_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& seqnum_and_tombstone_map : rep_->stripe_map_) {
|
||||
for (const auto& start_key_and_tombstone :
|
||||
seqnum_and_tombstone_map.second.raw_map) {
|
||||
const auto& tombstone = start_key_and_tombstone.second;
|
||||
if (icmp_.user_comparator()->Compare(start, tombstone.end_key_) < 0 &&
|
||||
icmp_.user_comparator()->Compare(tombstone.start_key_, end) <= 0 &&
|
||||
icmp_.user_comparator()->Compare(tombstone.start_key_,
|
||||
tombstone.end_key_) < 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RangeDelAggregator::ShouldAddTombstones(
|
||||
bool bottommost_level /* = false */) {
|
||||
// TODO(andrewkr): can we just open a file and throw it away if it ends up
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user