mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 03:22:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e68ffb872 | ||
|
|
41526f44f6 | ||
|
|
1412fd9a35 | ||
|
|
bda9def2d4 | ||
|
|
5467d7a116 | ||
|
|
28b01eb86b | ||
|
|
82fb020445 | ||
|
|
c4d49a7cd6 | ||
|
|
60f60c8475 | ||
|
|
826b5013a5 | ||
|
|
b91318fb8a | ||
|
|
55cb17e544 | ||
|
|
71cd1c2c59 | ||
|
|
d800ab757c | ||
|
|
0418aab87c | ||
|
|
753499f70d | ||
|
|
2a21b0410b | ||
|
|
35b5a76faf | ||
|
|
749cc74632 | ||
|
|
42b82e76d2 | ||
|
|
a71e3934fd | ||
|
|
f74f512df9 | ||
|
|
b2b06e5200 | ||
|
|
7faa39b288 | ||
|
|
f201a44b41 | ||
|
|
21fb8c0733 | ||
|
|
74bcb5ed20 | ||
|
|
4edd39fda2 | ||
|
|
48fd619a47 | ||
|
|
48e4e842b7 | ||
|
|
fb2e412943 | ||
|
|
5af651db24 | ||
|
|
a0ba0aa877 | ||
|
|
b88f8e87c5 | ||
|
|
08616b4933 | ||
|
|
52c9808c3a | ||
|
|
5e0d6b4cc9 | ||
|
|
ab53998372 | ||
|
|
b4d07123c4 | ||
|
|
aa09d03381 | ||
|
|
6fbe96baf8 | ||
|
|
257de78d9b | ||
|
|
d88dff4ef2 | ||
|
|
5027dd17a7 | ||
|
|
fea6fdd676 | ||
|
|
b1031d6c12 | ||
|
|
f47054015d | ||
|
|
e29d3b67c2 | ||
|
|
21e8daced5 | ||
|
|
b9311aa65c | ||
|
|
a249a0b75b | ||
|
|
040328a30d | ||
|
|
8cbe3e10ca | ||
|
|
f26a139d89 | ||
|
|
5691a1d8a4 | ||
|
|
017de666c7 | ||
|
|
1b7af5fb1a | ||
|
|
27bfe327b2 | ||
|
|
89cc404dea | ||
|
|
9e7fda8299 | ||
|
|
2e4b5cab0e | ||
|
|
ce4963fdfc | ||
|
|
e489270980 | ||
|
|
f8d8cf53fe | ||
|
|
02b3e3985c | ||
|
|
447f17127c | ||
|
|
991b585ee0 | ||
|
|
d6ae6dec69 | ||
|
|
aea3ce4c83 | ||
|
|
2ad68b971a | ||
|
|
67501cfc9a | ||
|
|
04b02dd121 | ||
|
|
8c55bb87c8 | ||
|
|
d062328977 | ||
|
|
5cd28833a2 | ||
|
|
37737c3a6b | ||
|
|
2c1f95291d | ||
|
|
043cb62d63 | ||
|
|
4985f60fc8 | ||
|
|
5aded67ddb | ||
|
|
912aec13c3 | ||
|
|
7cbb298dbe | ||
|
|
a06ad47112 | ||
|
|
3fdd5b9712 | ||
|
|
ed4fc31db5 | ||
|
|
e4922e181e | ||
|
|
6d8cd7ede1 | ||
|
|
bd55e5a1e7 | ||
|
|
0f60358b0b | ||
|
|
b90e29c901 | ||
|
|
0d7acadaf9 | ||
|
|
01be44181b | ||
|
|
9d6c961383 | ||
|
|
ab01da5437 | ||
|
|
d346ba2468 | ||
|
|
e91b4d0cf6 | ||
|
|
be1f1092c9 | ||
|
|
6009c473c7 | ||
|
|
3c21c64c78 | ||
|
|
13f7a01f61 | ||
|
|
7260662b39 | ||
|
|
26388247aa | ||
|
|
87dfc1d23e | ||
|
|
eb44ed655a | ||
|
|
e4437610dd | ||
|
|
501f051081 | ||
|
|
dec9009f89 | ||
|
|
4ed69dd0b8 | ||
|
|
21f4bb5a89 | ||
|
|
4defe306fb | ||
|
|
f517d9dd09 | ||
|
|
eb3894cf42 | ||
|
|
5c64fb67d2 | ||
|
|
9e9f5a0b92 | ||
|
|
c2a62a4cb2 | ||
|
|
9ed928e7a9 | ||
|
|
4bc8c88e6b | ||
|
|
0a1bd9c509 | ||
|
|
da5a9a65c1 | ||
|
|
d45eb6c6d2 | ||
|
|
abc0ae462b | ||
|
|
715256338a | ||
|
|
5735b3dc2a | ||
|
|
654ed9a280 | ||
|
|
e4d3f5d9b8 | ||
|
|
7afbb7420b | ||
|
|
d78a4401b5 | ||
|
|
42ac9c5f1e | ||
|
|
b666f85445 | ||
|
|
4c3f4496b5 | ||
|
|
3edb9461b7 | ||
|
|
0a88f38b7e | ||
|
|
41a9070f84 | ||
|
|
8d9bf5c498 | ||
|
|
40cfa3e021 | ||
|
|
06b4785fec | ||
|
|
d7242ff4d5 | ||
|
|
17f76fc564 | ||
|
|
215d12826d | ||
|
|
a958c26435 | ||
|
|
0a165bd7d9 | ||
|
|
3639f32889 | ||
|
|
8e061f9740 | ||
|
|
81747f1be6 | ||
|
|
ba65c816bb | ||
|
|
1d980a8e35 | ||
|
|
2adab1dde2 | ||
|
|
a182b2981d | ||
|
|
f54de92307 | ||
|
|
9e4aa798c3 | ||
|
|
a10e8a056d | ||
|
|
eb1d4d53c8 | ||
|
|
22d88e24db | ||
|
|
b06b191362 | ||
|
|
1cca091298 | ||
|
|
de28a25533 | ||
|
|
0fcb6dbed7 | ||
|
|
52ee07b021 | ||
|
|
66a91e2607 | ||
|
|
67036c0406 | ||
|
|
0e2da497c6 | ||
|
|
6d61358a09 | ||
|
|
2d9d36ea46 | ||
|
|
9377518981 | ||
|
|
1ec75ee76b | ||
|
|
607628d349 | ||
|
|
ce1be2ce37 | ||
|
|
f7669b40ba | ||
|
|
22696b0881 | ||
|
|
c1865e0f7f | ||
|
|
a88677d2cf | ||
|
|
80c75593ed | ||
|
|
5051755e35 | ||
|
|
4fd08f4b8b | ||
|
|
e14fbaae26 | ||
|
|
3c2262400f | ||
|
|
ee0e2201e0 | ||
|
|
5a0e9a4cfa | ||
|
|
9447a85404 | ||
|
|
32149059f9 | ||
|
|
4590b53a4b | ||
|
|
85bb308258 | ||
|
|
8ce1b8440a | ||
|
|
380e651afc | ||
|
|
1613fa9490 | ||
|
|
6a14d55bd9 | ||
|
|
de47e2bd4d | ||
|
|
f099af4c76 | ||
|
|
db74b1a219 | ||
|
|
b18f9c9eac | ||
|
|
4e395e875e | ||
|
|
2482d5fb45 | ||
|
|
7541c7a79e | ||
|
|
c8513cde0c | ||
|
|
b49b92cf28 | ||
|
|
c7004840d2 | ||
|
|
4ad928e170 | ||
|
|
e9b2af87f8 | ||
|
|
23a057007c | ||
|
|
dade61ac26 | ||
|
|
cce702a6e4 | ||
|
|
3586901f8f | ||
|
|
b2ce59537c | ||
|
|
4b3438d2d6 | ||
|
|
a081f798b0 | ||
|
|
b10d65c2a4 | ||
|
|
f85f99bf69 | ||
|
|
7b81095171 | ||
|
|
7c95868378 | ||
|
|
ea9e0757ff | ||
|
|
2a9c97108e | ||
|
|
d76ddf327d | ||
|
|
cec2c6436b | ||
|
|
badbff65b7 | ||
|
|
4a16c32ece | ||
|
|
99c4af7160 | ||
|
|
f57bc1d034 | ||
|
|
5440675c3d | ||
|
|
6584cec8f2 | ||
|
|
817eeb29b4 | ||
|
|
ffdf6eee19 | ||
|
|
ecf9003860 | ||
|
|
72f8cc703c | ||
|
|
6a17b07ca8 | ||
|
|
78837f5d61 | ||
|
|
4cc37f59e5 | ||
|
|
ff17a2abf3 | ||
|
|
49d88be021 | ||
|
|
0b63f51fbc | ||
|
|
3981345be1 | ||
|
|
2a2ebb6f5e | ||
|
|
2fc2fd92a9 | ||
|
|
3771e37970 | ||
|
|
236756f2cf | ||
|
|
64a0082c69 | ||
|
|
dd7a748cff | ||
|
|
4fe12baa66 | ||
|
|
6525ce4caf | ||
|
|
a117891b4b | ||
|
|
b248e98cf9 | ||
|
|
deda159b55 | ||
|
|
e408e98c8c | ||
|
|
a297643f2e | ||
|
|
d11c09d9e2 | ||
|
|
d367555027 | ||
|
|
ebdfe34cc4 | ||
|
|
d1be594636 | ||
|
|
6056d6317d | ||
|
|
76a67cf741 | ||
|
|
67c1ae8831 | ||
|
|
b693ba68b5 | ||
|
|
db3dfb164e | ||
|
|
87c91bd876 | ||
|
|
2914de64e8 | ||
|
|
8b79422b52 | ||
|
|
4beffe001d | ||
|
|
821bcb0b39 | ||
|
|
5370f44a83 | ||
|
|
56dd034115 | ||
|
|
ccecf3f4fb | ||
|
|
1b0069ce2d | ||
|
|
638c49f24f | ||
|
|
6b8e9c68b7 | ||
|
|
c38b075e7e | ||
|
|
8f399e3fee | ||
|
|
98d0b78eac | ||
|
|
9fd68b7fb6 | ||
|
|
59ddb50591 | ||
|
|
f4d986364e | ||
|
|
7882cb9773 | ||
|
|
44f5cc57a5 | ||
|
|
7c4615cf1f | ||
|
|
5bb0a7f73d | ||
|
|
86396cc189 | ||
|
|
c1db098dcf | ||
|
|
7da2eaf0d9 | ||
|
|
34723b4c4a | ||
|
|
7cc0dbd66f | ||
|
|
9253767a6a | ||
|
|
f35b16f246 | ||
|
|
c3a4bea5dc | ||
|
|
8234faabfd | ||
|
|
4990c0d1a5 | ||
|
|
7323e4c8ab | ||
|
|
ee027fc19f | ||
|
|
e4609a749b | ||
|
|
2306167d30 | ||
|
|
343304e1d3 | ||
|
|
cdc4eb6892 | ||
|
|
712dd27e67 | ||
|
|
0155c73dee | ||
|
|
c49ea68c55 | ||
|
|
726c2f7e5b | ||
|
|
d51dc96a79 | ||
|
|
c116b47804 | ||
|
|
64046e581c | ||
|
|
9ae92f50b2 | ||
|
|
7c01d65344 | ||
|
|
8796934af7 | ||
|
|
5e2c796589 | ||
|
|
557748ff7b | ||
|
|
811ee21112 | ||
|
|
afad5bd1c5 | ||
|
|
6920cde893 | ||
|
|
e72ea485ed | ||
|
|
9c8ac144bd | ||
|
|
d4c45428af | ||
|
|
d3bfd33972 | ||
|
|
7efd9c25c4 | ||
|
|
50b8d29b9c | ||
|
|
5c858ddd20 | ||
|
|
e5b5f12b81 | ||
|
|
0ce258f9b3 | ||
|
|
ac0d93b08f | ||
|
|
1ae46094de | ||
|
|
8745f013ff | ||
|
|
e12270dfee | ||
|
|
bbd6a5a184 | ||
|
|
9498069fc9 | ||
|
|
f8061a237e | ||
|
|
16e225f70d | ||
|
|
a4955b39ac | ||
|
|
ae0ad719de | ||
|
|
b2a8016df1 | ||
|
|
c6654588bd | ||
|
|
2a6d0cde72 | ||
|
|
ee8bf2e41f | ||
|
|
f85df120f2 | ||
|
|
89e4c48820 | ||
|
|
b06ca5f860 | ||
|
|
4ea0ab3cc5 | ||
|
|
12767b3130 | ||
|
|
a9d512a76b | ||
|
|
d5a51d4de3 | ||
|
|
89f319c2df | ||
|
|
b50632920d | ||
|
|
b9a97181aa | ||
|
|
663afef884 | ||
|
|
32604e6601 | ||
|
|
9ab38c45ad | ||
|
|
08ab1d83ac | ||
|
|
f9b14be495 | ||
|
|
68f3eb746c | ||
|
|
e70020e4f6 | ||
|
|
7bedd94406 | ||
|
|
68a8e6b8fa | ||
|
|
e70ba4e40e | ||
|
|
2a282e5f54 | ||
|
|
d9cfaa2b16 | ||
|
|
9430333f84 |
No files matched your search
@@ -32,6 +32,7 @@ build/
|
||||
ldb
|
||||
manifest_dump
|
||||
sst_dump
|
||||
column_aware_encoding_exp
|
||||
util/build_version.cc
|
||||
build_tools/VALGRIND_LOGS/
|
||||
coverage/COVERAGE_REPORT
|
||||
@@ -66,3 +67,5 @@ t
|
||||
LOG
|
||||
|
||||
db_logs/
|
||||
tp2/
|
||||
fbcode/
|
||||
+3
-9
@@ -9,7 +9,7 @@ compiler:
|
||||
addons:
|
||||
apt:
|
||||
sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
|
||||
packages: ['clang-3.6' , 'zlib1g-dev', 'libbz2-dev', 'libsnappy-dev', 'curl']
|
||||
packages: ['clang-3.6' , 'zlib1g-dev', 'libbz2-dev', 'libsnappy-dev', 'curl', 'gflags']
|
||||
env:
|
||||
# Run all tests before db_block_cache_test (db_test, db_test2)
|
||||
- JOB_NAME=unittests ROCKSDBTESTS_END=db_block_cache_test
|
||||
@@ -26,15 +26,9 @@ install:
|
||||
- pushd /tmp/ && curl -L https://github.com/gflags/gflags/archive/v2.1.2.tar.gz -o gflags.tar.gz && tar xfz gflags.tar.gz && cd gflags-2.1.2 && cmake . && make && popd
|
||||
|
||||
before_script:
|
||||
# Add gflags to include/library paths
|
||||
# TODO(noetzli): Remove when gflags available through Travis
|
||||
- export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/tmp/gflags-2.1.2/lib"
|
||||
- export LIBRARY_PATH="$LIBRARY_PATH:/tmp/gflags-2.1.2/lib"
|
||||
- export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/tmp/gflags-2.1.2/include"
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'linux' ]]; then CXX=clang++-3.6; fi
|
||||
- if [[ "${TRAVIS_OS_NAME}" == 'osx' ]]; then brew install gflags snappy; fi
|
||||
# Limit the maximum number of open file descriptors to 2000
|
||||
- ulimit -n 2000 || true
|
||||
# Limit the maximum number of open file descriptors to 8192
|
||||
- ulimit -n 8192 || true
|
||||
|
||||
script:
|
||||
- if [[ "${JOB_NAME}" == 'unittests' ]]; then OPT=-DTRAVIS V=1 make -j4 check_some; fi
|
||||
|
||||
+277
-100
@@ -1,7 +1,7 @@
|
||||
# This cmake build is for Windows 64-bit only.
|
||||
#
|
||||
# Prerequisites:
|
||||
# You must have Visual Studio 2013 Update 4 installed. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# You must have at least Visual Studio 2015 Update 3. Start the Developer Command Prompt window that is a part of Visual Studio installation.
|
||||
# Run the build commands from within the Developer Command Prompt window to have paths to the compiler and runtime libraries set.
|
||||
# You must have git.exe in your %PATH% environment variable.
|
||||
#
|
||||
@@ -13,8 +13,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 12 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# OR for VS Studio 15 cmake -G "Visual Studio 14 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=1 ..
|
||||
# sample command: cmake -G "Visual Studio 14 Win64" -DGFLAGS=1 -DSNAPPY=1 -DJEMALLOC=1 -DJNI=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
|
||||
@@ -29,37 +28,108 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(rocksdb)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -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})
|
||||
string(CONCAT GIT_DATE_TIME ${DATE} ${TIME})
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules/")
|
||||
|
||||
if(WIN32)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
option(WITH_JEMALLOC "build with JeMalloc" OFF)
|
||||
if(WITH_JEMALLOC)
|
||||
find_package(JeMalloc REQUIRED)
|
||||
add_definitions(-DROCKSDB_JEMALLOC)
|
||||
include_directories(${JEMALLOC_INCLUDE_DIR})
|
||||
endif()
|
||||
option(WITH_SNAPPY "build with SNAPPY" OFF)
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
add_definitions(-DSNAPPY)
|
||||
include_directories(${SNAPPY_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${SNAPPY_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
execute_process(COMMAND powershell -Command "Get-Date -format MM_dd_yyyy" OUTPUT_VARIABLE DATE)
|
||||
execute_process(COMMAND powershell -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()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
if (GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
|
||||
if(WIN32)
|
||||
execute_process(COMMAND $ENV{COMSPEC} /C ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE GIT_SHA)
|
||||
else()
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse HEAD OUTPUT_VARIABLE GIT_SHA)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_SHA 0)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA ${GIT_SHA})
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_CURRENT_SOURCE_DIR}/util/build_version.cc)
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
file(GENERATE OUTPUT ${BUILD_VERSION_CC} CONTENT
|
||||
"#include \"build_version.h\"
|
||||
const char* rocksdb_build_git_sha = \"rocksdb_build_git_sha:${GIT_SHA}\";
|
||||
const char* rocksdb_build_git_datetime = \"rocksdb_build_git_datetime:${GIT_DATE_TIME}\";
|
||||
const char* rocksdb_build_compile_date = __DATE__;
|
||||
")
|
||||
add_library(build_version OBJECT ${BUILD_VERSION_CC})
|
||||
target_include_directories(build_version PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util)
|
||||
if(WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
|
||||
add_custom_command(OUTPUT ${BUILD_VERSION_CC}
|
||||
COMMAND echo "#include \"build_version.h\"" > ${BUILD_VERSION_CC}
|
||||
COMMAND echo "const char* rocksdb_build_git_sha = \"rocksdb_build_git_sha:${GIT_SHA}\";" >> ${BUILD_VERSION_CC}
|
||||
COMMAND echo "const char* rocksdb_build_git_datetime = \"rocksdb_build_git_datetime:${GIT_DATE_TIME}\";" >> ${BUILD_VERSION_CC}
|
||||
COMMAND echo const char* rocksdb_build_compile_date = __DATE__\; >> ${BUILD_VERSION_CC}
|
||||
)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W3 /WX /wd4127 /wd4800 /wd4996 /wd4351")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall -Werror")
|
||||
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} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -fno-omit-frame-pointer -momit-leaf-frame-pointer")
|
||||
endif()
|
||||
|
||||
add_custom_target(GenerateBuildVersion DEPENDS ${BUILD_VERSION_CC})
|
||||
option(WITH_ASAN "build with ASAN" OFF)
|
||||
if(WITH_ASAN)
|
||||
add_definitions(-DROCKSDB_TSAN_RUN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
if(WITH_JEMALLOC)
|
||||
message(FATAL "ASAN does not work well with JeMalloc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
option(WITH_TSAN "build with TSAN" OFF)
|
||||
if(WITH_TSAN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread -pie")
|
||||
add_definitions(-DROCKSDB_TSAN_RUN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fPIC")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fPIC")
|
||||
if(WITH_JEMALLOC)
|
||||
message(FATAL "TSAN does not work well with JeMalloc")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W3 /WX /wd4127 /wd4800 /wd4996 /wd4351")
|
||||
option(WITH_UBSAN "build with UBSAN" OFF)
|
||||
if(WITH_UBSAN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
|
||||
if(WITH_JEMALLOC)
|
||||
message(FATAL "UBSAN does not work well with JeMalloc")
|
||||
endif()
|
||||
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
|
||||
@@ -70,33 +140,102 @@ else()
|
||||
set(OPTIMIZE_DEBUG ${OPTIMIZE_DEBUG_DEFAULT})
|
||||
endif()
|
||||
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /MDd")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm /MDd")
|
||||
if(WIN32)
|
||||
if((${OPTIMIZE_DEBUG} EQUAL 1))
|
||||
message(STATUS "Debug optimization is enabled")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "/Oxt /MDd")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /RTC1 /Gm /MDd")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /MD")
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oxt /Zp8 /Gm- /Gy /MD")
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp")
|
||||
endif()
|
||||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /DEBUG")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /DEBUG")
|
||||
option(ROCKSDB_LITE "Build RocksDBLite version" OFF)
|
||||
if(ROCKSDB_LITE)
|
||||
add_definitions(-DROCKSDB_LITE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
|
||||
endif()
|
||||
|
||||
add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DNOMINMAX)
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Cygwin")
|
||||
add_definitions(-fno-builtin-memcmp -DCYGWIN)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin")
|
||||
add_definitions(-DOS_MACOSX)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES arm)
|
||||
add_definitions(-DIOS_CROSS_COMPILE -DROCKSDB_LITE)
|
||||
# no debug info for IOS, that will make our library big
|
||||
add_definitions(-DNDEBUG)
|
||||
endif()
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
add_definitions(-DOS_SOLARIS)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_definitions(-DOS_FREEBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
add_definitions(-DOS_NETBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
|
||||
add_definitions(-DOS_OPENBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "DragonFly")
|
||||
add_definitions(-DOS_DRAGONFLYBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
add_definitions(-DOS_ANDROID)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
add_definitions(-DWIN32 -DOS_WIN -D_MBCS -DWIN64 -DNOMINMAX)
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
add_definitions(-DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX)
|
||||
endif()
|
||||
|
||||
option(WITH_FALLOCATE "build with fallocate" ON)
|
||||
|
||||
if(WITH_FALLOCATE)
|
||||
include(CheckCSourceCompiles)
|
||||
CHECK_C_SOURCE_COMPILES("
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
int main() {
|
||||
int fd = open(\"/dev/null\", 0);
|
||||
fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, 0, 1024);
|
||||
}
|
||||
" HAVE_FALLOCATE)
|
||||
if(HAVE_FALLOCATE)
|
||||
add_definitions(-DROCKSDB_FALLOCATE_PRESENT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(malloc_usable_size HAVE_MALLOC_USABLE_SIZE)
|
||||
if(HAVE_MALLOC_USABLE_SIZE)
|
||||
add_definitions(-DROCKSDB_MALLOC_USABLE_SIZE)
|
||||
endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.7.0/fused-src)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
if(WIN32)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
set(ROCKSDB_LIBS rocksdblib${ARTIFACT_SUFFIX})
|
||||
set(THIRDPARTY_LIBS ${THIRDPARTY_LIBS} gtest)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} Shlwapi.lib Rpcrt4.lib)
|
||||
|
||||
set(LIBS ${ROCKSDB_LIBS} ${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
add_subdirectory(third-party/gtest-1.7.0/fused-src/gtest)
|
||||
add_subdirectory(tools)
|
||||
|
||||
# Main library source code
|
||||
|
||||
set(SOURCES
|
||||
db/auto_roll_logger.cc
|
||||
db/builder.cc
|
||||
@@ -113,11 +252,11 @@ set(SOURCES
|
||||
db/db_impl.cc
|
||||
db/db_impl_debug.cc
|
||||
db/db_impl_experimental.cc
|
||||
db/db_impl_add_file.cc
|
||||
db/db_impl_readonly.cc
|
||||
db/db_info_dumper.cc
|
||||
db/db_iter.cc
|
||||
db/event_helpers.cc
|
||||
db/external_sst_file_ingestion_job.cc
|
||||
db/experimental.cc
|
||||
db/filename.cc
|
||||
db/file_indexer.cc
|
||||
@@ -133,6 +272,7 @@ set(SOURCES
|
||||
db/memtable_list.cc
|
||||
db/merge_helper.cc
|
||||
db/merge_operator.cc
|
||||
db/range_del_aggregator.cc
|
||||
db/repair.cc
|
||||
db/snapshot_impl.cc
|
||||
db/table_cache.cc
|
||||
@@ -153,12 +293,6 @@ set(SOURCES
|
||||
memtable/skiplistrep.cc
|
||||
memtable/vectorrep.cc
|
||||
port/stack_trace.cc
|
||||
port/win/io_win.cc
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/xpress_win.cc
|
||||
table/adaptive_table_factory.cc
|
||||
table/block.cc
|
||||
table/block_based_filter_block.cc
|
||||
@@ -192,12 +326,14 @@ set(SOURCES
|
||||
tools/dump/db_dump_tool.cc
|
||||
util/arena.cc
|
||||
util/bloom.cc
|
||||
util/build_version.cc
|
||||
util/cf_options.cc
|
||||
util/clock_cache.cc
|
||||
util/coding.cc
|
||||
util/compaction_job_stats_impl.cc
|
||||
util/comparator.cc
|
||||
util/concurrent_arena.cc
|
||||
util/crc32c.cc
|
||||
util/db_options.cc
|
||||
util/delete_scheduler.cc
|
||||
util/dynamic_bloom.cc
|
||||
util/env.cc
|
||||
@@ -220,7 +356,6 @@ set(SOURCES
|
||||
util/log_buffer.cc
|
||||
util/memenv.cc
|
||||
util/murmurhash.cc
|
||||
util/mutable_cf_options.cc
|
||||
util/options.cc
|
||||
util/options_helper.cc
|
||||
util/options_parser.cc
|
||||
@@ -236,10 +371,9 @@ set(SOURCES
|
||||
util/status_message.cc
|
||||
util/string_util.cc
|
||||
util/sync_point.cc
|
||||
util/testharness.cc
|
||||
util/testutil.cc
|
||||
util/thread_local.cc
|
||||
util/threadpool.cc
|
||||
util/threadpool_imp.cc
|
||||
util/thread_status_impl.cc
|
||||
util/thread_status_updater.cc
|
||||
util/thread_status_util.cc
|
||||
@@ -248,14 +382,15 @@ set(SOURCES
|
||||
util/xfunc.cc
|
||||
util/xxhash.cc
|
||||
utilities/backupable/backupable_db.cc
|
||||
utilities/blob_db/blob_db.cc
|
||||
utilities/checkpoint/checkpoint.cc
|
||||
utilities/compaction_filters/remove_emptyvalue_compactionfilter.cc
|
||||
utilities/date_tiered/date_tiered_db_impl.cc
|
||||
utilities/document/document_db.cc
|
||||
utilities/document/json_document.cc
|
||||
utilities/document/json_document_builder.cc
|
||||
utilities/env_mirror.cc
|
||||
utilities/env_registry.cc
|
||||
utilities/flashcache/flashcache.cc
|
||||
utilities/geodb/geodb_impl.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/memory/memory_util.cc
|
||||
@@ -264,7 +399,11 @@ set(SOURCES
|
||||
utilities/merge_operators/put.cc
|
||||
utilities/merge_operators/max.cc
|
||||
utilities/merge_operators/uint64add.cc
|
||||
utilities/option_change_migration/option_change_migration.cc
|
||||
utilities/options/options_util.cc
|
||||
utilities/persistent_cache/block_cache_tier.cc
|
||||
utilities/persistent_cache/block_cache_tier_file.cc
|
||||
utilities/persistent_cache/block_cache_tier_metadata.cc
|
||||
utilities/persistent_cache/persistent_cache_tier.cc
|
||||
utilities/persistent_cache/volatile_tier_impl.cc
|
||||
utilities/redis/redis_lists.cc
|
||||
@@ -282,55 +421,59 @@ set(SOURCES
|
||||
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
|
||||
)
|
||||
utilities/col_buf_encoder.cc
|
||||
utilities/col_buf_decoder.cc
|
||||
utilities/column_aware_encoding_util.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
|
||||
# For test util library that is build only in DEBUG mode
|
||||
# and linked to tests. Add test only code that is not #ifdefed for Release here.
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
table/mock_table.cc
|
||||
util/mock_env.cc
|
||||
util/fault_injection_test_env.cc
|
||||
util/thread_status_updater_debug.cc
|
||||
)
|
||||
if(WIN32)
|
||||
list(APPEND SOURCES
|
||||
port/win/io_win.cc
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/xpress_win.cc)
|
||||
else()
|
||||
list(APPEND SOURCES
|
||||
port/port_posix.cc
|
||||
util/env_posix.cc
|
||||
util/io_posix.cc)
|
||||
endif()
|
||||
|
||||
add_library(rocksdblib${ARTIFACT_SUFFIX} ${SOURCES})
|
||||
set_target_properties(rocksdblib${ARTIFACT_SUFFIX} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/rocksdblib${ARTIFACT_SUFFIX}.pdb")
|
||||
add_dependencies(rocksdblib${ARTIFACT_SUFFIX} GenerateBuildVersion)
|
||||
option(WITH_LIBRADOS "Build with librados" OFF)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND SOURCES
|
||||
utilities/env_librados.cc)
|
||||
list(APPEND THIRDPARTY_LIBS rados)
|
||||
endif()
|
||||
|
||||
add_library(rocksdblib${ARTIFACT_SUFFIX} STATIC ${SOURCES})
|
||||
target_link_libraries(rocksdblib${ARTIFACT_SUFFIX}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(rocksdblib${ARTIFACT_SUFFIX} PROPERTIES
|
||||
COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/rocksdblib${ARTIFACT_SUFFIX}.pdb")
|
||||
endif()
|
||||
|
||||
add_library(rocksdb${ARTIFACT_SUFFIX} SHARED ${SOURCES})
|
||||
set_target_properties(rocksdb${ARTIFACT_SUFFIX} PROPERTIES COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/rocksdb${ARTIFACT_SUFFIX}.pdb")
|
||||
add_dependencies(rocksdb${ARTIFACT_SUFFIX} GenerateBuildVersion)
|
||||
target_link_libraries(rocksdb${ARTIFACT_SUFFIX} ${LIBS})
|
||||
target_link_libraries(rocksdb${ARTIFACT_SUFFIX}
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if (DEFINED JNI)
|
||||
if (${JNI} EQUAL 1)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
else()
|
||||
message(STATUS "JNI library is disabled")
|
||||
endif()
|
||||
if(WIN32)
|
||||
set_target_properties(rocksdb${ARTIFACT_SUFFIX} PROPERTIES
|
||||
COMPILE_FLAGS "-DROCKSDB_DLL -DROCKSDB_LIBRARY_EXPORTS /Fd${CMAKE_CFG_INTDIR}/rocksdb${ARTIFACT_SUFFIX}.pdb")
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
if(WITH_JNI OR JNI)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
else()
|
||||
message(STATUS "JNI library is disabled")
|
||||
endif()
|
||||
|
||||
set(APPS
|
||||
tools/db_bench.cc
|
||||
db/memtablerep_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
tools/db_stress.cc
|
||||
tools/write_stress.cc
|
||||
tools/db_repl_stress.cc
|
||||
tools/ldb.cc
|
||||
tools/sst_dump.cc
|
||||
tools/dump/rocksdb_dump.cc
|
||||
tools/dump/rocksdb_undump.cc
|
||||
util/cache_bench.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc
|
||||
)
|
||||
|
||||
set(C_TESTS db/c_test.c)
|
||||
|
||||
set(TESTS
|
||||
db/auto_roll_logger_test.cc
|
||||
db/column_family_test.cc
|
||||
@@ -359,6 +502,7 @@ set(TESTS
|
||||
db/db_flush_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_sst_test.cc
|
||||
db/external_sst_file_test.cc
|
||||
db/db_universal_compaction_test.cc
|
||||
db/db_wal_test.cc
|
||||
db/dbformat_test.cc
|
||||
@@ -396,7 +540,6 @@ set(TESTS
|
||||
table/full_filter_block_test.cc
|
||||
table/merger_test.cc
|
||||
table/table_test.cc
|
||||
tools/db_sanity_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
tools/sst_dump_test.cc
|
||||
@@ -416,6 +559,7 @@ set(TESTS
|
||||
util/heap_test.cc
|
||||
util/histogram_test.cc
|
||||
util/iostats_context_test.cc
|
||||
util/lru_cache_test.cc
|
||||
util/mock_env_test.cc
|
||||
util/options_settable_test.cc
|
||||
util/options_test.cc
|
||||
@@ -425,13 +569,16 @@ set(TESTS
|
||||
util/thread_list_test.cc
|
||||
util/thread_local_test.cc
|
||||
utilities/backupable/backupable_db_test.cc
|
||||
utilities/blob_db/blob_db_test.cc
|
||||
utilities/checkpoint/checkpoint_test.cc
|
||||
utilities/date_tiered/date_tiered_test.cc
|
||||
utilities/document/document_db_test.cc
|
||||
utilities/document/json_document_test.cc
|
||||
utilities/env_registry_test.cc
|
||||
utilities/geodb/geodb_test.cc
|
||||
utilities/memory/memory_test.cc
|
||||
utilities/merge_operators/string_append/stringappend_test.cc
|
||||
utilities/option_change_migration/option_change_migration_test.cc
|
||||
utilities/options/options_util_test.cc
|
||||
utilities/persistent_cache/hash_table_test.cc
|
||||
utilities/persistent_cache/persistent_cache_test.cc
|
||||
@@ -442,21 +589,44 @@ set(TESTS
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
utilities/column_aware_encoding_test.cc
|
||||
)
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND TESTS utilities/env_librados_test.cc)
|
||||
endif()
|
||||
|
||||
set(EXES ${APPS})
|
||||
|
||||
foreach(sourcefile ${EXES})
|
||||
string(REPLACE ".cc" "" exename ${sourcefile})
|
||||
string(REGEX REPLACE "^((.+)/)+" "" exename ${exename})
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${LIBS})
|
||||
endforeach(sourcefile ${EXES})
|
||||
set(BENCHMARKS
|
||||
tools/db_bench.cc
|
||||
table/table_reader_bench.cc
|
||||
util/cache_bench.cc
|
||||
db/memtablerep_bench.cc
|
||||
utilities/column_aware_encoding_exp.cc
|
||||
utilities/persistent_cache/hash_table_bench.cc)
|
||||
add_library(testharness OBJECT util/testharness.cc)
|
||||
foreach(sourcefile ${BENCHMARKS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${LIBS} gtest)
|
||||
endforeach(sourcefile ${BENCHMARKS})
|
||||
|
||||
# For test util library that is build only in DEBUG mode
|
||||
# and linked to tests. Add test only code that is not #ifdefed for Release here.
|
||||
set(TESTUTIL_SOURCE
|
||||
db/db_test_util.cc
|
||||
table/mock_table.cc
|
||||
util/mock_env.cc
|
||||
util/fault_injection_test_env.cc
|
||||
util/thread_status_updater_debug.cc
|
||||
)
|
||||
# test utilities are only build in debug
|
||||
enable_testing()
|
||||
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND})
|
||||
set(TESTUTILLIB testutillib${ARTIFACT_SUFFIX})
|
||||
add_library(${TESTUTILLIB} STATIC ${TESTUTIL_SOURCE})
|
||||
set_target_properties(${TESTUTILLIB} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/testutillib${ARTIFACT_SUFFIX}.pdb")
|
||||
if(WIN32)
|
||||
set_target_properties(${TESTUTILLIB} PROPERTIES COMPILE_FLAGS "/Fd${CMAKE_CFG_INTDIR}/testutillib${ARTIFACT_SUFFIX}.pdb")
|
||||
endif()
|
||||
set_target_properties(${TESTUTILLIB}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
@@ -467,18 +637,23 @@ set_target_properties(${TESTUTILLIB}
|
||||
set(TEST_EXES ${TESTS})
|
||||
|
||||
foreach(sourcefile ${TEST_EXES})
|
||||
string(REPLACE ".cc" "" exename ${sourcefile})
|
||||
string(REGEX REPLACE "^((.+)/)+" "" exename ${exename})
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile}
|
||||
$<TARGET_OBJECTS:testharness>)
|
||||
set_target_properties(${exename}${ARTIFACT_SUFFIX}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${LIBS} testutillib${ARTIFACT_SUFFIX})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} ${LIBS} gtest)
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
endforeach(sourcefile ${TEST_EXES})
|
||||
|
||||
# C executables must link to a shared object
|
||||
set(C_TESTS db/c_test.c)
|
||||
set(C_TEST_EXES ${C_TESTS})
|
||||
|
||||
foreach(sourcefile ${C_TEST_EXES})
|
||||
@@ -491,4 +666,6 @@ foreach(sourcefile ${C_TEST_EXES})
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} rocksdb${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX})
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endforeach(sourcefile ${C_TEST_EXES})
|
||||
+33
-7
@@ -1,12 +1,38 @@
|
||||
## 4.10.2
|
||||
### Bug Fixes
|
||||
* backport the bug fix of the regression data corruption after DB recovery when stale WAL file deletion is reordered.
|
||||
# Rocksdb Change Log
|
||||
## 4.13.4
|
||||
### Public API Change
|
||||
* Removed flashcache support.
|
||||
|
||||
## 4.10.1
|
||||
### Bug Fixes
|
||||
* Fix the regression deadlock bug of DB recovery if options.max_successive_merges hits.
|
||||
## 4.13.0 (10/18/2016)
|
||||
### Public API Change
|
||||
* DB::GetOptions() reflect dynamic changed options (i.e. through DB::SetOptions()) and return copy of options instead of reference.
|
||||
* Added Statistics::getAndResetTickerCount().
|
||||
|
||||
## 4.10.0
|
||||
### New Features
|
||||
* Add DB::SetDBOptions() to dynamic change base_background_compactions and max_background_compactions.
|
||||
* Added Iterator::SeekForPrev(). This new API will seek to the last key that less than or equal to the target key.
|
||||
|
||||
## 4.12.0 (9/12/2016)
|
||||
### Public API Change
|
||||
* CancelAllBackgroundWork() flushes all memtables for databases containing writes that have bypassed the WAL (writes issued with WriteOptions::disableWAL=true) before shutting down background threads.
|
||||
* Merge options source_compaction_factor, max_grandparent_overlap_bytes and expanded_compaction_factor into max_compaction_bytes.
|
||||
* Remove ImmutableCFOptions.
|
||||
* Add a compression type ZSTD, which can work with ZSTD 0.8.0 or up. Still keep ZSTDNotFinal for compatibility reasons.
|
||||
|
||||
### New Features
|
||||
* Introduce NewClockCache, which is based on CLOCK algorithm with better concurrent performance in some cases. It can be used to replace the default LRU-based block cache and table cache. To use it, RocksDB need to be linked with TBB lib.
|
||||
* Change ticker/histogram statistics implementations to accumulate data in thread-local storage, which improves CPU performance by reducing cache coherency costs. Callers of CreateDBStatistics do not need to change anything to use this feature.
|
||||
* Block cache mid-point insertion, where index and filter block are inserted into LRU block cache with higher priority. The feature can be enabled by setting BlockBasedTableOptions::cache_index_and_filter_blocks_with_high_priority to true and high_pri_pool_ratio > 0 when creating NewLRUCache.
|
||||
|
||||
## 4.11.0 (8/1/2016)
|
||||
### Public API Change
|
||||
* options.memtable_prefix_bloom_huge_page_tlb_size => memtable_huge_page_size. When it is set, RocksDB will try to allocate memory from huge page for memtable too, rather than just memtable bloom filter.
|
||||
|
||||
### New Features
|
||||
* A tool to migrate DB after options change. See include/rocksdb/utilities/option_change_migration.h.
|
||||
* Add ReadOptions.background_purge_on_iterator_cleanup. If true, we avoid file deletion when destorying iterators.
|
||||
|
||||
## 4.10.0 (7/5/2016)
|
||||
### Public API Change
|
||||
* options.memtable_prefix_bloom_bits changes to options.memtable_prefix_bloom_bits_ratio and deprecate options.memtable_prefix_bloom_probes
|
||||
* enum type CompressionType and PerfLevel changes from char to unsigned char. Value of all PerfLevel shift by one.
|
||||
|
||||
@@ -130,8 +130,12 @@ am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
am__v_AR_1 =
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
LIB_SOURCES += utilities/env_librados.cc
|
||||
LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
# detect what platform we're building on
|
||||
dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; "$(CURDIR)/build_tools/build_detect_platform" "$(CURDIR)/make_config.mk"))
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
@@ -212,6 +216,10 @@ default: all
|
||||
WARNING_FLAGS = -W -Wextra -Wall -Wsign-compare -Wshadow \
|
||||
-Wno-unused-parameter
|
||||
|
||||
ifndef DISABLE_WARNING_AS_ERROR
|
||||
WARNING_FLAGS += -Werror
|
||||
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
|
||||
|
||||
@@ -260,6 +268,8 @@ VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
|
||||
BENCHTOOLOBJECTS = $(BENCH_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
EXPOBJECTS = $(EXP_LIB_SOURCES:.cc=.o) $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
TESTS = \
|
||||
db_test \
|
||||
db_test2 \
|
||||
@@ -275,6 +285,7 @@ TESTS = \
|
||||
db_iterator_test \
|
||||
db_options_test \
|
||||
db_sst_test \
|
||||
external_sst_file_test \
|
||||
db_tailing_iter_test \
|
||||
db_universal_compaction_test \
|
||||
db_wal_test \
|
||||
@@ -326,7 +337,9 @@ TESTS = \
|
||||
skiplist_test \
|
||||
stringappend_test \
|
||||
ttl_test \
|
||||
date_tiered_test \
|
||||
backupable_db_test \
|
||||
blob_db_test \
|
||||
document_db_test \
|
||||
json_document_test \
|
||||
sim_cache_test \
|
||||
@@ -359,6 +372,7 @@ TESTS = \
|
||||
compaction_job_test \
|
||||
thread_list_test \
|
||||
sst_dump_test \
|
||||
column_aware_encoding_test \
|
||||
compact_files_test \
|
||||
perf_context_test \
|
||||
optimistic_transaction_test \
|
||||
@@ -366,19 +380,23 @@ TESTS = \
|
||||
heap_test \
|
||||
compact_on_deletion_collector_test \
|
||||
compaction_job_stats_test \
|
||||
option_change_migration_test \
|
||||
transaction_test \
|
||||
ldb_cmd_test \
|
||||
iostats_context_test \
|
||||
persistent_cache_test \
|
||||
statistics_test \
|
||||
lru_cache_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
compact_on_deletion_collector_test \
|
||||
db_compaction_filter_test \
|
||||
db_compaction_test \
|
||||
db_sst_test \
|
||||
db_test \
|
||||
db_universal_compaction_test \
|
||||
db_wal_test \
|
||||
external_sst_file_test \
|
||||
fault_injection_test \
|
||||
inlineskiplist_test \
|
||||
manual_compaction_test \
|
||||
@@ -407,7 +425,7 @@ TEST_LIBS = \
|
||||
librocksdb_env_basic_test.a
|
||||
|
||||
# TODO: add back forward_iterator_bench, after making it build in all environemnts.
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench
|
||||
BENCHMARKS = db_bench table_reader_bench cache_bench memtablerep_bench column_aware_encoding_exp persistent_cache_bench
|
||||
|
||||
# if user didn't config LIBNAME, set the default
|
||||
ifeq ($(LIBNAME),)
|
||||
@@ -555,7 +573,7 @@ $(parallel_tests): $(PARALLEL_TEST)
|
||||
'#!/bin/sh' \
|
||||
"d=\$(TMPD)$$TEST_SCRIPT" \
|
||||
'mkdir -p $$d' \
|
||||
"TEST_TMPDIR=\$$d ./$$TEST_BINARY --gtest_filter=$$TEST_NAME" \
|
||||
"TEST_TMPDIR=\$$d $(DRIVER) ./$$TEST_BINARY --gtest_filter=$$TEST_NAME" \
|
||||
> $$TEST_SCRIPT; \
|
||||
chmod a=rx $$TEST_SCRIPT; \
|
||||
done
|
||||
@@ -582,7 +600,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)$$
|
||||
^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 \
|
||||
@@ -612,7 +630,7 @@ check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0:
|
||||
@@ -627,9 +645,9 @@ valgrind_check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| parallel -j$(J) --joblog=LOG $$eta --gnu \
|
||||
'if [[ "{}" == "./"* ]] ; then $(DRIVER) {} >& t/valgrind_log-{/}; ' \
|
||||
'else {} >& t/valgrind_log-{/}; fi'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) ' \
|
||||
'>& t/valgrind_log-{/}'
|
||||
|
||||
CLEAN_FILES += t LOG $(TMPD)
|
||||
|
||||
@@ -648,7 +666,7 @@ watch-log:
|
||||
check: all
|
||||
$(MAKE) gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) T="$$t" TMPD=$(TMPD) check_0; \
|
||||
@@ -675,9 +693,15 @@ blackbox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple blackbox
|
||||
python -u tools/db_crashtest.py blackbox
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
|
||||
whitebox_crash_test: db_stress
|
||||
python -u tools/db_crashtest.py --simple whitebox
|
||||
python -u tools/db_crashtest.py whitebox
|
||||
python -u tools/db_crashtest.py --simple whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
python -u tools/db_crashtest.py whitebox --random_kill_odd \
|
||||
$(CRASH_TEST_KILL_ODD)
|
||||
|
||||
asan_check:
|
||||
$(MAKE) clean
|
||||
@@ -700,9 +724,9 @@ ubsan_crash_test:
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_check: $(TESTS)
|
||||
$(MAKE) gen_parallel_tests
|
||||
$(MAKE) DRIVER="$(VALGRIND_VER) $(VALGRIND_OPTS)" gen_parallel_tests
|
||||
$(AM_V_GEN)if test "$(J)" != 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
$(MAKE) TMPD=$(TMPD) \
|
||||
@@ -724,10 +748,10 @@ parloop:
|
||||
for t in $(PAR_TEST); do \
|
||||
echo "===== Running $$t in parallel $(NUM_PAR)";\
|
||||
if [ $(db_test) -eq 1 ]; then \
|
||||
seq $(J) | v="$$t" parallel --gnu 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
seq $(J) | v="$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; export TEST_TMPDIR=$$s;' \
|
||||
'timeout 2m ./db_test --gtest_filter=$$v >> $$s/log-{} 2>1'; \
|
||||
else\
|
||||
seq $(J) | v="./$$t" parallel --gnu 's=$(TMPD)/rdb-{};' \
|
||||
seq $(J) | v="./$$t" build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{};' \
|
||||
'export TEST_TMPDIR=$$s; timeout 10m $$v >> $$s/log-{} 2>1'; \
|
||||
fi; \
|
||||
ret_code=$$?; \
|
||||
@@ -748,7 +772,7 @@ test_names = \
|
||||
|
||||
parallel_check: $(TESTS)
|
||||
$(AM_V_GEN)if test "$(J)" > 1 \
|
||||
&& (parallel --gnu --help 2>/dev/null) | \
|
||||
&& (build_tools/gnu_parallel --gnu --help 2>/dev/null) | \
|
||||
grep -q 'GNU Parallel'; \
|
||||
then \
|
||||
echo Running in parallel $(J); \
|
||||
@@ -758,7 +782,7 @@ parallel_check: $(TESTS)
|
||||
ret_bad=0; \
|
||||
echo $(J);\
|
||||
echo Test Dir: $(TMPD); \
|
||||
seq $(J) | parallel --gnu 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
seq $(J) | build_tools/gnu_parallel --gnu --plain 's=$(TMPD)/rdb-{}; rm -rf $$s; mkdir $$s'; \
|
||||
$(MAKE) PAR_TEST="$(shell $(test_names))" TMPD=$(TMPD) \
|
||||
J=$(J) db_test=1 parloop; \
|
||||
$(MAKE) PAR_TEST="$(filter-out db_test, $(TESTS))" \
|
||||
@@ -797,6 +821,7 @@ clean:
|
||||
find . -name "*.[oda]" -exec rm -f {} \;
|
||||
find . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm {} \;
|
||||
rm -rf bzip2* snappy* zlib* lz4*
|
||||
cd java; $(MAKE) clean
|
||||
|
||||
tags:
|
||||
ctags * -R
|
||||
@@ -829,6 +854,9 @@ db_bench: tools/db_bench.o $(BENCHTOOLOBJECTS)
|
||||
cache_bench: util/cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
persistent_cache_bench: utilities/persistent_cache/persistent_cache_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
memtablerep_bench: db/memtablerep_bench.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -850,7 +878,7 @@ arena_test: util/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_family_test: db/column_family_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
column_family_test: db/column_family_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
table_properties_collector_test: db/table_properties_collector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
@@ -871,6 +899,9 @@ cache_test: util/cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
coding_test: util/coding_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
option_change_migration_test: utilities/option_change_migration/option_change_migration_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
stringappend_test: utilities/merge_operators/string_append/stringappend_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -934,6 +965,9 @@ db_options_test: db/db_options_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARN
|
||||
db_sst_test: db/db_sst_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
external_sst_file_test: db/external_sst_file_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
db_tailing_iter_test: db/db_tailing_iter_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -976,6 +1010,9 @@ prefix_test: db/prefix_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
backupable_db_test: utilities/backupable/backupable_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_db_test: utilities/blob_db/blob_db_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
checkpoint_test: utilities/checkpoint/checkpoint_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -994,12 +1031,20 @@ spatial_db_test: utilities/spatialdb/spatial_db_test.o $(LIBOBJECTS) $(TESTHARNE
|
||||
env_mirror_test: utilities/env_mirror_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
env_librados_test: utilities/env_librados_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
endif
|
||||
|
||||
env_registry_test: utilities/env_registry_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
ttl_test: utilities/ttl/ttl_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
date_tiered_test: utilities/date_tiered/date_tiered_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
write_batch_with_index_test: utilities/write_batch_with_index/write_batch_with_index_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1156,6 +1201,9 @@ event_logger_test: util/event_logger_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
sst_dump_test: tools/sst_dump_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_test: utilities/column_aware_encoding_test.o $(TESTHARNESS) $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
optimistic_transaction_test: utilities/transactions/optimistic_transaction_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1186,6 +1234,9 @@ transaction_test: utilities/transactions/transaction_test.o $(LIBOBJECTS) $(TEST
|
||||
sst_dump: tools/sst_dump.o $(LIBOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
column_aware_encoding_exp: utilities/column_aware_encoding_exp.o $(EXPOBJECTS)
|
||||
$(AM_LINK)
|
||||
|
||||
repair_test: db/repair_test.o db/db_test_util.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1204,6 +1255,10 @@ persistent_cache_test: utilities/persistent_cache/persistent_cache_test.o db/db
|
||||
statistics_test: util/statistics_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
lru_cache_test: util/lru_cache_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -1274,29 +1329,29 @@ endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-1.2.8
|
||||
curl -O http://zlib.net/zlib-1.2.8.tar.gz
|
||||
curl -O -L http://zlib.net/zlib-1.2.8.tar.gz
|
||||
tar xvzf zlib-1.2.8.tar.gz
|
||||
cd zlib-1.2.8 && CFLAGS='-fPIC' ./configure --static && make
|
||||
cp zlib-1.2.8/libz.a .
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-1.0.6
|
||||
curl -O http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
|
||||
curl -O -L http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz
|
||||
tar xvzf bzip2-1.0.6.tar.gz
|
||||
cd bzip2-1.0.6 && make CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64'
|
||||
cp bzip2-1.0.6/libbz2.a .
|
||||
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-1.1.1
|
||||
curl -O https://snappy.googlecode.com/files/snappy-1.1.1.tar.gz
|
||||
tar xvzf snappy-1.1.1.tar.gz
|
||||
cd snappy-1.1.1 && ./configure --with-pic --enable-static
|
||||
cd snappy-1.1.1 && make
|
||||
cp snappy-1.1.1/.libs/libsnappy.a .
|
||||
-rm -rf snappy-1.1.3
|
||||
curl -O -L https://github.com/google/snappy/releases/download/1.1.3/snappy-1.1.3.tar.gz
|
||||
tar xvzf snappy-1.1.3.tar.gz
|
||||
cd snappy-1.1.3 && ./configure --with-pic --enable-static
|
||||
cd snappy-1.1.3 && make
|
||||
cp snappy-1.1.3/.libs/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-r127
|
||||
curl -O https://codeload.github.com/Cyan4973/lz4/tar.gz/r127
|
||||
curl -O -L https://codeload.github.com/Cyan4973/lz4/tar.gz/r127
|
||||
mv r127 lz4-r127.tar.gz
|
||||
tar xvzf lz4-r127.tar.gz
|
||||
cd lz4-r127/lib && make CFLAGS='-fPIC' all
|
||||
@@ -1307,7 +1362,7 @@ java_static_libobjects = $(patsubst %,jls/%,$(LIBOBJECTS))
|
||||
CLEAN_FILES += jls
|
||||
|
||||
JAVA_STATIC_FLAGS = -DZLIB -DBZIP2 -DSNAPPY -DLZ4
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-1.2.8 -I./bzip2-1.0.6 -I./snappy-1.1.1 -I./lz4-r127/lib
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-1.2.8 -I./bzip2-1.0.6 -I./snappy-1.1.3 -I./lz4-r127/lib
|
||||
|
||||
$(java_static_libobjects): jls/%.o: %.cc libz.a libbz2.a libsnappy.a liblz4.a
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
@@ -1332,12 +1387,15 @@ rocksdbjavastaticrelease: 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
|
||||
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease
|
||||
rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentral
|
||||
|
||||
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)-javadoc.jar -Dclassifier=javadoc
|
||||
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)-sources.jar -Dclassifier=sources
|
||||
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)-linux64.jar -Dclassifier=linux64
|
||||
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)-linux32.jar -Dclassifier=linux32
|
||||
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)-osx.jar -Dclassifier=osx
|
||||
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)-win64.jar -Dclassifier=win64
|
||||
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
|
||||
@@ -1358,6 +1416,12 @@ rocksdbjava: $(java_libobjects)
|
||||
jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
|
||||
jtest_compile: rocksdbjava
|
||||
cd java;$(MAKE) java_test
|
||||
|
||||
jtest_run:
|
||||
cd java;$(MAKE) run_test
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample;$(MAKE) test;
|
||||
|
||||
@@ -1366,7 +1430,7 @@ jdb_bench:
|
||||
|
||||
commit_prereq: build_tools/rocksdb-lego-determinator \
|
||||
build_tools/precommit_checker.py
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit tsan asan ubsan lite
|
||||
J=$(J) build_tools/precommit_checker.py unit unit_481 clang_unit release release_481 clang_release tsan asan ubsan lite unit_non_shm
|
||||
$(MAKE) clean && $(MAKE) jclean && $(MAKE) rocksdbjava;
|
||||
|
||||
xfunc:
|
||||
@@ -1414,7 +1478,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES)
|
||||
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)
|
||||
|
||||
# Add proper dependency support so changing a .h file forces a .cc file to
|
||||
|
||||
@@ -1,6 +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)
|
||||
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on LevelDB by Sanjay Ghemawat (sanjay@google.com)
|
||||
|
||||
@@ -71,3 +71,6 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
|
||||
## Apache Flink
|
||||
[Apache Flink](https://flink.apache.org/news/2016/03/08/release-1.0.0.html) uses RocksDB to store state locally on a machine.
|
||||
|
||||
## Dgraph
|
||||
[Dgraph](https://github.com/dgraph-io/dgraph) is an open-source, scalable, distributed, low latency, high throughput Graph database .They use RocksDB to store state locally on a machine.
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2015
|
||||
before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cmake -G "Visual Studio 12 Win64" -DOPTDBG=1 -DXPRESS=1 ..
|
||||
- cmake -G "Visual Studio 14 2015 Win64" -DOPTDBG=1 -DXPRESS=1 ..
|
||||
- cd ..
|
||||
build:
|
||||
project: build\rocksdb.sln
|
||||
|
||||
@@ -46,7 +46,7 @@ if (class_exists('ArcanistWorkflow')) {
|
||||
'BaseDirectoryScopedFormatLinter' => 'cpp_linter/BaseDirectoryScopedFormatLinter.php',
|
||||
'FacebookArcanistConfiguration' => 'config/FacebookOldArcanistConfiguration.php',
|
||||
'FacebookFbcodeLintEngine' => 'lint_engine/FacebookFbcodeLintEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookFbcodeUnitTestEngine.php',
|
||||
'FacebookFbcodeUnitTestEngine' => 'unit_engine/FacebookOldFbcodeUnitTestEngine.php',
|
||||
'FacebookHowtoevenLintEngine' => 'lint_engine/FacebookHowtoevenLintEngine.php',
|
||||
'FacebookHowtoevenLinter' => 'cpp_linter/FacebookHowtoevenLinter.php',
|
||||
'FbcodeClangFormatLinter' => 'cpp_linter/FbcodeClangFormatLinter.php',
|
||||
|
||||
@@ -25,6 +25,7 @@ function postURL($diffID, $url) {
|
||||
$cmd = 'echo \'{"diff_id": "' . $diffID . '", '
|
||||
. '"name":"click here for sandcastle tests for D' . $diffID . '", '
|
||||
. '"link":"' . $url . '"}\' | '
|
||||
. 'no_proxy=facebook.com,tfbnw.net,fb.com '
|
||||
. 'http_proxy=fwdproxy.any.facebook.com:8080 '
|
||||
. 'https_proxy=fwdproxy.any.facebook.com:8080 arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
@@ -40,6 +41,7 @@ function buildUpdateTestStatusCmd($diffID, $test, $status) {
|
||||
$cmd = 'echo \'{"diff_id": "' . $diffID . '", '
|
||||
. '"name":"' . $test . '", '
|
||||
. '"result":"' . $status . '"}\' | '
|
||||
. 'no_proxy=facebook.com,tfbnw.net,fb.com '
|
||||
. 'http_proxy=fwdproxy.any.facebook.com:8080 '
|
||||
. 'https_proxy=fwdproxy.any.facebook.com:8080 arc call-conduit '
|
||||
. 'differential.updateunitresults';
|
||||
@@ -62,7 +64,9 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
assert(strlen($diffID) > 0);
|
||||
assert(is_numeric($diffID));
|
||||
|
||||
$arcrc_content = exec("cat ~/.arcrc | gzip -f | base64 -w0");
|
||||
$arcrc_content = (PHP_OS == "Darwin" ?
|
||||
exec("cat ~/.arcrc | gzip -f | base64") :
|
||||
exec("cat ~/.arcrc | gzip -f | base64 -w0"));
|
||||
assert(strlen($arcrc_content) > 0);
|
||||
|
||||
// Sandcastle machines don't have arc setup. We copy the user certificate
|
||||
@@ -75,9 +79,10 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
);
|
||||
|
||||
// arc demands certain permission on its config.
|
||||
// also fix the sticky bit issue in sandcastle
|
||||
$fix_permission = array(
|
||||
"name" => "Fix environment",
|
||||
"shell" => "chmod 600 ~/.arcrc",
|
||||
"shell" => "chmod 600 ~/.arcrc && chmod +t /dev/shm",
|
||||
"user" => "root"
|
||||
);
|
||||
|
||||
@@ -103,7 +108,8 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
// Patch the code (keep your fingures crossed).
|
||||
$patch = array(
|
||||
"name" => "Patch " . $diffID,
|
||||
"shell" => "HTTPS_PROXY=fwdproxy:8080 arc --arcrc-file ~/.arcrc "
|
||||
"shell" => "no_proxy=facebook.com,tfbnw.net,fb.com "
|
||||
."HTTPS_PROXY=fwdproxy:8080 arc --arcrc-file ~/.arcrc "
|
||||
. "patch --nocommit --diff " . $diffID,
|
||||
"user" => "root"
|
||||
);
|
||||
@@ -115,7 +121,7 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
}
|
||||
|
||||
// Run the actual command.
|
||||
$cmd = $cmd . "./build_tools/precommit_checker.py " . $test
|
||||
$cmd = $cmd . "J=$(nproc) ./build_tools/precommit_checker.py " . $test
|
||||
. "; exit_code=$?; ";
|
||||
|
||||
if ($applyDiff) {
|
||||
@@ -125,15 +131,31 @@ function getSteps($applyDiff, $diffID, $username, $test) {
|
||||
. "; ";
|
||||
}
|
||||
|
||||
// shell command to sort the tests based on exit code and print
|
||||
// the output of the log files.
|
||||
$cat_sorted_logs = "
|
||||
while read code log_file;
|
||||
do echo \"################ cat \$log_file [exit_code : \$code] ################\";
|
||||
cat \$log_file;
|
||||
done < <(tail -n +2 LOG | sort -k7,7n -k4,4gr | awk '{print \$7,\$NF}')";
|
||||
|
||||
// Shell command to cat all log files
|
||||
$cat_all_logs = "for f in `ls t/!(run-*)`; do echo \$f;cat \$f; done";
|
||||
|
||||
// If LOG file exist use it to cat log files sorted by exit code, otherwise
|
||||
// cat everything
|
||||
$logs_cmd = "if [ -f LOG ]; then {$cat_sorted_logs}; else {$cat_all_logs}; fi";
|
||||
|
||||
$cmd = $cmd . " cat /tmp/precommit-check.log"
|
||||
. "; for f in `ls t/log-*`; do echo \$f; cat \$f; done;"
|
||||
. "[[ \$exit_code -eq 0 ]]";
|
||||
. "; shopt -s extglob; {$logs_cmd}"
|
||||
. "; shopt -u extglob; [[ \$exit_code -eq 0 ]]";
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$run_test = array(
|
||||
"name" => "Run " . $test,
|
||||
"shell" => $cmd,
|
||||
"user" => "root",
|
||||
"parser" => "python build_tools/error_filter.py " . $test,
|
||||
);
|
||||
|
||||
$steps[] = $run_test;
|
||||
@@ -164,6 +186,14 @@ function getSandcastleConfig() {
|
||||
} else {
|
||||
// This is a typical `[p]arc diff` case. Fetch the values from the specific
|
||||
// configuration files.
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
if (file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists(SECONDARY_TOKEN_FILE)) {
|
||||
break;
|
||||
}
|
||||
// If we failed to fetch the tokens, sleep for 0.2 second and try again
|
||||
usleep(200000);
|
||||
}
|
||||
assert(file_exists(PRIMARY_TOKEN_FILE) ||
|
||||
file_exists(SECONDARY_TOKEN_FILE));
|
||||
|
||||
@@ -212,21 +242,11 @@ function getSandcastleConfig() {
|
||||
assert(is_numeric($diffID));
|
||||
}
|
||||
|
||||
if (strcmp(getenv("ROCKSDB_CHECK_ALL"), 1) == 0) {
|
||||
// Extract all tests from the CI definition.
|
||||
$output = file_get_contents("build_tools/rocksdb-lego-determinator");
|
||||
assert(strlen($output) > 0);
|
||||
|
||||
preg_match_all('/[ ]{2}([a-zA-Z0-9_]+)[\)]{1}/', $output, $matches);
|
||||
$tests = $matches[1];
|
||||
assert(count($tests) > 0);
|
||||
} else {
|
||||
// Manually list of tests we want to run in Sandcastle.
|
||||
$tests = array(
|
||||
"unit", "unit_481", "clang_unit", "tsan", "asan", "lite_test",
|
||||
"valgrind", "release"
|
||||
);
|
||||
}
|
||||
// List of tests we want to run in Sandcastle.
|
||||
$tests = array("unit", "unit_non_shm", "unit_481", "clang_unit", "tsan",
|
||||
"asan", "lite_test", "valgrind", "release", "release_481",
|
||||
"clang_release", "punit", "clang_analyze", "code_cov",
|
||||
"java_build", "no_compression", "unity", "ubsan");
|
||||
|
||||
$send_email_template = array(
|
||||
'type' => 'email',
|
||||
@@ -255,7 +275,9 @@ function getSandcastleConfig() {
|
||||
// execute. Why compress the job definitions? Otherwise we run over the max
|
||||
// string size.
|
||||
$cmd = "echo " . base64_encode(json_encode($arg))
|
||||
. " | gzip -f | base64 -w0";
|
||||
. (PHP_OS == "Darwin" ?
|
||||
" | gzip -f | base64" :
|
||||
" | gzip -f | base64 -w0");
|
||||
assert(strlen($cmd) > 0);
|
||||
|
||||
$arg_encoded = shell_exec($cmd);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistBaseUnitTestEngine {
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// For a call to `arc call-conduit differential.updateunitresults` to
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
class FacebookFbcodeUnitTestEngine extends ArcanistBaseUnitTestEngine {
|
||||
|
||||
public function run() {
|
||||
// For a call to `arc call-conduit differential.updateunitresults` to
|
||||
// succeed we need at least one entry here.
|
||||
$result = new ArcanistUnitTestResult();
|
||||
$result->setName("dummy_placeholder_entry");
|
||||
$result->setResult(ArcanistUnitTestResult::RESULT_PASS);
|
||||
return array($result);
|
||||
}
|
||||
}
|
||||
@@ -19,22 +19,21 @@
|
||||
#
|
||||
# The PLATFORM_CCFLAGS and PLATFORM_CXXFLAGS might include the following:
|
||||
#
|
||||
# -DLEVELDB_PLATFORM_POSIX if cstdatomic is present
|
||||
# -DLEVELDB_PLATFORM_NOATOMIC if it is not
|
||||
# -DROCKSDB_PLATFORM_POSIX if posix-platform based
|
||||
# -DSNAPPY if the Snappy library is present
|
||||
# -DLZ4 if the LZ4 library is present
|
||||
# -DZSTD if the ZSTD library is present
|
||||
# -DNUMA if the NUMA library is present
|
||||
# -DTBB if the TBB library is present
|
||||
#
|
||||
# Using gflags in rocksdb:
|
||||
# Our project depends on gflags, which requires users to take some extra steps
|
||||
# before they can compile the whole repository:
|
||||
# 1. Install gflags. You may download it from here:
|
||||
# https://code.google.com/p/gflags/
|
||||
# 2. Once install, add the include path/lib path for gflags to CPATH and
|
||||
# LIBRARY_PATH respectively. If installed with default mode, the
|
||||
# lib and include path will be /usr/local/lib and /usr/local/include
|
||||
# Mac user can do this by having brew installed and running brew install gflags
|
||||
# https://gflags.github.io/gflags/ (Mac users can `brew install gflags`)
|
||||
# 2. Once installed, add the include path for gflags to your CPATH env var and
|
||||
# the lib path to LIBRARY_PATH. If installed with default settings, the lib
|
||||
# will be /usr/local/lib and the include path will be /usr/local/include
|
||||
|
||||
OUTPUT=$1
|
||||
if test -z "$OUTPUT"; then
|
||||
@@ -52,7 +51,12 @@ if [ -z "$ROCKSDB_NO_FBCODE" -a -d /mnt/gvfs/third-party ]; then
|
||||
FBCODE_BUILD="true"
|
||||
# If we're compiling with TSAN we need pic build
|
||||
PIC_BUILD=$COMPILE_WITH_TSAN
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
if [ -z "$ROCKSDB_FBCODE_BUILD_WITH_481" ]; then
|
||||
source "$PWD/build_tools/fbcode_config.sh"
|
||||
else
|
||||
# we need this to build with MySQL. Don't use for other purposes.
|
||||
source "$PWD/build_tools/fbcode_config4.8.1.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Delete existing output, if it exists
|
||||
@@ -165,7 +169,7 @@ case "$TARGET_OS" in
|
||||
;;
|
||||
OS_ANDROID_CROSSCOMPILE)
|
||||
PLATFORM=OS_ANDROID
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DLEVELDB_PLATFORM_POSIX"
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_ANDROID -DROCKSDB_PLATFORM_POSIX"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS " # All pthread features are in the Android C library
|
||||
# PORT_FILES=port/android/android.cc
|
||||
CROSS_COMPILE=true
|
||||
@@ -283,7 +287,7 @@ EOF
|
||||
# Test whether numa is available
|
||||
$CXX $CFLAGS -x c++ - -o /dev/null -lnuma 2>/dev/null <<EOF
|
||||
#include <numa.h>
|
||||
#inlcude <numaif.h>
|
||||
#include <numaif.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
@@ -292,6 +296,17 @@ EOF
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -lnuma"
|
||||
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() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DTBB"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -ltbb"
|
||||
JAVA_LDFLAGS="$JAVA_LDFLAGS -ltbb"
|
||||
fi
|
||||
|
||||
# Test whether jemalloc is available
|
||||
if echo 'int main() {}' | $CXX $CFLAGS -x c++ - -o /dev/null -ljemalloc \
|
||||
2>/dev/null; then
|
||||
|
||||
+17
-19
@@ -1,19 +1,17 @@
|
||||
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/7331085db891a2ef4a88a48a751d834e8d68f4cb/7.x/centos7-native/b2ef2b6
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/963d9aeda70cc4779885b1277484fe7544a04e3e/9.0.0/platform007/9e92d53/
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/6ace84e956873d53638c738b6f65f3f469cca74c/7.x/platform007/5620abc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/192b0f42d63dcf6210d6ceae387b49af049e6e0c/2.26/platform007/f259413
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/7f9bdaada18f59bc27ec2b0871eb8a6144343aef/1.1.3/platform007/ca4da3d
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2d9f0b9a4274cc21f61272a9e89bdb859bce8f1f/1.2.8/platform007/ca4da3d
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/dc49a21c5fceec6456a7a28a94dcd16690af1337/1.0.6/platform007/ca4da3d
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0f607f8fc442ea7d6b876931b1898bb573d5e5da/1.9.1/platform007/ca4da3d
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/ca22bc441a4eb709e9e0b1f9fec9750fed7b31c5/1.4.x/platform007/15a3614
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/0b9929d2588991c65a57168bf88aff2db87c5d48/2.2.0/platform007/ca4da3d
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/c26f08f47ac35fc31da2633b7da92d6b863246eb/master/platform007/c26c002
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/3f3fb57a5ccc5fd21c66416c0b83e0aa76a05376/2.0.11/platform007/ca4da3d
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/40c73d874898b386a71847f1b99115d93822d11f/1.4/platform007/6f3e0a9
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/4ce8e8dba77cdbd81b75d6f0c32fd7a1b76a11ec/2018_U5/platform007/ca4da3d
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/fb251ecd2f5ae16f8671f7014c246e52a748fe0b/fb/platform007/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/ab9f09bba370e7066cafd4eb59752db93f2e8312/2.29.1/platform007/15a3614
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d42d152a15636529b0861ec493927200ebebca8e/3.15.0/platform007/ca4da3d
|
||||
LUA_BASE=/mnt/gvfs/third-party2/lua/f0cd714433206d5139df61659eb7b28b1dea6683/5.3.4/platform007/5007832
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.9.x/centos6-native/108cf83
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.9.x/gcc-4.9-glibc-2.20/024dbc3
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.20/gcc-4.9-glibc-2.20/500e281
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.9-glibc-2.20/e9936bf
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/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/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-5-glibc-2.23/9bc6787
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-5-glibc-2.23/9bc6787
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/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/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-5-glibc-2.23/b1847cb
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.9-glibc-2.20/e9936bf
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-5-glibc-2.23/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.10.0/gcc-4.9-glibc-2.20/e9936bf
|
||||
@@ -1,16 +1,17 @@
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/ebc96bc2fb751b5a0300b8d91a95bdf24ac1d88b/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/b91de48a4974ec839946d824402b098d43454cef/stable/centos6-native/7aaccbe
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/53e0eac8911888a105aa98b9a35fe61cf1d8b278/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/ee36ac9a72dfac4a995f1b215bb4c0fc8a0f6f91/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
GCC_BASE=/mnt/gvfs/third-party2/gcc/cf7d14c625ce30bae1a4661c2319c5a283e4dd22/4.8.1/centos6-native/cc6c9dc
|
||||
CLANG_BASE=/mnt/gvfs/third-party2/llvm-fb/8598c375b0e94e1448182eb3df034704144a838d/stable/centos6-native/3f16ddd
|
||||
LIBGCC_BASE=/mnt/gvfs/third-party2/libgcc/d6e0a7da6faba45f5e5b1638f9edd7afc2f34e7d/4.8.1/gcc-4.8.1-glibc-2.17/8aac7fc
|
||||
GLIBC_BASE=/mnt/gvfs/third-party2/glibc/d282e6e8f3d20f4e40a516834847bdc038e07973/2.17/gcc-4.8.1-glibc-2.17/99df8fc
|
||||
SNAPPY_BASE=/mnt/gvfs/third-party2/snappy/8c38a4c1e52b4c2cc8a9cdc31b9c947ed7dbfcb4/1.1.3/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/2b24f1e99454f9ca7b0301720f94836dae1bf71b/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/af7c14c9b652cdd5ec34eadd25c3f38a9b306c5d/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZLIB_BASE=/mnt/gvfs/third-party2/zlib/0882df3713c7a84f15abe368dc004581f20b39d7/1.2.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
BZIP2_BASE=/mnt/gvfs/third-party2/bzip2/740325875f6729f42d28deaa2147b0854f3a347e/1.0.6/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LZ4_BASE=/mnt/gvfs/third-party2/lz4/0e790b441e2d9acd68d51e1d2e028f88c6a79ddf/r131/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/1408484d03b15492aa54b10356104e9dc22e1cc5/0.6.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/5a5c7a6608cb32f1e1e7f814023d5bdfbd136370/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/a4c2adecffcaa68d5585d06be2252e3efa52555b/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/1abc0d3c01743b854676423cf2d3629912f34930/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
ZSTD_BASE=/mnt/gvfs/third-party2/zstd/9455f75ff7f4831dc9fda02a6a0f8c68922fad8f/1.0.0/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
GFLAGS_BASE=/mnt/gvfs/third-party2/gflags/f001a51b2854957676d07306ef3abf67186b5c8b/2.1.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
JEMALLOC_BASE=/mnt/gvfs/third-party2/jemalloc/fc8a13ca1fffa4d0765c716c5a0b49f0c107518f/master/gcc-4.8.1-glibc-2.17/8d31e51
|
||||
NUMA_BASE=/mnt/gvfs/third-party2/numa/17c514c4d102a25ca15f4558be564eeed76f4b6a/2.0.8/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
LIBUNWIND_BASE=/mnt/gvfs/third-party2/libunwind/ad576de2a1ea560c4d3434304f0fc4e079bede42/trunk/gcc-4.8.1-glibc-2.17/675d945
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/f24be37d170e04be6e469af487644d4d62e1c6c1/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/9d65c666b9adf8f2a989fd4b98a9a5e7d3afa233/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/9cee5a3628dc9d4b93897972c58eba865e25b270/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
TBB_BASE=/mnt/gvfs/third-party2/tbb/9d9a554877d0c5bef330fe818ab7178806dd316a/4.0_update2/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
KERNEL_HEADERS_BASE=/mnt/gvfs/third-party2/kernel-headers/7c111ff27e0c466235163f00f280a9d617c3d2ec/4.0.9-36_fbk5_2933_gd092e3f/gcc-4.8.1-glibc-2.17/da39a3e
|
||||
BINUTILS_BASE=/mnt/gvfs/third-party2/binutils/b7fd454c4b10c6a81015d4524ed06cdeab558490/2.26/centos6-native/da39a3e
|
||||
VALGRIND_BASE=/mnt/gvfs/third-party2/valgrind/d7f4d4d86674a57668e3a96f76f0e17dd0eb8765/3.8.1/gcc-4.8.1-glibc-2.17/c3f970a
|
||||
@@ -119,7 +119,9 @@ _TEST_NAME_TO_PARSERS = {
|
||||
'unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'release': [CompilerErrorParser, GTestErrorParser],
|
||||
'unit_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'release_481': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_unit': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_release': [CompilerErrorParser, GTestErrorParser],
|
||||
'clang_analyze': [CompilerErrorParser, ScanBuildErrorParser],
|
||||
'code_cov': [CompilerErrorParser, GTestErrorParser],
|
||||
'unity': [CompilerErrorParser, GTestErrorParser],
|
||||
|
||||
@@ -13,7 +13,7 @@ source "$BASEDIR/dependencies.sh"
|
||||
CFLAGS=""
|
||||
|
||||
# libgcc
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include/c++/7.3.0"
|
||||
LIBGCC_INCLUDE="$LIBGCC_BASE/include"
|
||||
LIBGCC_LIBS=" -L $LIBGCC_BASE/lib"
|
||||
|
||||
# glibc
|
||||
@@ -43,15 +43,11 @@ if test -z $PIC_BUILD; then
|
||||
LZ4_INCLUDE=" -I $LZ4_BASE/include/"
|
||||
LZ4_LIBS=" $LZ4_BASE/lib/liblz4.a"
|
||||
CFLAGS+=" -DLZ4"
|
||||
fi
|
||||
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
if test -z $PIC_BUILD; then
|
||||
ZSTD_INCLUDE=" -I $ZSTD_BASE/include/"
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd.a"
|
||||
else
|
||||
ZSTD_LIBS=" $ZSTD_BASE/lib/libzstd_pic.a"
|
||||
CFLAGS+=" -DZSTD"
|
||||
fi
|
||||
CFLAGS+=" -DZSTD"
|
||||
|
||||
# location of gflags headers and libraries
|
||||
GFLAGS_INCLUDE=" -I $GFLAGS_BASE/include/"
|
||||
@@ -60,7 +56,7 @@ if test -z $PIC_BUILD; then
|
||||
else
|
||||
GFLAGS_LIBS=" $GFLAGS_BASE/lib/libgflags_pic.a"
|
||||
fi
|
||||
CFLAGS+=" -DGFLAGS=gflags"
|
||||
CFLAGS+=" -DGFLAGS=google"
|
||||
|
||||
# location of jemalloc
|
||||
JEMALLOC_INCLUDE=" -I $JEMALLOC_BASE/include/"
|
||||
@@ -108,8 +104,8 @@ if [ -z "$USE_CLANG" ]; then
|
||||
CXX="$GCC_BASE/bin/g++"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
JEMALLOC=1
|
||||
else
|
||||
# clang
|
||||
@@ -120,8 +116,8 @@ else
|
||||
KERNEL_HEADERS_INCLUDE="$KERNEL_HEADERS_BASE/include"
|
||||
|
||||
CFLAGS+=" -B$BINUTILS/gold -nostdinc -nostdlib"
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/7.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x "
|
||||
CFLAGS+=" -isystem $LIBGCC_BASE/include/c++/4.9.x/x86_64-facebook-linux "
|
||||
CFLAGS+=" -isystem $GLIBC_INCLUDE"
|
||||
CFLAGS+=" -isystem $LIBGCC_INCLUDE"
|
||||
CFLAGS+=" -isystem $CLANG_INCLUDE"
|
||||
@@ -132,14 +128,13 @@ else
|
||||
fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -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 -DHAVE_SSE42"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -B$BINUTILS/gold"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/platform007/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.9-glibc-2.20/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/platform007/lib"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.9-glibc-2.20/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
@@ -149,4 +144,4 @@ EXEC_LDFLAGS_SHARED="$SNAPPY_LIBS $ZLIB_LIBS $BZIP_LIBS $LZ4_LIBS $ZSTD_LIBS $GF
|
||||
|
||||
VALGRIND_VER="$VALGRIND_BASE/bin/"
|
||||
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
export CC CXX AR CFLAGS CXXFLAGS EXEC_LDFLAGS EXEC_LDFLAGS_SHARED VALGRIND_VER JEMALLOC_LIB JEMALLOC_INCLUDE CLANG_ANALYZER CLANG_SCAN_BUILD
|
||||
@@ -48,13 +48,17 @@ NUMA_LIB=" $NUMA_BASE/lib/libnuma.a"
|
||||
# location of libunwind
|
||||
LIBUNWIND="$LIBUNWIND_BASE/lib/libunwind.a"
|
||||
|
||||
# location of tbb
|
||||
TBB_INCLUDE=" -isystem $TBB_BASE/include/"
|
||||
TBB_LIBS="$TBB_BASE/lib/libtbb.a"
|
||||
|
||||
# use Intel SSE support for checksum calculations
|
||||
export USE_SSE=1
|
||||
|
||||
BINUTILS="$BINUTILS_BASE/bin"
|
||||
AR="$BINUTILS/ar"
|
||||
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE"
|
||||
DEPS_INCLUDE="$SNAPPY_INCLUDE $ZLIB_INCLUDE $BZIP2_INCLUDE $LZ4_INCLUDE $ZSTD_INCLUDE $GFLAGS_INCLUDE $NUMA_INCLUDE $TBB_INCLUDE"
|
||||
|
||||
STDLIBS="-L $GCC_BASE/lib64"
|
||||
|
||||
@@ -90,13 +94,15 @@ fi
|
||||
|
||||
CFLAGS+=" $DEPS_INCLUDE"
|
||||
CFLAGS+=" -DROCKSDB_PLATFORM_POSIX -DROCKSDB_LIB_IO_POSIX -DROCKSDB_FALLOCATE_PRESENT -DROCKSDB_MALLOC_USABLE_SIZE"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA"
|
||||
CFLAGS+=" -DSNAPPY -DGFLAGS=google -DZLIB -DBZIP2 -DLZ4 -DZSTD -DNUMA -DTBB"
|
||||
CXXFLAGS+=" $CFLAGS"
|
||||
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB"
|
||||
EXEC_LDFLAGS=" $SNAPPY_LIBS $ZLIB_LIBS $BZIP2_LIBS $LZ4_LIBS $ZSTD_LIBS $GFLAGS_LIBS $NUMA_LIB $TBB_LIBS"
|
||||
EXEC_LDFLAGS+=" -Wl,--dynamic-linker,/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib/ld.so"
|
||||
EXEC_LDFLAGS+=" $LIBUNWIND"
|
||||
EXEC_LDFLAGS+=" -Wl,-rpath=/usr/local/fbcode/gcc-4.8.1-glibc-2.17/lib"
|
||||
# required by libtbb
|
||||
EXEC_LDFLAGS+=" -ldl"
|
||||
|
||||
PLATFORM_LDFLAGS="$LIBGCC_LIBS $GLIBC_LIBS $STDLIBS -lgcc -lstdc++"
|
||||
|
||||
|
||||
Executable
+7936
File diff suppressed because it is too large.
Load diff
@@ -94,7 +94,7 @@ class PreCommitChecker(Env):
|
||||
#
|
||||
def get_commands(self, test):
|
||||
status, out = self.GetOutput(
|
||||
"build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
"RATIO=1 build_tools/rocksdb-lego-determinator %s" % test, ".")
|
||||
return status, out
|
||||
|
||||
#
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# Input Value
|
||||
# -------------------------------------------------------------------------
|
||||
# EMAIL Email address to report on trigger conditions
|
||||
# ONCAL Email address to raise a task on failure
|
||||
# ONCALL Email address to raise a task on failure
|
||||
# TRIGGER Trigger conditions for email. Valid values are fail, warn, all
|
||||
# SUBSCRIBER Email addresss to add as subscriber for task
|
||||
#
|
||||
@@ -42,6 +42,9 @@ if [ ! -z $ONCALL ]; then
|
||||
},"
|
||||
fi
|
||||
|
||||
# For now, create the tasks using only the dedicated task creation tool.
|
||||
CREATE_TASK=
|
||||
|
||||
REPORT=
|
||||
if [[ ! -z $REPORT_EMAIL || ! -z $CREATE_TASK ]]; then
|
||||
REPORT="'report': [
|
||||
@@ -56,10 +59,26 @@ fi
|
||||
CLEANUP_ENV="
|
||||
{
|
||||
'name':'Cleanup environment',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && make clean',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && (chmod +t /dev/shm || true) && make clean',
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
# We will eventually set the RATIO to 1, but we want do this
|
||||
# in steps. RATIO=$(nproc) will make it work as J=1
|
||||
if [ -z $RATIO ]; then
|
||||
RATIO=$(nproc)
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_J ]; then
|
||||
PARALLEL_J="J=$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
if [ -z $PARALLEL_j ]; then
|
||||
PARALLEL_j="-j$(expr $(nproc) / ${RATIO})"
|
||||
fi
|
||||
|
||||
PARALLELISM="$PARALLEL_J $PARALLEL_j"
|
||||
|
||||
DEBUG="OPT=-g"
|
||||
SHM="TEST_TMPDIR=/dev/shm/rocksdb"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
@@ -73,6 +92,19 @@ HTTP_PROXY="https_proxy=http://fwdproxy.29.prn1:8080 http_proxy=http://fwdproxy.
|
||||
SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-7u10-64/; export PATH=\$PATH:\$JAVA_HOME/bin"
|
||||
PARSER="'parser':'python build_tools/error_filter.py $1'"
|
||||
|
||||
CONTRUN_NAME="ROCKSDB_CONTRUN_NAME"
|
||||
|
||||
# This code is getting called under various scenarios. What we care about is to
|
||||
# understand when it's called from nightly contruns because in that case we'll
|
||||
# create tasks for any failures. To follow the existing pattern, we'll check
|
||||
# the value of $ONCALL. If it's a diff then just call `false` to make sure
|
||||
# that errors will be properly propagated to the caller.
|
||||
if [ ! -z $ONCALL ]; then
|
||||
TASK_CREATION_TOOL="/usr/local/bin/mysql_mtr_filter --rocksdb"
|
||||
else
|
||||
TASK_CREATION_TOOL="false"
|
||||
fi
|
||||
|
||||
ARTIFACTS=" 'artifacts': [
|
||||
{
|
||||
'name':'database',
|
||||
@@ -110,14 +142,14 @@ PARALLEL_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || cat t/log-*',
|
||||
'shell':'$DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version under gcc-4.8.1',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || cat t/log-*',
|
||||
'shell':'$GCC_481 $DEBUG make -j$(nproc) all && $SHM make check > /dev/null 2>&1 || $CONTRUN_NAME=punit_check_gcc481 $TASK_CREATION_TOOL || cat t/log-*',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -137,7 +169,29 @@ UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make J=1 check',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB unit test not under /dev/shm
|
||||
#
|
||||
UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'timeout': 86400,
|
||||
'shell':'$DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -149,15 +203,15 @@ UNIT_TEST_COMMANDS="[
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_UNIT_TEST_COMMANDS="[
|
||||
RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Release Unit Test',
|
||||
'name':'Rocksdb Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make J=1 release',
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -177,7 +231,27 @@ UNIT_TEST_COMMANDS_481="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make J=1 check',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build and unit tests
|
||||
#
|
||||
RELEASE_BUILD_COMMANDS_481="[
|
||||
{
|
||||
'name':'Rocksdb Release on GCC 4.8.1',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release on GCC 4.8.1',
|
||||
'shell':'$GCC_481 make $PARALLEL_j release || $CONTRUN_NAME=release_gcc481 $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -197,7 +271,27 @@ CLANG_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug',
|
||||
'shell':'$CLANG $SHM $DEBUG make J=1 check',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB release build with CLANG
|
||||
#
|
||||
CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb CLANG Release Build',
|
||||
'oncall':'$ONCALL',
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'$CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -217,7 +311,7 @@ CLANG_ANALYZE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'RocksDB build and analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make J=1 analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -237,7 +331,7 @@ CODE_COV_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test and collect code coverage info',
|
||||
'shell':'$SHM $DEBUG make J=1 coverage',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -257,7 +351,7 @@ UNITY_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test unity test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -277,7 +371,7 @@ LITE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'$LITE make J=1 static_lib',
|
||||
'shell':'$LITE make J=1 static_lib || $CONTRUN_NAME=lite_static_lib $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -297,7 +391,7 @@ LITE_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'$SHM $LITE make J=1 check',
|
||||
'shell':'$SHM $LITE make J=1 check || $CONTRUN_NAME=lite_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -318,14 +412,14 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress',
|
||||
'shell':'$SHM $DEBUG make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -346,7 +440,7 @@ WRITE_STRESS_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB write stress tests',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress',
|
||||
'shell':'make write_stress && python tools/write_stress_runner.py --runtime_sec=3600 --db=/tmp/rocksdb_write_stress || $CONTRUN_NAME=write_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -368,7 +462,7 @@ ASAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under ASAN',
|
||||
'shell':'set -o pipefail && $SHM $ASAN $DEBUG make J=1 asan_check |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'shell':'set -o pipefail && ($SHM $ASAN $DEBUG make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL) |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -390,7 +484,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -410,7 +504,7 @@ UBSAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under UBSAN',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make J=1 ubsan_check',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $DEBUG make $PARALLELISM ubsan_check || $CONTRUN_NAME=ubsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -432,7 +526,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test',
|
||||
'shell':'$SHM $DEBUG make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -448,11 +542,13 @@ VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under valgrind',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit tests',
|
||||
'shell':'$DISABLE_JEMALLOC $SHM $DEBUG make valgrind_check',
|
||||
'timeout': 86400,
|
||||
'shell':'$DISABLE_JEMALLOC $SHM $DEBUG make $PARALLELISM valgrind_check || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -468,11 +564,13 @@ TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Unit Test under TSAN',
|
||||
'oncall':'$ONCALL',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make J=1 check',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -494,7 +592,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make J=1 crash_test',
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -503,8 +601,6 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
TSAN_CRASH_TEST_COMMANDS=$DISABLE_COMMANDS
|
||||
|
||||
#
|
||||
# RocksDB format compatible
|
||||
#
|
||||
@@ -558,7 +654,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -591,7 +687,7 @@ NO_COMPRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -648,7 +744,7 @@ REGRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Make and run script',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -668,7 +764,7 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB for Java',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -685,15 +781,24 @@ case $1 in
|
||||
unit)
|
||||
echo $UNIT_TEST_COMMANDS
|
||||
;;
|
||||
unit_non_shm)
|
||||
echo $UNIT_TEST_NON_SHM_COMMANDS
|
||||
;;
|
||||
release)
|
||||
echo $RELEASE_UNIT_TEST_COMMANDS
|
||||
echo $RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
unit_481)
|
||||
echo $UNIT_TEST_COMMANDS_481
|
||||
;;
|
||||
release_481)
|
||||
echo $RELEASE_BUILD_COMMANDS_481
|
||||
;;
|
||||
clang_unit)
|
||||
echo $CLANG_UNIT_TEST_COMMANDS
|
||||
;;
|
||||
clang_release)
|
||||
echo $CLANG_RELEASE_BUILD_COMMANDS
|
||||
;;
|
||||
clang_analyze)
|
||||
echo $CLANG_ANALYZE_COMMANDS
|
||||
;;
|
||||
|
||||
@@ -82,6 +82,7 @@ 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-4.9-glibc-2.20
|
||||
|
||||
get_lib_base kernel-headers LATEST
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
@@ -119,6 +120,7 @@ get_lib_base gflags LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base jemalloc LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base numa LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base libunwind LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base tbb 4.0_update2 gcc-4.8.1-glibc-2.17
|
||||
|
||||
get_lib_base kernel-headers LATEST gcc-4.8.1-glibc-2.17
|
||||
get_lib_base binutils LATEST centos6-native
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# - Find JeMalloc library
|
||||
# Find the native JeMalloc includes and library
|
||||
# This module defines
|
||||
# JEMALLOC_INCLUDE_DIRS, where to find jemalloc.h, Set when
|
||||
# JEMALLOC_INCLUDE_DIR is found.
|
||||
# JEMALLOC_LIBRARIES, libraries to link against to use JeMalloc.
|
||||
# JEMALLOC_FOUND, If false, do not try to use JeMalloc.
|
||||
#
|
||||
find_path(JEMALLOC_INCLUDE_DIR
|
||||
jemalloc/jemalloc.h)
|
||||
|
||||
find_library(JEMALLOC_LIBRARIES
|
||||
jemalloc)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(JeMalloc DEFAULT_MSG
|
||||
JEMALLOC_LIBRARIES JEMALLOC_INCLUDE_DIR)
|
||||
|
||||
MARK_AS_ADVANCED(
|
||||
JEMALLOC_INCLUDE_DIR
|
||||
JEMALLOC_LIBRARIES
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# SNAPPY_INCLUDE_DIR - where to find snappy.h, etc.
|
||||
# SNAPPY_LIBRARIES - List of libraries when using snappy.
|
||||
# SNAPPY_FOUND - True if snappy found.
|
||||
|
||||
find_path(SNAPPY_INCLUDE_DIR
|
||||
NAMES snappy.h
|
||||
HINTS ${SNAPPY_ROOT_DIR}/include)
|
||||
|
||||
find_library(SNAPPY_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${SNAPPY_ROOT_DIR}/lib)
|
||||
|
||||
# handle the QUIETLY and REQUIRED arguments and set UUID_FOUND to TRUE if
|
||||
# all listed variables are TRUE
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG SNAPPY_LIBRARIES SNAPPY_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
SNAPPY_LIBRARIES
|
||||
SNAPPY_INCLUDE_DIR)
|
||||
+9
-6
@@ -45,6 +45,7 @@ TableBuilder* NewTableBuilder(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
int level,
|
||||
const std::string* compression_dict, const bool skip_filters) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
@@ -53,7 +54,7 @@ TableBuilder* NewTableBuilder(
|
||||
TableBuilderOptions(ioptions, internal_comparator,
|
||||
int_tbl_prop_collector_factories, compression_type,
|
||||
compression_opts, compression_dict, skip_filters,
|
||||
column_family_name),
|
||||
column_family_name, level),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
@@ -108,19 +109,21 @@ Status BuildTable(
|
||||
builder = NewTableBuilder(
|
||||
ioptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
column_family_id, column_family_name, file_writer.get(), compression,
|
||||
compression_opts);
|
||||
compression_opts, level);
|
||||
}
|
||||
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg;
|
||||
range_del_agg.reset(new RangeDelAggregator(internal_comparator, snapshots));
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
ioptions.merge_operator, nullptr, ioptions.info_log,
|
||||
mutable_cf_options.min_partial_merge_operands,
|
||||
true /* internal key corruption is not ok */,
|
||||
snapshots.empty() ? 0 : snapshots.back());
|
||||
|
||||
CompactionIterator c_iter(iter, internal_comparator.user_comparator(),
|
||||
&merge, kMaxSequenceNumber, &snapshots,
|
||||
earliest_write_conflict_snapshot, env,
|
||||
true /* internal key corruption is not ok */);
|
||||
CompactionIterator c_iter(
|
||||
iter, internal_comparator.user_comparator(), &merge, kMaxSequenceNumber,
|
||||
&snapshots, earliest_write_conflict_snapshot, env,
|
||||
true /* internal key corruption is not ok */, range_del_agg.get());
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const Slice& key = c_iter.key();
|
||||
|
||||
+2
-2
@@ -12,14 +12,13 @@
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/immutable_options.h"
|
||||
#include "rocksdb/listener.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -49,6 +48,7 @@ TableBuilder* NewTableBuilder(
|
||||
uint32_t column_family_id, const std::string& column_family_name,
|
||||
WritableFileWriter* file, const CompressionType compression_type,
|
||||
const CompressionOptions& compression_opts,
|
||||
int level,
|
||||
const std::string* compression_dict = nullptr,
|
||||
const bool skip_filters = false);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ using rocksdb::CompactionFilterContext;
|
||||
using rocksdb::CompactionOptionsFIFO;
|
||||
using rocksdb::Comparator;
|
||||
using rocksdb::CompressionType;
|
||||
using rocksdb::WALRecoveryMode;
|
||||
using rocksdb::DB;
|
||||
using rocksdb::DBOptions;
|
||||
using rocksdb::Env;
|
||||
@@ -123,6 +124,7 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
char** new_value, size_t *new_value_length,
|
||||
unsigned char* value_changed);
|
||||
const char* (*name_)(void*);
|
||||
unsigned char ignore_snapshots_;
|
||||
|
||||
virtual ~rocksdb_compactionfilter_t() {
|
||||
(*destructor_)(state_);
|
||||
@@ -148,6 +150,8 @@ struct rocksdb_compactionfilter_t : public CompactionFilter {
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool IgnoreSnapshots() const override { return ignore_snapshots_; }
|
||||
};
|
||||
|
||||
struct rocksdb_compactionfilterfactory_t : public CompactionFilterFactory {
|
||||
@@ -269,33 +273,31 @@ struct rocksdb_mergeoperator_t : public MergeOperator {
|
||||
|
||||
virtual const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
virtual bool FullMerge(const Slice& key, const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
size_t n = operand_list.size();
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
size_t n = merge_in.operand_list.size();
|
||||
std::vector<const char*> operand_pointers(n);
|
||||
std::vector<size_t> operand_sizes(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
Slice operand(operand_list[i]);
|
||||
Slice operand(merge_in.operand_list[i]);
|
||||
operand_pointers[i] = operand.data();
|
||||
operand_sizes[i] = operand.size();
|
||||
}
|
||||
|
||||
const char* existing_value_data = nullptr;
|
||||
size_t existing_value_len = 0;
|
||||
if (existing_value != nullptr) {
|
||||
existing_value_data = existing_value->data();
|
||||
existing_value_len = existing_value->size();
|
||||
if (merge_in.existing_value != nullptr) {
|
||||
existing_value_data = merge_in.existing_value->data();
|
||||
existing_value_len = merge_in.existing_value->size();
|
||||
}
|
||||
|
||||
unsigned char success;
|
||||
size_t new_value_len;
|
||||
char* tmp_new_value = (*full_merge_)(
|
||||
state_, key.data(), key.size(), existing_value_data, existing_value_len,
|
||||
&operand_pointers[0], &operand_sizes[0], static_cast<int>(n), &success,
|
||||
&new_value_len);
|
||||
new_value->assign(tmp_new_value, new_value_len);
|
||||
state_, merge_in.key.data(), merge_in.key.size(), existing_value_data,
|
||||
existing_value_len, &operand_pointers[0], &operand_sizes[0],
|
||||
static_cast<int>(n), &success, &new_value_len);
|
||||
merge_out->new_value.assign(tmp_new_value, new_value_len);
|
||||
|
||||
if (delete_value_ != nullptr) {
|
||||
(*delete_value_)(state_, tmp_new_value, new_value_len);
|
||||
@@ -1514,14 +1516,9 @@ void rocksdb_options_set_max_bytes_for_level_multiplier(
|
||||
opt->rep.max_bytes_for_level_multiplier = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_expanded_compaction_factor(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.expanded_compaction_factor = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_grandparent_overlap_factor(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_grandparent_overlap_factor = n;
|
||||
void rocksdb_options_set_max_compaction_bytes(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.max_compaction_bytes = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier_additional(
|
||||
@@ -1558,6 +1555,10 @@ void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* opt,
|
||||
int n) {}
|
||||
|
||||
void rocksdb_options_set_wal_recovery_mode(rocksdb_options_t* opt,int mode) {
|
||||
opt->rep.wal_recovery_mode = static_cast<WALRecoveryMode>(mode);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
@@ -1777,21 +1778,12 @@ void rocksdb_options_set_delete_obsolete_files_period_micros(
|
||||
opt->rep.delete_obsolete_files_period_micros = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_source_compaction_factor(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.expanded_compaction_factor = n;
|
||||
}
|
||||
|
||||
void rocksdb_options_prepare_for_bulk_load(rocksdb_options_t* opt) {
|
||||
opt->rep.PrepareForBulkLoad();
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_vector_rep(rocksdb_options_t *opt) {
|
||||
static rocksdb::VectorRepFactory* factory = 0;
|
||||
if (!factory) {
|
||||
factory = new rocksdb::VectorRepFactory;
|
||||
}
|
||||
opt->rep.memtable_factory.reset(factory);
|
||||
opt->rep.memtable_factory.reset(new rocksdb::VectorRepFactory);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_prefix_bloom_size_ratio(
|
||||
@@ -1799,44 +1791,34 @@ void rocksdb_options_set_memtable_prefix_bloom_size_ratio(
|
||||
opt->rep.memtable_prefix_bloom_size_ratio = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_memtable_prefix_bloom_huge_page_tlb_size(
|
||||
rocksdb_options_t* opt, size_t v) {
|
||||
opt->rep.memtable_prefix_bloom_huge_page_tlb_size = v;
|
||||
void rocksdb_options_set_memtable_huge_page_size(rocksdb_options_t* opt,
|
||||
size_t v) {
|
||||
opt->rep.memtable_huge_page_size = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hash_skip_list_rep(
|
||||
rocksdb_options_t *opt, size_t bucket_count,
|
||||
int32_t skiplist_height, int32_t skiplist_branching_factor) {
|
||||
static rocksdb::MemTableRepFactory* factory = 0;
|
||||
if (!factory) {
|
||||
factory = rocksdb::NewHashSkipListRepFactory(
|
||||
bucket_count, skiplist_height, skiplist_branching_factor);
|
||||
}
|
||||
rocksdb::MemTableRepFactory* factory = rocksdb::NewHashSkipListRepFactory(
|
||||
bucket_count, skiplist_height, skiplist_branching_factor);
|
||||
opt->rep.memtable_factory.reset(factory);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_hash_link_list_rep(
|
||||
rocksdb_options_t *opt, size_t bucket_count) {
|
||||
static rocksdb::MemTableRepFactory* factory = 0;
|
||||
if (!factory) {
|
||||
factory = rocksdb::NewHashLinkListRepFactory(bucket_count);
|
||||
}
|
||||
opt->rep.memtable_factory.reset(factory);
|
||||
opt->rep.memtable_factory.reset(rocksdb::NewHashLinkListRepFactory(bucket_count));
|
||||
}
|
||||
|
||||
void rocksdb_options_set_plain_table_factory(
|
||||
rocksdb_options_t *opt, uint32_t user_key_len, int bloom_bits_per_key,
|
||||
double hash_table_ratio, size_t index_sparseness) {
|
||||
static rocksdb::TableFactory* factory = 0;
|
||||
if (!factory) {
|
||||
rocksdb::PlainTableOptions options;
|
||||
options.user_key_len = user_key_len;
|
||||
options.bloom_bits_per_key = bloom_bits_per_key;
|
||||
options.hash_table_ratio = hash_table_ratio;
|
||||
options.index_sparseness = index_sparseness;
|
||||
rocksdb::PlainTableOptions options;
|
||||
options.user_key_len = user_key_len;
|
||||
options.bloom_bits_per_key = bloom_bits_per_key;
|
||||
options.hash_table_ratio = hash_table_ratio;
|
||||
options.index_sparseness = index_sparseness;
|
||||
|
||||
factory = rocksdb::NewPlainTableFactory(options);
|
||||
}
|
||||
rocksdb::TableFactory* factory = rocksdb::NewPlainTableFactory(options);
|
||||
opt->rep.table_factory.reset(factory);
|
||||
}
|
||||
|
||||
@@ -1921,10 +1903,17 @@ rocksdb_compactionfilter_t* rocksdb_compactionfilter_create(
|
||||
result->state_ = state;
|
||||
result->destructor_ = destructor;
|
||||
result->filter_ = filter;
|
||||
result->ignore_snapshots_ = false;
|
||||
result->name_ = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_compactionfilter_set_ignore_snapshots(
|
||||
rocksdb_compactionfilter_t* filter,
|
||||
unsigned char whether_ignore) {
|
||||
filter->ignore_snapshots_ = whether_ignore;
|
||||
}
|
||||
|
||||
void rocksdb_compactionfilter_destroy(rocksdb_compactionfilter_t* filter) {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
+23
@@ -575,6 +575,7 @@ int main(int argc, char** argv) {
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_set_create_if_missing(options, 0);
|
||||
rocksdb_options_set_error_if_exists(options, 0);
|
||||
rocksdb_options_set_wal_recovery_mode(options, 2);
|
||||
rocksdb_repair_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
@@ -943,6 +944,28 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// Simple sanity check that setting memtable rep works.
|
||||
StartPhase("memtable_reps");
|
||||
{
|
||||
// Create database with vector memtable.
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_memtable_vector_rep(options);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
// Create database with hash skiplist memtable.
|
||||
rocksdb_close(db);
|
||||
rocksdb_destroy_db(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
|
||||
rocksdb_options_set_hash_skip_list_rep(options, 5000, 4, 4);
|
||||
db = rocksdb_open(options, dbname, &err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
|
||||
+79
-34
@@ -45,6 +45,11 @@ ColumnFamilyHandleImpl::ColumnFamilyHandleImpl(
|
||||
|
||||
ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
if (cfd_ != nullptr) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (auto& listener : cfd_->ioptions()->listeners) {
|
||||
listener->OnColumnFamilyHandleDeletionStarted(this);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
// user thread
|
||||
JobContext job_context(0);
|
||||
@@ -71,26 +76,23 @@ Status ColumnFamilyHandleImpl::GetDescriptor(ColumnFamilyDescriptor* desc) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// accessing mutable cf-options requires db mutex.
|
||||
InstrumentedMutexLock l(mutex_);
|
||||
*desc = ColumnFamilyDescriptor(
|
||||
cfd()->GetName(),
|
||||
BuildColumnFamilyOptions(*cfd()->options(),
|
||||
*cfd()->GetLatestMutableCFOptions()));
|
||||
*desc = ColumnFamilyDescriptor(cfd()->GetName(), cfd()->GetLatestCFOptions());
|
||||
return Status::OK();
|
||||
#else
|
||||
return Status::NotSupported();
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
const Comparator* ColumnFamilyHandleImpl::user_comparator() const {
|
||||
const Comparator* ColumnFamilyHandleImpl::GetComparator() const {
|
||||
return cfd()->user_comparator();
|
||||
}
|
||||
|
||||
void GetIntTblPropCollectorFactory(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories) {
|
||||
auto& collector_factories = cf_options.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < cf_options.table_properties_collector_factories.size();
|
||||
auto& collector_factories = ioptions.table_properties_collector_factories;
|
||||
for (size_t i = 0; i < ioptions.table_properties_collector_factories.size();
|
||||
++i) {
|
||||
assert(collector_factories[i]);
|
||||
int_tbl_prop_collector_factories->emplace_back(
|
||||
@@ -136,7 +138,7 @@ Status CheckConcurrentWritesSupported(const ColumnFamilyOptions& cf_options) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
const ColumnFamilyOptions& src) {
|
||||
ColumnFamilyOptions result = src;
|
||||
@@ -158,6 +160,10 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
result.min_write_buffer_number_to_merge =
|
||||
std::min(result.min_write_buffer_number_to_merge,
|
||||
result.max_write_buffer_number - 1);
|
||||
if (result.min_write_buffer_number_to_merge < 1) {
|
||||
result.min_write_buffer_number_to_merge = 1;
|
||||
}
|
||||
|
||||
if (result.num_levels < 1) {
|
||||
result.num_levels = 1;
|
||||
}
|
||||
@@ -204,6 +210,10 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
result.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
if (result.max_bytes_for_level_multiplier <= 0) {
|
||||
result.max_bytes_for_level_multiplier = 1;
|
||||
}
|
||||
|
||||
if (result.level0_file_num_compaction_trigger == 0) {
|
||||
Warn(db_options.info_log.get(),
|
||||
"level0_file_num_compaction_trigger cannot be 0");
|
||||
@@ -262,6 +272,10 @@ ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
}
|
||||
}
|
||||
|
||||
if (result.max_compaction_bytes == 0) {
|
||||
result.max_compaction_bytes = result.target_file_size_base * 25;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -330,7 +344,7 @@ void SuperVersionUnrefHandle(void* ptr) {
|
||||
ColumnFamilyData::ColumnFamilyData(
|
||||
uint32_t id, const std::string& name, Version* _dummy_versions,
|
||||
Cache* _table_cache, WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& cf_options, const DBOptions* db_options,
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, ColumnFamilySet* column_family_set)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
@@ -339,14 +353,14 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
refs_(0),
|
||||
dropped_(false),
|
||||
internal_comparator_(cf_options.comparator),
|
||||
options_(*db_options,
|
||||
SanitizeOptions(*db_options, &internal_comparator_, cf_options)),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_, ioptions_),
|
||||
initial_cf_options_(
|
||||
SanitizeOptions(db_options, &internal_comparator_, cf_options)),
|
||||
ioptions_(db_options, initial_cf_options_),
|
||||
mutable_cf_options_(initial_cf_options_),
|
||||
write_buffer_manager_(write_buffer_manager),
|
||||
mem_(nullptr),
|
||||
imm_(options_.min_write_buffer_number_to_merge,
|
||||
options_.max_write_buffer_number_to_maintain),
|
||||
imm_(ioptions_.min_write_buffer_number_to_merge,
|
||||
ioptions_.max_write_buffer_number_to_maintain),
|
||||
super_version_(nullptr),
|
||||
super_version_number_(0),
|
||||
local_sv_(new ThreadLocalPtr(&SuperVersionUnrefHandle)),
|
||||
@@ -360,12 +374,12 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
Ref();
|
||||
|
||||
// Convert user defined table properties collector factories to internal ones.
|
||||
GetIntTblPropCollectorFactory(options_, &int_tbl_prop_collector_factories_);
|
||||
GetIntTblPropCollectorFactory(ioptions_, &int_tbl_prop_collector_factories_);
|
||||
|
||||
// if _dummy_versions is nullptr, then this is a dummy column family.
|
||||
if (_dummy_versions != nullptr) {
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, db_options->env, this));
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, env_options, _table_cache));
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
@@ -397,7 +411,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
if (column_family_set_->NumberOfColumnFamilies() < 10) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"--------------- Options for column family [%s]:\n", name.c_str());
|
||||
options_.DumpCFOptions(ioptions_.info_log);
|
||||
initial_cf_options_.Dump(ioptions_.info_log);
|
||||
} else {
|
||||
Log(InfoLogLevel::INFO_LEVEL, ioptions_.info_log,
|
||||
"\t(skipping printing options)\n");
|
||||
@@ -474,6 +488,10 @@ void ColumnFamilyData::SetDropped() {
|
||||
column_family_set_->RemoveColumnFamily(this);
|
||||
}
|
||||
|
||||
ColumnFamilyOptions ColumnFamilyData::GetLatestCFOptions() const {
|
||||
return BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
const double kSlowdownRatio = 1.2;
|
||||
|
||||
namespace {
|
||||
@@ -531,14 +549,31 @@ int GetL0ThresholdSpeedupCompaction(int level0_file_num_compaction_trigger,
|
||||
// SanitizeOptions() ensures it.
|
||||
assert(level0_file_num_compaction_trigger <= level0_slowdown_writes_trigger);
|
||||
|
||||
if (level0_file_num_compaction_trigger < 0) {
|
||||
return std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
const int64_t twice_level0_trigger =
|
||||
static_cast<int64_t>(level0_file_num_compaction_trigger) * 2;
|
||||
|
||||
const int64_t one_fourth_trigger_slowdown =
|
||||
static_cast<int64_t>(level0_file_num_compaction_trigger) +
|
||||
((level0_slowdown_writes_trigger - level0_file_num_compaction_trigger) /
|
||||
4);
|
||||
|
||||
assert(twice_level0_trigger >= 0);
|
||||
assert(one_fourth_trigger_slowdown >= 0);
|
||||
|
||||
// 1/4 of the way between L0 compaction trigger threshold and slowdown
|
||||
// condition.
|
||||
// Or twice as compaction trigger, if it is smaller.
|
||||
return std::min(level0_file_num_compaction_trigger * 2,
|
||||
level0_file_num_compaction_trigger +
|
||||
(level0_slowdown_writes_trigger -
|
||||
level0_file_num_compaction_trigger) /
|
||||
4);
|
||||
int64_t res = std::min(twice_level0_trigger, one_fourth_trigger_slowdown);
|
||||
if (res >= port::kMaxInt32) {
|
||||
return port::kMaxInt32;
|
||||
} else {
|
||||
// res fits in int
|
||||
return static_cast<int>(res);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -558,8 +593,9 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"(waiting for flush), max_write_buffer_number is set to %d",
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number);
|
||||
} else if (vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_stop_writes_trigger) {
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
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);
|
||||
if (compaction_picker_->IsLevel0CompactionInProgress()) {
|
||||
@@ -569,7 +605,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
Log(InfoLogLevel::WARN_LEVEL, ioptions_.info_log,
|
||||
"[%s] Stopping writes because we have %d level-0 files",
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count());
|
||||
} else if (mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit > 0 &&
|
||||
compaction_needed_bytes >=
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ = write_controller->GetStopToken();
|
||||
@@ -594,7 +631,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
name_.c_str(), imm()->NumNotFlushed(),
|
||||
mutable_cf_options.max_write_buffer_number,
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.level0_slowdown_writes_trigger >= 0 &&
|
||||
vstorage->l0_delay_trigger_count() >=
|
||||
mutable_cf_options.level0_slowdown_writes_trigger) {
|
||||
write_controller_token_ =
|
||||
@@ -611,7 +649,8 @@ void ColumnFamilyData::RecalculateWriteStallConditions(
|
||||
"rate %" PRIu64,
|
||||
name_.c_str(), vstorage->l0_delay_trigger_count(),
|
||||
write_controller->delayed_write_rate());
|
||||
} else if (mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
} else if (!mutable_cf_options.disable_auto_compactions &&
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit > 0 &&
|
||||
vstorage->estimated_compaction_needed_bytes() >=
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit) {
|
||||
write_controller_token_ =
|
||||
@@ -701,6 +740,13 @@ Compaction* ColumnFamilyData::PickCompaction(
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
const Slice& smallest_user_key, const Slice& largest_user_key,
|
||||
int level) const {
|
||||
return compaction_picker_->RangeOverlapWithCompaction(
|
||||
smallest_user_key, largest_user_key, level);
|
||||
}
|
||||
|
||||
const int ColumnFamilyData::kCompactAllLevels = -1;
|
||||
const int ColumnFamilyData::kCompactToBaseLevel = -2;
|
||||
|
||||
@@ -851,14 +897,14 @@ Status ColumnFamilyData::SetOptions(
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
const DBOptions* db_options,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options,
|
||||
Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(0, "", nullptr, nullptr, nullptr,
|
||||
ColumnFamilyOptions(), db_options,
|
||||
ColumnFamilyOptions(), *db_options,
|
||||
env_options, nullptr)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
@@ -930,7 +976,7 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
db_options_, env_options_, this);
|
||||
*db_options_, env_options_, this);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
@@ -1007,8 +1053,7 @@ uint32_t GetColumnFamilyID(ColumnFamilyHandle* column_family) {
|
||||
const Comparator* GetColumnFamilyUserComparator(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
if (column_family != nullptr) {
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
return cfh->user_comparator();
|
||||
return column_family->GetComparator();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+25
-13
@@ -15,15 +15,15 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/table_cache.h"
|
||||
#include "db/table_properties_collector.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "rocksdb/compaction_job_stats.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/thread_local.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -55,11 +55,11 @@ class ColumnFamilyHandleImpl : public ColumnFamilyHandle {
|
||||
// destroy without mutex
|
||||
virtual ~ColumnFamilyHandleImpl();
|
||||
virtual ColumnFamilyData* cfd() const { return cfd_; }
|
||||
virtual const Comparator* user_comparator() const;
|
||||
|
||||
virtual uint32_t GetID() const override;
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Status GetDescriptor(ColumnFamilyDescriptor* desc) override;
|
||||
virtual const Comparator* GetComparator() const override;
|
||||
|
||||
private:
|
||||
ColumnFamilyData* cfd_;
|
||||
@@ -136,14 +136,14 @@ extern Status CheckCompressionSupported(const ColumnFamilyOptions& cf_options);
|
||||
extern Status CheckConcurrentWritesSupported(
|
||||
const ColumnFamilyOptions& cf_options);
|
||||
|
||||
extern ColumnFamilyOptions SanitizeOptions(const DBOptions& db_options,
|
||||
extern ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
const InternalKeyComparator* icmp,
|
||||
const ColumnFamilyOptions& src);
|
||||
// Wrap user defined table proproties collector factories `from cf_options`
|
||||
// into internal ones in int_tbl_prop_collector_factories. Add a system internal
|
||||
// one too.
|
||||
extern void GetIntTblPropCollectorFactory(
|
||||
const ColumnFamilyOptions& cf_options,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>*
|
||||
int_tbl_prop_collector_factories);
|
||||
|
||||
@@ -201,9 +201,6 @@ class ColumnFamilyData {
|
||||
void SetLogNumber(uint64_t log_number) { log_number_ = log_number; }
|
||||
uint64_t GetLogNumber() const { return log_number_; }
|
||||
|
||||
// !!! To be deprecated! Please don't not use this function anymore!
|
||||
const Options* options() const { return &options_; }
|
||||
|
||||
// thread-safe
|
||||
const EnvOptions* soptions() const;
|
||||
const ImmutableCFOptions* ioptions() const { return &ioptions_; }
|
||||
@@ -218,6 +215,12 @@ class ColumnFamilyData {
|
||||
const MutableCFOptions* GetLatestMutableCFOptions() const {
|
||||
return &mutable_cf_options_;
|
||||
}
|
||||
|
||||
// REQUIRES: DB mutex held
|
||||
// Build ColumnFamiliesOptions with immutable options and latest mutable
|
||||
// options.
|
||||
ColumnFamilyOptions GetLatestCFOptions() const;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// REQUIRES: DB mutex held
|
||||
Status SetOptions(
|
||||
@@ -249,6 +252,13 @@ class ColumnFamilyData {
|
||||
// REQUIRES: DB mutex held
|
||||
Compaction* PickCompaction(const MutableCFOptions& mutable_options,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Check if the passed range overlap with any running compactions.
|
||||
// REQUIRES: DB mutex held
|
||||
bool RangeOverlapWithCompaction(const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key,
|
||||
int level) const;
|
||||
|
||||
// A flag to tell a manual compaction is to compact all levels together
|
||||
// instad of for specific level.
|
||||
static const int kCompactAllLevels;
|
||||
@@ -325,7 +335,8 @@ class ColumnFamilyData {
|
||||
Version* dummy_versions, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
const ColumnFamilyOptions& options,
|
||||
const DBOptions* db_options, const EnvOptions& env_options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options,
|
||||
ColumnFamilySet* column_family_set);
|
||||
|
||||
uint32_t id_;
|
||||
@@ -340,7 +351,7 @@ class ColumnFamilyData {
|
||||
std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
|
||||
int_tbl_prop_collector_factories_;
|
||||
|
||||
const Options options_;
|
||||
const ColumnFamilyOptions initial_cf_options_;
|
||||
const ImmutableCFOptions ioptions_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
|
||||
@@ -436,7 +447,8 @@ class ColumnFamilySet {
|
||||
ColumnFamilyData* current_;
|
||||
};
|
||||
|
||||
ColumnFamilySet(const std::string& dbname, const DBOptions* db_options,
|
||||
ColumnFamilySet(const std::string& dbname,
|
||||
const ImmutableDBOptions* db_options,
|
||||
const EnvOptions& env_options, Cache* table_cache,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
WriteController* write_controller);
|
||||
@@ -493,7 +505,7 @@ class ColumnFamilySet {
|
||||
ColumnFamilyData* default_cfd_cache_;
|
||||
|
||||
const std::string db_name_;
|
||||
const DBOptions* const db_options_;
|
||||
const ImmutableDBOptions* const db_options_;
|
||||
const EnvOptions env_options_;
|
||||
Cache* table_cache_;
|
||||
WriteBufferManager* write_buffer_manager_;
|
||||
|
||||
+350
-61
@@ -1,7 +1,8 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
// This source code is licensed under the BSD-style license found in
|
||||
// the LICENSE file in the root directory of this source tree. An
|
||||
// additional grant of patent rights can be found in the PATENTS file
|
||||
// in the same directory.
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
@@ -279,29 +280,23 @@ class ColumnFamilyTest : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE // TEST functions in DB are not supported in lite
|
||||
void WaitForFlush(int cf) {
|
||||
#ifndef ROCKSDB_LITE // TEST functions are not supported in lite
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf]));
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void WaitForCompaction() {
|
||||
#ifndef ROCKSDB_LITE // TEST functions are not supported in lite
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
uint64_t MaxTotalInMemoryState() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
return dbfull()->TEST_MaxTotalInMemoryState();
|
||||
#else
|
||||
return 0;
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
void AssertMaxTotalInMemoryState(uint64_t value) {
|
||||
ASSERT_EQ(value, MaxTotalInMemoryState());
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
Status Put(int cf, const std::string& key, const std::string& value) {
|
||||
return db_->Put(WriteOptions(), handles_[cf], Slice(key), Slice(value));
|
||||
@@ -797,6 +792,7 @@ TEST_F(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE // TEST functions used are not supported
|
||||
TEST_F(ColumnFamilyTest, FlushTest) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two"});
|
||||
@@ -911,7 +907,47 @@ TEST_F(ColumnFamilyTest, LogDeletionTest) {
|
||||
AssertCountLiveLogFiles(4);
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(ColumnFamilyTest, CrashAfterFlush) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
db_options_.env = fault_env.get();
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
|
||||
WriteBatch batch;
|
||||
batch.Put(handles_[0], Slice("foo"), Slice("bar"));
|
||||
batch.Put(handles_[1], Slice("foo"), Slice("bar"));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
Flush(0);
|
||||
fault_env->SetFilesystemActive(false);
|
||||
|
||||
std::vector<std::string> names;
|
||||
for (auto name : names_) {
|
||||
if (name != "") {
|
||||
names.push_back(name);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
fault_env->DropUnsyncedFileData();
|
||||
fault_env->ResetState();
|
||||
Open(names, {});
|
||||
|
||||
// Write batch should be atomic.
|
||||
ASSERT_EQ(Get(0, "foo"), Get(1, "foo"));
|
||||
|
||||
Close();
|
||||
db_options_.env = env_;
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, OpenNonexistentColumnFamily) {
|
||||
ASSERT_OK(TryOpen({"default"}));
|
||||
Close();
|
||||
ASSERT_TRUE(TryOpen({"default", "dne"}).IsInvalidArgument());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE // WaitForFlush() is not supported
|
||||
// Makes sure that obsolete log files get deleted
|
||||
TEST_F(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
// disable flushing stale column families
|
||||
@@ -1016,6 +1052,7 @@ TEST_F(ColumnFamilyTest, DifferentWriteBufferSizes) {
|
||||
AssertCountLiveLogFiles(7);
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // Cuckoo is not supported in lite
|
||||
TEST_F(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
@@ -1040,6 +1077,39 @@ TEST_F(ColumnFamilyTest, MemtableNotSupportSnapshot) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
class TestComparator : public Comparator {
|
||||
int Compare(const rocksdb::Slice& a, const rocksdb::Slice& b) const override {
|
||||
return 0;
|
||||
}
|
||||
const char* Name() const override { return "Test"; }
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const rocksdb::Slice& limit) const override {}
|
||||
void FindShortSuccessor(std::string* key) const override {}
|
||||
};
|
||||
|
||||
static TestComparator third_comparator;
|
||||
static TestComparator fourth_comparator;
|
||||
|
||||
// Test that we can retrieve the comparator from a created CF
|
||||
TEST_F(ColumnFamilyTest, GetComparator) {
|
||||
Open();
|
||||
// Add a column family with no comparator specified
|
||||
CreateColumnFamilies({"first"});
|
||||
const Comparator* comp = handles_[0]->GetComparator();
|
||||
ASSERT_EQ(comp, BytewiseComparator());
|
||||
|
||||
// Add three column families - one with no comparator and two
|
||||
// with comparators specified
|
||||
ColumnFamilyOptions second, third, fourth;
|
||||
second.comparator = &third_comparator;
|
||||
third.comparator = &fourth_comparator;
|
||||
CreateColumnFamilies({"second", "third", "fourth"}, {second, third, fourth});
|
||||
ASSERT_EQ(handles_[1]->GetComparator(), BytewiseComparator());
|
||||
ASSERT_EQ(handles_[2]->GetComparator(), &third_comparator);
|
||||
ASSERT_EQ(handles_[3]->GetComparator(), &fourth_comparator);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
Open();
|
||||
CreateColumnFamilies({"first", "second"});
|
||||
@@ -1070,6 +1140,7 @@ TEST_F(ColumnFamilyTest, DifferentMergeOperators) {
|
||||
Close();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE // WaitForFlush() is not supported
|
||||
TEST_F(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
@@ -1081,7 +1152,8 @@ TEST_F(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
default_cf.max_compaction_bytes = static_cast<uint64_t>(1) << 60;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
default_cf.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -1137,6 +1209,7 @@ TEST_F(ColumnFamilyTest, DifferentCompactionStyles) {
|
||||
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Sync points not supported in RocksDB Lite
|
||||
@@ -1153,7 +1226,7 @@ TEST_F(ColumnFamilyTest, MultipleManualCompactions) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1252,7 +1325,7 @@ TEST_F(ColumnFamilyTest, AutomaticAndManualCompactions) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1345,7 +1418,7 @@ TEST_F(ColumnFamilyTest, ManualAndAutomaticCompactions) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1441,7 +1514,7 @@ TEST_F(ColumnFamilyTest, SameCFManualManualCompactions) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1540,7 +1613,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactions) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1630,7 +1703,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticCompactionsLevel) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1727,7 +1800,7 @@ TEST_F(ColumnFamilyTest, SameCFManualAutomaticConflict) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -1847,7 +1920,7 @@ TEST_F(ColumnFamilyTest, SameCFAutomaticManualCompactions) {
|
||||
default_cf.num_levels = 3;
|
||||
default_cf.write_buffer_size = 64 << 10; // 64KB
|
||||
default_cf.target_file_size_base = 30 << 10;
|
||||
default_cf.source_compaction_factor = 100;
|
||||
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));
|
||||
@@ -2030,6 +2103,7 @@ TEST_F(ColumnFamilyTest, ReadOnlyDBTest) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // WaitForFlush() is not supported in lite
|
||||
TEST_F(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
Open();
|
||||
CreateColumnFamiliesAndReopen({"one", "two", "three", "four"});
|
||||
@@ -2051,7 +2125,9 @@ TEST_F(ColumnFamilyTest, DontRollEmptyLogs) {
|
||||
ASSERT_EQ(static_cast<size_t>(total_new_writable_files), handles_.size() + 1);
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // WaitForCompaction() is not supported in lite
|
||||
TEST_F(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
Open();
|
||||
CreateColumnFamilies({"one", "two"});
|
||||
@@ -2085,6 +2161,7 @@ TEST_F(ColumnFamilyTest, FlushStaleColumnFamilies) {
|
||||
ASSERT_EQ(0, dbfull()->TEST_total_log_size());
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(ColumnFamilyTest, CreateMissingColumnFamilies) {
|
||||
Status s = TryOpen({"one", "two"});
|
||||
@@ -2111,8 +2188,8 @@ TEST_F(ColumnFamilyTest, SanitizeOptions) {
|
||||
original.write_buffer_size =
|
||||
l * 4 * 1024 * 1024 + i * 1024 * 1024 + j * 1024 + k;
|
||||
|
||||
ColumnFamilyOptions result =
|
||||
SanitizeOptions(db_options, nullptr, original);
|
||||
ColumnFamilyOptions result = SanitizeOptions(
|
||||
ImmutableDBOptions(db_options), nullptr, original);
|
||||
ASSERT_TRUE(result.level0_stop_writes_trigger >=
|
||||
result.level0_slowdown_writes_trigger);
|
||||
ASSERT_TRUE(result.level0_slowdown_writes_trigger >=
|
||||
@@ -2373,14 +2450,13 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
|
||||
MutableCFOptions mutable_cf_options(
|
||||
Options(db_options_, column_family_options_),
|
||||
ImmutableCFOptions(Options(db_options_, column_family_options_)));
|
||||
MutableCFOptions mutable_cf_options(column_family_options_);
|
||||
|
||||
mutable_cf_options.level0_slowdown_writes_trigger = 20;
|
||||
mutable_cf_options.level0_stop_writes_trigger = 10000;
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
mutable_cf_options.hard_pending_compaction_bytes_limit = 2000;
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2392,7 +2468,7 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(400);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2400,7 +2476,7 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.2,
|
||||
dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2456,7 +2532,7 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(3001);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2481,7 +2557,7 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate / 1.2,
|
||||
dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(101);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2527,16 +2603,17 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
vstorage->set_l0_delay_trigger_count(50);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(60);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_TRUE(!dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(70);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(500);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2544,7 +2621,6 @@ TEST_F(ColumnFamilyTest, WriteStallSingleColumnFamily) {
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
ASSERT_EQ(kBaseRate, dbfull()->TEST_write_controler().delayed_write_rate());
|
||||
|
||||
mutable_cf_options.disable_auto_compactions = false;
|
||||
vstorage->set_l0_delay_trigger_count(71);
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(501);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
@@ -2563,9 +2639,7 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
|
||||
VersionStorageInfo* vstorage = cfd->current()->storage_info();
|
||||
|
||||
MutableCFOptions mutable_cf_options(
|
||||
Options(db_options_, column_family_options_),
|
||||
ImmutableCFOptions(Options(db_options_, column_family_options_)));
|
||||
MutableCFOptions mutable_cf_options(column_family_options_);
|
||||
|
||||
// Speed up threshold = min(4 * 2, 4 + (36 - 4)/4) = 8
|
||||
mutable_cf_options.level0_file_num_compaction_trigger = 4;
|
||||
@@ -2577,31 +2651,31 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(50);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(300);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(45);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(7);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(9);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(6);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
// Speed up threshold = min(4 * 2, 4 + (12 - 4)/4) = 6
|
||||
mutable_cf_options.level0_file_num_compaction_trigger = 4;
|
||||
@@ -2610,15 +2684,15 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupSingleColumnFamily) {
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(5);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(7);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(3);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
@@ -2634,9 +2708,7 @@ TEST_F(ColumnFamilyTest, WriteStallTwoColumnFamilies) {
|
||||
static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
VersionStorageInfo* vstorage1 = cfd1->current()->storage_info();
|
||||
|
||||
MutableCFOptions mutable_cf_options(
|
||||
Options(db_options_, column_family_options_),
|
||||
ImmutableCFOptions(Options(db_options_, column_family_options_)));
|
||||
MutableCFOptions mutable_cf_options(column_family_options_);
|
||||
mutable_cf_options.level0_slowdown_writes_trigger = 20;
|
||||
mutable_cf_options.level0_stop_writes_trigger = 10000;
|
||||
mutable_cf_options.soft_pending_compaction_bytes_limit = 200;
|
||||
@@ -2718,9 +2790,7 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
static_cast<ColumnFamilyHandleImpl*>(handles_[1])->cfd();
|
||||
VersionStorageInfo* vstorage1 = cfd1->current()->storage_info();
|
||||
|
||||
MutableCFOptions mutable_cf_options(
|
||||
Options(db_options_, column_family_options_),
|
||||
ImmutableCFOptions(Options(db_options_, column_family_options_)));
|
||||
MutableCFOptions mutable_cf_options(column_family_options_);
|
||||
// Speed up threshold = min(4 * 2, 4 + (36 - 4)/4) = 8
|
||||
mutable_cf_options.level0_file_num_compaction_trigger = 4;
|
||||
mutable_cf_options.level0_slowdown_writes_trigger = 36;
|
||||
@@ -2734,43 +2804,262 @@ TEST_F(ColumnFamilyTest, CompactionSpeedupTwoColumnFamilies) {
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(40);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(60);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(30);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(70);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->TEST_set_estimated_compaction_needed_bytes(20);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->TEST_set_estimated_compaction_needed_bytes(3);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(9);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage1->set_l0_delay_trigger_count(2);
|
||||
cfd1->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(6, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(6, dbfull()->TEST_BGCompactionsAllowed());
|
||||
|
||||
vstorage->set_l0_delay_trigger_count(0);
|
||||
cfd->RecalculateWriteStallConditions(mutable_cf_options);
|
||||
ASSERT_EQ(2, dbfull()->BGCompactionsAllowed());
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(ColumnFamilyTest, FlushCloseWALFiles) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"DBImpl::BGWorkFlush:done", "FlushCloseWALFiles:0"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Block flush jobs from running
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
|
||||
Env::Priority::HIGH);
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
TEST_SYNC_POINT("FlushCloseWALFiles:0");
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // WaitForFlush() is not supported
|
||||
TEST_F(ColumnFamilyTest, IteratorCloseWALFile1) {
|
||||
SpecialEnv env(Env::Default());
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
// Create an iterator holding the current super version.
|
||||
Iterator* it = db_->NewIterator(ReadOptions(), handles_[1]);
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
Flush(1);
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
// Flush jobs will close previous WAL files after finishing. By
|
||||
// block flush jobs from running, we trigger a condition where
|
||||
// the iterator destructor should close the WAL files.
|
||||
test::SleepingBackgroundTask sleeping_task;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
|
||||
Env::Priority::HIGH);
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
delete it;
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
WaitForFlush(1);
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(ColumnFamilyTest, IteratorCloseWALFile2) {
|
||||
SpecialEnv env(Env::Default());
|
||||
// Allow both of flush and purge job to schedule.
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(2));
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
// Create an iterator holding the current super version.
|
||||
ReadOptions ro;
|
||||
ro.background_purge_on_iterator_cleanup = true;
|
||||
Iterator* it = db_->NewIterator(ro, handles_[1]);
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
Flush(1);
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:0",
|
||||
"DBImpl::BGWorkPurge:start"},
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:2",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BGWorkPurge:end", "ColumnFamilyTest::IteratorCloseWALFile2:1"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
delete it;
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:0");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:1");
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:2");
|
||||
WaitForFlush(1);
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
#ifndef ROCKSDB_LITE // TEST functions are not supported in lite
|
||||
TEST_F(ColumnFamilyTest, ForwardIteratorCloseWALFile) {
|
||||
SpecialEnv env(Env::Default());
|
||||
// Allow both of flush and purge job to schedule.
|
||||
env.SetBackgroundThreads(2, Env::HIGH);
|
||||
db_options_.env = &env;
|
||||
db_options_.max_background_flushes = 1;
|
||||
column_family_options_.memtable_factory.reset(new SpecialSkipListFactory(3));
|
||||
column_family_options_.level0_file_num_compaction_trigger = 2;
|
||||
Open();
|
||||
CreateColumnFamilies({"one"});
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodar2", "mirko"));
|
||||
Flush(1);
|
||||
|
||||
// Create an iterator holding the current super version, as well as
|
||||
// the SST file just flushed.
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
ro.background_purge_on_iterator_cleanup = true;
|
||||
Iterator* it = db_->NewIterator(ro, handles_[1]);
|
||||
// A flush will make `it` hold the last reference of its super version.
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodar2", "mirko"));
|
||||
Flush(1);
|
||||
|
||||
WaitForCompaction();
|
||||
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(0, "fodor", "mirko"));
|
||||
ASSERT_OK(Put(1, "fodor", "mirko"));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency({
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:0",
|
||||
"DBImpl::BGWorkPurge:start"},
|
||||
{"ColumnFamilyTest::IteratorCloseWALFile2:2",
|
||||
"DBImpl::BackgroundCallFlush:start"},
|
||||
{"DBImpl::BGWorkPurge:end", "ColumnFamilyTest::IteratorCloseWALFile2:1"},
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
ASSERT_OK(db_->Put(wo, handles_[1], "fodor", "mirko"));
|
||||
|
||||
env.delete_count_.store(0);
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
// Deleting the iterator will clear its super version, triggering
|
||||
// closing all files
|
||||
it->Seek("");
|
||||
ASSERT_EQ(2, env.num_open_wal_file_.load());
|
||||
ASSERT_EQ(0, env.delete_count_.load());
|
||||
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:0");
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:1");
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
ASSERT_EQ(1, env.delete_count_.load());
|
||||
TEST_SYNC_POINT("ColumnFamilyTest::IteratorCloseWALFile2:2");
|
||||
WaitForFlush(1);
|
||||
ASSERT_EQ(1, env.num_open_wal_file_.load());
|
||||
ASSERT_EQ(1, env.delete_count_.load());
|
||||
|
||||
delete it;
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Reopen();
|
||||
ASSERT_EQ("mirko", Get(0, "fodor"));
|
||||
ASSERT_EQ("mirko", Get(1, "fodor"));
|
||||
db_options_.env = env_;
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Disable on windows because SyncWAL requires env->IsSyncThreadSafe()
|
||||
// to return true which is not so in unbuffered mode.
|
||||
#ifndef OS_WIN
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_impl.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -49,6 +50,10 @@ class FlushedFileCollector : public EventListener {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void ClearFlushedFiles() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
flushed_files_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> flushed_files_;
|
||||
@@ -115,13 +120,12 @@ TEST_F(CompactFilesTest, L0ConflictsFiles) {
|
||||
TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
Options options;
|
||||
// to trigger compaction more easily
|
||||
const int kWriteBufferSize = 10000;
|
||||
const int kWriteBufferSize = 65536;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
// Small slowdown and stop trigger for experimental purpose.
|
||||
options.level0_slowdown_writes_trigger = 20;
|
||||
options.level0_stop_writes_trigger = 20;
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.write_buffer_size = kWriteBufferSize;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
@@ -144,6 +148,7 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files, 1));
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForCompact();
|
||||
|
||||
// verify all compaction input files are deleted
|
||||
for (auto fname : l0_files) {
|
||||
@@ -152,6 +157,46 @@ TEST_F(CompactFilesTest, ObsoleteFiles) {
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, NotCutOutputOnLevel0) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Disable RocksDB background compaction.
|
||||
options.compaction_style = kCompactionStyleNone;
|
||||
options.level0_slowdown_writes_trigger = 1000;
|
||||
options.level0_stop_writes_trigger = 1000;
|
||||
options.write_buffer_size = 65536;
|
||||
options.max_write_buffer_number = 2;
|
||||
options.compression = kNoCompression;
|
||||
options.max_compaction_bytes = 5000;
|
||||
|
||||
// Add listener
|
||||
FlushedFileCollector* collector = new FlushedFileCollector();
|
||||
options.listeners.emplace_back(collector);
|
||||
|
||||
DB* db = nullptr;
|
||||
DestroyDB(db_name_, options);
|
||||
Status s = DB::Open(options, db_name_, &db);
|
||||
assert(s.ok());
|
||||
assert(db);
|
||||
|
||||
// create couple files
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
db->Put(WriteOptions(), ToString(i), std::string(1000, 'a' + (i % 26)));
|
||||
}
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForFlushMemTable();
|
||||
auto l0_files_1 = collector->GetFlushedFiles();
|
||||
collector->ClearFlushedFiles();
|
||||
for (int i = 0; i < 500; ++i) {
|
||||
db->Put(WriteOptions(), ToString(i), std::string(1000, 'a' + (i % 26)));
|
||||
}
|
||||
reinterpret_cast<DBImpl*>(db)->TEST_WaitForFlushMemTable();
|
||||
auto l0_files_2 = collector->GetFlushedFiles();
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files_1, 0));
|
||||
ASSERT_OK(db->CompactFiles(CompactionOptions(), l0_files_2, 0));
|
||||
// no assertion failure
|
||||
delete db;
|
||||
}
|
||||
|
||||
TEST_F(CompactFilesTest, CapturingPendingFiles) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -151,9 +151,9 @@ Status CompactedDBImpl::Open(const Options& options,
|
||||
std::unique_ptr<CompactedDBImpl> db(new CompactedDBImpl(db_options, dbname));
|
||||
Status s = db->Init(options);
|
||||
if (s.ok()) {
|
||||
Log(INFO_LEVEL, db->db_options_.info_log,
|
||||
Log(INFO_LEVEL, db->immutable_db_options_.info_log,
|
||||
"Opened the db as fully compacted mode");
|
||||
LogFlush(db->db_options_.info_log);
|
||||
LogFlush(db->immutable_db_options_.info_log);
|
||||
*dbptr = db.release();
|
||||
}
|
||||
return s;
|
||||
|
||||
+7
-5
@@ -140,11 +140,12 @@ bool Compaction::IsFullCompaction(
|
||||
}
|
||||
|
||||
Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
const ImmutableCFOptions& _immutable_cf_options,
|
||||
const MutableCFOptions& _mutable_cf_options,
|
||||
std::vector<CompactionInputFiles> _inputs,
|
||||
int _output_level, uint64_t _target_file_size,
|
||||
uint64_t _max_grandparent_overlap_bytes,
|
||||
uint32_t _output_path_id, CompressionType _compression,
|
||||
uint64_t _max_compaction_bytes, uint32_t _output_path_id,
|
||||
CompressionType _compression,
|
||||
std::vector<FileMetaData*> _grandparents,
|
||||
bool _manual_compaction, double _score,
|
||||
bool _deletion_compaction,
|
||||
@@ -152,7 +153,8 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
: start_level_(_inputs[0].level),
|
||||
output_level_(_output_level),
|
||||
max_output_file_size_(_target_file_size),
|
||||
max_grandparent_overlap_bytes_(_max_grandparent_overlap_bytes),
|
||||
max_compaction_bytes_(_max_compaction_bytes),
|
||||
immutable_cf_options_(_immutable_cf_options),
|
||||
mutable_cf_options_(_mutable_cf_options),
|
||||
input_version_(nullptr),
|
||||
number_levels_(vstorage->num_levels()),
|
||||
@@ -247,7 +249,7 @@ bool Compaction::IsTrivialMove() const {
|
||||
return (start_level_ != output_level_ && num_input_levels() == 1 &&
|
||||
input(0, 0)->fd.GetPathId() == output_path_id() &&
|
||||
InputCompressionMatchesOutput() &&
|
||||
TotalFileSize(grandparents_) <= max_grandparent_overlap_bytes_);
|
||||
TotalFileSize(grandparents_) <= max_compaction_bytes_);
|
||||
}
|
||||
|
||||
void Compaction::AddInputDeletions(VersionEdit* out_edit) {
|
||||
@@ -427,7 +429,7 @@ bool Compaction::IsOutputLevelEmpty() const {
|
||||
}
|
||||
|
||||
bool Compaction::ShouldFormSubcompactions() const {
|
||||
if (mutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr) {
|
||||
if (immutable_cf_options_.max_subcompactions <= 1 || cfd_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
|
||||
+16
-8
@@ -8,10 +8,10 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#pragma once
|
||||
#include "db/version_set.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
#include "db/version_set.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -35,9 +35,10 @@ class CompactionFilter;
|
||||
class Compaction {
|
||||
public:
|
||||
Compaction(VersionStorageInfo* input_version,
|
||||
const ImmutableCFOptions& immutable_cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
std::vector<CompactionInputFiles> inputs, int output_level,
|
||||
uint64_t target_file_size, uint64_t max_grandparent_overlap_bytes,
|
||||
uint64_t target_file_size, uint64_t max_compaction_bytes,
|
||||
uint32_t output_path_id, CompressionType compression,
|
||||
std::vector<FileMetaData*> grandparents,
|
||||
bool manual_compaction = false, double score = -1,
|
||||
@@ -103,6 +104,8 @@ class Compaction {
|
||||
return &inputs_[compaction_input_level].files;
|
||||
}
|
||||
|
||||
const std::vector<CompactionInputFiles>* inputs() { return &inputs_; }
|
||||
|
||||
// Returns the LevelFilesBrief of the specified compaction input level.
|
||||
const LevelFilesBrief* input_levels(size_t compaction_input_level) const {
|
||||
return &input_levels_[compaction_input_level];
|
||||
@@ -171,6 +174,12 @@ class Compaction {
|
||||
// How many total levels are there?
|
||||
int number_levels() const { return number_levels_; }
|
||||
|
||||
// Return the ImmutableCFOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const ImmutableCFOptions* immutable_cf_options() const {
|
||||
return &immutable_cf_options_;
|
||||
}
|
||||
|
||||
// Return the MutableCFOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const MutableCFOptions* mutable_cf_options() const {
|
||||
@@ -229,9 +238,7 @@ class Compaction {
|
||||
return grandparents_;
|
||||
}
|
||||
|
||||
uint64_t max_grandparent_overlap_bytes() const {
|
||||
return max_grandparent_overlap_bytes_;
|
||||
}
|
||||
uint64_t max_compaction_bytes() const { return max_compaction_bytes_; }
|
||||
|
||||
private:
|
||||
// mark (or clear) all files that are being compacted
|
||||
@@ -254,8 +261,9 @@ class Compaction {
|
||||
const int start_level_; // the lowest level to be compacted
|
||||
const int output_level_; // levels to which output files are stored
|
||||
uint64_t max_output_file_size_;
|
||||
uint64_t max_grandparent_overlap_bytes_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
uint64_t max_compaction_bytes_;
|
||||
const ImmutableCFOptions immutable_cf_options_;
|
||||
const MutableCFOptions mutable_cf_options_;
|
||||
Version* input_version_;
|
||||
VersionEdit edit_;
|
||||
const int number_levels_;
|
||||
|
||||
+42
-11
@@ -14,8 +14,9 @@ CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key, const Compaction* compaction,
|
||||
const CompactionFilter* compaction_filter, LogBuffer* log_buffer)
|
||||
bool expect_valid_internal_key, RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction, const CompactionFilter* compaction_filter,
|
||||
LogBuffer* log_buffer)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -23,6 +24,7 @@ CompactionIterator::CompactionIterator(
|
||||
earliest_write_conflict_snapshot_(earliest_write_conflict_snapshot),
|
||||
env_(env),
|
||||
expect_valid_internal_key_(expect_valid_internal_key),
|
||||
range_del_agg_(range_del_agg),
|
||||
compaction_(compaction),
|
||||
compaction_filter_(compaction_filter),
|
||||
log_buffer_(log_buffer),
|
||||
@@ -36,11 +38,11 @@ CompactionIterator::CompactionIterator(
|
||||
|
||||
if (snapshots_->size() == 0) {
|
||||
// optimize for fast path if there are no snapshots
|
||||
visible_at_tip_ = last_sequence;
|
||||
earliest_snapshot_ = visible_at_tip_;
|
||||
visible_at_tip_ = true;
|
||||
earliest_snapshot_ = last_sequence;
|
||||
latest_snapshot_ = 0;
|
||||
} else {
|
||||
visible_at_tip_ = 0;
|
||||
visible_at_tip_ = false;
|
||||
earliest_snapshot_ = snapshots_->at(0);
|
||||
latest_snapshot_ = snapshots_->back();
|
||||
}
|
||||
@@ -49,6 +51,12 @@ CompactionIterator::CompactionIterator(
|
||||
} else {
|
||||
ignore_snapshots_ = false;
|
||||
}
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
CompactionIterator::~CompactionIterator() {
|
||||
// input_ Iteartor lifetime is longer than pinned_iters_mgr_ lifetime
|
||||
input_->SetPinnedItersMgr(nullptr);
|
||||
}
|
||||
|
||||
void CompactionIterator::ResetRecordCounts() {
|
||||
@@ -83,6 +91,8 @@ void CompactionIterator::Next() {
|
||||
ikey_.user_key = current_key_.GetUserKey();
|
||||
valid_ = true;
|
||||
} else {
|
||||
// We consumed all pinned merge operands, release pinned iterators
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
// MergeHelper moves the iterator to the first record after the merged
|
||||
// records, so even though we reached the end of the merge output, we do
|
||||
// not want to advance the iterator.
|
||||
@@ -145,6 +155,7 @@ void CompactionIterator::NextFromInput() {
|
||||
if (!has_current_user_key_ ||
|
||||
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetKey(key_, &ikey_);
|
||||
current_user_key_ = ikey_.user_key;
|
||||
has_current_user_key_ = true;
|
||||
@@ -202,8 +213,9 @@ void CompactionIterator::NextFromInput() {
|
||||
SequenceNumber last_snapshot = current_user_key_snapshot_;
|
||||
SequenceNumber prev_snapshot = 0; // 0 means no previous snapshot
|
||||
current_user_key_snapshot_ =
|
||||
visible_at_tip_ ? visible_at_tip_ : findEarliestVisibleSnapshot(
|
||||
ikey_.sequence, &prev_snapshot);
|
||||
visible_at_tip_
|
||||
? earliest_snapshot_
|
||||
: findEarliestVisibleSnapshot(ikey_.sequence, &prev_snapshot);
|
||||
|
||||
if (clear_and_output_next_key_) {
|
||||
// In the previous iteration we encountered a single delete that we could
|
||||
@@ -246,6 +258,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// those operations for a given key is documented as being undefined. So
|
||||
// we can choose how to handle such a combinations of operations. We will
|
||||
// try to compact out as much as we can in these cases.
|
||||
// We will report counts on these anomalous cases.
|
||||
|
||||
// The easiest way to process a SingleDelete during iteration is to peek
|
||||
// ahead at the next key.
|
||||
@@ -268,6 +281,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// First SingleDelete has been skipped since we already called
|
||||
// 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_) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
@@ -277,7 +291,12 @@ void CompactionIterator::NextFromInput() {
|
||||
|
||||
// Note: it doesn't matter whether the second key is a Put or if it
|
||||
// is an unexpected Merge or Delete. We will compact it out
|
||||
// either way.
|
||||
// either way. We will maintain counts of how many mismatches
|
||||
// happened
|
||||
if (next_ikey.type != kTypeValue) {
|
||||
++iter_stats_.num_single_del_mismatch;
|
||||
}
|
||||
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
// Already called input_->Next() once. Call it a second time to
|
||||
@@ -305,7 +324,7 @@ void CompactionIterator::NextFromInput() {
|
||||
}
|
||||
} else {
|
||||
// We are at the end of the input, could not parse the next key, or hit
|
||||
// the next key. The iterator returns the single delete if the key
|
||||
// a different key. The iterator returns the single delete if the key
|
||||
// possibly exists beyond the current output level. We set
|
||||
// has_current_user_key to false so that if the iterator is at the next
|
||||
// key, we do not compare it again against the previous key at the next
|
||||
@@ -318,6 +337,7 @@ void CompactionIterator::NextFromInput() {
|
||||
// Key doesn't exist outside of this range.
|
||||
// Can compact out this SingleDelete.
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
++iter_stats_.num_single_del_fallthru;
|
||||
} else {
|
||||
// Output SingleDelete
|
||||
valid_ = true;
|
||||
@@ -368,11 +388,13 @@ void CompactionIterator::NextFromInput() {
|
||||
return;
|
||||
}
|
||||
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
// We know the merge type entry is not hidden, otherwise we would
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
merge_helper_->MergeUntil(input_, prev_snapshot, bottommost_level_);
|
||||
merge_helper_->MergeUntil(input_, range_del_agg_, prev_snapshot,
|
||||
bottommost_level_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
if (merge_out_iter_.Valid()) {
|
||||
@@ -395,9 +417,18 @@ void CompactionIterator::NextFromInput() {
|
||||
// batch consumed by the merge operator should not shadow any keys
|
||||
// coming after the merges
|
||||
has_current_user_key_ = false;
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
} else {
|
||||
valid_ = true;
|
||||
// 1. new user key -OR-
|
||||
// 2. different snapshot stripe
|
||||
bool should_delete =
|
||||
range_del_agg_->ShouldDelete(key_, true /* for_compaction */);
|
||||
if (should_delete) {
|
||||
input_->Next();
|
||||
} else {
|
||||
valid_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#include "db/compaction.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "rocksdb/compaction_filter.h"
|
||||
#include "util/log_buffer.h"
|
||||
|
||||
@@ -33,6 +35,10 @@ struct CompactionIteratorStats {
|
||||
uint64_t num_input_corrupt_records = 0;
|
||||
uint64_t total_input_raw_key_bytes = 0;
|
||||
uint64_t total_input_raw_value_bytes = 0;
|
||||
|
||||
// Single-Delete diagnostics for exceptional situations
|
||||
uint64_t num_single_del_fallthru = 0;
|
||||
uint64_t num_single_del_mismatch = 0;
|
||||
};
|
||||
|
||||
class CompactionIterator {
|
||||
@@ -42,10 +48,13 @@ class CompactionIterator {
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot, Env* env,
|
||||
bool expect_valid_internal_key,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
LogBuffer* log_buffer = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
void ResetRecordCounts();
|
||||
|
||||
// Seek to the beginning of the compaction iterator output.
|
||||
@@ -92,12 +101,13 @@ class CompactionIterator {
|
||||
const SequenceNumber earliest_write_conflict_snapshot_;
|
||||
Env* env_;
|
||||
bool expect_valid_internal_key_;
|
||||
RangeDelAggregator* range_del_agg_;
|
||||
const Compaction* compaction_;
|
||||
const CompactionFilter* compaction_filter_;
|
||||
LogBuffer* log_buffer_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
SequenceNumber visible_at_tip_;
|
||||
bool visible_at_tip_;
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
bool ignore_snapshots_;
|
||||
@@ -136,6 +146,9 @@ class CompactionIterator {
|
||||
bool clear_and_output_next_key_ = false;
|
||||
|
||||
MergeOutputIterator merge_out_iter_;
|
||||
// PinnedIteratorsManager used to pin input_ Iterator blocks while reading
|
||||
// merge operands and then releasing them after consuming them.
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
std::string compaction_filter_value_;
|
||||
// "level_ptrs" holds indices that remember which file of an associated
|
||||
// level we were last checking during the last call to compaction->
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/compaction_iterator.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "util/testharness.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
@@ -11,33 +15,45 @@ namespace rocksdb {
|
||||
|
||||
class CompactionIteratorTest : public testing::Test {
|
||||
public:
|
||||
CompactionIteratorTest() : cmp_(BytewiseComparator()), snapshots_({}) {}
|
||||
CompactionIteratorTest()
|
||||
: cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}
|
||||
|
||||
void InitIterators(const std::vector<std::string>& ks,
|
||||
const std::vector<std::string>& vs,
|
||||
const std::vector<std::string>& range_del_ks,
|
||||
const std::vector<std::string>& range_del_vs,
|
||||
SequenceNumber last_sequence) {
|
||||
std::unique_ptr<InternalIterator> range_del_iter(
|
||||
new test::VectorIterator(range_del_ks, range_del_vs));
|
||||
range_del_agg_.reset(new RangeDelAggregator(icmp_, snapshots_));
|
||||
ASSERT_OK(range_del_agg_->AddTombstones(std::move(range_del_iter)));
|
||||
|
||||
void InitIterator(const std::vector<std::string>& ks,
|
||||
const std::vector<std::string>& vs,
|
||||
SequenceNumber last_sequence) {
|
||||
merge_helper_.reset(new MergeHelper(Env::Default(), cmp_, nullptr, nullptr,
|
||||
nullptr, 0U, false, 0));
|
||||
iter_.reset(new test::VectorIterator(ks, vs));
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
iter_.get(), cmp_, merge_helper_.get(), last_sequence, &snapshots_,
|
||||
kMaxSequenceNumber, Env::Default(), false));
|
||||
kMaxSequenceNumber, Env::Default(), false, range_del_agg_.get()));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot) { snapshots_.push_back(snapshot); }
|
||||
|
||||
const Comparator* cmp_;
|
||||
const InternalKeyComparator icmp_;
|
||||
std::vector<SequenceNumber> snapshots_;
|
||||
std::unique_ptr<MergeHelper> merge_helper_;
|
||||
std::unique_ptr<test::VectorIterator> iter_;
|
||||
std::unique_ptr<CompactionIterator> c_iter_;
|
||||
std::unique_ptr<RangeDelAggregator> range_del_agg_;
|
||||
};
|
||||
|
||||
// It is possible that the output of the compaction iterator is empty even if
|
||||
// the input is not.
|
||||
TEST_F(CompactionIteratorTest, EmptyResult) {
|
||||
InitIterator({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue)},
|
||||
{"", "val"}, 5);
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue)},
|
||||
{"", "val"}, {}, {}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
@@ -45,10 +61,10 @@ TEST_F(CompactionIteratorTest, EmptyResult) {
|
||||
// If there is a corruption after a single deletion, the corrupted key should
|
||||
// be preserved.
|
||||
TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
InitIterator({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue, true),
|
||||
test::KeyStr("b", 10, kTypeValue)},
|
||||
{"", "val", "val2"}, 10);
|
||||
InitIterators({test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 3, kTypeValue, true),
|
||||
test::KeyStr("b", 10, kTypeValue)},
|
||||
{"", "val", "val2"}, {}, {}, 10);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("a", 5, kTypeSingleDeletion),
|
||||
@@ -63,6 +79,43 @@ TEST_F(CompactionIteratorTest, CorruptionAfterSingleDeletion) {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, SimpleRangeDeletion) {
|
||||
InitIterators({test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("morning", 2, kTypeValue),
|
||||
test::KeyStr("night", 3, kTypeValue)},
|
||||
{"zao", "zao", "wan"},
|
||||
{test::KeyStr("ma", 4, kTypeRangeDeletion)}, {"mz"}, 5);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 3, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
TEST_F(CompactionIteratorTest, RangeDeletionWithSnapshots) {
|
||||
AddSnapshot(10);
|
||||
std::vector<std::string> ks1;
|
||||
ks1.push_back(test::KeyStr("ma", 28, kTypeRangeDeletion));
|
||||
std::vector<std::string> vs1{"mz"};
|
||||
std::vector<std::string> ks2{test::KeyStr("morning", 15, kTypeValue),
|
||||
test::KeyStr("morning", 5, kTypeValue),
|
||||
test::KeyStr("night", 40, kTypeValue),
|
||||
test::KeyStr("night", 20, kTypeValue)};
|
||||
std::vector<std::string> vs2{"zao 15", "zao 5", "wan 40", "wan 20"};
|
||||
InitIterators(ks2, vs2, ks1, vs1, 40);
|
||||
c_iter_->SeekToFirst();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("morning", 5, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_TRUE(c_iter_->Valid());
|
||||
ASSERT_EQ(test::KeyStr("night", 40, kTypeValue), c_iter_->key().ToString());
|
||||
c_iter_->Next();
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+83
-30
@@ -99,6 +99,8 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t current_output_file_size;
|
||||
|
||||
// State during the subcompaction
|
||||
uint64_t total_bytes;
|
||||
uint64_t num_input_records;
|
||||
@@ -121,6 +123,7 @@ struct CompactionJob::SubcompactionState {
|
||||
end(_end),
|
||||
outfile(nullptr),
|
||||
builder(nullptr),
|
||||
current_output_file_size(0),
|
||||
total_bytes(0),
|
||||
num_input_records(0),
|
||||
num_output_records(0),
|
||||
@@ -142,6 +145,7 @@ struct CompactionJob::SubcompactionState {
|
||||
outputs = std::move(o.outputs);
|
||||
outfile = std::move(o.outfile);
|
||||
builder = std::move(o.builder);
|
||||
current_output_file_size = std::move(o.current_output_file_size);
|
||||
total_bytes = std::move(o.total_bytes);
|
||||
num_input_records = std::move(o.num_input_records);
|
||||
num_output_records = std::move(o.num_output_records);
|
||||
@@ -161,7 +165,7 @@ struct CompactionJob::SubcompactionState {
|
||||
|
||||
// Returns true iff we should stop building the current output
|
||||
// before processing "internal_key".
|
||||
bool ShouldStopBefore(const Slice& internal_key) {
|
||||
bool ShouldStopBefore(const Slice& internal_key, uint64_t curr_file_size) {
|
||||
const InternalKeyComparator* icmp =
|
||||
&compaction->column_family_data()->internal_comparator();
|
||||
const std::vector<FileMetaData*>& grandparents = compaction->grandparents();
|
||||
@@ -182,7 +186,8 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
seen_key = true;
|
||||
|
||||
if (overlapped_bytes > compaction->max_grandparent_overlap_bytes()) {
|
||||
if (overlapped_bytes + curr_file_size >
|
||||
compaction->max_compaction_bytes()) {
|
||||
// Too much overlap for current output; start new output
|
||||
overlapped_bytes = 0;
|
||||
return true;
|
||||
@@ -257,7 +262,7 @@ void CompactionJob::AggregateStatistics() {
|
||||
}
|
||||
|
||||
CompactionJob::CompactionJob(
|
||||
int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
int job_id, Compaction* compaction, const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory, Statistics* stats,
|
||||
@@ -292,7 +297,7 @@ CompactionJob::CompactionJob(
|
||||
assert(log_buffer_ != nullptr);
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
cfd->options()->enable_thread_tracking);
|
||||
db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_COMPACTION);
|
||||
ReportStartedCompaction(compaction);
|
||||
}
|
||||
@@ -306,7 +311,7 @@ void CompactionJob::ReportStartedCompaction(
|
||||
Compaction* compaction) {
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
ThreadStatusUtil::SetColumnFamily(cfd, cfd->ioptions()->env,
|
||||
cfd->options()->enable_thread_tracking);
|
||||
db_options_.enable_thread_tracking);
|
||||
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
@@ -479,9 +484,8 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
static_cast<uint64_t>(db_options_.max_subcompactions),
|
||||
max_output_files});
|
||||
|
||||
double mean = sum * 1.0 / subcompactions;
|
||||
|
||||
if (subcompactions > 1) {
|
||||
double mean = sum * 1.0 / subcompactions;
|
||||
// Greedily add ranges to the subcompaction until the sum of the ranges'
|
||||
// sizes becomes >= the expected mean size of a subcompaction
|
||||
sum = 0;
|
||||
@@ -534,7 +538,7 @@ Status CompactionJob::Run() {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
if (output_directory_ && !db_options_.disableDataSync) {
|
||||
if (output_directory_ && !db_options_.disable_data_sync) {
|
||||
output_directory_->Fsync();
|
||||
}
|
||||
|
||||
@@ -586,6 +590,16 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
const auto& stats = compaction_stats_;
|
||||
|
||||
double read_write_amp = 0.0;
|
||||
double write_amp = 0.0;
|
||||
if (stats.bytes_read_non_output_levels > 0) {
|
||||
read_write_amp = (stats.bytes_written + stats.bytes_read_output_level +
|
||||
stats.bytes_read_non_output_levels) /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
write_amp = stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels);
|
||||
}
|
||||
LogToBuffer(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
@@ -598,16 +612,10 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
stats.bytes_written / static_cast<double>(stats.micros),
|
||||
compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
stats.num_input_files_in_output_level,
|
||||
stats.num_output_files,
|
||||
stats.num_input_files_in_output_level, stats.num_output_files,
|
||||
stats.bytes_read_non_output_levels / 1048576.0,
|
||||
stats.bytes_read_output_level / 1048576.0,
|
||||
stats.bytes_written / 1048576.0,
|
||||
(stats.bytes_written + stats.bytes_read_output_level +
|
||||
stats.bytes_read_non_output_levels) /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels),
|
||||
stats.bytes_written /
|
||||
static_cast<double>(stats.bytes_read_non_output_levels),
|
||||
stats.bytes_written / 1048576.0, read_write_amp, write_amp,
|
||||
status.ToString().c_str(), stats.num_input_records,
|
||||
stats.num_dropped_records);
|
||||
|
||||
@@ -624,6 +632,13 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
<< "num_output_records" << compact_->num_output_records
|
||||
<< "num_subcompactions" << compact_->sub_compact_states.size();
|
||||
|
||||
if (compaction_job_stats_ != nullptr) {
|
||||
stream << "num_single_delete_mismatches"
|
||||
<< compaction_job_stats_->num_single_del_mismatch;
|
||||
stream << "num_single_delete_fallthrough"
|
||||
<< compaction_job_stats_->num_single_del_fallthru;
|
||||
}
|
||||
|
||||
if (measure_io_stats_ && compaction_job_stats_ != nullptr) {
|
||||
stream << "file_write_nanos" << compaction_job_stats_->file_write_nanos;
|
||||
stream << "file_range_sync_nanos"
|
||||
@@ -646,8 +661,11 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact != nullptr);
|
||||
std::unique_ptr<InternalIterator> input(
|
||||
versions_->MakeInputIterator(sub_compact->compaction));
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
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()));
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -668,7 +686,6 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
}
|
||||
|
||||
ColumnFamilyData* cfd = sub_compact->compaction->column_family_data();
|
||||
const MutableCFOptions* mutable_cf_options =
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
|
||||
@@ -725,7 +742,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_, env_, false,
|
||||
sub_compact->compaction, compaction_filter));
|
||||
range_del_agg.get(), sub_compact->compaction, compaction_filter));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
const auto& c_iter_stats = c_iter->iter_stats();
|
||||
@@ -750,9 +767,12 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
if (end != nullptr &&
|
||||
cfd->user_comparator()->Compare(c_iter->user_key(), *end) >= 0) {
|
||||
break;
|
||||
} else if (sub_compact->ShouldStopBefore(key) &&
|
||||
} else if (sub_compact->compaction->output_level() != 0 &&
|
||||
sub_compact->ShouldStopBefore(
|
||||
key, sub_compact->current_output_file_size) &&
|
||||
sub_compact->builder != nullptr) {
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact);
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact,
|
||||
range_del_agg.get());
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -775,6 +795,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact->builder != nullptr);
|
||||
assert(sub_compact->current_output() != nullptr);
|
||||
sub_compact->builder->Add(key, value);
|
||||
sub_compact->current_output_file_size = sub_compact->builder->FileSize();
|
||||
sub_compact->current_output()->meta.UpdateBoundaries(
|
||||
key, c_iter->ikey().sequence);
|
||||
sub_compact->num_output_records++;
|
||||
@@ -828,21 +849,29 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
}
|
||||
|
||||
Status input_status = input->status();
|
||||
c_iter->Next();
|
||||
|
||||
// Close output file if it is big enough
|
||||
// TODO(aekmekji): determine if file should be closed earlier than this
|
||||
// during subcompactions (i.e. if output size, estimated by input size, is
|
||||
// going to be 1.2MB and max_output_file_size = 1MB, prefer to have 0.6MB
|
||||
// and 0.6MB instead of 1MB and 0.2MB)
|
||||
if (sub_compact->builder->FileSize() >=
|
||||
sub_compact->compaction->max_output_file_size()) {
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact);
|
||||
if (sub_compact->compaction->output_level() != 0 &&
|
||||
sub_compact->current_output_file_size >=
|
||||
sub_compact->compaction->max_output_file_size()) {
|
||||
const Slice* next_key = nullptr;
|
||||
if (c_iter->Valid()) {
|
||||
next_key = &c_iter->key();
|
||||
}
|
||||
status = FinishCompactionOutputFile(input_status, sub_compact,
|
||||
range_del_agg.get(), next_key);
|
||||
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);
|
||||
}
|
||||
}
|
||||
c_iter->Next();
|
||||
}
|
||||
|
||||
sub_compact->num_input_records = c_iter_stats.num_input_records;
|
||||
@@ -850,6 +879,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
c_iter_stats.num_input_deletion_records;
|
||||
sub_compact->compaction_job_stats.num_corrupt_keys =
|
||||
c_iter_stats.num_input_corrupt_records;
|
||||
sub_compact->compaction_job_stats.num_single_del_fallthru =
|
||||
c_iter_stats.num_single_del_fallthru;
|
||||
sub_compact->compaction_job_stats.num_single_del_mismatch =
|
||||
c_iter_stats.num_single_del_mismatch;
|
||||
sub_compact->compaction_job_stats.total_input_raw_key_bytes +=
|
||||
c_iter_stats.total_input_raw_key_bytes;
|
||||
sub_compact->compaction_job_stats.total_input_raw_value_bytes +=
|
||||
@@ -865,8 +898,13 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
status = Status::ShutdownInProgress(
|
||||
"Database shutdown or Column family drop during compaction");
|
||||
}
|
||||
if (status.ok() && sub_compact->builder == nullptr &&
|
||||
range_del_agg->ShouldAddTombstones(bottommost_level_)) {
|
||||
status = OpenCompactionOutputFile(sub_compact);
|
||||
}
|
||||
if (status.ok() && sub_compact->builder != nullptr) {
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact);
|
||||
status = FinishCompactionOutputFile(input->status(), sub_compact,
|
||||
range_del_agg.get());
|
||||
}
|
||||
if (status.ok()) {
|
||||
status = input->status();
|
||||
@@ -917,7 +955,9 @@ void CompactionJob::RecordDroppedKeys(
|
||||
}
|
||||
|
||||
Status CompactionJob::FinishCompactionOutputFile(
|
||||
const Status& input_status, SubcompactionState* sub_compact) {
|
||||
const Status& input_status, SubcompactionState* sub_compact,
|
||||
RangeDelAggregator* range_del_agg,
|
||||
const Slice* next_table_min_key /* = nullptr */) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_SYNC_FILE);
|
||||
assert(sub_compact != nullptr);
|
||||
@@ -932,6 +972,16 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// Check for iterator errors
|
||||
Status s = input_status;
|
||||
auto meta = &sub_compact->current_output()->meta;
|
||||
if (s.ok()) {
|
||||
// For the first output table, include range tombstones before the min key
|
||||
// boundary. For subsequent output tables, this is unnecessary because we
|
||||
// extend each file's max key boundary up until the next file's min key when
|
||||
// range tombstones fall in the gap.
|
||||
range_del_agg->AddToBuilder(
|
||||
sub_compact->builder.get(),
|
||||
sub_compact->outputs.size() == 1 /* extend_before_min_key */,
|
||||
next_table_min_key, meta, bottommost_level_);
|
||||
}
|
||||
const uint64_t current_entries = sub_compact->builder->NumEntries();
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
if (s.ok()) {
|
||||
@@ -945,7 +995,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
|
||||
// Finish and check for file errors
|
||||
if (s.ok() && !db_options_.disableDataSync) {
|
||||
if (s.ok() && !db_options_.disable_data_sync) {
|
||||
StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
}
|
||||
@@ -1010,6 +1060,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
}
|
||||
|
||||
sub_compact->builder.reset();
|
||||
sub_compact->current_output_file_size = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -1117,7 +1168,9 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
*cfd->ioptions(), cfd->internal_comparator(),
|
||||
cfd->int_tbl_prop_collector_factories(), cfd->GetID(), cfd->GetName(),
|
||||
sub_compact->outfile.get(), sub_compact->compaction->output_compression(),
|
||||
cfd->ioptions()->compression_opts, &sub_compact->compression_dict,
|
||||
cfd->ioptions()->compression_opts,
|
||||
sub_compact->compaction->output_level(),
|
||||
&sub_compact->compression_dict,
|
||||
skip_filters));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
|
||||
+8
-3
@@ -25,6 +25,7 @@
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/range_del_aggregator.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
@@ -37,6 +38,7 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
@@ -52,7 +54,8 @@ class Arena;
|
||||
|
||||
class CompactionJob {
|
||||
public:
|
||||
CompactionJob(int job_id, Compaction* compaction, const DBOptions& db_options,
|
||||
CompactionJob(int job_id, Compaction* compaction,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
std::atomic<bool>* shutting_down, LogBuffer* log_buffer,
|
||||
Directory* db_directory, Directory* output_directory,
|
||||
@@ -94,7 +97,9 @@ class CompactionJob {
|
||||
void ProcessKeyValueCompaction(SubcompactionState* sub_compact);
|
||||
|
||||
Status FinishCompactionOutputFile(const Status& input_status,
|
||||
SubcompactionState* sub_compact);
|
||||
SubcompactionState* sub_compact,
|
||||
RangeDelAggregator* range_del_agg = nullptr,
|
||||
const Slice* next_table_min_key = nullptr);
|
||||
Status InstallCompactionResults(const MutableCFOptions& mutable_cf_options);
|
||||
void RecordCompactionIOStats();
|
||||
Status OpenCompactionOutputFile(SubcompactionState* sub_compact);
|
||||
@@ -120,7 +125,7 @@ class CompactionJob {
|
||||
|
||||
// DBImpl state
|
||||
const std::string& dbname_;
|
||||
const DBOptions& db_options_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const EnvOptions& env_options_;
|
||||
|
||||
Env* env_;
|
||||
|
||||
@@ -957,7 +957,7 @@ TEST_P(CompactionJobStatsTest, UniversalCompactionTest) {
|
||||
uint64_t key_base = 100000000l;
|
||||
// Note: key_base must be multiple of num_keys_per_L0_file
|
||||
int num_keys_per_table = 100;
|
||||
const uint32_t kTestScale = 8;
|
||||
const uint32_t kTestScale = 6;
|
||||
const int kKeySize = 10;
|
||||
const int kValueSize = 900;
|
||||
double compression_ratio = 1.0;
|
||||
@@ -1008,8 +1008,9 @@ TEST_P(CompactionJobStatsTest, UniversalCompactionTest) {
|
||||
num_input_units,
|
||||
num_keys_per_table * num_input_units,
|
||||
1.0, 0, false));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
}
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 4U);
|
||||
ASSERT_EQ(stats_checker->NumberOfUnverifiedStats(), 3U);
|
||||
|
||||
for (uint64_t start_key = key_base;
|
||||
start_key <= key_base * kTestScale;
|
||||
|
||||
@@ -68,7 +68,8 @@ class CompactionJobTest : public testing::Test {
|
||||
CompactionJobTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/compaction_job_test"),
|
||||
mutable_cf_options_(Options(), ImmutableCFOptions(Options())),
|
||||
db_options_(),
|
||||
mutable_cf_options_(cf_options_),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
@@ -239,10 +240,10 @@ class CompactionJobTest : public testing::Test {
|
||||
num_input_files += level_files.size();
|
||||
}
|
||||
|
||||
Compaction compaction(cfd->current()->storage_info(),
|
||||
Compaction compaction(cfd->current()->storage_info(), *cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
compaction_input_files, 1, 1024 * 1024, 10, 0,
|
||||
kNoCompression, {}, true);
|
||||
compaction_input_files, 1, 1024 * 1024,
|
||||
10 * 1024 * 1024, 0, kNoCompression, {}, true);
|
||||
compaction.SetInputVersion(cfd->current());
|
||||
|
||||
LogBuffer log_buffer(InfoLogLevel::INFO_LEVEL, db_options_.info_log.get());
|
||||
@@ -280,11 +281,11 @@ class CompactionJobTest : public testing::Test {
|
||||
Env* env_;
|
||||
std::string dbname_;
|
||||
EnvOptions env_options_;
|
||||
ImmutableDBOptions db_options_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
MutableCFOptions mutable_cf_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
DBOptions db_options_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
InstrumentedMutex mutex_;
|
||||
|
||||
+187
-57
@@ -151,17 +151,15 @@ CompactionPicker::~CompactionPicker() {}
|
||||
|
||||
// Delete this compaction from the list of running compactions.
|
||||
void CompactionPicker::ReleaseCompactionFiles(Compaction* c, Status status) {
|
||||
if (c->start_level() == 0 ||
|
||||
ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
level0_compactions_in_progress_.erase(c);
|
||||
}
|
||||
UnregisterCompaction(c);
|
||||
if (!status.ok()) {
|
||||
c->ResetNextCompactionIndex();
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionPicker::GetRange(const CompactionInputFiles& inputs,
|
||||
InternalKey* smallest, InternalKey* largest) {
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) const {
|
||||
const int level = inputs.level;
|
||||
assert(!inputs.empty());
|
||||
smallest->Clear();
|
||||
@@ -190,7 +188,8 @@ void CompactionPicker::GetRange(const CompactionInputFiles& inputs,
|
||||
|
||||
void CompactionPicker::GetRange(const CompactionInputFiles& inputs1,
|
||||
const CompactionInputFiles& inputs2,
|
||||
InternalKey* smallest, InternalKey* largest) {
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) const {
|
||||
assert(!inputs1.empty() || !inputs2.empty());
|
||||
if (inputs1.empty()) {
|
||||
GetRange(inputs2, smallest, largest);
|
||||
@@ -206,6 +205,33 @@ void CompactionPicker::GetRange(const CompactionInputFiles& inputs1,
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionPicker::GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
InternalKey* smallest,
|
||||
InternalKey* largest) const {
|
||||
InternalKey current_smallest;
|
||||
InternalKey current_largest;
|
||||
bool initialized = false;
|
||||
for (const auto& in : inputs) {
|
||||
if (in.empty()) {
|
||||
continue;
|
||||
}
|
||||
GetRange(in, ¤t_smallest, ¤t_largest);
|
||||
if (!initialized) {
|
||||
*smallest = current_smallest;
|
||||
*largest = current_largest;
|
||||
initialized = true;
|
||||
} else {
|
||||
if (icmp_->Compare(current_smallest, *smallest) < 0) {
|
||||
*smallest = current_smallest;
|
||||
}
|
||||
if (icmp_->Compare(current_largest, *largest) > 0) {
|
||||
*largest = current_largest;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(initialized);
|
||||
}
|
||||
|
||||
bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
VersionStorageInfo* vstorage,
|
||||
CompactionInputFiles* inputs) {
|
||||
@@ -250,6 +276,42 @@ bool CompactionPicker::ExpandWhileOverlapping(const std::string& cf_name,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CompactionPicker::RangeOverlapWithCompaction(
|
||||
const Slice& smallest_user_key, const Slice& largest_user_key,
|
||||
int level) const {
|
||||
const Comparator* ucmp = icmp_->user_comparator();
|
||||
for (Compaction* c : compactions_in_progress_) {
|
||||
if (c->output_level() == level &&
|
||||
ucmp->Compare(smallest_user_key, c->GetLargestUserKey()) <= 0 &&
|
||||
ucmp->Compare(largest_user_key, c->GetSmallestUserKey()) >= 0) {
|
||||
// Overlap
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Did not overlap with any running compaction in level `level`
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CompactionPicker::FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level) const {
|
||||
bool is_empty = true;
|
||||
for (auto& in : inputs) {
|
||||
if (!in.empty()) {
|
||||
is_empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_empty) {
|
||||
// No files in inputs
|
||||
return false;
|
||||
}
|
||||
|
||||
InternalKey smallest, largest;
|
||||
GetRange(inputs, &smallest, &largest);
|
||||
return RangeOverlapWithCompaction(smallest.user_key(), largest.user_key(),
|
||||
level);
|
||||
}
|
||||
|
||||
// Returns true if any one of specified files are being compacted
|
||||
bool CompactionPicker::FilesInCompaction(
|
||||
const std::vector<FileMetaData*>& files) {
|
||||
@@ -266,10 +328,6 @@ Compaction* CompactionPicker::FormCompaction(
|
||||
const std::vector<CompactionInputFiles>& input_files, int output_level,
|
||||
VersionStorageInfo* vstorage, const MutableCFOptions& mutable_cf_options,
|
||||
uint32_t output_path_id) {
|
||||
uint64_t max_grandparent_overlap_bytes =
|
||||
output_level + 1 < vstorage->num_levels()
|
||||
? mutable_cf_options.MaxGrandParentOverlapBytes(output_level + 1)
|
||||
: std::numeric_limits<uint64_t>::max();
|
||||
assert(input_files.size());
|
||||
|
||||
// TODO(rven ): we might be able to run concurrent level 0 compaction
|
||||
@@ -278,16 +336,19 @@ Compaction* CompactionPicker::FormCompaction(
|
||||
if ((input_files[0].level == 0) && !level0_compactions_in_progress_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto c = new Compaction(
|
||||
vstorage, mutable_cf_options, input_files, output_level,
|
||||
compact_options.output_file_size_limit, max_grandparent_overlap_bytes,
|
||||
output_path_id, compact_options.compression, /* grandparents */ {}, true);
|
||||
// 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
|
||||
if ((c != nullptr) && (input_files[0].level == 0)) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -390,6 +451,9 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
vstorage->GetOverlappingInputs(output_level, &smallest, &largest,
|
||||
&output_level_inputs->files, *parent_index,
|
||||
parent_index);
|
||||
if (!output_level_inputs->empty()) {
|
||||
ExpandWhileOverlapping(cf_name, vstorage, output_level_inputs);
|
||||
}
|
||||
|
||||
if (FilesInCompaction(output_level_inputs->files)) {
|
||||
return false;
|
||||
@@ -413,8 +477,7 @@ bool CompactionPicker::SetupOtherInputs(
|
||||
const uint64_t inputs1_size =
|
||||
TotalCompensatedFileSize(output_level_inputs->files);
|
||||
const uint64_t expanded0_size = TotalCompensatedFileSize(expanded0.files);
|
||||
uint64_t limit =
|
||||
mutable_cf_options.ExpandedCompactionByteSizeLimit(input_level);
|
||||
uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
if (expanded0.size() > inputs->size() &&
|
||||
inputs1_size + expanded0_size < limit &&
|
||||
!FilesInCompaction(expanded0.files) &&
|
||||
@@ -507,16 +570,24 @@ Compaction* CompactionPicker::CompactRange(
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 2 non-exclusive manual compactions could run at the same time producing
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(inputs, output_level)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, output_path_id,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
/* max_compaction_bytes */ LLONG_MAX, output_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options,
|
||||
output_level, 1),
|
||||
/* grandparents */ {}, /* is manual */ true);
|
||||
if (start_level == 0) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -548,8 +619,7 @@ Compaction* CompactionPicker::CompactRange(
|
||||
// and we must not pick one file and drop another older file if the
|
||||
// two files overlap.
|
||||
if (input_level > 0) {
|
||||
const uint64_t limit = mutable_cf_options.MaxFileSizeForLevel(input_level) *
|
||||
mutable_cf_options.source_compaction_factor;
|
||||
const uint64_t limit = mutable_cf_options.max_compaction_bytes;
|
||||
uint64_t total = 0;
|
||||
for (size_t i = 0; i + 1 < inputs.size(); ++i) {
|
||||
uint64_t s = inputs[i]->compensated_file_size;
|
||||
@@ -606,27 +676,33 @@ Compaction* CompactionPicker::CompactRange(
|
||||
}
|
||||
}
|
||||
|
||||
// 2 non-exclusive manual compactions could run at the same time producing
|
||||
// overlaping outputs in the same level.
|
||||
if (FilesRangeOverlapWithCompaction(compaction_inputs, output_level)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
*manual_conflict = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<FileMetaData*> grandparents;
|
||||
GetGrandparents(vstorage, inputs, output_level_inputs, &grandparents);
|
||||
Compaction* compaction = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(compaction_inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(input_level),
|
||||
output_path_id,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(compaction_inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.max_compaction_bytes, output_path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
std::move(grandparents), /* is manual compaction */ true);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::CompactRange:Return", compaction);
|
||||
if (input_level == 0) {
|
||||
level0_compactions_in_progress_.insert(compaction);
|
||||
}
|
||||
RegisterCompaction(compaction);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage->ComputeCompactionScore(mutable_cf_options);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
|
||||
return compaction;
|
||||
}
|
||||
@@ -856,6 +932,30 @@ Status CompactionPicker::SanitizeCompactionInputFiles(
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
void CompactionPicker::RegisterCompaction(Compaction* c) {
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
assert(ioptions_.compaction_style != kCompactionStyleLevel ||
|
||||
!FilesRangeOverlapWithCompaction(*c->inputs(), c->output_level()));
|
||||
if (c->start_level() == 0 ||
|
||||
ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
compactions_in_progress_.insert(c);
|
||||
}
|
||||
|
||||
void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (c->start_level() == 0 ||
|
||||
ioptions_.compaction_style == kCompactionStyleUniversal) {
|
||||
level0_compactions_in_progress_.erase(c);
|
||||
}
|
||||
compactions_in_progress_.erase(c);
|
||||
}
|
||||
|
||||
bool LevelCompactionPicker::NeedsCompaction(
|
||||
const VersionStorageInfo* vstorage) const {
|
||||
if (!vstorage->FilesMarkedForCompaction().empty()) {
|
||||
@@ -939,7 +1039,8 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
output_level = (level == 0) ? vstorage->base_level() : level + 1;
|
||||
if (PickCompactionBySize(vstorage, level, output_level, &inputs,
|
||||
&parent_index, &base_index) &&
|
||||
ExpandWhileOverlapping(cf_name, vstorage, &inputs)) {
|
||||
ExpandWhileOverlapping(cf_name, vstorage, &inputs) &&
|
||||
!FilesRangeOverlapWithCompaction({inputs}, output_level)) {
|
||||
// found the compaction!
|
||||
if (level == 0) {
|
||||
// L0 score = `num L0 files` / `level0_file_num_compaction_trigger`
|
||||
@@ -1012,12 +1113,24 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
compaction_inputs.push_back(output_level_inputs);
|
||||
}
|
||||
|
||||
// In some edge cases we could pick a compaction that will be compacting
|
||||
// a key range that overlap with another running compaction, and both
|
||||
// of them have the same output leve. This could happen if
|
||||
// (1) we are running a non-exclusive manual compaction
|
||||
// (2) AddFile ingest a new file into the LSM tree
|
||||
// We need to disallow this from happening.
|
||||
if (FilesRangeOverlapWithCompaction(compaction_inputs, output_level)) {
|
||||
// This compaction output could potentially conflict with the output
|
||||
// of a currently running compaction, we cannot run it.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<FileMetaData*> grandparents;
|
||||
GetGrandparents(vstorage, inputs, output_level_inputs, &grandparents);
|
||||
auto c = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(compaction_inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.MaxGrandParentOverlapBytes(level),
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(compaction_inputs),
|
||||
output_level, mutable_cf_options.MaxFileSizeForLevel(output_level),
|
||||
mutable_cf_options.max_compaction_bytes,
|
||||
GetPathId(ioptions_, mutable_cf_options, output_level),
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, output_level,
|
||||
vstorage->base_level()),
|
||||
@@ -1026,15 +1139,13 @@ Compaction* LevelCompactionPicker::PickCompaction(
|
||||
|
||||
// If it's level 0 compaction, make sure we don't execute any other level 0
|
||||
// compactions in parallel
|
||||
if (level == 0) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
RegisterCompaction(c);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
// being compacted). Since we just changed compaction score, we recalculate it
|
||||
// here
|
||||
vstorage->ComputeCompactionScore(mutable_cf_options);
|
||||
vstorage->ComputeCompactionScore(ioptions_, mutable_cf_options);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("LevelCompactionPicker::PickCompaction:Return", c);
|
||||
|
||||
@@ -1310,6 +1421,8 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
sorted_runs.size() <
|
||||
(unsigned int)mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
LogToBuffer(log_buffer, "[%s] Universal: nothing to do\n", cf_name.c_str());
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
@@ -1344,19 +1457,34 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
assert(sorted_runs.size() >=
|
||||
static_cast<size_t>(
|
||||
mutable_cf_options.level0_file_num_compaction_trigger));
|
||||
unsigned int num_files =
|
||||
static_cast<unsigned int>(sorted_runs.size()) -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger;
|
||||
if ((c = PickCompactionUniversalReadAmp(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
// Get the total number of sorted runs that are not being compacted
|
||||
int num_sr_not_compacted = 0;
|
||||
for (size_t i = 0; i < sorted_runs.size(); i++) {
|
||||
if (sorted_runs[i].being_compacted == false) {
|
||||
num_sr_not_compacted++;
|
||||
}
|
||||
}
|
||||
|
||||
// The number of sorted runs that are not being compacted is greater than
|
||||
// the maximum allowed number of sorted runs
|
||||
if (num_sr_not_compacted >
|
||||
mutable_cf_options.level0_file_num_compaction_trigger) {
|
||||
unsigned int num_files =
|
||||
num_sr_not_compacted -
|
||||
mutable_cf_options.level0_file_num_compaction_trigger + 1;
|
||||
if ((c = PickCompactionUniversalReadAmp(
|
||||
cf_name, mutable_cf_options, vstorage, score, UINT_MAX,
|
||||
num_files, sorted_runs, log_buffer)) != nullptr) {
|
||||
LogToBuffer(log_buffer,
|
||||
"[%s] Universal: compacting for file num -- %u\n",
|
||||
cf_name.c_str(), num_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c == nullptr) {
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1407,8 +1535,10 @@ Compaction* UniversalCompactionPicker::PickCompaction(
|
||||
MeasureTime(ioptions_.statistics, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
RegisterCompaction(c);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("UniversalCompactionPicker::PickCompaction:Return",
|
||||
c);
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1627,7 +1757,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalReadAmp(
|
||||
compaction_reason = CompactionReason::kUniversalSizeRatio;
|
||||
}
|
||||
return new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs), output_level,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), output_level,
|
||||
mutable_cf_options.MaxFileSizeForLevel(output_level), LLONG_MAX, path_id,
|
||||
GetCompressionType(ioptions_, vstorage, mutable_cf_options, start_level,
|
||||
1, enable_compression),
|
||||
@@ -1751,7 +1881,7 @@ Compaction* UniversalCompactionPicker::PickCompactionUniversalSizeAmp(
|
||||
}
|
||||
|
||||
return new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs),
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs),
|
||||
vstorage->num_levels() - 1,
|
||||
mutable_cf_options.MaxFileSizeForLevel(vstorage->num_levels() - 1),
|
||||
/* max_grandparent_overlap_bytes */ LLONG_MAX, path_id,
|
||||
@@ -1816,10 +1946,10 @@ Compaction* FIFOCompactionPicker::PickCompaction(
|
||||
}
|
||||
}
|
||||
Compaction* c = new Compaction(
|
||||
vstorage, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
vstorage, ioptions_, mutable_cf_options, std::move(inputs), 0, 0, 0, 0,
|
||||
kNoCompression, {}, /* is manual */ false, vstorage->CompactionScore(0),
|
||||
/* is deletion compaction */ true, CompactionReason::kFIFOMaxSize);
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
RegisterCompaction(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
+31
-5
@@ -20,8 +20,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -114,6 +113,12 @@ class CompactionPicker {
|
||||
return !level0_compactions_in_progress_.empty();
|
||||
}
|
||||
|
||||
// Return true if the passed key range overlap with a compaction output
|
||||
// that is currently running.
|
||||
bool RangeOverlapWithCompaction(const Slice& smallest_user_key,
|
||||
const Slice& largest_user_key,
|
||||
int level) const;
|
||||
|
||||
protected:
|
||||
int NumberLevels() const { return ioptions_.num_levels; }
|
||||
|
||||
@@ -121,14 +126,20 @@ class CompactionPicker {
|
||||
// *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void GetRange(const CompactionInputFiles& inputs, InternalKey* smallest,
|
||||
InternalKey* largest);
|
||||
InternalKey* largest) const;
|
||||
|
||||
// Stores the minimal range that covers all entries in inputs1 and inputs2
|
||||
// in *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty
|
||||
void GetRange(const CompactionInputFiles& inputs1,
|
||||
const CompactionInputFiles& inputs2, InternalKey* smallest,
|
||||
InternalKey* largest);
|
||||
InternalKey* largest) const;
|
||||
|
||||
// Stores the minimal range that covers all entries in inputs
|
||||
// in *smallest, *largest.
|
||||
// REQUIRES: inputs is not empty (at least on entry have one file)
|
||||
void GetRange(const std::vector<CompactionInputFiles>& inputs,
|
||||
InternalKey* smallest, InternalKey* largest) const;
|
||||
|
||||
// Add more files to the inputs on "level" to make sure that
|
||||
// no newer version of a key is compacted to "level+1" while leaving an older
|
||||
@@ -149,6 +160,11 @@ class CompactionPicker {
|
||||
const InternalKey* smallest,
|
||||
const InternalKey* largest, int level, int* index);
|
||||
|
||||
// Returns true if the key range that `inputs` files cover overlap with the
|
||||
// key range of a currently running compaction.
|
||||
bool FilesRangeOverlapWithCompaction(
|
||||
const std::vector<CompactionInputFiles>& inputs, int level) const;
|
||||
|
||||
bool SetupOtherInputs(const std::string& cf_name,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
VersionStorageInfo* vstorage,
|
||||
@@ -171,10 +187,20 @@ class CompactionPicker {
|
||||
const ColumnFamilyMetaData& cf_meta, const int output_level) const;
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Register this compaction in the set of running compactions
|
||||
void RegisterCompaction(Compaction* c);
|
||||
|
||||
// Remove this compaction from the set of running compactions
|
||||
void UnregisterCompaction(Compaction* c);
|
||||
|
||||
// Keeps track of all compactions that are running on Level0.
|
||||
// It is protected by DB mutex
|
||||
// Protected by DB mutex
|
||||
std::set<Compaction*> level0_compactions_in_progress_;
|
||||
|
||||
// Keeps track of all compactions that are running.
|
||||
// Protected by DB mutex
|
||||
std::unordered_set<Compaction*> compactions_in_progress_;
|
||||
|
||||
const InternalKeyComparator* const icmp_;
|
||||
};
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class CompactionPickerTest : public testing::Test {
|
||||
: ucmp_(BytewiseComparator()),
|
||||
icmp_(ucmp_),
|
||||
ioptions_(options_),
|
||||
mutable_cf_options_(options_, ioptions_),
|
||||
mutable_cf_options_(options_),
|
||||
level_compaction_picker(ioptions_, &icmp_),
|
||||
cf_name_("dummy"),
|
||||
log_buffer_(InfoLogLevel::INFO_LEVEL, &logger_),
|
||||
@@ -66,8 +66,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
void NewVersionStorage(int num_levels, CompactionStyle style) {
|
||||
DeleteVersionStorage();
|
||||
options_.num_levels = num_levels;
|
||||
vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, style, nullptr));
|
||||
vstorage_.reset(new VersionStorageInfo(&icmp_, ucmp_, options_.num_levels,
|
||||
style, nullptr, false));
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
@@ -115,11 +115,11 @@ class CompactionPickerTest : public testing::Test {
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
vstorage_->GenerateFileIndexer();
|
||||
vstorage_->GenerateLevelFilesBrief();
|
||||
vstorage_->ComputeCompactionScore(mutable_cf_options_);
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
vstorage_->GenerateLevel0NonOverlapping();
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
@@ -350,10 +350,10 @@ TEST_F(CompactionPickerTest, Level0TriggerDynamic4) {
|
||||
TEST_F(CompactionPickerTest, LevelTriggerDynamic4) {
|
||||
int num_levels = ioptions_.num_levels;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = true;
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 200;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
@@ -489,10 +489,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_multiplier = 10;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10 * 1024 * 1024;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
Add(2, 6U, "150", "179", 50000000U);
|
||||
Add(2, 7U, "180", "220", 50000000U);
|
||||
@@ -516,10 +516,10 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping1) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
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_.compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
Add(2, 6U, "150", "175",
|
||||
60000000U); // Overlaps with file 26, 27, total size 521M
|
||||
@@ -546,9 +546,9 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
|
||||
TEST_F(CompactionPickerTest, CompactionPriMinOverlapping3) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
ioptions_.compaction_pri = kMinOverlappingRatio;
|
||||
mutable_cf_options_.max_bytes_for_level_base = 10000000;
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
mutable_cf_options_.compaction_pri = kMinOverlappingRatio;
|
||||
|
||||
// file 7 and 8 over lap with the same file, but file 8 is smaller so
|
||||
// it will be picked.
|
||||
@@ -598,7 +598,7 @@ TEST_F(CompactionPickerTest, ParentIndexResetBug) {
|
||||
// ranges (with different sequence numbers) in the input files.
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.compaction_pri = kByCompensatedSize;
|
||||
ioptions_.compaction_pri = kByCompensatedSize;
|
||||
|
||||
Add(1, 1U, "100", "150", 1U);
|
||||
// Overlapping user keys
|
||||
@@ -621,9 +621,9 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys2) {
|
||||
// Overlapping user keys on same level and output level
|
||||
Add(1, 1U, "200", "400", 1000000000U);
|
||||
Add(1, 2U, "400", "500", 1U, 0, 0);
|
||||
Add(2, 3U, "400", "600", 1U);
|
||||
// The following file is not in the compaction despite overlapping user keys
|
||||
Add(2, 4U, "600", "700", 1U, 0, 0);
|
||||
Add(2, 3U, "000", "100", 1U);
|
||||
Add(2, 4U, "100", "600", 1U, 0, 0);
|
||||
Add(2, 5U, "600", "700", 1U, 0, 0);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -631,10 +631,12 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys2) {
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(1, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(4U, compaction->input(1, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 2)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys3) {
|
||||
@@ -666,6 +668,31 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys3) {
|
||||
ASSERT_EQ(7U, compaction->input(1, 1)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, OverlappingUserKeys4) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000;
|
||||
|
||||
Add(1, 1U, "100", "150", 1U);
|
||||
Add(1, 2U, "150", "199", 1U, 0, 0);
|
||||
Add(1, 3U, "200", "250", 1100000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 1U, 0, 0);
|
||||
Add(1, 5U, "300", "350", 1U, 0, 0);
|
||||
|
||||
Add(2, 6U, "100", "115", 1U);
|
||||
Add(2, 7U, "125", "325", 1U);
|
||||
Add(2, 8U, "350", "400", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(3U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(7U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NotScheduleL1IfL0WithHigherPri1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
@@ -1034,6 +1061,57 @@ TEST_F(CompactionPickerTest, IsBottommostLevelTest) {
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 800000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// It cannot expand because adding file 1 and 3, the compaction size will
|
||||
// exceed mutable_cf_options_.max_bytes_for_level_base.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "160", "256", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(2U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, MaxCompactionBytesNotHit) {
|
||||
mutable_cf_options_.max_bytes_for_level_base = 1000000u;
|
||||
mutable_cf_options_.max_compaction_bytes = 1000000u;
|
||||
ioptions_.level_compaction_dynamic_level_bytes = false;
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
// A compaction should be triggered and pick file 2 and 5.
|
||||
// and it expands to file 1 and 3 too.
|
||||
Add(1, 1U, "100", "150", 300000U);
|
||||
Add(1, 2U, "151", "200", 300001U, 0, 0);
|
||||
Add(1, 3U, "201", "250", 300000U, 0, 0);
|
||||
Add(1, 4U, "251", "300", 300000U, 0, 0);
|
||||
Add(2, 5U, "000", "233", 1U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_TRUE(compaction.get() != nullptr);
|
||||
ASSERT_EQ(2U, compaction->num_input_levels());
|
||||
ASSERT_EQ(3U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
ASSERT_EQ(1U, compaction->input(0, 0)->fd.GetNumber());
|
||||
ASSERT_EQ(2U, compaction->input(0, 1)->fd.GetNumber());
|
||||
ASSERT_EQ(3U, compaction->input(0, 2)->fd.GetNumber());
|
||||
ASSERT_EQ(5U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -40,6 +40,10 @@ class KVIter : public Iterator {
|
||||
virtual void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstdlib>
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/lru_cache.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -171,7 +172,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
InitTable(options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(0, 0, false);
|
||||
std::shared_ptr<Cache> compressed_cache = NewLRUCache(0, 0, false);
|
||||
std::shared_ptr<Cache> compressed_cache = NewLRUCache(1 << 25, 0, false);
|
||||
table_options.block_cache = cache;
|
||||
table_options.block_cache_compressed = compressed_cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
@@ -203,9 +204,6 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
cache->SetCapacity(usage);
|
||||
cache->SetStrictCapacityLimit(true);
|
||||
ASSERT_EQ(usage, cache->GetPinnedUsage());
|
||||
// compressed_cache->SetCapacity(compressed_usage);
|
||||
compressed_cache->SetCapacity(0);
|
||||
// compressed_cache->SetStrictCapacityLimit(true);
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(kNumBlocks - 1));
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
@@ -270,16 +268,16 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksOfNewTableAddedToCache) {
|
||||
ASSERT_EQ(2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
|
||||
// Make sure index block is in cache.
|
||||
auto index_block_hit = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
auto index_block_hit = TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT);
|
||||
value = Get(1, "key");
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(index_block_hit + 1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
value = Get(1, "key");
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(index_block_hit + 2,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
@@ -323,6 +321,91 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
filter_bytes_insert);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A mock cache wraps LRUCache, and record how many entries have been
|
||||
// inserted for each priority.
|
||||
class MockCache : public LRUCache {
|
||||
public:
|
||||
static uint32_t high_pri_insert_count;
|
||||
static uint32_t low_pri_insert_count;
|
||||
|
||||
MockCache() : LRUCache(1 << 25, 0, false, 0.0) {}
|
||||
|
||||
virtual Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle, Priority priority) override {
|
||||
if (priority == Priority::LOW) {
|
||||
low_pri_insert_count++;
|
||||
} else {
|
||||
high_pri_insert_count++;
|
||||
}
|
||||
return LRUCache::Insert(key, value, charge, deleter, handle, priority);
|
||||
}
|
||||
};
|
||||
|
||||
uint32_t MockCache::high_pri_insert_count = 0;
|
||||
uint32_t MockCache::low_pri_insert_count = 0;
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, IndexAndFilterBlocksCachePriority) {
|
||||
for (auto priority : {Cache::Priority::LOW, Cache::Priority::HIGH}) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.block_cache.reset(new MockCache());
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
table_options.cache_index_and_filter_blocks_with_high_priority =
|
||||
priority == Cache::Priority::HIGH ? true : false;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
MockCache::high_pri_insert_count = 0;
|
||||
MockCache::low_pri_insert_count = 0;
|
||||
|
||||
// Create a new table.
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
|
||||
// index/filter blocks added to block cache right after table creation.
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(2, /* only index/filter were added */
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
ASSERT_EQ(0, TestGetTickerCount(options, BLOCK_CACHE_DATA_MISS));
|
||||
if (priority == Cache::Priority::LOW) {
|
||||
ASSERT_EQ(0, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(2, MockCache::low_pri_insert_count);
|
||||
} else {
|
||||
ASSERT_EQ(2, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(0, MockCache::low_pri_insert_count);
|
||||
}
|
||||
|
||||
// Access data block.
|
||||
ASSERT_EQ("value", Get("foo"));
|
||||
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(3, /*adding data block*/
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
ASSERT_EQ(1, TestGetTickerCount(options, BLOCK_CACHE_DATA_MISS));
|
||||
|
||||
// Data block should be inserted with low priority.
|
||||
if (priority == Cache::Priority::LOW) {
|
||||
ASSERT_EQ(0, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(3, MockCache::low_pri_insert_count);
|
||||
} else {
|
||||
ASSERT_EQ(2, MockCache::high_pri_insert_count);
|
||||
ASSERT_EQ(1, MockCache::low_pri_insert_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
@@ -305,7 +305,8 @@ TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
// Preloading iterator issues one table cache lookup and creates
|
||||
// a new table reader. One file is created for flush and one for compaction.
|
||||
// Compaction inputs make no table cache look-up.
|
||||
// Compaction inputs make no table cache look-up for data iterators or
|
||||
// range tombstone iterators since they're already cached.
|
||||
ASSERT_EQ(num_table_cache_lookup, 2);
|
||||
// Create new iterator for:
|
||||
// (1) 1 for verifying flush results
|
||||
@@ -649,6 +650,72 @@ TEST_F(DBCompactionTest, MinorCompactionsHappen) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, UserKeyCrossFile1) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create first file and flush to l0
|
||||
Put("4", "A");
|
||||
Put("3", "A");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Put("2", "A");
|
||||
Delete("3");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
// move both files down to l1
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("2", "B");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, UserKeyCrossFile2) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// create first file and flush to l0
|
||||
Put("4", "A");
|
||||
Put("3", "A");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
Put("2", "A");
|
||||
SingleDelete("3");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
// move both files down to l1
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("2", "B");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
ASSERT_EQ("NOT_FOUND", Get("3"));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ZeroSeqIdCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
|
||||
@@ -137,7 +137,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.max_background_compactions = 2;
|
||||
options.num_levels = 5;
|
||||
options.expanded_compaction_factor = 0; // Force not expanding in compactions
|
||||
options.max_compaction_bytes = 0; // Force not expanding in compactions
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
@@ -223,8 +223,9 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "true"},
|
||||
}));
|
||||
// Write about 600K more
|
||||
for (int i = 0; i < 1500; i++) {
|
||||
// Write about 650K more.
|
||||
// Each file is about 11KB, with 9KB of data.
|
||||
for (int i = 0; i < 1300; i++) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
||||
RandomString(&rnd, 380)));
|
||||
}
|
||||
@@ -294,7 +295,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
options.max_background_compactions = 1;
|
||||
const int kNumLevels = 5;
|
||||
options.num_levels = kNumLevels;
|
||||
options.expanded_compaction_factor = 0; // Force not expanding in compactions
|
||||
options.max_compaction_bytes = 1; // Force not expanding in compactions
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -35,10 +35,10 @@ Status DBImpl::DisableFileDeletions() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
++disable_delete_obsolete_files_;
|
||||
if (disable_delete_obsolete_files_ == 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Disabled");
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Disabled, but already disabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
@@ -59,12 +59,12 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
--disable_delete_obsolete_files_;
|
||||
}
|
||||
if (disable_delete_obsolete_files_ == 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Enabled");
|
||||
should_purge_files = true;
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
} else {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::WARN_LEVEL, immutable_db_options_.info_log,
|
||||
"File Deletions Enable, but not really enabled. Counter: %d",
|
||||
disable_delete_obsolete_files_);
|
||||
}
|
||||
@@ -73,7 +73,7 @@ Status DBImpl::EnableFileDeletions(bool force) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
job_context.Clean();
|
||||
LogFlush(db_options_.info_log);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
|
||||
if (!status.ok()) {
|
||||
mutex_.Unlock();
|
||||
Log(InfoLogLevel::ERROR_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::ERROR_LEVEL, immutable_db_options_.info_log,
|
||||
"Cannot Flush data %s\n", status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
|
||||
+731
-398
File diff suppressed because it is too large.
Load diff
+61
-36
@@ -40,6 +40,7 @@
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
@@ -155,6 +156,9 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& options_map) override;
|
||||
|
||||
using DB::NumberLevels;
|
||||
virtual int NumberLevels(ColumnFamilyHandle* column_family) override;
|
||||
using DB::MaxMemCompactionLevel;
|
||||
@@ -165,10 +169,9 @@ class DBImpl : public DB {
|
||||
virtual const std::string& GetName() const override;
|
||||
virtual Env* GetEnv() const override;
|
||||
using DB::GetOptions;
|
||||
virtual const Options& GetOptions(
|
||||
ColumnFamilyHandle* column_family) const override;
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override;
|
||||
using DB::GetDBOptions;
|
||||
virtual const DBOptions& GetDBOptions() const override;
|
||||
virtual DBOptions GetDBOptions() const override;
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const FlushOptions& options,
|
||||
ColumnFamilyHandle* column_family) override;
|
||||
@@ -252,17 +255,16 @@ class DBImpl : public DB {
|
||||
//
|
||||
// Returns OK or NotFound on success,
|
||||
// other status on unexpected error.
|
||||
// TODO(andrewkr): this API need to be aware of range deletion operations
|
||||
Status GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool cache_only, SequenceNumber* seq,
|
||||
bool* found_record_for_key);
|
||||
|
||||
using DB::AddFile;
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::vector<ExternalSstFileInfo>& file_info_list,
|
||||
bool move_file) override;
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& file_path_list,
|
||||
bool move_file) override;
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& ingestion_options) override;
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -359,12 +361,18 @@ class DBImpl : public DB {
|
||||
uint64_t TEST_FindMinLogContainingOutstandingPrep();
|
||||
uint64_t TEST_FindMinPrepLogReferencedByMemTable();
|
||||
|
||||
int TEST_BGCompactionsAllowed() const;
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
// Return maximum background compaction allowed to be scheduled based on
|
||||
// compaction status.
|
||||
int BGCompactionsAllowed() const;
|
||||
|
||||
// move logs pending closing from job_context to the DB queue and
|
||||
// schedule a purge
|
||||
void ScheduleBgLogWriterClose(JobContext* job_context);
|
||||
|
||||
// Returns the list of live files in 'live' and the list
|
||||
// of all files in the filesystem in 'candidate_files'.
|
||||
// If force == false and the last call was less than
|
||||
@@ -386,6 +394,10 @@ class DBImpl : public DB {
|
||||
|
||||
const SnapshotList& snapshots() const { return snapshots_; }
|
||||
|
||||
const ImmutableDBOptions& immutable_db_options() const {
|
||||
return immutable_db_options_;
|
||||
}
|
||||
|
||||
void CancelAllBackgroundWork(bool wait);
|
||||
|
||||
// Find Super version and reference it. Based on options, it might return
|
||||
@@ -452,7 +464,7 @@ class DBImpl : public DB {
|
||||
~RecoveredTransaction() { delete batch_; }
|
||||
};
|
||||
|
||||
bool allow_2pc() const { return db_options_.allow_2pc; }
|
||||
bool allow_2pc() const { return immutable_db_options_.allow_2pc; }
|
||||
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
recovered_transactions() {
|
||||
@@ -493,6 +505,9 @@ class DBImpl : public DB {
|
||||
|
||||
void MarkLogAsHavingPrepSectionFlushed(uint64_t log);
|
||||
void MarkLogAsContainingPrepSection(uint64_t log);
|
||||
void AddToLogsToFreeQueue(log::Writer* log_writer) {
|
||||
logs_to_free_queue_.push_back(log_writer);
|
||||
}
|
||||
|
||||
Status NewDB();
|
||||
|
||||
@@ -500,7 +515,9 @@ class DBImpl : public DB {
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
unique_ptr<VersionSet> versions_;
|
||||
const DBOptions db_options_;
|
||||
const DBOptions initial_db_options_;
|
||||
const ImmutableDBOptions immutable_db_options_;
|
||||
MutableDBOptions mutable_db_options_;
|
||||
Statistics* stats_;
|
||||
std::unordered_map<std::string, RecoveredTransaction*>
|
||||
recovered_transactions_;
|
||||
@@ -601,6 +618,8 @@ class DBImpl : public DB {
|
||||
// and blocked by any other pending_outputs_ calls)
|
||||
void ReleaseFileNumberFromPendingOutputs(std::list<uint64_t>::iterator v);
|
||||
|
||||
Status SyncClosedLogs(JobContext* job_context);
|
||||
|
||||
// Flush the in-memory write buffer to storage. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful.
|
||||
Status FlushMemTableToOutputFile(ColumnFamilyData* cfd,
|
||||
@@ -610,7 +629,7 @@ class DBImpl : public DB {
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
SequenceNumber* max_sequence, bool read_only);
|
||||
SequenceNumber* next_sequence, bool read_only);
|
||||
|
||||
// The following two methods are used to flush a memtable to
|
||||
// storage. The first one is used at database RecoveryTime (when the
|
||||
@@ -629,26 +648,28 @@ class DBImpl : public DB {
|
||||
Status SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context);
|
||||
|
||||
// Force current memtable contents to be flushed.
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options);
|
||||
Status FlushMemTable(ColumnFamilyData* cfd, const FlushOptions& options,
|
||||
bool writes_stopped = false);
|
||||
|
||||
// Wait for memtable flushed
|
||||
Status WaitForFlushMemTable(ColumnFamilyData* cfd);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Finds the lowest level in the DB that the ingested file can be added to
|
||||
|
||||
Status CompactFilesImpl(const CompactionOptions& compact_options,
|
||||
ColumnFamilyData* cfd, Version* version,
|
||||
const std::vector<std::string>& input_file_names,
|
||||
const int output_level, int output_path_id,
|
||||
JobContext* job_context, LogBuffer* log_buffer);
|
||||
|
||||
// Wait for current IngestExternalFile() calls to finish.
|
||||
// REQUIRES: mutex_ held
|
||||
int PickLevelForIngestedFile(ColumnFamilyData* cfd,
|
||||
const ExternalSstFileInfo& file_info);
|
||||
|
||||
Status CompactFilesImpl(
|
||||
const CompactionOptions& compact_options, ColumnFamilyData* cfd,
|
||||
Version* version, const std::vector<std::string>& input_file_names,
|
||||
const int output_level, int output_path_id, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
Status ReadExternalSstFileInfo(ColumnFamilyHandle* column_family,
|
||||
const std::string& file_path,
|
||||
ExternalSstFileInfo* file_info);
|
||||
void WaitForIngestFile();
|
||||
|
||||
#else
|
||||
// IngestExternalFile is not supported in ROCKSDB_LITE so this function
|
||||
// will be no-op
|
||||
void WaitForIngestFile() {}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
ColumnFamilyData* GetColumnFamilyDataByName(const std::string& cf_name);
|
||||
@@ -670,11 +691,6 @@ class DBImpl : public DB {
|
||||
Status BackgroundFlush(bool* madeProgress, JobContext* job_context,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
// Compare options before and after to see whether flush or compaction is
|
||||
// needed immediately after dynamic option change.
|
||||
bool NeedFlushOrCompaction(const MutableCFOptions& base_options,
|
||||
const MutableCFOptions& new_options);
|
||||
|
||||
void PrintStatistics();
|
||||
|
||||
// dump rocksdb.stats to LOG
|
||||
@@ -701,6 +717,10 @@ class DBImpl : public DB {
|
||||
|
||||
const Snapshot* GetSnapshotImpl(bool is_write_conflict_boundary);
|
||||
|
||||
// Persist RocksDB options under the single write thread
|
||||
// REQUIRES: mutex locked
|
||||
Status PersistOptions();
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
@@ -712,7 +732,7 @@ class DBImpl : public DB {
|
||||
// same time.
|
||||
InstrumentedMutex options_files_mutex_;
|
||||
// State below is protected by mutex_
|
||||
InstrumentedMutex mutex_;
|
||||
mutable InstrumentedMutex mutex_;
|
||||
|
||||
std::atomic<bool> shutting_down_;
|
||||
// This condition variable is signaled on these conditions:
|
||||
@@ -723,6 +743,7 @@ class DBImpl : public DB {
|
||||
// * whenever bg_flush_scheduled_ or bg_purge_scheduled_ value decreases
|
||||
// (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.
|
||||
InstrumentedCondVar bg_cv_;
|
||||
uint64_t logfile_number_;
|
||||
std::deque<uint64_t>
|
||||
@@ -882,6 +903,9 @@ class DBImpl : public DB {
|
||||
|
||||
// A queue to store filenames of the files to be purged
|
||||
std::deque<PurgeFileInfo> purge_queue_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
std::deque<log::Writer*> logs_to_free_queue_;
|
||||
int unscheduled_flushes_;
|
||||
int unscheduled_compactions_;
|
||||
|
||||
@@ -961,9 +985,9 @@ class DBImpl : public DB {
|
||||
// The options to access storage files
|
||||
const EnvOptions env_options_;
|
||||
|
||||
// A set of compactions that are running right now
|
||||
// Number of running IngestExternalFile() calls.
|
||||
// REQUIRES: mutex held
|
||||
std::unordered_set<Compaction*> running_compactions_;
|
||||
int num_running_ingest_file_;
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
WalManager wal_manager_;
|
||||
@@ -1060,13 +1084,14 @@ class DBImpl : public DB {
|
||||
bool ShouldntRunManualCompaction(ManualCompaction* m);
|
||||
bool HaveManualCompaction(ColumnFamilyData* cfd);
|
||||
bool MCOverlap(ManualCompaction* m, ManualCompaction* m1);
|
||||
|
||||
size_t GetWalPreallocateBlockSize(uint64_t write_buffer_size) const;
|
||||
};
|
||||
|
||||
// Sanitize db options. The caller should delete result.info_log if
|
||||
// it is not equal to src.info_log.
|
||||
extern Options SanitizeOptions(const std::string& db,
|
||||
const InternalKeyComparator* icmp,
|
||||
const Options& src);
|
||||
|
||||
extern DBOptions SanitizeOptions(const std::string& db, const DBOptions& src);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
|
||||
@@ -1,419 +0,0 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/db_impl.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "db/builder.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
namespace {
|
||||
// RAII Timer
|
||||
class StatsTimer {
|
||||
public:
|
||||
explicit StatsTimer(Env* env, uint64_t* counter)
|
||||
: env_(env), counter_(counter), start_micros_(env_->NowMicros()) {}
|
||||
~StatsTimer() { *counter_ += env_->NowMicros() - start_micros_; }
|
||||
|
||||
private:
|
||||
Env* env_;
|
||||
uint64_t* counter_;
|
||||
uint64_t start_micros_;
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
Status DBImpl::ReadExternalSstFileInfo(ColumnFamilyHandle* column_family,
|
||||
const std::string& file_path,
|
||||
ExternalSstFileInfo* file_info) {
|
||||
Status status;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
file_info->file_path = file_path;
|
||||
status = env_->GetFileSize(file_path, &file_info->file_size);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Access the file using TableReader to extract
|
||||
// version, number of entries, smallest user key, largest user key
|
||||
std::unique_ptr<RandomAccessFile> sst_file;
|
||||
status = env_->NewRandomAccessFile(file_path, &sst_file, env_options_);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
std::unique_ptr<RandomAccessFileReader> sst_file_reader;
|
||||
sst_file_reader.reset(new RandomAccessFileReader(std::move(sst_file)));
|
||||
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
status = cfd->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(*cfd->ioptions(), env_options_,
|
||||
cfd->internal_comparator()),
|
||||
std::move(sst_file_reader), file_info->file_size, &table_reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Get the external sst file version from table properties
|
||||
const UserCollectedProperties& user_collected_properties =
|
||||
table_reader->GetTableProperties()->user_collected_properties;
|
||||
UserCollectedProperties::const_iterator external_sst_file_version_iter =
|
||||
user_collected_properties.find(ExternalSstFilePropertyNames::kVersion);
|
||||
if (external_sst_file_version_iter == user_collected_properties.end()) {
|
||||
return Status::InvalidArgument("Generated table version not found");
|
||||
}
|
||||
|
||||
file_info->version =
|
||||
DecodeFixed32(external_sst_file_version_iter->second.c_str());
|
||||
if (file_info->version == 1) {
|
||||
// version 1 imply that all sequence numbers in table equal 0
|
||||
file_info->sequence_number = 0;
|
||||
} else {
|
||||
return Status::InvalidArgument("Generated table version is not supported");
|
||||
}
|
||||
// Get number of entries in table
|
||||
file_info->num_entries = table_reader->GetTableProperties()->num_entries;
|
||||
|
||||
ParsedInternalKey key;
|
||||
std::unique_ptr<InternalIterator> iter(
|
||||
table_reader->NewIterator(ReadOptions()));
|
||||
|
||||
// Get first (smallest) key from file
|
||||
iter->SeekToFirst();
|
||||
if (!ParseInternalKey(iter->key(), &key)) {
|
||||
return Status::Corruption("Generated table have corrupted keys");
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption("Generated table have non zero sequence number");
|
||||
}
|
||||
file_info->smallest_key = key.user_key.ToString();
|
||||
|
||||
// Get last (largest) key from file
|
||||
iter->SeekToLast();
|
||||
if (!ParseInternalKey(iter->key(), &key)) {
|
||||
return Status::Corruption("Generated table have corrupted keys");
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption("Generated table have non zero sequence number");
|
||||
}
|
||||
file_info->largest_key = key.user_key.ToString();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& file_path_list,
|
||||
bool move_file) {
|
||||
Status status;
|
||||
auto num_files = file_path_list.size();
|
||||
if (num_files == 0) {
|
||||
return Status::InvalidArgument("The list of files is empty");
|
||||
}
|
||||
|
||||
std::vector<ExternalSstFileInfo> file_infos(num_files);
|
||||
std::vector<ExternalSstFileInfo> file_info_list(num_files);
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
status = ReadExternalSstFileInfo(column_family, file_path_list[i],
|
||||
&file_info_list[i]);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return AddFile(column_family, file_info_list, move_file);
|
||||
}
|
||||
|
||||
Status DBImpl::AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::vector<ExternalSstFileInfo>& file_info_list,
|
||||
bool move_file) {
|
||||
Status status;
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
|
||||
auto num_files = file_info_list.size();
|
||||
if (num_files == 0) {
|
||||
return Status::InvalidArgument("The list of files is empty");
|
||||
}
|
||||
|
||||
// Verify that passed files dont have overlapping ranges
|
||||
if (num_files > 1) {
|
||||
std::vector<const ExternalSstFileInfo*> sorted_file_info_list(num_files);
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
sorted_file_info_list[i] = &file_info_list[i];
|
||||
}
|
||||
|
||||
auto* vstorage = cfd->current()->storage_info();
|
||||
std::sort(
|
||||
sorted_file_info_list.begin(), sorted_file_info_list.end(),
|
||||
[&vstorage, &file_info_list](const ExternalSstFileInfo* info1,
|
||||
const ExternalSstFileInfo* info2) {
|
||||
return vstorage->InternalComparator()->user_comparator()->Compare(
|
||||
info1->smallest_key, info2->smallest_key) < 0;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < num_files - 1; i++) {
|
||||
if (sorted_file_info_list[i]->largest_key >=
|
||||
sorted_file_info_list[i + 1]->smallest_key) {
|
||||
return Status::NotSupported("Cannot add overlapping range among files");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint64_t> micro_list(num_files, 0);
|
||||
std::vector<FileMetaData> meta_list(num_files);
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
StatsTimer t(env_, µ_list[i]);
|
||||
if (file_info_list[i].num_entries == 0) {
|
||||
return Status::InvalidArgument("File contain no entries");
|
||||
}
|
||||
if (file_info_list[i].version != 1) {
|
||||
return Status::InvalidArgument(
|
||||
"Generated table version is not supported");
|
||||
}
|
||||
// version 1 imply that file have only Put Operations with Sequence Number =
|
||||
// 0
|
||||
|
||||
meta_list[i].smallest =
|
||||
InternalKey(file_info_list[i].smallest_key,
|
||||
file_info_list[i].sequence_number, ValueType::kTypeValue);
|
||||
meta_list[i].largest =
|
||||
InternalKey(file_info_list[i].largest_key,
|
||||
file_info_list[i].sequence_number, ValueType::kTypeValue);
|
||||
if (!meta_list[i].smallest.Valid() || !meta_list[i].largest.Valid()) {
|
||||
return Status::Corruption("Generated table have corrupted keys");
|
||||
}
|
||||
meta_list[i].smallest_seqno = file_info_list[i].sequence_number;
|
||||
meta_list[i].largest_seqno = file_info_list[i].sequence_number;
|
||||
if (meta_list[i].smallest_seqno != 0 || meta_list[i].largest_seqno != 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Non zero sequence numbers are not supported");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::list<uint64_t>::iterator> pending_outputs_inserted_elem_list(
|
||||
num_files);
|
||||
// Generate locations for the new tables
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
StatsTimer t(env_, µ_list[i]);
|
||||
pending_outputs_inserted_elem_list[i] =
|
||||
CaptureCurrentFileNumberInPendingOutputs();
|
||||
meta_list[i].fd = FileDescriptor(versions_->NewFileNumber(), 0,
|
||||
file_info_list[i].file_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy/Move external files into DB
|
||||
std::vector<std::string> db_fname_list(num_files);
|
||||
size_t j = 0;
|
||||
for (; j < num_files; j++) {
|
||||
StatsTimer t(env_, µ_list[j]);
|
||||
db_fname_list[j] =
|
||||
TableFileName(db_options_.db_paths, meta_list[j].fd.GetNumber(),
|
||||
meta_list[j].fd.GetPathId());
|
||||
if (move_file) {
|
||||
status = env_->LinkFile(file_info_list[j].file_path, db_fname_list[j]);
|
||||
if (status.IsNotSupported()) {
|
||||
// Original file is on a different FS, use copy instead of hard linking
|
||||
status =
|
||||
CopyFile(env_, file_info_list[j].file_path, db_fname_list[j], 0);
|
||||
}
|
||||
} else {
|
||||
status = CopyFile(env_, file_info_list[j].file_path, db_fname_list[j], 0);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:FileCopied");
|
||||
if (!status.ok()) {
|
||||
for (size_t i = 0; i < j; i++) {
|
||||
Status s = env_->DeleteFile(db_fname_list[i]);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
db_fname_list[i].c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
|
||||
WriteThread::Writer w;
|
||||
write_thread_.EnterUnbatched(&w, &mutex_);
|
||||
|
||||
if (!snapshots_.empty()) {
|
||||
// Check that no snapshots are being held
|
||||
status =
|
||||
Status::NotSupported("Cannot add a file while holding snapshots");
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
// Verify that added file key range dont overlap with any keys in DB
|
||||
SuperVersion* sv = cfd->GetSuperVersion()->Ref();
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
ScopedArenaIterator iter(NewInternalIterator(ro, cfd, sv, &arena));
|
||||
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
StatsTimer t(env_, µ_list[i]);
|
||||
InternalKey range_start(file_info_list[i].smallest_key,
|
||||
kMaxSequenceNumber, kTypeValue);
|
||||
iter->Seek(range_start.Encode());
|
||||
status = iter->status();
|
||||
|
||||
if (status.ok() && iter->Valid()) {
|
||||
ParsedInternalKey seek_result;
|
||||
if (ParseInternalKey(iter->key(), &seek_result)) {
|
||||
auto* vstorage = cfd->current()->storage_info();
|
||||
if (vstorage->InternalComparator()->user_comparator()->Compare(
|
||||
seek_result.user_key, file_info_list[i].largest_key) <= 0) {
|
||||
status = Status::NotSupported("Cannot add overlapping range");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
status = Status::Corruption("DB have corrupted keys");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The level the file will be ingested into
|
||||
std::vector<int> target_level_list(num_files, 0);
|
||||
if (status.ok()) {
|
||||
// Add files to L0
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
StatsTimer t(env_, µ_list[i]);
|
||||
// Add file to the lowest possible level
|
||||
target_level_list[i] = PickLevelForIngestedFile(cfd, file_info_list[i]);
|
||||
edit.AddFile(target_level_list[i], meta_list[i].fd.GetNumber(),
|
||||
meta_list[i].fd.GetPathId(), meta_list[i].fd.GetFileSize(),
|
||||
meta_list[i].smallest, meta_list[i].largest,
|
||||
meta_list[i].smallest_seqno, meta_list[i].largest_seqno,
|
||||
meta_list[i].marked_for_compaction);
|
||||
}
|
||||
status = versions_->LogAndApply(cfd, mutable_cf_options, &edit, &mutex_,
|
||||
directories_.GetDbDir());
|
||||
}
|
||||
write_thread_.ExitUnbatched(&w);
|
||||
|
||||
if (status.ok()) {
|
||||
delete InstallSuperVersionAndScheduleWork(cfd, nullptr,
|
||||
mutable_cf_options);
|
||||
}
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
// Update internal stats
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = micro_list[i];
|
||||
stats.bytes_written = meta_list[i].fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
cfd->internal_stats()->AddCompactionStats(target_level_list[i], stats);
|
||||
cfd->internal_stats()->AddCFStats(InternalStats::BYTES_INGESTED_ADD_FILE,
|
||||
meta_list[i].fd.GetFileSize());
|
||||
ReleaseFileNumberFromPendingOutputs(
|
||||
pending_outputs_inserted_elem_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
// We failed to add the files to the database
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
Status s = env_->DeleteFile(db_fname_list[i]);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
db_fname_list[i].c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
} else if (status.ok() && move_file) {
|
||||
// The files were moved and added successfully, remove original file links
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
Status s = env_->DeleteFile(file_info_list[i].file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"%s was added to DB successfully but failed to remove original "
|
||||
"file "
|
||||
"link : %s",
|
||||
file_info_list[i].file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Finds the lowest level in the DB that the ingested file can be added to
|
||||
int DBImpl::PickLevelForIngestedFile(ColumnFamilyData* cfd,
|
||||
const ExternalSstFileInfo& file_info) {
|
||||
mutex_.AssertHeld();
|
||||
|
||||
int target_level = 0;
|
||||
auto* vstorage = cfd->current()->storage_info();
|
||||
auto* ucmp = vstorage->InternalComparator()->user_comparator();
|
||||
|
||||
Slice file_smallest_user_key(file_info.smallest_key);
|
||||
Slice file_largest_user_key(file_info.largest_key);
|
||||
|
||||
for (int lvl = cfd->NumberLevels() - 1; lvl >= vstorage->base_level();
|
||||
lvl--) {
|
||||
if (vstorage->OverlapInLevel(lvl, &file_smallest_user_key,
|
||||
&file_largest_user_key) == false) {
|
||||
// Make sure that the file dont overlap with the output of any
|
||||
// compaction running right now
|
||||
Slice compaction_smallest_user_key;
|
||||
Slice compaction_largest_user_key;
|
||||
bool overlap_with_compaction_output = false;
|
||||
for (Compaction* c : running_compactions_) {
|
||||
if (c->column_family_data()->GetID() != cfd->GetID() ||
|
||||
c->output_level() != lvl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
compaction_smallest_user_key = c->GetSmallestUserKey();
|
||||
compaction_largest_user_key = c->GetLargestUserKey();
|
||||
|
||||
if (ucmp->Compare(file_smallest_user_key,
|
||||
compaction_largest_user_key) <= 0 &&
|
||||
ucmp->Compare(file_largest_user_key,
|
||||
compaction_smallest_user_key) >= 0) {
|
||||
overlap_with_compaction_output = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap_with_compaction_output == false) {
|
||||
// Level lvl is the lowest level that dont have any files with key
|
||||
// range overlapping with our file key range and no compactions
|
||||
// planning to add overlapping files in it.
|
||||
target_level = lvl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target_level;
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -178,5 +178,10 @@ Status DBImpl::TEST_GetLatestMutableCFOptions(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
int DBImpl::TEST_BGCompactionsAllowed() const {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return BGCompactionsAllowed();
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
#endif // NDEBUG
|
||||
@@ -49,7 +49,8 @@ Status DBImpl::SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
// Since we have some more files to compact, we should also recompute
|
||||
// compaction score
|
||||
vstorage->ComputeCompactionScore(*cfd->GetLatestMutableCFOptions());
|
||||
vstorage->ComputeCompactionScore(*cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
SchedulePendingCompaction(cfd);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
@@ -60,7 +61,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
assert(column_family);
|
||||
|
||||
if (target_level < 1) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Invalid target level %d\n", target_level);
|
||||
return Status::InvalidArgument("Invalid target level");
|
||||
}
|
||||
@@ -74,7 +75,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
const auto* vstorage = cfd->current()->storage_info();
|
||||
|
||||
if (target_level >= vstorage->num_levels()) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Target level %d does not exist\n", target_level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument("Target level does not exist");
|
||||
@@ -93,7 +94,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
for (size_t i = 0; i < l0_files.size(); ++i) {
|
||||
auto f = l0_files[i];
|
||||
if (f->being_compacted) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. File %" PRIu64 " being compacted\n",
|
||||
f->fd.GetNumber());
|
||||
job_context.Clean();
|
||||
@@ -103,7 +104,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
if (i == 0) continue;
|
||||
auto prev_f = l0_files[i - 1];
|
||||
if (icmp->Compare(prev_f->largest, f->smallest) >= 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Files %" PRIu64 " and %" PRIu64
|
||||
" have overlapping ranges\n",
|
||||
prev_f->fd.GetNumber(), f->fd.GetNumber());
|
||||
@@ -115,7 +116,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
// Check that all levels up to target_level are empty.
|
||||
for (int level = 1; level <= target_level; ++level) {
|
||||
if (vstorage->NumLevelFiles(level) > 0) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
Log(InfoLogLevel::INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"PromoteL0 FAILED. Level %d not empty\n", level);
|
||||
job_context.Clean();
|
||||
return Status::InvalidArgument(
|
||||
@@ -140,7 +141,7 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
cfd, &job_context, *cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
} // lock released here
|
||||
LogFlush(db_options_.info_log);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
job_context.Clean();
|
||||
|
||||
return status;
|
||||
|
||||
@@ -19,8 +19,9 @@ namespace rocksdb {
|
||||
DBImplReadOnly::DBImplReadOnly(const DBOptions& db_options,
|
||||
const std::string& dbname)
|
||||
: DBImpl(db_options, dbname) {
|
||||
Log(INFO_LEVEL, db_options_.info_log, "Opening the db in read only mode");
|
||||
LogFlush(db_options_.info_log);
|
||||
Log(INFO_LEVEL, immutable_db_options_.info_log,
|
||||
"Opening the db in read only mode");
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
}
|
||||
|
||||
DBImplReadOnly::~DBImplReadOnly() {
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/filename.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/env.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
void DumpDBFileSummary(const DBOptions& options, const std::string& dbname) {
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname) {
|
||||
if (options.info_log == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
+3
-2
@@ -6,8 +6,9 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
#include "util/db_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
void DumpDBFileSummary(const DBOptions& options, const std::string& dbname);
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname);
|
||||
} // namespace rocksdb
|
||||
@@ -111,6 +111,7 @@ TEST_F(DBIOFailureTest, NoSpaceCompactRange) {
|
||||
Status s = dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr,
|
||||
true /* disallow trivial move */);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_TRUE(s.IsNoSpace());
|
||||
|
||||
env_->no_space_.store(false, std::memory_order_release);
|
||||
} while (ChangeCompactOptions());
|
||||
|
||||
+138
-70
@@ -105,7 +105,8 @@ class DBIter: public Iterator {
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false)
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false)
|
||||
: arena_mode_(arena_mode),
|
||||
env_(env),
|
||||
logger_(ioptions.info_log),
|
||||
@@ -120,7 +121,8 @@ class DBIter: public Iterator {
|
||||
version_number_(version_number),
|
||||
iterate_upper_bound_(iterate_upper_bound),
|
||||
prefix_same_as_start_(prefix_same_as_start),
|
||||
pin_thru_lifetime_(pin_data) {
|
||||
pin_thru_lifetime_(pin_data),
|
||||
total_order_seek_(total_order_seek) {
|
||||
RecordTick(statistics_, NO_ITERATORS);
|
||||
prefix_extractor_ = ioptions.prefix_extractor;
|
||||
max_skip_ = max_sequential_skip_in_iterations;
|
||||
@@ -133,7 +135,9 @@ class DBIter: public Iterator {
|
||||
}
|
||||
virtual ~DBIter() {
|
||||
// Release pinned data if any
|
||||
pinned_iters_mgr_.ReleasePinnedIterators();
|
||||
if (pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
RecordTick(statistics_, NO_ITERATORS, -1);
|
||||
local_stats_.BumpGlobalStatistics(statistics_);
|
||||
if (!arena_mode_) {
|
||||
@@ -155,7 +159,9 @@ class DBIter: public Iterator {
|
||||
virtual Slice value() const override {
|
||||
assert(valid_);
|
||||
if (current_entry_is_merged_) {
|
||||
return saved_value_;
|
||||
// If pinned_value_ is set then the result of merge operator is one of
|
||||
// the merge operands and we should return it.
|
||||
return pinned_value_.data() ? pinned_value_ : saved_value_;
|
||||
} else if (direction_ == kReverse) {
|
||||
return pinned_value_;
|
||||
} else {
|
||||
@@ -195,10 +201,12 @@ class DBIter: public Iterator {
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
|
||||
private:
|
||||
void ReverseToForward();
|
||||
void ReverseToBackward();
|
||||
void PrevInternal();
|
||||
void FindParseableKey(ParsedInternalKey* ikey, Direction direction);
|
||||
@@ -221,8 +229,8 @@ class DBIter: public Iterator {
|
||||
|
||||
// Release blocks pinned by TempPinData()
|
||||
void ReleaseTempPinnedData() {
|
||||
if (!pin_thru_lifetime_) {
|
||||
pinned_iters_mgr_.ReleasePinnedIterators();
|
||||
if (!pin_thru_lifetime_ && pinned_iters_mgr_.PinningEnabled()) {
|
||||
pinned_iters_mgr_.ReleasePinnedData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +259,7 @@ class DBIter: public Iterator {
|
||||
Direction direction_;
|
||||
bool valid_;
|
||||
bool current_entry_is_merged_;
|
||||
// for prefix seek mode to support prev()
|
||||
Statistics* statistics_;
|
||||
uint64_t max_skip_;
|
||||
uint64_t version_number_;
|
||||
@@ -261,6 +270,7 @@ class DBIter: public Iterator {
|
||||
// Means that we will pin all data blocks we read as long the Iterator
|
||||
// is not deleted, will be true if ReadOptions::pin_data is true
|
||||
const bool pin_thru_lifetime_;
|
||||
const bool total_order_seek_;
|
||||
// List of operands for merge operator.
|
||||
MergeContext merge_context_;
|
||||
LocalStatistics local_stats_;
|
||||
@@ -286,14 +296,10 @@ inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
void DBIter::Next() {
|
||||
assert(valid_);
|
||||
|
||||
// Release temporarily pinned blocks from last operation
|
||||
ReleaseTempPinnedData();
|
||||
if (direction_ == kReverse) {
|
||||
// We only pin blocks when doing kReverse
|
||||
ReleaseTempPinnedData();
|
||||
FindNextUserKey();
|
||||
direction_ = kForward;
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToFirst();
|
||||
}
|
||||
ReverseToForward();
|
||||
} else if (iter_->Valid() && !current_entry_is_merged_) {
|
||||
// If the current value is not a merge, the iter position is the
|
||||
// current key, which is already returned. We can safely issue a
|
||||
@@ -433,9 +439,12 @@ void DBIter::MergeValuesNewToOld() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporarily pin the blocks that hold merge operands
|
||||
TempPinData();
|
||||
merge_context_.Clear();
|
||||
// Start the merge process by pushing the first operand
|
||||
merge_context_.PushOperand(iter_->value());
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
|
||||
ParsedInternalKey ikey;
|
||||
for (iter_->Next(); iter_->Valid(); iter_->Next()) {
|
||||
@@ -459,15 +468,15 @@ void DBIter::MergeValuesNewToOld() {
|
||||
const Slice val = iter_->value();
|
||||
MergeHelper::TimedFullMerge(merge_operator_, ikey.user_key, &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
// iter_ is positioned after put
|
||||
iter_->Next();
|
||||
return;
|
||||
} else if (kTypeMerge == ikey.type) {
|
||||
// hit a merge, add the value as an operand and run associative merge.
|
||||
// when complete, add result to operands and continue.
|
||||
const Slice& val = iter_->value();
|
||||
merge_context_.PushOperand(val);
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
@@ -479,15 +488,15 @@ void DBIter::MergeValuesNewToOld() {
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
}
|
||||
|
||||
void DBIter::Prev() {
|
||||
assert(valid_);
|
||||
ReleaseTempPinnedData();
|
||||
if (direction_ == kForward) {
|
||||
ReverseToBackward();
|
||||
}
|
||||
ReleaseTempPinnedData();
|
||||
PrevInternal();
|
||||
if (statistics_ != nullptr) {
|
||||
local_stats_.prev_count_++;
|
||||
@@ -496,14 +505,29 @@ void DBIter::Prev() {
|
||||
local_stats_.bytes_read_ += (key().size() + value().size());
|
||||
}
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
void DBIter::ReverseToForward() {
|
||||
if (prefix_extractor_ != nullptr && !total_order_seek_) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(ParsedInternalKey(
|
||||
saved_key_.GetKey(), kMaxSequenceNumber, kValueTypeForSeek));
|
||||
iter_->Seek(last_key.GetKey());
|
||||
}
|
||||
FindNextUserKey();
|
||||
direction_ = kForward;
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::ReverseToBackward() {
|
||||
if (prefix_extractor_ != nullptr && !total_order_seek_) {
|
||||
IterKey last_key;
|
||||
last_key.SetInternalKey(
|
||||
ParsedInternalKey(saved_key_.GetKey(), 0, kValueTypeForSeekForPrev));
|
||||
iter_->SeekForPrev(last_key.GetKey());
|
||||
}
|
||||
if (current_entry_is_merged_) {
|
||||
// Not placed in the same key. Need to call Prev() until finding the
|
||||
// previous key.
|
||||
@@ -550,6 +574,11 @@ void DBIter::PrevInternal() {
|
||||
if (user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
FindPrevUserKey();
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_ &&
|
||||
prefix_extractor_->Transform(saved_key_.GetKey())
|
||||
.compare(prefix_start_key_) != 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!iter_->Valid()) {
|
||||
@@ -561,6 +590,7 @@ void DBIter::PrevInternal() {
|
||||
}
|
||||
}
|
||||
// We haven't found any key - iterator is not valid
|
||||
// Or the prefix is different than start prefix
|
||||
assert(!iter_->Valid());
|
||||
valid_ = false;
|
||||
}
|
||||
@@ -580,6 +610,9 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
ParsedInternalKey ikey;
|
||||
FindParseableKey(&ikey, kReverse);
|
||||
|
||||
// Temporarily pin blocks that hold (merge operands / the value)
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
size_t num_skipped = 0;
|
||||
while (iter_->Valid() && ikey.sequence <= sequence_ &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
@@ -592,8 +625,7 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
switch (last_key_entry_type) {
|
||||
case kTypeValue:
|
||||
merge_context_.Clear();
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
assert(iter_->IsValuePinned());
|
||||
pinned_value_ = iter_->value();
|
||||
last_not_merge_type = kTypeValue;
|
||||
break;
|
||||
@@ -605,7 +637,8 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
break;
|
||||
case kTypeMerge:
|
||||
assert(merge_operator_ != nullptr);
|
||||
merge_context_.PushOperandBack(iter_->value());
|
||||
merge_context_.PushOperandBack(
|
||||
iter_->value(), iter_->IsValuePinned() /* operand_pinned */);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -625,16 +658,18 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
return false;
|
||||
case kTypeMerge:
|
||||
current_entry_is_merged_ = true;
|
||||
if (last_not_merge_type == kTypeDeletion) {
|
||||
if (last_not_merge_type == kTypeDeletion ||
|
||||
last_not_merge_type == kTypeSingleDeletion) {
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_);
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_);
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(),
|
||||
&pinned_value_,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -651,6 +686,9 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
// This function is used in FindValueForCurrentKey.
|
||||
// We use Seek() function instead of Prev() to find necessary value
|
||||
bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
// FindValueForCurrentKey will enable pinning before calling
|
||||
// FindValueForCurrentKeyUsingSeek()
|
||||
assert(pinned_iters_mgr_.PinningEnabled());
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key, ParsedInternalKey(saved_key_.GetKey(), sequence_,
|
||||
kValueTypeForSeek));
|
||||
@@ -664,8 +702,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeDeletion ||
|
||||
ikey.type == kTypeSingleDeletion) {
|
||||
if (ikey.type == kTypeValue) {
|
||||
ReleaseTempPinnedData();
|
||||
TempPinData();
|
||||
assert(iter_->IsValuePinned());
|
||||
pinned_value_ = iter_->value();
|
||||
valid_ = true;
|
||||
return true;
|
||||
@@ -681,7 +718,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
while (iter_->Valid() &&
|
||||
user_comparator_->Equal(ikey.user_key, saved_key_.GetKey()) &&
|
||||
ikey.type == kTypeMerge) {
|
||||
merge_context_.PushOperand(iter_->value());
|
||||
merge_context_.PushOperand(iter_->value(),
|
||||
iter_->IsValuePinned() /* operand_pinned */);
|
||||
iter_->Next();
|
||||
FindParseableKey(&ikey, kForward);
|
||||
}
|
||||
@@ -691,7 +729,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
ikey.type == kTypeDeletion || ikey.type == kTypeSingleDeletion) {
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
// Make iter_ valid and point to saved_key_
|
||||
if (!iter_->Valid() ||
|
||||
!user_comparator_->Equal(ikey.user_key, saved_key_.GetKey())) {
|
||||
@@ -705,7 +743,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
const Slice& val = iter_->value();
|
||||
MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_,
|
||||
logger_, statistics_, env_);
|
||||
logger_, statistics_, env_, &pinned_value_);
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
@@ -778,7 +816,6 @@ void DBIter::Seek(const Slice& target) {
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->Seek(saved_key_.GetKey());
|
||||
}
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
if (prefix_extractor_ && prefix_same_as_start_) {
|
||||
@@ -799,6 +836,46 @@ void DBIter::Seek(const Slice& target) {
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
}
|
||||
}
|
||||
|
||||
void DBIter::SeekForPrev(const Slice& target) {
|
||||
StopWatch sw(env_, statistics_, DB_SEEK);
|
||||
ReleaseTempPinnedData();
|
||||
saved_key_.Clear();
|
||||
// now saved_key is used to store internal key.
|
||||
saved_key_.SetInternalKey(target, 0 /* sequence_number */,
|
||||
kValueTypeForSeekForPrev);
|
||||
|
||||
{
|
||||
PERF_TIMER_GUARD(seek_internal_seek_time);
|
||||
iter_->SeekForPrev(saved_key_.GetKey());
|
||||
}
|
||||
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (iter_->Valid()) {
|
||||
if (prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_key_ = prefix_extractor_->Transform(target);
|
||||
}
|
||||
direction_ = kReverse;
|
||||
ClearSavedValue();
|
||||
PrevInternal();
|
||||
if (!valid_) {
|
||||
prefix_start_key_.clear();
|
||||
}
|
||||
if (statistics_ != nullptr) {
|
||||
if (valid_) {
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK_FOUND);
|
||||
RecordTick(statistics_, ITER_BYTES_READ, key().size() + value().size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valid_ = false;
|
||||
}
|
||||
if (valid_ && prefix_extractor_ && prefix_same_as_start_) {
|
||||
prefix_start_buf_.SetKey(prefix_start_key_);
|
||||
prefix_start_key_ = prefix_start_buf_.GetKey();
|
||||
@@ -856,25 +933,15 @@ void DBIter::SeekToLast() {
|
||||
// it will seek to the last key before the
|
||||
// ReadOptions.iterate_upper_bound
|
||||
if (iter_->Valid() && iterate_upper_bound_ != nullptr) {
|
||||
saved_key_.SetKey(*iterate_upper_bound_, false /* copy */);
|
||||
std::string last_key;
|
||||
AppendInternalKey(&last_key,
|
||||
ParsedInternalKey(saved_key_.GetKey(), kMaxSequenceNumber,
|
||||
kValueTypeForSeek));
|
||||
|
||||
iter_->Seek(last_key);
|
||||
|
||||
if (!iter_->Valid()) {
|
||||
iter_->SeekToLast();
|
||||
} else {
|
||||
iter_->Prev();
|
||||
if (!iter_->Valid()) {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
SeekForPrev(*iterate_upper_bound_);
|
||||
if (!Valid()) {
|
||||
return;
|
||||
} else if (user_comparator_->Equal(*iterate_upper_bound_, key())) {
|
||||
Prev();
|
||||
}
|
||||
} else {
|
||||
PrevInternal();
|
||||
}
|
||||
PrevInternal();
|
||||
if (statistics_ != nullptr) {
|
||||
RecordTick(statistics_, NUMBER_DB_SEEK);
|
||||
if (valid_) {
|
||||
@@ -888,18 +955,16 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* NewDBIterator(Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number,
|
||||
const Slice* iterate_upper_bound,
|
||||
bool prefix_same_as_start, bool pin_data) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, ioptions, user_key_comparator, internal_iter, sequence,
|
||||
false, max_sequential_skip_in_iterations, version_number,
|
||||
iterate_upper_bound, prefix_same_as_start, pin_data);
|
||||
Iterator* NewDBIterator(
|
||||
Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, const Slice* iterate_upper_bound,
|
||||
bool prefix_same_as_start, bool pin_data, bool total_order_seek) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, ioptions, user_key_comparator, internal_iter, sequence, false,
|
||||
max_sequential_skip_in_iterations, version_number, iterate_upper_bound,
|
||||
prefix_same_as_start, pin_data, total_order_seek);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
@@ -917,6 +982,9 @@ inline void ArenaWrappedDBIter::SeekToLast() { db_iter_->SeekToLast(); }
|
||||
inline void ArenaWrappedDBIter::Seek(const Slice& target) {
|
||||
db_iter_->Seek(target);
|
||||
}
|
||||
inline void ArenaWrappedDBIter::SeekForPrev(const Slice& target) {
|
||||
db_iter_->SeekForPrev(target);
|
||||
}
|
||||
inline void ArenaWrappedDBIter::Next() { db_iter_->Next(); }
|
||||
inline void ArenaWrappedDBIter::Prev() { db_iter_->Prev(); }
|
||||
inline Slice ArenaWrappedDBIter::key() const { return db_iter_->key(); }
|
||||
@@ -935,15 +1003,15 @@ ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ImmutableCFOptions& ioptions,
|
||||
const Comparator* user_key_comparator, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound, bool prefix_same_as_start,
|
||||
bool pin_data) {
|
||||
const Slice* iterate_upper_bound, bool prefix_same_as_start, bool pin_data,
|
||||
bool total_order_seek) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
Arena* arena = iter->GetArena();
|
||||
auto mem = arena->AllocateAligned(sizeof(DBIter));
|
||||
DBIter* db_iter =
|
||||
new (mem) DBIter(env, ioptions, user_key_comparator, nullptr, sequence,
|
||||
true, max_sequential_skip_in_iterations, version_number,
|
||||
iterate_upper_bound, prefix_same_as_start, pin_data);
|
||||
DBIter* db_iter = new (mem) DBIter(
|
||||
env, ioptions, user_key_comparator, nullptr, sequence, true,
|
||||
max_sequential_skip_in_iterations, version_number, iterate_upper_bound,
|
||||
prefix_same_as_start, pin_data, total_order_seek);
|
||||
|
||||
iter->SetDBIter(db_iter);
|
||||
|
||||
|
||||
+7
-3
@@ -10,11 +10,12 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "util/arena.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -30,7 +31,8 @@ extern Iterator* NewDBIterator(
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false);
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false);
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
// iterator is supposed be allocated. This class is used as an entry point of
|
||||
@@ -56,6 +58,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
virtual void SeekToFirst() override;
|
||||
virtual void SeekToLast() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void Next() override;
|
||||
virtual void Prev() override;
|
||||
virtual Slice key() const override;
|
||||
@@ -76,6 +79,7 @@ extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
const Comparator* user_key_comparator, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
const Slice* iterate_upper_bound = nullptr,
|
||||
bool prefix_same_as_start = false, bool pin_data = false);
|
||||
bool prefix_same_as_start = false, bool pin_data = false,
|
||||
bool total_order_seek = false);
|
||||
|
||||
} // namespace rocksdb
|
||||
+61
-3
@@ -117,6 +117,11 @@ class TestIterator : public InternalIterator {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SeekForPrev(const Slice& target) override {
|
||||
assert(initialized_);
|
||||
SeekForPrevImpl(target, &cmp);
|
||||
}
|
||||
|
||||
virtual void Next() override {
|
||||
assert(initialized_);
|
||||
if (data_.empty() || (iter_ == data_.size() - 1)) {
|
||||
@@ -150,6 +155,9 @@ class TestIterator : public InternalIterator {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
virtual bool IsKeyPinned() const override { return true; }
|
||||
virtual bool IsValuePinned() const override { return true; }
|
||||
|
||||
private:
|
||||
bool initialized_;
|
||||
bool valid_;
|
||||
@@ -349,7 +357,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
db_iter->SeekToLast();
|
||||
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_key_skipped_count), 1);
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_key_skipped_count), 7);
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
@@ -472,7 +480,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
db_iter->SeekToLast();
|
||||
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_delete_skipped_count), 0);
|
||||
ASSERT_EQ(static_cast<int>(perf_context.internal_delete_skipped_count), 1);
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
@@ -1121,6 +1129,26 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
|
||||
{
|
||||
// put, singledelete, merge
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
internal_iter->AddPut("a", "val_a");
|
||||
internal_iter->AddSingleDeletion("a");
|
||||
internal_iter->AddMerge("a", "merge_1");
|
||||
internal_iter->AddMerge("a", "merge_2");
|
||||
internal_iter->AddPut("b", "val_b");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ImmutableCFOptions(options), BytewiseComparator(), internal_iter,
|
||||
10, options.max_sequential_skip_in_iterations, 0));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, DBIterator6) {
|
||||
@@ -1723,6 +1751,15 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_1,merge_2");
|
||||
|
||||
db_iter->SeekForPrev("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_3,merge_4");
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_5,merge_6");
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
@@ -1731,6 +1768,15 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_3,merge_4");
|
||||
|
||||
db_iter->SeekForPrev("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_3,merge_4");
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "merge_5,merge_6");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1761,6 +1807,18 @@ TEST_F(DBIteratorTest, DBIterator10) {
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
|
||||
db_iter->SeekForPrev("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
db_iter->Next();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "d");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "4");
|
||||
|
||||
db_iter->Prev();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
ASSERT_EQ(db_iter->value().ToString(), "3");
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
|
||||
@@ -1911,7 +1969,7 @@ TEST_F(DBIterWithMergeIterTest, InnerMergeIterator1) {
|
||||
|
||||
TEST_F(DBIterWithMergeIterTest, InnerMergeIterator2) {
|
||||
// Test Prev() when one child iterator is at its end.
|
||||
db_iter_->Seek("g");
|
||||
db_iter_->SeekForPrev("g");
|
||||
ASSERT_TRUE(db_iter_->Valid());
|
||||
ASSERT_EQ(db_iter_->key().ToString(), "g");
|
||||
ASSERT_EQ(db_iter_->value().ToString(), "3");
|
||||
|
||||
+189
-9
@@ -204,6 +204,22 @@ TEST_F(DBIteratorTest, IterSeekBeforePrev) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("0", "f"));
|
||||
ASSERT_OK(Put("1", "h"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("2", "j"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
iter->SeekForPrev(Slice("0"));
|
||||
iter->Next();
|
||||
iter->SeekForPrev(Slice("1"));
|
||||
iter->Next();
|
||||
delete iter;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::string MakeLongKey(size_t length, char c) {
|
||||
return std::string(length, c);
|
||||
@@ -231,6 +247,13 @@ TEST_F(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(64, 4) + "->4");
|
||||
|
||||
iter->SeekForPrev(MakeLongKey(127, 3));
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(127, 3) + "->3");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(32, 2) + "->2");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), MakeLongKey(50, 1) + "->1");
|
||||
delete iter;
|
||||
|
||||
iter = db_->NewIterator(ReadOptions());
|
||||
@@ -261,6 +284,11 @@ TEST_F(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
iter->SeekForPrev(Slice("b"));
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
@@ -284,7 +312,13 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
|
||||
iter->Prev();
|
||||
iter->SeekForPrev(Slice("d"));
|
||||
ASSERT_EQ(IterStatus(iter), "d->e");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
iter->Prev();
|
||||
delete iter;
|
||||
}
|
||||
@@ -294,10 +328,13 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
dbfull()->Flush(FlushOptions());
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("d", "e"));
|
||||
ASSERT_OK(Put("e", "f"));
|
||||
auto iter = db_->NewIterator(ReadOptions());
|
||||
auto iter2 = db_->NewIterator(ReadOptions());
|
||||
iter->Seek(Slice("c"));
|
||||
iter2->SeekForPrev(Slice("d"));
|
||||
ASSERT_EQ(IterStatus(iter), "c->d");
|
||||
ASSERT_EQ(IterStatus(iter2), "c->d");
|
||||
|
||||
// Create a key that needs to be skipped for Seq too new
|
||||
for (uint64_t i = 0; i < last_options_.max_sequential_skip_in_iterations + 1;
|
||||
@@ -307,9 +344,12 @@ TEST_F(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "a->b");
|
||||
|
||||
iter->Prev();
|
||||
iter2->Prev();
|
||||
ASSERT_EQ(IterStatus(iter2), "a->b");
|
||||
iter2->Prev();
|
||||
delete iter;
|
||||
delete iter2;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterEmpty) {
|
||||
@@ -326,6 +366,9 @@ TEST_F(DBIteratorTest, IterEmpty) {
|
||||
iter->Seek("foo");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekForPrev("foo");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
@@ -358,14 +401,24 @@ TEST_F(DBIteratorTest, IterSingle) {
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("a");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("a");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("b");
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
delete iter;
|
||||
} while (ChangeCompactOptions());
|
||||
@@ -411,11 +464,21 @@ TEST_F(DBIteratorTest, IterMulti) {
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Seek("ax");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->SeekForPrev("c");
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->SeekForPrev("bx");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->Seek("z");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("b");
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb");
|
||||
iter->SeekForPrev("");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
// Switch from reverse to forward
|
||||
iter->SeekToLast();
|
||||
@@ -603,7 +666,8 @@ TEST_F(DBIteratorTest, IterMultiWithDelete) {
|
||||
// TODO: merge operator does not support backward iteration yet
|
||||
if (kPlainTableAllBytesPrefix != option_config_ &&
|
||||
kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&
|
||||
kHashLinkList != option_config_) {
|
||||
kHashLinkList != option_config_ &&
|
||||
kHashSkipList != option_config_) { // doesn't support SeekToLast
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "ka->va");
|
||||
}
|
||||
@@ -658,6 +722,7 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
options.snapshot = snapshot;
|
||||
Iterator* iter = db_->NewIterator(options, handles_[1]);
|
||||
|
||||
ASSERT_OK(Put(1, "key0", "val0"));
|
||||
// Put more values after the snapshot
|
||||
ASSERT_OK(Put(1, "key100", "val100"));
|
||||
ASSERT_OK(Put(1, "key101", "val101"));
|
||||
@@ -668,7 +733,7 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
// TODO: merge operator does not support backward iteration yet
|
||||
if (kPlainTableAllBytesPrefix != option_config_ &&
|
||||
kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&
|
||||
kHashLinkList != option_config_) {
|
||||
kHashLinkList != option_config_ && kHashSkipList != option_config_) {
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "key4->val4");
|
||||
iter->Prev();
|
||||
@@ -682,6 +747,26 @@ TEST_F(DBIteratorTest, IterWithSnapshot) {
|
||||
iter->Next();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
|
||||
if (!CurrentOptions().merge_operator) {
|
||||
// TODO(gzh): merge operator does not support backward iteration yet
|
||||
if (kPlainTableAllBytesPrefix != option_config_ &&
|
||||
kBlockBasedTableWithWholeKeyHashIndex != option_config_ &&
|
||||
kHashLinkList != option_config_ && kHashSkipList != option_config_) {
|
||||
iter->SeekForPrev("key1");
|
||||
ASSERT_EQ(IterStatus(iter), "key1->val1");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "key2->val2");
|
||||
iter->Next();
|
||||
ASSERT_EQ(IterStatus(iter), "key3->val3");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "key2->val2");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(IterStatus(iter), "key1->val1");
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
}
|
||||
}
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
delete iter;
|
||||
// skip as HashCuckooRep does not support snapshot
|
||||
@@ -745,6 +830,19 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("g1")), 0);
|
||||
|
||||
iter->SeekForPrev("g1");
|
||||
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("g1")), 0);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("foo1")), 0);
|
||||
|
||||
iter->Prev();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("foo")), 0);
|
||||
}
|
||||
|
||||
// testing iterate_upper_bound and forward iterator
|
||||
@@ -877,7 +975,7 @@ TEST_F(DBIteratorTest, DBIteratorBoundTest) {
|
||||
|
||||
// TODO(3.13): fix the issue of Seek() + Prev() which might not necessary
|
||||
// return the biggest key which is smaller than the seek key.
|
||||
TEST_F(DBIteratorTest, PrevAfterMerge) {
|
||||
TEST_F(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator = MergeOperators::CreatePutOperator();
|
||||
@@ -898,6 +996,14 @@ TEST_F(DBIteratorTest, PrevAfterMerge) {
|
||||
it->Prev();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("1", it->key().ToString());
|
||||
|
||||
it->SeekForPrev("1");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("1", it->key().ToString());
|
||||
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("2", it->key().ToString());
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
@@ -980,7 +1086,6 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
|
||||
{
|
||||
// Test Seek to random keys
|
||||
printf("Testing seek on %" ROCKSDB_PRIszt " keys\n", random_keys.size());
|
||||
std::vector<Slice> keys_slices;
|
||||
std::vector<std::string> true_keys;
|
||||
for (auto& k : random_keys) {
|
||||
@@ -1002,9 +1107,31 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test SeekForPrev to random keys
|
||||
std::vector<Slice> keys_slices;
|
||||
std::vector<std::string> true_keys;
|
||||
for (auto& k : random_keys) {
|
||||
iter->SeekForPrev(k);
|
||||
if (!iter->Valid()) {
|
||||
ASSERT_EQ(true_data.upper_bound(k), true_data.begin());
|
||||
continue;
|
||||
}
|
||||
std::string prop_value;
|
||||
ASSERT_OK(
|
||||
iter->GetProperty("rocksdb.iterator.is-key-pinned", &prop_value));
|
||||
ASSERT_EQ("1", prop_value);
|
||||
keys_slices.push_back(iter->key());
|
||||
true_keys.push_back((--true_data.upper_bound(k))->first);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < keys_slices.size(); i++) {
|
||||
ASSERT_EQ(keys_slices[i].ToString(), true_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Test iterating all data forward
|
||||
printf("Testing iterating forward on all keys\n");
|
||||
std::vector<Slice> all_keys;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
std::string prop_value;
|
||||
@@ -1025,7 +1152,6 @@ TEST_F(DBIteratorTest, PinnedDataIteratorRandomized) {
|
||||
|
||||
{
|
||||
// Test iterating all data backward
|
||||
printf("Testing iterating backward on all keys\n");
|
||||
std::vector<Slice> all_keys;
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
std::string prop_value;
|
||||
@@ -1230,6 +1356,60 @@ TEST_F(DBIteratorTest, PinnedDataIteratorReadAfterUpdate) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
Options options = CurrentOptions();
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
// Enable prefix bloom for SST files
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put("a1", "va1"));
|
||||
ASSERT_OK(Put("a2", "va2"));
|
||||
ASSERT_OK(Put("a3", "va3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("b1", "vb1"));
|
||||
ASSERT_OK(Put("b2", "vb2"));
|
||||
ASSERT_OK(Put("b3", "vb3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("b4", "vb4"));
|
||||
ASSERT_OK(Put("d1", "vd1"));
|
||||
ASSERT_OK(Put("d2", "vd2"));
|
||||
ASSERT_OK(Put("d4", "vd4"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
MoveFilesToLevel(1);
|
||||
{
|
||||
ReadOptions ro;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
iter->SeekForPrev("a4");
|
||||
ASSERT_EQ(iter->key().ToString(), "a3");
|
||||
ASSERT_EQ(iter->value().ToString(), "va3");
|
||||
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_EQ(iter->key().ToString(), "b3");
|
||||
iter->SeekForPrev("d3");
|
||||
ASSERT_EQ(iter->key().ToString(), "d2");
|
||||
iter->SeekForPrev("b5");
|
||||
ASSERT_EQ(iter->key().ToString(), "b4");
|
||||
delete iter;
|
||||
}
|
||||
|
||||
{
|
||||
ReadOptions ro;
|
||||
ro.prefix_same_as_start = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, IterPrevKeyCrossingBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
+233
-67
@@ -6,92 +6,258 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/random.h"
|
||||
#include "util/sync_point.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
class DBOptionsTest : public DBTestBase {
|
||||
public:
|
||||
DBOptionsTest() : DBTestBase("/db_options_test") {}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::unordered_map<std::string, std::string> GetMutableDBOptionsMap(
|
||||
const DBOptions& options) {
|
||||
std::string options_str;
|
||||
GetStringFromDBOptions(&options_str, options);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : db_options_type_info) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GetMutableCFOptionsMap(
|
||||
const ColumnFamilyOptions& options) {
|
||||
std::string options_str;
|
||||
GetStringFromColumnFamilyOptions(&options_str, options);
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : cf_options_type_info) {
|
||||
if (opt.second.is_mutable &&
|
||||
opt.second.verification != OptionVerificationType::kDeprecated) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
}
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GetRandomizedMutableCFOptionsMap(
|
||||
Random* rnd) {
|
||||
Options options;
|
||||
ImmutableDBOptions db_options(options);
|
||||
test::RandomInitCFOptions(&options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(db_options, nullptr, options);
|
||||
auto opt_map = GetMutableCFOptionsMap(sanitized_options);
|
||||
delete options.compaction_filter;
|
||||
return opt_map;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::string> GetRandomizedMutableDBOptionsMap(
|
||||
Random* rnd) {
|
||||
DBOptions db_options;
|
||||
test::RandomInitDBOptions(&db_options, rnd);
|
||||
auto sanitized_options = SanitizeOptions(dbname_, db_options);
|
||||
return GetMutableDBOptionsMap(sanitized_options);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
};
|
||||
|
||||
// When write stalls, user can enable auto compaction to unblock writes.
|
||||
// However, we had an issue where the stalled write thread blocks the attempt
|
||||
// to persist auto compaction option, thus creating a deadlock. The test
|
||||
// verifies the issue is fixed.
|
||||
TEST_F(DBOptionsTest, EnableAutoCompactionToUnblockWrites) {
|
||||
// RocksDB lite don't support dynamic options.
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBOptionsTest, GetLatestDBOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1000 * 1000; // 1M
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_slowdown_writes_trigger = 1;
|
||||
options.level0_stop_writes_trigger = 1;
|
||||
options.compression = kNoCompression;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::DelayWrite:Wait",
|
||||
"DBOptionsTest::EnableAutoCompactionToUnblockWrites:1"},
|
||||
{"DBImpl::BackgroundCompaction:Finish",
|
||||
"DBOptionsTest::EnableAutoCompactionToUnblockWrites:1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Stall writes.
|
||||
options.create_if_missing = true;
|
||||
Random rnd(228);
|
||||
Reopen(options);
|
||||
env_->StartThread(
|
||||
[](void* arg) {
|
||||
std::string value(1000, 'v');
|
||||
auto* t = static_cast<DBOptionsTest*>(arg);
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
ASSERT_OK(t->Put(t->Key(i), value));
|
||||
}
|
||||
},
|
||||
this);
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionToUnblockWrites:1");
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ColumnFamilyHandle* handle = dbfull()->DefaultColumnFamily();
|
||||
// We will get a deadlock here if we hit the issue.
|
||||
dbfull()->EnableAutoCompaction({handle});
|
||||
env_->WaitForJoin();
|
||||
auto new_options = GetRandomizedMutableDBOptionsMap(&rnd);
|
||||
ASSERT_OK(dbfull()->SetDBOptions(new_options));
|
||||
ASSERT_EQ(new_options, GetMutableDBOptionsMap(dbfull()->GetDBOptions()));
|
||||
}
|
||||
|
||||
// Similar to EnableAutoCompactionAfterStallDeadlock. See comments there.
|
||||
TEST_F(DBOptionsTest, ToggleStopTriggerToUnblockWrites) {
|
||||
TEST_F(DBOptionsTest, GetLatestCFOptions) {
|
||||
// GetOptions should be able to get latest option changed by SetOptions.
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1000 * 1000; // 1M
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_slowdown_writes_trigger = 1;
|
||||
options.level0_stop_writes_trigger = 1;
|
||||
options.compression = kNoCompression;
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::DelayWrite:Wait",
|
||||
"DBOptionsTest::ToggleStopTriggerToUnblockWrites:1"},
|
||||
{"DBImpl::BackgroundCompaction:Finish",
|
||||
"DBOptionsTest::ToggleStopTriggerToUnblockWrites:1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Stall writes.
|
||||
options.create_if_missing = true;
|
||||
Random rnd(228);
|
||||
Reopen(options);
|
||||
env_->StartThread(
|
||||
[](void* arg) {
|
||||
std::string value(1000, 'v');
|
||||
auto* t = static_cast<DBOptionsTest*>(arg);
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
ASSERT_OK(t->Put(t->Key(i), value));
|
||||
}
|
||||
},
|
||||
this);
|
||||
TEST_SYNC_POINT("DBOptionsTest::ToggleStopTriggerToUnblockWrites:1");
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
// We will get a deadlock here if we hit the issue.
|
||||
dbfull()->SetOptions({{"level0_stop_writes_trigger", "1000000"},
|
||||
{"level0_slowdown_writes_trigger", "1000000"}});
|
||||
env_->WaitForJoin();
|
||||
CreateColumnFamilies({"foo"}, options);
|
||||
ReopenWithColumnFamilies({"default", "foo"}, options);
|
||||
auto options_default = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
auto options_foo = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[0], options_default));
|
||||
ASSERT_OK(dbfull()->SetOptions(handles_[1], options_foo));
|
||||
ASSERT_EQ(options_default,
|
||||
GetMutableCFOptionsMap(dbfull()->GetOptions(handles_[0])));
|
||||
ASSERT_EQ(options_foo,
|
||||
GetMutableCFOptionsMap(dbfull()->GetOptions(handles_[1])));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsAndReopen) {
|
||||
Random rnd(1044);
|
||||
auto rand_opts = GetRandomizedMutableCFOptionsMap(&rnd);
|
||||
ASSERT_OK(dbfull()->SetOptions(rand_opts));
|
||||
// Verify if DB can be reopen after setting options.
|
||||
Options options;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, EnableAutoCompactionAndTriggerStall) {
|
||||
const std::string kValue(1024, 'v');
|
||||
for (int method_type = 0; method_type < 2; method_type++) {
|
||||
for (int option_type = 0; option_type < 4; option_type++) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.write_buffer_size = 1024 * 1024 * 10;
|
||||
options.compression = CompressionType::kNoCompression;
|
||||
options.level0_file_num_compaction_trigger = 1;
|
||||
options.level0_stop_writes_trigger = std::numeric_limits<int>::max();
|
||||
options.level0_slowdown_writes_trigger = std::numeric_limits<int>::max();
|
||||
options.hard_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
options.soft_pending_compaction_bytes_limit =
|
||||
std::numeric_limits<uint64_t>::max();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
int i = 0;
|
||||
for (; i < 1024; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
Flush();
|
||||
for (; i < 1024 * 2; i++) {
|
||||
Put(Key(i), kValue);
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
ASSERT_EQ(2, NumTableFilesAtLevel(0));
|
||||
uint64_t l0_size = SizeAtLevel(0);
|
||||
|
||||
switch (option_type) {
|
||||
case 0:
|
||||
// test with level0_stop_writes_trigger
|
||||
options.level0_stop_writes_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
break;
|
||||
case 1:
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
break;
|
||||
case 2:
|
||||
options.hard_pending_compaction_bytes_limit = l0_size;
|
||||
options.soft_pending_compaction_bytes_limit = l0_size;
|
||||
break;
|
||||
case 3:
|
||||
options.soft_pending_compaction_bytes_limit = l0_size;
|
||||
break;
|
||||
}
|
||||
Reopen(options);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:1",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"DBImpl::BackgroundCompaction():BeforePickCompaction",
|
||||
"DBOptionsTest::EnableAutoCompactionAndTriggerStall:2"},
|
||||
{"DBOptionsTest::EnableAutoCompactionAndTriggerStall:3",
|
||||
"DBImpl::BackgroundCompaction():AfterPickCompaction"}});
|
||||
// Block background compaction.
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
switch (method_type) {
|
||||
case 0:
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_OK(dbfull()->EnableAutoCompaction(
|
||||
{dbfull()->DefaultColumnFamily()}));
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:1");
|
||||
// Wait for stall condition recalculate.
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:2");
|
||||
|
||||
switch (option_type) {
|
||||
case 0:
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
break;
|
||||
case 1:
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
break;
|
||||
case 2:
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().IsStopped());
|
||||
break;
|
||||
case 3:
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_TRUE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
break;
|
||||
}
|
||||
TEST_SYNC_POINT("DBOptionsTest::EnableAutoCompactionAndTriggerStall:3");
|
||||
|
||||
// Background compaction executed.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().IsStopped());
|
||||
ASSERT_FALSE(dbfull()->TEST_write_controler().NeedsDelay());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetOptionsMayTriggerCompaction) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 1000;
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// Need to insert two keys to avoid trivial move.
|
||||
ASSERT_OK(Put("foo", ToString(i)));
|
||||
ASSERT_OK(Put("bar", ToString(i)));
|
||||
Flush();
|
||||
}
|
||||
ASSERT_EQ("3", FilesPerLevel());
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "3"}}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ("0,1", FilesPerLevel());
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBackgroundCompactionThreads) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.base_background_compactions = 1; // default value
|
||||
options.max_background_compactions = 1; // default value
|
||||
Reopen(options);
|
||||
ASSERT_EQ(1, dbfull()->TEST_BGCompactionsAllowed());
|
||||
ASSERT_OK(dbfull()->SetDBOptions({{"base_background_compactions", "2"},
|
||||
{"max_background_compactions", "3"}}));
|
||||
ASSERT_EQ(2, dbfull()->TEST_BGCompactionsAllowed());
|
||||
auto stop_token = dbfull()->TEST_write_controler().GetStopToken();
|
||||
ASSERT_EQ(3, dbfull()->TEST_BGCompactionsAllowed());
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -557,9 +557,9 @@ TEST_F(DBPropertiesTest, NumImmutableMemTable) {
|
||||
ASSERT_EQ(num, "3");
|
||||
ASSERT_TRUE(dbfull()->GetProperty(
|
||||
handles_[1], "rocksdb.cur-size-active-mem-table", &num));
|
||||
// "192" is the size of the metadata of an empty skiplist, this would
|
||||
// "384" is the size of the metadata of two empty skiplists, this would
|
||||
// break if we change the default skiplist implementation
|
||||
ASSERT_EQ(num, "192");
|
||||
ASSERT_EQ(num, "384");
|
||||
|
||||
uint64_t int_num;
|
||||
uint64_t base_total_size;
|
||||
@@ -594,7 +594,8 @@ TEST_F(DBPropertiesTest, NumImmutableMemTable) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, GetProperty) {
|
||||
// TODO(techdept) : Disabled flaky test #12863555
|
||||
TEST_F(DBPropertiesTest, DISABLED_GetProperty) {
|
||||
// Set sizes to both background thread pool to be 1 and block them.
|
||||
env_->SetBackgroundThreads(1, Env::HIGH);
|
||||
env_->SetBackgroundThreads(1, Env::LOW);
|
||||
|
||||
+46
-977
File diff suppressed because it is too large.
Load diff
@@ -138,14 +138,14 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.write_buffer_size = 4096;
|
||||
options.max_write_buffer_number = 8;
|
||||
options.max_write_buffer_number = 3;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.level0_slowdown_writes_trigger = 2;
|
||||
options.level0_stop_writes_trigger = 4;
|
||||
options.target_file_size_base = 2048;
|
||||
options.max_bytes_for_level_base = 10240;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
options.soft_rate_limit = 1.1;
|
||||
options.hard_pending_compaction_bytes_limit = 16 * 1024;
|
||||
options.num_levels = 8;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
@@ -155,6 +155,12 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
|
||||
Flush();
|
||||
}
|
||||
|
||||
db_->PauseBackgroundWork();
|
||||
|
||||
// Ensure that we have at least L0, L1 and L2
|
||||
|
||||
@@ -699,6 +699,58 @@ TEST_F(DBTestTailingIterator, ForwardIteratorVersionProperty) {
|
||||
}
|
||||
ASSERT_EQ(v3, v4);
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
const Slice upper_bound("cc", 3);
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
|
||||
|
||||
// 1st L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// 2nd L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
|
||||
iter->Seek("aa");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
||||
ReadOptions read_options;
|
||||
read_options.tailing = true;
|
||||
const Slice upper_bound("cc", 3);
|
||||
read_options.iterate_upper_bound = &upper_bound;
|
||||
|
||||
|
||||
// 1st L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "aa", "SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// 2nd L0 file
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "zz", "NOT-SEEN"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().ToString(), "aa");
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
+28
-24
@@ -1193,7 +1193,7 @@ bool MinLevelToCompress(CompressionType& type, Options& options, int wbits,
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTDNotFinalCompression;
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
@@ -2646,15 +2646,11 @@ class ModelDB : public DB {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
using DB::AddFile;
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::vector<ExternalSstFileInfo>& file_info_list,
|
||||
bool move_file) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
virtual Status AddFile(ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& file_path_list,
|
||||
bool move_file) override {
|
||||
using DB::IngestExternalFile;
|
||||
virtual Status IngestExternalFile(
|
||||
ColumnFamilyHandle* column_family,
|
||||
const std::vector<std::string>& external_files,
|
||||
const IngestExternalFileOptions& options) override {
|
||||
return Status::NotSupported("Not implemented.");
|
||||
}
|
||||
|
||||
@@ -2762,6 +2758,12 @@ class ModelDB : public DB {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
virtual Status SetDBOptions(
|
||||
const std::unordered_map<std::string, std::string>& new_options)
|
||||
override {
|
||||
return Status::NotSupported("Not supported operation.");
|
||||
}
|
||||
|
||||
using DB::CompactFiles;
|
||||
virtual Status CompactFiles(const CompactionOptions& compact_options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
@@ -2806,13 +2808,12 @@ class ModelDB : public DB {
|
||||
virtual Env* GetEnv() const override { return nullptr; }
|
||||
|
||||
using DB::GetOptions;
|
||||
virtual const Options& GetOptions(
|
||||
ColumnFamilyHandle* column_family) const override {
|
||||
virtual Options GetOptions(ColumnFamilyHandle* column_family) const override {
|
||||
return options_;
|
||||
}
|
||||
|
||||
using DB::GetDBOptions;
|
||||
virtual const DBOptions& GetDBOptions() const override { return options_; }
|
||||
virtual DBOptions GetDBOptions() const override { return options_; }
|
||||
|
||||
using DB::Flush;
|
||||
virtual Status Flush(const rocksdb::FlushOptions& options,
|
||||
@@ -2882,6 +2883,10 @@ class ModelDB : public DB {
|
||||
virtual void Seek(const Slice& k) override {
|
||||
iter_ = map_->lower_bound(k.ToString());
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
iter_ = map_->upper_bound(k.ToString());
|
||||
Prev();
|
||||
}
|
||||
virtual void Next() override { ++iter_; }
|
||||
virtual void Prev() override {
|
||||
if (iter_ == map_->begin()) {
|
||||
@@ -4271,10 +4276,8 @@ TEST_F(DBTest, DynamicCompactionOptions) {
|
||||
options.level0_file_num_compaction_trigger = 3;
|
||||
options.level0_slowdown_writes_trigger = 4;
|
||||
options.level0_stop_writes_trigger = 8;
|
||||
options.max_grandparent_overlap_factor = 10;
|
||||
options.expanded_compaction_factor = 25;
|
||||
options.source_compaction_factor = 1;
|
||||
options.target_file_size_base = k64KB;
|
||||
options.max_compaction_bytes = options.target_file_size_base * 10;
|
||||
options.target_file_size_multiplier = 1;
|
||||
options.max_bytes_for_level_base = k128KB;
|
||||
options.max_bytes_for_level_multiplier = 4;
|
||||
@@ -4710,7 +4713,7 @@ TEST_F(DBTest, CompressionStatsTest) {
|
||||
type = kXpressCompression;
|
||||
fprintf(stderr, "using xpress\n");
|
||||
} else if (ZSTD_Supported()) {
|
||||
type = kZSTDNotFinalCompression;
|
||||
type = kZSTD;
|
||||
fprintf(stderr, "using ZSTD\n");
|
||||
} else {
|
||||
fprintf(stderr, "skipping test, compression disabled\n");
|
||||
@@ -4720,7 +4723,7 @@ TEST_F(DBTest, CompressionStatsTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = type;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
options.statistics->stats_level_ = StatsLevel::kAll;
|
||||
options.statistics->stats_level_ = StatsLevel::kExceptTimeForMutex;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int kNumKeysWritten = 100000;
|
||||
@@ -4731,6 +4734,7 @@ TEST_F(DBTest, CompressionStatsTest) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_GT(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED), 0);
|
||||
|
||||
for (int i = 0; i < kNumKeysWritten; ++i) {
|
||||
@@ -4750,6 +4754,7 @@ TEST_F(DBTest, CompressionStatsTest) {
|
||||
// compressible string
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 128) + std::string(128, 'a')));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NUMBER_BLOCK_COMPRESSED)
|
||||
- currentCompressions, 0);
|
||||
|
||||
@@ -4848,12 +4853,11 @@ class DelayedMergeOperator : public MergeOperator {
|
||||
|
||||
public:
|
||||
explicit DelayedMergeOperator(DBTest* d) : db_test_(d) {}
|
||||
virtual bool FullMerge(const Slice& key, const Slice* existing_value,
|
||||
const std::deque<std::string>& operand_list,
|
||||
std::string* new_value,
|
||||
Logger* logger) const override {
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
db_test_->env_->addon_time_.fetch_add(1000);
|
||||
*new_value = "";
|
||||
merge_out->new_value = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5022,7 +5026,7 @@ TEST_F(DBTest, SuggestCompactRangeTest) {
|
||||
options.compression = kNoCompression;
|
||||
options.max_bytes_for_level_base = 450 << 10;
|
||||
options.target_file_size_base = 98 << 10;
|
||||
options.max_grandparent_overlap_factor = 1 << 20; // inf
|
||||
options.max_compaction_bytes = static_cast<uint64_t>(1) << 60; // inf
|
||||
|
||||
Reopen(options);
|
||||
|
||||
|
||||
+713
-32
@@ -961,9 +961,9 @@ TEST_F(DBTest2, PresetCompressionDict) {
|
||||
compression_types.push_back(kLZ4Compression);
|
||||
compression_types.push_back(kLZ4HCCompression);
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
#if ZSTD_VERSION_NUMBER >= 500 // v0.5.0+
|
||||
compression_types.push_back(kZSTDNotFinalCompression);
|
||||
#endif // ZSTD_VERSION_NUMBER >= 500
|
||||
if (ZSTD_Supported()) {
|
||||
compression_types.push_back(kZSTD);
|
||||
}
|
||||
|
||||
for (auto compression_type : compression_types) {
|
||||
options.compression = compression_type;
|
||||
@@ -1210,6 +1210,37 @@ class PinL0IndexAndFilterBlocksTest : public DBTestBase,
|
||||
PinL0IndexAndFilterBlocksTest() : DBTestBase("/db_pin_l0_index_bloom_test") {}
|
||||
virtual void SetUp() override { infinite_max_files_ = GetParam(); }
|
||||
|
||||
void CreateTwoLevels(Options* options) {
|
||||
if (infinite_max_files_) {
|
||||
options->max_open_files = -1;
|
||||
}
|
||||
options->create_if_missing = true;
|
||||
options->statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options->table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, *options);
|
||||
|
||||
Put(1, "a", "begin");
|
||||
Put(1, "z", "end");
|
||||
ASSERT_OK(Flush(1));
|
||||
// move this table to L1
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
|
||||
// reset block cache
|
||||
table_options.block_cache = NewLRUCache(64 * 1024);
|
||||
options->table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, *options);
|
||||
// create new table at L0
|
||||
Put(1, "a2", "begin2");
|
||||
Put(1, "z2", "end2");
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
}
|
||||
|
||||
bool infinite_max_files_;
|
||||
};
|
||||
|
||||
@@ -1261,35 +1292,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
MultiLevelIndexAndFilterBlocksCachedWithPinning) {
|
||||
Options options = CurrentOptions();
|
||||
if (infinite_max_files_) {
|
||||
options.max_open_files = -1;
|
||||
}
|
||||
options.create_if_missing = true;
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
Put(1, "a", "begin");
|
||||
Put(1, "z", "end");
|
||||
ASSERT_OK(Flush(1));
|
||||
// move this table to L1
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
|
||||
// reset block cache
|
||||
table_options.block_cache = NewLRUCache(64 * 1024);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// create new table at L0
|
||||
Put(1, "a2", "begin2");
|
||||
Put(1, "z2", "end2");
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options);
|
||||
// get base cache values
|
||||
uint64_t fm = TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t fh = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
@@ -1309,14 +1312,125 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
// the file is opened, prefetching results in a cache filter miss
|
||||
// the block is loaded and added to the cache,
|
||||
// then the get results in a cache hit for L1
|
||||
// When we have inifinite max_files, there is still cache miss because we have
|
||||
// reset the block cache
|
||||
value = Get(1, "a");
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(im + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
}
|
||||
|
||||
TEST_P(PinL0IndexAndFilterBlocksTest, DisablePrefetchingNonL0IndexAndFilter) {
|
||||
Options options = CurrentOptions();
|
||||
PinL0IndexAndFilterBlocksTest::CreateTwoLevels(&options);
|
||||
|
||||
// Get base cache values
|
||||
uint64_t fm = TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS);
|
||||
uint64_t fh = TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT);
|
||||
uint64_t im = TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS);
|
||||
uint64_t ih = TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT);
|
||||
|
||||
// Reopen database. If max_open_files is set as -1, table readers will be
|
||||
// preloaded. This will trigger a BlockBasedTable::Open() and prefetch
|
||||
// L0 index and filter. Level 1's prefetching is disabled in DB::Open()
|
||||
TryReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
if (infinite_max_files_) {
|
||||
// After reopen, cache miss are increased by one because we read (and only
|
||||
// read) filter and index on L0
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
// If max_open_files is not -1, we do not preload table readers, so there is
|
||||
// no change.
|
||||
ASSERT_EQ(fm, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
std::string value;
|
||||
// this should be read from L0
|
||||
value = Get(1, "a2");
|
||||
// If max_open_files is -1, we have pinned index and filter in Rep, so there
|
||||
// will not be changes in index and filter misses or hits. If max_open_files
|
||||
// is not -1, Get() will open a TableReader and prefetch index and filter.
|
||||
ASSERT_EQ(fm + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
// this should be read from L1
|
||||
value = Get(1, "a");
|
||||
if (infinite_max_files_) {
|
||||
// In inifinite max files case, there's a cache miss in executing Get()
|
||||
// because index and filter are not prefetched before.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
} else {
|
||||
// In this case, cache miss will be increased by one in
|
||||
// BlockBasedTable::Open() because this is not in DB::Open() code path so we
|
||||
// will prefetch L1's index and filter. Cache hit will also be increased by
|
||||
// one because Get() will read index and filter from the block cache
|
||||
// prefetched in previous Open() call.
|
||||
ASSERT_EQ(fm + 2, TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(fh + 1, TestGetTickerCount(options, BLOCK_CACHE_FILTER_HIT));
|
||||
ASSERT_EQ(im + 2, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(ih + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(PinL0IndexAndFilterBlocksTest,
|
||||
PinL0IndexAndFilterBlocksTest, ::testing::Bool());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, MaxCompactionBytesTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.memtable_factory.reset(
|
||||
new SpecialSkipListFactory(DBTestBase::kNumKeysByGenerateNewRandomFile));
|
||||
options.compaction_style = kCompactionStyleLevel;
|
||||
options.write_buffer_size = 200 << 10;
|
||||
options.arena_block_size = 4 << 10;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
options.num_levels = 4;
|
||||
options.compression = kNoCompression;
|
||||
options.max_bytes_for_level_base = 450 << 10;
|
||||
options.target_file_size_base = 100 << 10;
|
||||
// Infinite for full compaction.
|
||||
options.max_compaction_bytes = options.target_file_size_base * 100;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
|
||||
for (int num = 0; num < 8; num++) {
|
||||
GenerateNewRandomFile(&rnd);
|
||||
}
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_EQ("0,0,8", FilesPerLevel(0));
|
||||
|
||||
// When compact from Ln -> Ln+1, cut a file if the file overlaps with
|
||||
// more than three files in Ln+1.
|
||||
options.max_compaction_bytes = options.target_file_size_base * 3;
|
||||
Reopen(options);
|
||||
|
||||
GenerateNewRandomFile(&rnd);
|
||||
// Add three more small files that overlap with the previous file
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Put("a", "z");
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Output files to L1 are cut to three pieces, according to
|
||||
// options.max_compaction_bytes
|
||||
ASSERT_EQ("0,3,8", FilesPerLevel(0));
|
||||
}
|
||||
|
||||
static void UniqueIdCallback(void* arg) {
|
||||
int* result = reinterpret_cast<int*>(arg);
|
||||
if (*result == -1) {
|
||||
@@ -1488,6 +1602,293 @@ TEST_F(DBTest2, SyncPointMarker) {
|
||||
}
|
||||
#endif
|
||||
|
||||
class MergeOperatorPinningTest : public DBTest2,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
MergeOperatorPinningTest() { disable_block_cache_ = GetParam(); }
|
||||
|
||||
bool disable_block_cache_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(MergeOperatorPinningTest, MergeOperatorPinningTest,
|
||||
::testing::Bool());
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(MergeOperatorPinningTest, OperandsMultiBlocks) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1; // every block will contain one entry
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.merge_operator = MergeOperators::CreateStringAppendTESTOperator();
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kKeysPerFile = 10;
|
||||
const int kOperandsPerKeyPerFile = 7;
|
||||
const int kOperandSize = 100;
|
||||
// Filse to write in L0 before compacting to lower level
|
||||
const int kFilesPerLevel = 3;
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
int batch_num = 1;
|
||||
int lvl_to_fill = 4;
|
||||
int key_id = 0;
|
||||
while (true) {
|
||||
for (int j = 0; j < kKeysPerFile; j++) {
|
||||
std::string key = Key(key_id % 35);
|
||||
key_id++;
|
||||
for (int k = 0; k < kOperandsPerKeyPerFile; k++) {
|
||||
std::string val = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, val));
|
||||
if (true_data[key].size() == 0) {
|
||||
true_data[key] = val;
|
||||
} else {
|
||||
true_data[key] += "," + val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lvl_to_fill == -1) {
|
||||
// Keep last batch in memtable and stop
|
||||
break;
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
if (batch_num % kFilesPerLevel == 0) {
|
||||
if (lvl_to_fill != 0) {
|
||||
MoveFilesToLevel(lvl_to_fill);
|
||||
}
|
||||
lvl_to_fill--;
|
||||
}
|
||||
batch_num++;
|
||||
}
|
||||
|
||||
// 3 L0 files
|
||||
// 1 L1 file
|
||||
// 3 L2 files
|
||||
// 1 L3 file
|
||||
// 3 L4 Files
|
||||
ASSERT_EQ(FilesPerLevel(), "3,1,3,1,3");
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
}
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, Randomized) {
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
std::map<std::string, std::string> true_data;
|
||||
|
||||
const int kTotalMerges = 10000;
|
||||
// Every key gets ~10 operands
|
||||
const int kKeyRange = kTotalMerges / 10;
|
||||
const int kOperandSize = 20;
|
||||
const int kNumPutBefore = kKeyRange / 10; // 10% value
|
||||
const int kNumPutAfter = kKeyRange / 10; // 10% overwrite
|
||||
const int kNumDelete = kKeyRange / 10; // 10% delete
|
||||
|
||||
// kNumPutBefore keys will have base values
|
||||
for (int i = 0; i < kNumPutBefore; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Do kTotalMerges merges
|
||||
for (int i = 0; i < kTotalMerges; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), key, value));
|
||||
|
||||
if (true_data[key] < value) {
|
||||
true_data[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite random kNumPutAfter keys
|
||||
for (int i = 0; i < kNumPutAfter; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
std::string value = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
true_data[key] = value;
|
||||
}
|
||||
|
||||
// Delete random kNumDelete keys
|
||||
for (int i = 0; i < kNumDelete; i++) {
|
||||
std::string key = Key(rnd.Next() % kKeyRange);
|
||||
ASSERT_OK(db_->Delete(WriteOptions(), key));
|
||||
|
||||
true_data.erase(key);
|
||||
}
|
||||
|
||||
VerifyDBFromMap(true_data);
|
||||
|
||||
// Skip HashCuckoo since it does not support merge operators
|
||||
} while (ChangeOptions(kSkipMergePut | kSkipHashCuckoo));
|
||||
}
|
||||
|
||||
class MergeOperatorHook : public MergeOperator {
|
||||
public:
|
||||
explicit MergeOperatorHook(std::shared_ptr<MergeOperator> _merge_op)
|
||||
: merge_op_(_merge_op) {}
|
||||
|
||||
virtual bool FullMergeV2(const MergeOperationInput& merge_in,
|
||||
MergeOperationOutput* merge_out) const override {
|
||||
before_merge_();
|
||||
bool res = merge_op_->FullMergeV2(merge_in, merge_out);
|
||||
after_merge_();
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual const char* Name() const override { return merge_op_->Name(); }
|
||||
|
||||
std::shared_ptr<MergeOperator> merge_op_;
|
||||
std::function<void()> before_merge_ = []() {};
|
||||
std::function<void()> after_merge_ = []() {};
|
||||
};
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, EvictCacheBeforeMerge) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
auto merge_hook =
|
||||
std::make_shared<MergeOperatorHook>(MergeOperators::CreateMaxOperator());
|
||||
options.merge_operator = merge_hook;
|
||||
options.disable_auto_compactions = true;
|
||||
options.level0_slowdown_writes_trigger = (1 << 30);
|
||||
options.level0_stop_writes_trigger = (1 << 30);
|
||||
options.max_open_files = 20;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
if (bbto.no_block_cache == false) {
|
||||
bbto.block_cache = NewLRUCache(64 * 1024 * 1024);
|
||||
} else {
|
||||
bbto.block_cache = nullptr;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 30;
|
||||
const int kNumKeys = 1000;
|
||||
const int kOperandSize = 100;
|
||||
Random rnd(301);
|
||||
|
||||
// 1000 keys every key have 30 operands, every operand is in a different file
|
||||
std::map<std::string, std::string> true_data;
|
||||
for (int i = 0; i < kNumOperands; i++) {
|
||||
for (int j = 0; j < kNumKeys; j++) {
|
||||
std::string k = Key(j);
|
||||
std::string v = RandomString(&rnd, kOperandSize);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), k, v));
|
||||
|
||||
true_data[k] = std::max(true_data[k], v);
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
std::vector<uint64_t> file_numbers = ListTableFiles(env_, dbname_);
|
||||
ASSERT_EQ(file_numbers.size(), kNumOperands);
|
||||
int merge_cnt = 0;
|
||||
|
||||
// Code executed before merge operation
|
||||
merge_hook->before_merge_ = [&]() {
|
||||
// Evict all tables from cache before every merge operation
|
||||
for (uint64_t num : file_numbers) {
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), num);
|
||||
}
|
||||
// Decrease cache capacity to force all unrefed blocks to be evicted
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(1);
|
||||
}
|
||||
merge_cnt++;
|
||||
};
|
||||
|
||||
// Code executed after merge operation
|
||||
merge_hook->after_merge_ = [&]() {
|
||||
// Increase capacity again after doing the merge
|
||||
if (bbto.block_cache) {
|
||||
bbto.block_cache->SetCapacity(64 * 1024 * 1024);
|
||||
}
|
||||
};
|
||||
|
||||
size_t total_reads;
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
ASSERT_EQ(merge_cnt, total_reads);
|
||||
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
}
|
||||
|
||||
TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
Options options = CurrentOptions();
|
||||
options.merge_operator = MergeOperators::CreateMaxOperator();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = disable_block_cache_;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumOperands = 100;
|
||||
const int kNumWrites = 100000;
|
||||
|
||||
std::function<void()> writer_func = [&]() {
|
||||
int k = 0;
|
||||
for (int i = 0; i < kNumWrites; i++) {
|
||||
db_->Merge(WriteOptions(), Key(k), Key(k));
|
||||
|
||||
if (i && i % kNumOperands == 0) {
|
||||
k++;
|
||||
}
|
||||
if (i && i % 127 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
if (i && i % 317 == 0) {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::function<void()> reader_func = [&]() {
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < (kNumWrites / kNumOperands); i++) {
|
||||
while (!iter->Valid()) {
|
||||
// wait for the key to be written
|
||||
env_->SleepForMicroseconds(100);
|
||||
iter->Seek(Key(i));
|
||||
}
|
||||
ASSERT_EQ(iter->key(), Key(i));
|
||||
ASSERT_EQ(iter->value(), Key(i));
|
||||
|
||||
iter->Next();
|
||||
}
|
||||
|
||||
delete iter;
|
||||
};
|
||||
|
||||
std::thread writer_thread(writer_func);
|
||||
std::thread reader_thread(reader_func);
|
||||
|
||||
writer_thread.join();
|
||||
reader_thread.join();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, MaxSuccessiveMergesInRecovery) {
|
||||
Options options;
|
||||
options = CurrentOptions(options);
|
||||
@@ -1504,6 +1905,286 @@ TEST_F(DBTest2, MaxSuccessiveMergesInRecovery) {
|
||||
options.max_successive_merges = 3;
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
size_t GetEncodedEntrySize(size_t key_size, size_t value_size) {
|
||||
std::string buffer;
|
||||
|
||||
PutVarint32(&buffer, static_cast<uint32_t>(0));
|
||||
PutVarint32(&buffer, static_cast<uint32_t>(key_size));
|
||||
PutVarint32(&buffer, static_cast<uint32_t>(value_size));
|
||||
|
||||
return buffer.size() + key_size + value_size;
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, ReadAmpBitmap) {
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions bbto;
|
||||
// Disable delta encoding to make it easier to calculate read amplification
|
||||
bbto.use_delta_encoding = false;
|
||||
// Huge block cache to make it easier to calculate read amplification
|
||||
bbto.block_cache = NewLRUCache(1024 * 1024 * 1024);
|
||||
bbto.read_amp_bytes_per_bit = 16;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.statistics = rocksdb::CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kNumEntries = 10000;
|
||||
|
||||
Random rnd(301);
|
||||
for (size_t i = 0; i < kNumEntries; i++) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(i)), RandomString(&rnd, 100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Close();
|
||||
Reopen(options);
|
||||
|
||||
// Read keys/values randomly and verify that reported read amp error
|
||||
// is less than 2%
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
for (size_t i = 0; i < kNumEntries * 5; i++) {
|
||||
int key_idx = rnd.Next() % kNumEntries;
|
||||
std::string k = Key(key_idx);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), k, &value));
|
||||
|
||||
if (read_keys.find(key_idx) == read_keys.end()) {
|
||||
auto ik = InternalKey(k, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes += GetEncodedEntrySize(ik.size(), value.size());
|
||||
read_keys.insert(key_idx);
|
||||
}
|
||||
|
||||
double expected_read_amp =
|
||||
static_cast<double>(total_useful_bytes) /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
double read_amp =
|
||||
static_cast<double>(options.statistics->getTickerCount(
|
||||
READ_AMP_ESTIMATE_USEFUL_BYTES)) /
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
double error_pct = fabs(expected_read_amp - read_amp) * 100;
|
||||
// Error between reported read amp and real read amp should be less than 2%
|
||||
EXPECT_LE(error_pct, 2);
|
||||
}
|
||||
|
||||
// Make sure we read every thing in the DB (which is smaller than our cache)
|
||||
Iterator* iter = db_->NewIterator(ReadOptions());
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ASSERT_EQ(iter->value().ToString(), Get(iter->key().ToString()));
|
||||
}
|
||||
delete iter;
|
||||
|
||||
// Read amp is 100% since we read all what we loaded in memory
|
||||
ASSERT_EQ(options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES),
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
if (dbname_.find("dev/shm") != std::string::npos) {
|
||||
// /dev/shm dont support getting a unique file id, this mean that
|
||||
// running this test on /dev/shm will fail because lru_cache will load
|
||||
// the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> lru_cache = NewLRUCache(1024 * 1024 * 1024);
|
||||
std::shared_ptr<Statistics> stats = rocksdb::CreateDBStatistics();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
BlockBasedTableOptions bbto;
|
||||
// Disable delta encoding to make it easier to calculate read amplification
|
||||
bbto.use_delta_encoding = false;
|
||||
// Huge block cache to make it easier to calculate read amplification
|
||||
bbto.block_cache = lru_cache;
|
||||
bbto.read_amp_bytes_per_bit = 16;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.statistics = stats;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const int kNumEntries = 10000;
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumEntries; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 100)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Close();
|
||||
Reopen(options);
|
||||
|
||||
uint64_t total_useful_bytes = 0;
|
||||
std::set<int> read_keys;
|
||||
std::string value;
|
||||
// Iter1: Read half the DB, Read even keys
|
||||
// Key(0), Key(2), Key(4), Key(6), Key(8), ...
|
||||
for (int i = 0; i < kNumEntries; i += 2) {
|
||||
std::string k = Key(i);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), k, &value));
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto ik = InternalKey(k, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes += GetEncodedEntrySize(ik.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_useful_bytes_iter1 =
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
|
||||
size_t total_loaded_bytes_iter1 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
Close();
|
||||
std::shared_ptr<Statistics> new_statistics = rocksdb::CreateDBStatistics();
|
||||
// Destroy old statistics obj that the blocks in lru_cache are pointing to
|
||||
options.statistics.reset();
|
||||
// Use the statistics object that we just created
|
||||
options.statistics = new_statistics;
|
||||
Reopen(options);
|
||||
|
||||
// Iter2: Read half the DB, Read odd keys
|
||||
// Key(1), Key(3), Key(5), Key(7), Key(9), ...
|
||||
for (int i = 1; i < kNumEntries; i += 2) {
|
||||
std::string k = Key(i);
|
||||
ASSERT_OK(db_->Get(ReadOptions(), k, &value));
|
||||
|
||||
if (read_keys.find(i) == read_keys.end()) {
|
||||
auto ik = InternalKey(k, 0, ValueType::kTypeValue);
|
||||
total_useful_bytes += GetEncodedEntrySize(ik.size(), value.size());
|
||||
read_keys.insert(i);
|
||||
}
|
||||
}
|
||||
|
||||
size_t total_useful_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_ESTIMATE_USEFUL_BYTES);
|
||||
size_t total_loaded_bytes_iter2 =
|
||||
options.statistics->getTickerCount(READ_AMP_TOTAL_READ_BYTES);
|
||||
|
||||
// We reached read_amp of 100% because we read all the keys in the DB
|
||||
ASSERT_EQ(total_useful_bytes_iter1 + total_useful_bytes_iter2,
|
||||
total_loaded_bytes_iter1 + total_loaded_bytes_iter2);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, AutomaticCompactionOverlapManualCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 3;
|
||||
options.IncreaseParallelism(20);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "a"));
|
||||
ASSERT_OK(Put(Key(5), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(10), "a"));
|
||||
ASSERT_OK(Put(Key(15), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
// Trivial move 2 files to L2
|
||||
ASSERT_EQ("0,0,2", FilesPerLevel());
|
||||
|
||||
// While the compaction is running, we will create 2 new files that
|
||||
// can fit in L2, these 2 files will be moved to L2 and overlap with
|
||||
// the running compaction and break the LSM consistency.
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start", [&](void* arg) {
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"},
|
||||
{"max_bytes_for_level_base", "1"}}));
|
||||
ASSERT_OK(Put(Key(6), "a"));
|
||||
ASSERT_OK(Put(Key(7), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(8), "a"));
|
||||
ASSERT_OK(Put(Key(9), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Run a manual compaction that will compact the 2 files in L2
|
||||
// into 1 file in L2
|
||||
cro.exclusive_manual_compaction = false;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, ManualCompactionOverlapManualCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 2;
|
||||
options.IncreaseParallelism(20);
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "a"));
|
||||
ASSERT_OK(Put(Key(5), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(10), "a"));
|
||||
ASSERT_OK(Put(Key(15), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// Trivial move 2 files to L1
|
||||
ASSERT_EQ("0,2", FilesPerLevel());
|
||||
|
||||
std::function<void()> bg_manual_compact = [&]() {
|
||||
std::string k1 = Key(6);
|
||||
std::string k2 = Key(9);
|
||||
Slice k1s(k1);
|
||||
Slice k2s(k2);
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(db_->CompactRange(cro, &k1s, &k2s));
|
||||
};
|
||||
std::thread bg_thread;
|
||||
|
||||
// While the compaction is running, we will create 2 new files that
|
||||
// can fit in L1, these 2 files will be moved to L1 and overlap with
|
||||
// the running compaction and break the LSM consistency.
|
||||
std::atomic<bool> flag(false);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():Start", [&](void* arg) {
|
||||
if (flag.exchange(true)) {
|
||||
// We want to make sure to call this callback only once
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(Put(Key(6), "a"));
|
||||
ASSERT_OK(Put(Key(7), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(Key(8), "a"));
|
||||
ASSERT_OK(Put(Key(9), "a"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Start a non-exclusive manual compaction in a bg thread
|
||||
bg_thread = std::thread(bg_manual_compact);
|
||||
// This manual compaction conflict with the other manual compaction
|
||||
// so it should wait until the first compaction finish
|
||||
env_->SleepForMicroseconds(1000000);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Run a manual compaction that will compact the 2 files in L1
|
||||
// into 1 file in L1
|
||||
CompactRangeOptions cro;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
bg_thread.join();
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+85
-23
@@ -30,6 +30,8 @@ SpecialEnv::SpecialEnv(Env* base)
|
||||
manifest_write_error_.store(false, std::memory_order_release);
|
||||
log_write_error_.store(false, std::memory_order_release);
|
||||
random_file_open_counter_.store(0, std::memory_order_relaxed);
|
||||
delete_count_.store(0, std::memory_order_relaxed);
|
||||
num_open_wal_file_.store(0);
|
||||
log_write_slowdown_ = 0;
|
||||
bytes_written_ = 0;
|
||||
sync_counter_ = 0;
|
||||
@@ -730,7 +732,7 @@ double DBTestBase::CompressionRatioAtLevel(int level, int cf) {
|
||||
|
||||
int DBTestBase::TotalTableFiles(int cf, int levels) {
|
||||
if (levels == -1) {
|
||||
levels = CurrentOptions().num_levels;
|
||||
levels = (cf == 0) ? db_->NumberLevels() : db_->NumberLevels(handles_[1]);
|
||||
}
|
||||
int result = 0;
|
||||
for (int level = 0; level < levels; level++) {
|
||||
@@ -1074,36 +1076,96 @@ std::vector<std::uint64_t> DBTestBase::ListTableFiles(Env* env,
|
||||
return file_numbers;
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void DBTestBase::VerifyDBFromMap(std::map<std::string, std::string> true_data,
|
||||
size_t* total_reads_res, bool tailing_iter) {
|
||||
size_t total_reads = 0;
|
||||
|
||||
Status DBTestBase::GenerateAndAddExternalFile(const Options options,
|
||||
std::vector<int> keys,
|
||||
size_t file_id) {
|
||||
std::string file_path =
|
||||
test::TmpDir(env_) + "/sst_files/" + ToString(file_id);
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options, options.comparator);
|
||||
|
||||
Status s = sst_file_writer.Open(file_path);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
for (auto& kv : true_data) {
|
||||
ASSERT_EQ(Get(kv.first), kv.second);
|
||||
total_reads++;
|
||||
}
|
||||
for (auto& entry : keys) {
|
||||
std::string k = Key(entry);
|
||||
std::string v = k + ToString(file_id);
|
||||
s = sst_file_writer.Add(k, v);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
|
||||
// Normal Iterator
|
||||
{
|
||||
int iter_cnt = 0;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
// Verify Iterator::Next()
|
||||
iter_cnt = 0;
|
||||
auto data_iter = true_data.begin();
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
}
|
||||
s = sst_file_writer.Finish();
|
||||
ASSERT_EQ(data_iter, true_data.end()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
|
||||
if (s.ok()) {
|
||||
s = db_->AddFile(std::vector<std::string>(1, file_path));
|
||||
// Verify Iterator::Prev()
|
||||
iter_cnt = 0;
|
||||
auto data_rev = true_data.rbegin();
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev(), data_rev++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_rev->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_rev->second);
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_rev, true_data.rend()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
|
||||
// Verify Iterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
iter->Seek(kv.first);
|
||||
ASSERT_EQ(kv.first, iter->key().ToString());
|
||||
ASSERT_EQ(kv.second, iter->value().ToString());
|
||||
total_reads++;
|
||||
}
|
||||
|
||||
delete iter;
|
||||
}
|
||||
|
||||
return s;
|
||||
if (tailing_iter) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Tailing iterator
|
||||
int iter_cnt = 0;
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
ro.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
// Verify ForwardIterator::Next()
|
||||
iter_cnt = 0;
|
||||
auto data_iter = true_data.begin();
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), data_iter++) {
|
||||
ASSERT_EQ(iter->key().ToString(), data_iter->first);
|
||||
ASSERT_EQ(iter->value().ToString(), data_iter->second);
|
||||
iter_cnt++;
|
||||
total_reads++;
|
||||
}
|
||||
ASSERT_EQ(data_iter, true_data.end()) << iter_cnt << " / "
|
||||
<< true_data.size();
|
||||
|
||||
// Verify ForwardIterator::Seek()
|
||||
for (auto kv : true_data) {
|
||||
iter->Seek(kv.first);
|
||||
ASSERT_EQ(kv.first, iter->key().ToString());
|
||||
ASSERT_EQ(kv.second, iter->value().ToString());
|
||||
total_reads++;
|
||||
}
|
||||
|
||||
delete iter;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
if (total_reads_res) {
|
||||
*total_reads_res = total_reads;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
uint64_t DBTestBase::GetNumberOfSstFilesForColumnFamily(
|
||||
DB* db, std::string column_family_name) {
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
|
||||
+31
-6
@@ -221,7 +221,7 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Drop writes on the floor
|
||||
return Status::OK();
|
||||
} else if (env_->no_space_.load(std::memory_order_acquire)) {
|
||||
return Status::IOError("No space left on device");
|
||||
return Status::NoSpace("No space left on device");
|
||||
} else {
|
||||
env_->bytes_written_ += data.size();
|
||||
return base_->Append(data);
|
||||
@@ -285,7 +285,10 @@ class SpecialEnv : public EnvWrapper {
|
||||
class WalFile : public WritableFile {
|
||||
public:
|
||||
WalFile(SpecialEnv* env, unique_ptr<WritableFile>&& b)
|
||||
: env_(env), base_(std::move(b)) {}
|
||||
: env_(env), base_(std::move(b)) {
|
||||
env_->num_open_wal_file_.fetch_add(1);
|
||||
}
|
||||
virtual ~WalFile() { env_->num_open_wal_file_.fetch_add(-1); }
|
||||
Status Append(const Slice& data) override {
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_SYNC_POINT("SpecialEnv::WalFile::Append:1");
|
||||
@@ -307,7 +310,18 @@ class SpecialEnv : public EnvWrapper {
|
||||
return s;
|
||||
}
|
||||
Status Truncate(uint64_t size) override { return base_->Truncate(size); }
|
||||
Status Close() override { return base_->Close(); }
|
||||
Status Close() override {
|
||||
// SyncPoint is not supported in Released Windows Mode.
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
// Check preallocation size
|
||||
// preallocation size is never passed to base file.
|
||||
size_t preallocation_size = preallocation_block_size();
|
||||
TEST_SYNC_POINT_CALLBACK("DBTestWalFile.GetPreallocationStatus",
|
||||
&preallocation_size);
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
return base_->Close();
|
||||
}
|
||||
Status Flush() override { return base_->Flush(); }
|
||||
Status Sync() override {
|
||||
++env_->sync_counter_;
|
||||
@@ -443,6 +457,11 @@ class SpecialEnv : public EnvWrapper {
|
||||
addon_time_.load();
|
||||
}
|
||||
|
||||
virtual Status DeleteFile(const std::string& fname) override {
|
||||
delete_count_.fetch_add(1);
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
Random rnd_;
|
||||
port::Mutex rnd_mutex_; // Lock to pretect rnd_
|
||||
|
||||
@@ -470,6 +489,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
// Number of WAL files that are still open for write.
|
||||
std::atomic<int> num_open_wal_file_;
|
||||
|
||||
bool count_random_reads_;
|
||||
anon::AtomicCounter random_read_counter_;
|
||||
std::atomic<size_t> random_read_bytes_counter_;
|
||||
@@ -494,6 +516,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int64_t> addon_time_;
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
bool time_elapse_only_sleep_;
|
||||
|
||||
bool no_sleep_;
|
||||
@@ -797,10 +821,11 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
std::vector<std::uint64_t> ListTableFiles(Env* env, const std::string& path);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status GenerateAndAddExternalFile(const Options options,
|
||||
std::vector<int> keys, size_t file_id);
|
||||
void VerifyDBFromMap(std::map<std::string, std::string> true_data,
|
||||
size_t* total_reads_res = nullptr,
|
||||
bool tailing_iter = false);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
uint64_t GetNumberOfSstFilesForColumnFamily(DB* db,
|
||||
std::string column_family_name);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -637,10 +637,132 @@ TEST_P(DBTestUniversalCompactionParallel, UniversalCompactionParallel) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBTestUniversalCompactionParallel, PickByFileNumberBug) {
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 1 * 1024; // 1KB
|
||||
options.level0_file_num_compaction_trigger = 7;
|
||||
options.max_background_compactions = 2;
|
||||
options.target_file_size_base = 1024 * 1024; // 1MB
|
||||
|
||||
// Disable size amplifiction compaction
|
||||
options.compaction_options_universal.max_size_amplification_percent =
|
||||
UINT_MAX;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBTestUniversalCompactionParallel::PickByFileNumberBug:0",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"UniversalCompactionPicker::PickCompaction:Return",
|
||||
"DBTestUniversalCompactionParallel::PickByFileNumberBug:1"},
|
||||
{"DBTestUniversalCompactionParallel::PickByFileNumberBug:2",
|
||||
"CompactionJob::Run():Start"}});
|
||||
|
||||
int total_picked_compactions = 0;
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UniversalCompactionPicker::PickCompaction:Return", [&](void* arg) {
|
||||
if (arg) {
|
||||
total_picked_compactions++;
|
||||
}
|
||||
});
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Write 7 files to trigger compaction
|
||||
int key_idx = 1;
|
||||
for (int i = 1; i <= 70; i++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
if (i % 10 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the 1st background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
|
||||
// Write 3 files while 1st compaction is held
|
||||
// These 3 files have different sizes to avoid compacting based on size_ratio
|
||||
int num_keys = 1000;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 1; j <= num_keys; j++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
num_keys -= 100;
|
||||
}
|
||||
|
||||
// Wait for the 2nd background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
|
||||
// Hold the 1st and 2nd compaction from finishing
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:2");
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// Although 2 compaction threads started, the second one did not compact
|
||||
// anything because the number of files not being compacted is less than
|
||||
// level0_file_num_compaction_trigger
|
||||
EXPECT_EQ(total_picked_compactions, 1);
|
||||
EXPECT_EQ(TotalTableFiles(), 4);
|
||||
|
||||
// Stop SyncPoint and destroy the DB and reopen it again
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
key_idx = 1;
|
||||
total_picked_compactions = 0;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Write 7 files to trigger compaction
|
||||
for (int i = 1; i <= 70; i++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
if (i % 10 == 0) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the 1st background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
rocksdb::SyncPoint::GetInstance()->ClearTrace();
|
||||
|
||||
// Write 8 files while 1st compaction is held
|
||||
// These 8 files have different sizes to avoid compacting based on size_ratio
|
||||
num_keys = 1000;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (int j = 1; j <= num_keys; j++) {
|
||||
std::string k = Key(key_idx++);
|
||||
ASSERT_OK(Put(k, k));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
num_keys -= 100;
|
||||
}
|
||||
|
||||
// Wait for the 2nd background compaction process to start
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:0");
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:1");
|
||||
|
||||
// Hold the 1st and 2nd compaction from finishing
|
||||
TEST_SYNC_POINT("DBTestUniversalCompactionParallel::PickByFileNumberBug:2");
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
// This time we will trigger a compaction because of size ratio and
|
||||
// another compaction because of number of files that are not compacted
|
||||
// greater than 7
|
||||
EXPECT_GE(total_picked_compactions, 2);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBTestUniversalCompactionParallel,
|
||||
DBTestUniversalCompactionParallel,
|
||||
::testing::Combine(::testing::Values(1, 10),
|
||||
::testing::Bool()));
|
||||
::testing::Values(false)));
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionOptions) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -996,13 +1118,13 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 2, 4)
|
||||
// (1, 2, 4) -> (3, 4)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 4) -> (8)
|
||||
// (1, 3, 4) -> (8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
|
||||
@@ -1016,22 +1138,22 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
|
||||
// (1, 2, 8)
|
||||
// (1, 2, 8) -> (3, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 8) -> (4, 8)
|
||||
// (1, 3, 8) -> (4, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
|
||||
// (1, 4, 8)
|
||||
// (1, 4, 8) -> (5, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[3].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
for (int i = 0; i < key_idx; i++) {
|
||||
auto v = Get(Key(i));
|
||||
@@ -1195,12 +1317,12 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 2, 4)
|
||||
// (1, 2, 4) -> (3, 4)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(2, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 4) -> (8)
|
||||
// (1, 3, 4) -> (8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
@@ -1215,20 +1337,20 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionSecondPathRatio) {
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 2, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(2, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 1, 2, 8) -> (4, 8)
|
||||
// (1, 2, 8) -> (3, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 4, 8)
|
||||
// (1, 3, 8) -> (4, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(1, GetSstFileCount(dbname_));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
// (1, 4, 8) -> (5, 8)
|
||||
GenerateNewFile(&rnd, &key_idx);
|
||||
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
|
||||
ASSERT_EQ(0, GetSstFileCount(dbname_));
|
||||
|
||||
for (int i = 0; i < key_idx; i++) {
|
||||
auto v = Get(Key(i));
|
||||
|
||||
+249
-1
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "util/fault_injection_test_env.h"
|
||||
#include "util/options_helper.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
@@ -291,7 +292,170 @@ TEST_F(DBWALTest, RecoveryWithEmptyLog) {
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_F(DBWALTest, PreallocateBlock) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 10 * 1000 * 1000;
|
||||
options.max_total_wal_size = 0;
|
||||
|
||||
size_t expected_preallocation_size = static_cast<size_t>(
|
||||
options.write_buffer_size + options.write_buffer_size / 10);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::atomic<int> called(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
|
||||
options.max_total_wal_size = 1000 * 1000;
|
||||
expected_preallocation_size = static_cast<size_t>(options.max_total_wal_size);
|
||||
Reopen(options);
|
||||
called.store(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
|
||||
options.db_write_buffer_size = 800 * 1000;
|
||||
expected_preallocation_size =
|
||||
static_cast<size_t>(options.db_write_buffer_size);
|
||||
Reopen(options);
|
||||
called.store(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
|
||||
expected_preallocation_size = 700 * 1000;
|
||||
std::shared_ptr<WriteBufferManager> write_buffer_manager =
|
||||
std::make_shared<WriteBufferManager>(static_cast<uint64_t>(700 * 1000));
|
||||
options.write_buffer_manager = write_buffer_manager;
|
||||
Reopen(options);
|
||||
called.store(0);
|
||||
rocksdb::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBTestWalFile.GetPreallocationStatus", [&](void* arg) {
|
||||
ASSERT_TRUE(arg != nullptr);
|
||||
size_t preallocation_size = *(static_cast<size_t*>(arg));
|
||||
ASSERT_EQ(expected_preallocation_size, preallocation_size);
|
||||
called.fetch_add(1);
|
||||
});
|
||||
rocksdb::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
Close();
|
||||
rocksdb::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
}
|
||||
#endif // !(defined NDEBUG) || !defined(OS_WIN)
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBWALTest, FullPurgePreservesRecycledLog) {
|
||||
// For github issue #1303
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.recycle_log_file_num = 2;
|
||||
if (i != 0) {
|
||||
options.wal_dir = alternative_wal_dir_;
|
||||
}
|
||||
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_GT(log_files.size(), 0);
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Now the original WAL is in log_files[0] and should be marked for
|
||||
// recycling.
|
||||
// Verify full purge cannot remove this file.
|
||||
JobContext job_context(0);
|
||||
dbfull()->TEST_LockMutex();
|
||||
dbfull()->FindObsoleteFiles(&job_context, true /* force */);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
dbfull()->PurgeObsoleteFiles(job_context);
|
||||
|
||||
if (i == 0) {
|
||||
ASSERT_OK(
|
||||
env_->FileExists(LogFileName(dbname_, log_files[0]->LogNumber())));
|
||||
} else {
|
||||
ASSERT_OK(env_->FileExists(
|
||||
LogFileName(alternative_wal_dir_, log_files[0]->LogNumber())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, GetSortedWalFiles) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(0, log_files.size());
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "v1"));
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
ASSERT_EQ(1, log_files.size());
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoveryWithLogDataForSomeCFs) {
|
||||
// Test for regression of WAL cleanup missing files that don't contain data
|
||||
// for every column family.
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
ASSERT_OK(Put(1, "foo", "v1"));
|
||||
ASSERT_OK(Put(1, "foo", "v2"));
|
||||
uint64_t earliest_log_nums[2];
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
|
||||
}
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
if (log_files.size() > 0) {
|
||||
earliest_log_nums[i] = log_files[0]->LogNumber();
|
||||
} else {
|
||||
earliest_log_nums[i] = port::kMaxUint64;
|
||||
}
|
||||
}
|
||||
// Check at least the first WAL was cleaned up during the recovery.
|
||||
ASSERT_LT(earliest_log_nums[0], earliest_log_nums[1]);
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithLargeLog) {
|
||||
do {
|
||||
{
|
||||
@@ -471,6 +635,41 @@ TEST_F(DBWALTest, SyncMultipleLogs) {
|
||||
ASSERT_OK(dbfull()->SyncWAL());
|
||||
}
|
||||
|
||||
// Github issue 1339. Prior the fix we read sequence id from the first log to
|
||||
// a local variable, then keep increase the variable as we replay logs,
|
||||
// ignoring actual sequence id of the records. This is incorrect if some writes
|
||||
// come with WAL disabled.
|
||||
TEST_F(DBWALTest, PartOfWritesWithWALDisabled) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = CurrentOptions();
|
||||
options.env = fault_env.get();
|
||||
options.disable_auto_compactions = true;
|
||||
// TODO(yiwu): fix for 2PC.
|
||||
options.allow_2pc = false;
|
||||
WriteOptions wal_on, wal_off;
|
||||
wal_on.sync = true;
|
||||
wal_on.disableWAL = false;
|
||||
wal_off.disableWAL = true;
|
||||
CreateAndReopenWithCF({"dummy"}, options);
|
||||
ASSERT_OK(Put(1, "dummy", "d1", wal_on)); // seq id 1
|
||||
ASSERT_OK(Put(1, "dummy", "d2", wal_off));
|
||||
ASSERT_OK(Put(1, "dummy", "d3", wal_off));
|
||||
ASSERT_OK(Put(0, "key", "v4", wal_on)); // seq id 4
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_OK(Put(0, "key", "v5", wal_on)); // seq id 5
|
||||
ASSERT_EQ("v5", Get(0, "key"));
|
||||
// Simulate a crash.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
Close();
|
||||
fault_env->ResetState();
|
||||
ReopenWithColumnFamilies({"default", "dummy"}, options);
|
||||
// Prior to the fix, we may incorrectly recover "v5" with sequence id = 3.
|
||||
ASSERT_EQ("v5", Get(0, "key"));
|
||||
// Destroy DB before destruct fault_env.
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
//
|
||||
// Test WAL recovery for the various modes available
|
||||
//
|
||||
@@ -488,7 +687,7 @@ class RecoveryTestHelper {
|
||||
// Create WAL files with values filled in
|
||||
static void FillData(DBWALTest* test, const Options& options,
|
||||
const size_t wal_count, size_t* count) {
|
||||
const DBOptions& db_options = options;
|
||||
const ImmutableDBOptions db_options(options);
|
||||
|
||||
*count = 0;
|
||||
|
||||
@@ -807,6 +1006,32 @@ TEST_F(DBWALTest, AvoidFlushDuringRecovery) {
|
||||
ASSERT_EQ(2, TotalTableFiles());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WalCleanupAfterAvoidFlushDuringRecovery) {
|
||||
// Verifies WAL files that were present during recovery, but not flushed due
|
||||
// to avoid_flush_during_recovery, will be considered for deletion at a later
|
||||
// stage. We check at least one such file is deleted during Flush().
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
Reopen(options);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
// Flush() triggers deletion of obsolete tracked files
|
||||
Flush();
|
||||
}
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
if (i == 0) {
|
||||
ASSERT_GT(log_files.size(), 0);
|
||||
} else {
|
||||
ASSERT_EQ(0, log_files.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithoutFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
@@ -967,6 +1192,29 @@ TEST_F(DBWALTest, RecoverFromCorruptedWALWithoutFlush) {
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBWALTest, WalTermTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
wo.disableWAL = false;
|
||||
|
||||
WriteBatch batch;
|
||||
batch.Put("foo", "bar");
|
||||
batch.MarkWalTerminationPoint();
|
||||
batch.Put("foo2", "bar2");
|
||||
|
||||
ASSERT_OK(dbfull()->Write(wo, &batch));
|
||||
|
||||
// make sure we can re-open it.
|
||||
ASSERT_OK(TryReopenWithColumnFamilies({"default", "pikachu"}, options));
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
ASSERT_EQ("NOT_FOUND", Get(1, "foo2"));
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+11
-2
@@ -20,9 +20,18 @@
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
// kValueTypeForSeek defines the ValueType that should be passed when
|
||||
// constructing a ParsedInternalKey object for seeking to a particular
|
||||
// sequence number (since we sort sequence numbers in decreasing order
|
||||
// and the value type is embedded as the low 8 bits in the sequence
|
||||
// number in internal keys, we need to use the highest-numbered
|
||||
// ValueType, not the lowest).
|
||||
const ValueType kValueTypeForSeek = kTypeSingleDeletion;
|
||||
const ValueType kValueTypeForSeekForPrev = kTypeDeletion;
|
||||
|
||||
uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {
|
||||
assert(seq <= kMaxSequenceNumber);
|
||||
assert(IsValueType(t));
|
||||
assert(IsExtendedValueType(t));
|
||||
return (seq << 8) | t;
|
||||
}
|
||||
|
||||
@@ -31,7 +40,7 @@ void UnPackSequenceAndType(uint64_t packed, uint64_t* seq, ValueType* t) {
|
||||
*t = static_cast<ValueType>(packed & 0xff);
|
||||
|
||||
assert(*seq <= kMaxSequenceNumber);
|
||||
assert(IsValueType(*t));
|
||||
assert(IsExtendedValueType(*t));
|
||||
}
|
||||
|
||||
void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
|
||||
|
||||
+62
-10
@@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/filter_policy.h"
|
||||
@@ -44,28 +45,34 @@ enum ValueType : unsigned char {
|
||||
kTypeCommitXID = 0xB, // WAL only.
|
||||
kTypeRollbackXID = 0xC, // WAL only.
|
||||
kTypeNoop = 0xD, // WAL only.
|
||||
kTypeColumnFamilyRangeDeletion = 0xE, // WAL only.
|
||||
kTypeRangeDeletion = 0xF, // meta block
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
};
|
||||
|
||||
// kValueTypeForSeek defines the ValueType that should be passed when
|
||||
// constructing a ParsedInternalKey object for seeking to a particular
|
||||
// sequence number (since we sort sequence numbers in decreasing order
|
||||
// and the value type is embedded as the low 8 bits in the sequence
|
||||
// number in internal keys, we need to use the highest-numbered
|
||||
// ValueType, not the lowest).
|
||||
static const ValueType kValueTypeForSeek = kTypeSingleDeletion;
|
||||
// Defined in dbformat.cc
|
||||
extern const ValueType kValueTypeForSeek;
|
||||
extern const ValueType kValueTypeForSeekForPrev;
|
||||
|
||||
// Checks whether a type is a value type (i.e. a type used in memtables and sst
|
||||
// files).
|
||||
// Checks whether a type is an inline value type
|
||||
// (i.e. a type used in memtable skiplist and sst file datablock).
|
||||
inline bool IsValueType(ValueType t) {
|
||||
return t <= kTypeMerge || t == kTypeSingleDeletion;
|
||||
}
|
||||
|
||||
// Checks whether a type is from user operation
|
||||
// kTypeRangeDeletion is in meta block so this API is separated from above
|
||||
inline bool IsExtendedValueType(ValueType t) {
|
||||
return IsValueType(t) || t == kTypeRangeDeletion;
|
||||
}
|
||||
|
||||
// We leave eight bits empty at the bottom so a type and sequence#
|
||||
// can be packed together into 64-bits.
|
||||
static const SequenceNumber kMaxSequenceNumber =
|
||||
((0x1ull << 56) - 1);
|
||||
|
||||
static const SequenceNumber kDisableGlobalSequenceNumber = port::kMaxUint64;
|
||||
|
||||
struct ParsedInternalKey {
|
||||
Slice user_key;
|
||||
SequenceNumber sequence;
|
||||
@@ -208,7 +215,7 @@ inline bool ParseInternalKey(const Slice& internal_key,
|
||||
result->type = static_cast<ValueType>(c);
|
||||
assert(result->type <= ValueType::kMaxValue);
|
||||
result->user_key = Slice(internal_key.data(), n - 8);
|
||||
return IsValueType(result->type);
|
||||
return IsExtendedValueType(result->type);
|
||||
}
|
||||
|
||||
// Update the sequence number in the internal key.
|
||||
@@ -352,6 +359,15 @@ class IterKey {
|
||||
return Slice(key_, key_n);
|
||||
}
|
||||
|
||||
// Copy the key into IterKey own buf_
|
||||
void OwnKey() {
|
||||
assert(IsKeyPinned() == true);
|
||||
|
||||
Reserve(key_size_);
|
||||
memcpy(buf_, key_, key_size_);
|
||||
key_ = buf_;
|
||||
}
|
||||
|
||||
// Update the sequence number in the internal key. Guarantees not to
|
||||
// invalidate slices to the key (and the user key).
|
||||
void UpdateInternalKey(uint64_t seq, ValueType t) {
|
||||
@@ -488,4 +504,40 @@ extern bool ReadKeyFromWriteBatchEntry(Slice* input, Slice* key,
|
||||
extern Status ReadRecordFromWriteBatch(Slice* input, char* tag,
|
||||
uint32_t* column_family, Slice* key,
|
||||
Slice* value, Slice* blob, Slice* xid);
|
||||
|
||||
// When user call DeleteRange() to delete a range of keys,
|
||||
// we will store a serialized RangeTombstone in MemTable and SST.
|
||||
// the struct here is a easy-understood form
|
||||
// start/end_key_ is the start/end user key of the range to be deleted
|
||||
struct RangeTombstone {
|
||||
Slice start_key_;
|
||||
Slice end_key_;
|
||||
SequenceNumber seq_;
|
||||
explicit RangeTombstone(Slice sk, Slice ek, SequenceNumber sn)
|
||||
: start_key_(sk), end_key_(ek), seq_(sn) {}
|
||||
|
||||
explicit RangeTombstone(ParsedInternalKey parsed_key, Slice value) {
|
||||
start_key_ = parsed_key.user_key;
|
||||
seq_ = parsed_key.sequence;
|
||||
end_key_ = value;
|
||||
}
|
||||
|
||||
// be careful to use Serialize(), allocates new memory
|
||||
std::pair<InternalKey, Slice> Serialize() const {
|
||||
auto key = InternalKey(start_key_, seq_, kTypeRangeDeletion);
|
||||
Slice value = end_key_;
|
||||
return std::make_pair(std::move(key), std::move(value));
|
||||
}
|
||||
|
||||
// be careful to use SerializeKey(), allocates new memory
|
||||
InternalKey SerializeKey() const {
|
||||
return InternalKey(start_key_, seq_, kTypeRangeDeletion);
|
||||
}
|
||||
|
||||
// be careful to use SerializeEndKey(), allocates new memory
|
||||
InternalKey SerializeEndKey() const {
|
||||
return InternalKey(end_key_, seq_, kTypeRangeDeletion);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,491 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#include "db/external_sst_file_ingestion_job.h"
|
||||
|
||||
#define __STDC_FORMAT_MACROS
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/version_edit.h"
|
||||
#include "table/merger.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "table/sst_file_writer_collectors.h"
|
||||
#include "table/table_builder.h"
|
||||
#include "util/file_reader_writer.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/sync_point.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
Status ExternalSstFileIngestionJob::Prepare(
|
||||
const std::vector<std::string>& external_files_paths) {
|
||||
Status status;
|
||||
|
||||
// Read the information of files we are ingesting
|
||||
for (const std::string& file_path : external_files_paths) {
|
||||
IngestedFileInfo file_to_ingest;
|
||||
status = GetIngestedFileInfo(file_path, &file_to_ingest);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
files_to_ingest_.push_back(file_to_ingest);
|
||||
}
|
||||
|
||||
const Comparator* ucmp = cfd_->internal_comparator().user_comparator();
|
||||
auto num_files = files_to_ingest_.size();
|
||||
if (num_files == 0) {
|
||||
return Status::InvalidArgument("The list of files is empty");
|
||||
} else if (num_files > 1) {
|
||||
// Verify that passed files dont have overlapping ranges
|
||||
autovector<const IngestedFileInfo*> sorted_files;
|
||||
for (size_t i = 0; i < num_files; i++) {
|
||||
sorted_files.push_back(&files_to_ingest_[i]);
|
||||
}
|
||||
|
||||
std::sort(
|
||||
sorted_files.begin(), sorted_files.end(),
|
||||
[&ucmp](const IngestedFileInfo* info1, const IngestedFileInfo* info2) {
|
||||
return ucmp->Compare(info1->smallest_user_key,
|
||||
info2->smallest_user_key) < 0;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < num_files - 1; i++) {
|
||||
if (ucmp->Compare(sorted_files[i]->largest_user_key,
|
||||
sorted_files[i + 1]->smallest_user_key) >= 0) {
|
||||
return Status::NotSupported("Files have overlapping ranges");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.num_entries == 0) {
|
||||
return Status::InvalidArgument("File contain no entries");
|
||||
}
|
||||
|
||||
if (!f.smallest_internal_key().Valid() ||
|
||||
!f.largest_internal_key().Valid()) {
|
||||
return Status::Corruption("Generated table have corrupted keys");
|
||||
}
|
||||
}
|
||||
|
||||
// Copy/Move external files into DB
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
f.fd = FileDescriptor(versions_->NewFileNumber(), 0, f.file_size);
|
||||
|
||||
const std::string path_outside_db = f.external_file_path;
|
||||
const std::string path_inside_db =
|
||||
TableFileName(db_options_.db_paths, f.fd.GetNumber(), f.fd.GetPathId());
|
||||
|
||||
if (ingestion_options_.move_files) {
|
||||
status = env_->LinkFile(path_outside_db, path_inside_db);
|
||||
if (status.IsNotSupported()) {
|
||||
// Original file is on a different FS, use copy instead of hard linking
|
||||
status = CopyFile(env_, path_outside_db, path_inside_db, 0);
|
||||
}
|
||||
} else {
|
||||
status = CopyFile(env_, path_outside_db, path_inside_db, 0);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::AddFile:FileCopied");
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
f.internal_file_path = path_inside_db;
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
// We failed, remove all files that we copied into the db
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
if (f.internal_file_path == "") {
|
||||
break;
|
||||
}
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed) {
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
Status status =
|
||||
IngestedFilesOverlapWithMemtables(super_version, flush_needed);
|
||||
|
||||
if (status.ok() && *flush_needed &&
|
||||
!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::Run() {
|
||||
Status status;
|
||||
#ifndef NDEBUG
|
||||
// We should never run the job with a memtable that is overlapping
|
||||
// with the files we are ingesting
|
||||
bool need_flush = false;
|
||||
status = NeedsFlush(&need_flush);
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
|
||||
bool consumed_seqno = false;
|
||||
bool force_global_seqno = false;
|
||||
const SequenceNumber last_seqno = versions_->LastSequence();
|
||||
if (ingestion_options_.snapshot_consistency && !db_snapshots_->empty()) {
|
||||
// We need to assign a global sequence number to all the files even
|
||||
// if the dont overlap with any ranges since we have snapshots
|
||||
force_global_seqno = true;
|
||||
}
|
||||
|
||||
SuperVersion* super_version = cfd_->GetSuperVersion();
|
||||
edit_.SetColumnFamily(cfd_->GetID());
|
||||
// The levels that the files will be ingested into
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
bool overlap_with_db = false;
|
||||
status = AssignLevelForIngestedFile(super_version, &f, &overlap_with_db);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (overlap_with_db || force_global_seqno) {
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, last_seqno + 1);
|
||||
consumed_seqno = true;
|
||||
} else {
|
||||
status = AssignGlobalSeqnoForIngestedFile(&f, 0);
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
edit_.AddFile(f.picked_level, f.fd.GetNumber(), f.fd.GetPathId(),
|
||||
f.fd.GetFileSize(), f.smallest_internal_key(),
|
||||
f.largest_internal_key(), f.assigned_seqno, f.assigned_seqno,
|
||||
false);
|
||||
}
|
||||
|
||||
if (consumed_seqno) {
|
||||
versions_->SetLastSequence(last_seqno + 1);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void ExternalSstFileIngestionJob::UpdateStats() {
|
||||
// Update internal stats for new ingested files
|
||||
uint64_t total_keys = 0;
|
||||
uint64_t total_l0_files = 0;
|
||||
uint64_t total_time = env_->NowMicros() - job_start_time_;
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = total_time;
|
||||
stats.bytes_written = f.fd.GetFileSize();
|
||||
stats.num_output_files = 1;
|
||||
cfd_->internal_stats()->AddCompactionStats(f.picked_level, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_INGESTED_ADD_FILE,
|
||||
f.fd.GetFileSize());
|
||||
total_keys += f.num_entries;
|
||||
if (f.picked_level == 0) {
|
||||
total_l0_files += 1;
|
||||
}
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[AddFile] External SST file %s was ingested in L%d with path %s "
|
||||
"(global_seqno=%" PRIu64 ")\n",
|
||||
f.external_file_path.c_str(), f.picked_level,
|
||||
f.internal_file_path.c_str(), f.assigned_seqno);
|
||||
}
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::INGESTED_NUM_KEYS_TOTAL,
|
||||
total_keys);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::INGESTED_NUM_FILES_TOTAL,
|
||||
files_to_ingest_.size());
|
||||
cfd_->internal_stats()->AddCFStats(
|
||||
InternalStats::INGESTED_LEVEL0_NUM_FILES_TOTAL, total_l0_files);
|
||||
}
|
||||
|
||||
void ExternalSstFileIngestionJob::Cleanup(const Status& status) {
|
||||
if (!status.ok()) {
|
||||
// We failed to add the files to the database
|
||||
// remove all the files we copied
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
Status s = env_->DeleteFile(f.internal_file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"AddFile() clean up for file %s failed : %s",
|
||||
f.internal_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
} else if (status.ok() && ingestion_options_.move_files) {
|
||||
// The files were moved and added successfully, remove original file links
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
Status s = env_->DeleteFile(f.external_file_path);
|
||||
if (!s.ok()) {
|
||||
Log(InfoLogLevel::WARN_LEVEL, db_options_.info_log,
|
||||
"%s was added to DB successfully but failed to remove original "
|
||||
"file link : %s",
|
||||
f.external_file_path.c_str(), s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
const std::string& external_file, IngestedFileInfo* file_to_ingest) {
|
||||
file_to_ingest->external_file_path = external_file;
|
||||
|
||||
// Get external file size
|
||||
Status status = env_->GetFileSize(external_file, &file_to_ingest->file_size);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Create TableReader for external file
|
||||
std::unique_ptr<TableReader> table_reader;
|
||||
std::unique_ptr<RandomAccessFile> sst_file;
|
||||
std::unique_ptr<RandomAccessFileReader> sst_file_reader;
|
||||
|
||||
status = env_->NewRandomAccessFile(external_file, &sst_file, env_options_);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
sst_file_reader.reset(new RandomAccessFileReader(std::move(sst_file)));
|
||||
|
||||
status = cfd_->ioptions()->table_factory->NewTableReader(
|
||||
TableReaderOptions(*cfd_->ioptions(), env_options_,
|
||||
cfd_->internal_comparator()),
|
||||
std::move(sst_file_reader), file_to_ingest->file_size, &table_reader);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Get the external file properties
|
||||
auto props = table_reader->GetTableProperties();
|
||||
const auto& uprops = props->user_collected_properties;
|
||||
|
||||
// Get table version
|
||||
auto version_iter = uprops.find(ExternalSstFilePropertyNames::kVersion);
|
||||
if (version_iter == uprops.end()) {
|
||||
return Status::Corruption("External file version not found");
|
||||
}
|
||||
file_to_ingest->version = DecodeFixed32(version_iter->second.c_str());
|
||||
|
||||
auto seqno_iter = uprops.find(ExternalSstFilePropertyNames::kGlobalSeqno);
|
||||
if (file_to_ingest->version == 2) {
|
||||
// version 2 imply that we have global sequence number
|
||||
if (seqno_iter == uprops.end()) {
|
||||
return Status::Corruption(
|
||||
"External file global sequence number not found");
|
||||
}
|
||||
|
||||
// Set the global sequence number
|
||||
file_to_ingest->original_seqno = DecodeFixed64(seqno_iter->second.c_str());
|
||||
file_to_ingest->global_seqno_offset = props->properties_offsets.at(
|
||||
ExternalSstFilePropertyNames::kGlobalSeqno);
|
||||
|
||||
if (file_to_ingest->global_seqno_offset == 0) {
|
||||
return Status::Corruption("Was not able to find file global seqno field");
|
||||
}
|
||||
} else {
|
||||
return Status::InvalidArgument("external file version is not supported");
|
||||
}
|
||||
// Get number of entries in table
|
||||
file_to_ingest->num_entries = props->num_entries;
|
||||
|
||||
ParsedInternalKey key;
|
||||
std::unique_ptr<InternalIterator> iter(
|
||||
table_reader->NewIterator(ReadOptions()));
|
||||
|
||||
// Get first (smallest) key from file
|
||||
iter->SeekToFirst();
|
||||
if (!ParseInternalKey(iter->key(), &key)) {
|
||||
return Status::Corruption("external file have corrupted keys");
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption("external file have non zero sequence number");
|
||||
}
|
||||
file_to_ingest->smallest_user_key = key.user_key.ToString();
|
||||
|
||||
// Get last (largest) key from file
|
||||
iter->SeekToLast();
|
||||
if (!ParseInternalKey(iter->key(), &key)) {
|
||||
return Status::Corruption("external file have corrupted keys");
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption("external file have non zero sequence number");
|
||||
}
|
||||
file_to_ingest->largest_user_key = key.user_key.ToString();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::IngestedFilesOverlapWithMemtables(
|
||||
SuperVersion* sv, bool* overlap) {
|
||||
// Create an InternalIterator over all memtables
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
MergeIteratorBuilder merge_iter_builder(&cfd_->internal_comparator(), &arena);
|
||||
merge_iter_builder.AddIterator(sv->mem->NewIterator(ro, &arena));
|
||||
sv->imm->AddIterators(ro, &merge_iter_builder);
|
||||
ScopedArenaIterator memtable_iter(merge_iter_builder.Finish());
|
||||
|
||||
Status status;
|
||||
*overlap = false;
|
||||
for (IngestedFileInfo& f : files_to_ingest_) {
|
||||
status =
|
||||
IngestedFileOverlapWithIteratorRange(&f, memtable_iter.get(), overlap);
|
||||
if (!status.ok() || *overlap == true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::AssignLevelForIngestedFile(
|
||||
SuperVersion* sv, IngestedFileInfo* file_to_ingest, bool* overlap_with_db) {
|
||||
*overlap_with_db = false;
|
||||
|
||||
Arena arena;
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
|
||||
Status status;
|
||||
int target_level = 0;
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
for (int lvl = 0; lvl < cfd_->NumberLevels(); lvl++) {
|
||||
if (lvl > 0 && lvl < vstorage->base_level()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vstorage->NumLevelFiles(lvl) > 0) {
|
||||
bool overlap_with_level = false;
|
||||
MergeIteratorBuilder merge_iter_builder(&cfd_->internal_comparator(),
|
||||
&arena);
|
||||
sv->current->AddIteratorsForLevel(ro, env_options_, &merge_iter_builder,
|
||||
lvl);
|
||||
ScopedArenaIterator level_iter(merge_iter_builder.Finish());
|
||||
|
||||
status = IngestedFileOverlapWithIteratorRange(
|
||||
file_to_ingest, level_iter.get(), &overlap_with_level);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (overlap_with_level) {
|
||||
// We must use L0 or any level higher than `lvl` to be able to overwrite
|
||||
// the keys that we overlap with in this level, We also need to assign
|
||||
// this file a seqno to overwrite the existing keys in level `lvl`
|
||||
*overlap_with_db = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We dont overlap with any keys in this level, but we still need to check
|
||||
// if our file can fit in it
|
||||
|
||||
if (IngestedFileFitInLevel(file_to_ingest, lvl)) {
|
||||
target_level = lvl;
|
||||
}
|
||||
}
|
||||
file_to_ingest->picked_level = target_level;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
IngestedFileInfo* file_to_ingest, SequenceNumber seqno) {
|
||||
if (file_to_ingest->original_seqno == seqno) {
|
||||
// This file already have the correct global seqno
|
||||
return Status::OK();
|
||||
} else if (!ingestion_options_.allow_global_seqno) {
|
||||
return Status::InvalidArgument("Global seqno is required, but disabled");
|
||||
} else if (file_to_ingest->global_seqno_offset == 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Trying to set global seqno for a file that dont have a global seqno "
|
||||
"field");
|
||||
}
|
||||
|
||||
std::unique_ptr<RandomRWFile> rwfile;
|
||||
Status status = env_->NewRandomRWFile(file_to_ingest->internal_file_path,
|
||||
&rwfile, env_options_);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Write the new seqno in the global sequence number field in the file
|
||||
std::string seqno_val;
|
||||
PutFixed64(&seqno_val, seqno);
|
||||
status = rwfile->Write(file_to_ingest->global_seqno_offset, seqno_val);
|
||||
if (status.ok()) {
|
||||
file_to_ingest->assigned_seqno = seqno;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status ExternalSstFileIngestionJob::IngestedFileOverlapWithIteratorRange(
|
||||
const IngestedFileInfo* file_to_ingest, InternalIterator* iter,
|
||||
bool* overlap) {
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
auto* ucmp = vstorage->InternalComparator()->user_comparator();
|
||||
InternalKey range_start(file_to_ingest->smallest_user_key, kMaxSequenceNumber,
|
||||
kValueTypeForSeek);
|
||||
iter->Seek(range_start.Encode());
|
||||
if (!iter->status().ok()) {
|
||||
return iter->status();
|
||||
}
|
||||
|
||||
*overlap = false;
|
||||
if (iter->Valid()) {
|
||||
ParsedInternalKey seek_result;
|
||||
if (!ParseInternalKey(iter->key(), &seek_result)) {
|
||||
return Status::Corruption("DB have corrupted keys");
|
||||
}
|
||||
|
||||
if (ucmp->Compare(seek_result.user_key, file_to_ingest->largest_user_key) <=
|
||||
0) {
|
||||
*overlap = true;
|
||||
}
|
||||
}
|
||||
|
||||
return iter->status();
|
||||
}
|
||||
|
||||
bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
|
||||
const IngestedFileInfo* file_to_ingest, int level) {
|
||||
if (level == 0) {
|
||||
// Files can always fit in L0
|
||||
return true;
|
||||
}
|
||||
|
||||
auto* vstorage = cfd_->current()->storage_info();
|
||||
Slice file_smallest_user_key(file_to_ingest->smallest_user_key);
|
||||
Slice file_largest_user_key(file_to_ingest->largest_user_key);
|
||||
|
||||
if (vstorage->OverlapInLevel(level, &file_smallest_user_key,
|
||||
&file_largest_user_key)) {
|
||||
// File overlap with another files in this level, we cannot
|
||||
// add it to this level
|
||||
return false;
|
||||
}
|
||||
if (cfd_->RangeOverlapWithCompaction(file_smallest_user_key,
|
||||
file_largest_user_key, level)) {
|
||||
// File overlap with a running compaction output that will be stored
|
||||
// in this level, we cannot add this file to this level
|
||||
return false;
|
||||
}
|
||||
|
||||
// File did not overlap with level files, our compaction output
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
struct IngestedFileInfo {
|
||||
// External file path
|
||||
std::string external_file_path;
|
||||
// Smallest user key in external file
|
||||
std::string smallest_user_key;
|
||||
// Largest user key in external file
|
||||
std::string largest_user_key;
|
||||
// Sequence number for keys in external file
|
||||
SequenceNumber original_seqno;
|
||||
// Offset of the global sequence number field in the file, will
|
||||
// be zero if version is 1 (global seqno is not supported)
|
||||
size_t global_seqno_offset;
|
||||
// External file size
|
||||
uint64_t file_size;
|
||||
// total number of keys in external file
|
||||
uint64_t num_entries;
|
||||
// Version of external file
|
||||
int version;
|
||||
|
||||
// FileDescriptor for the file inside the DB
|
||||
FileDescriptor fd;
|
||||
// file path that we picked for file inside the DB
|
||||
std::string internal_file_path = "";
|
||||
// Global sequence number that we picked for the file inside the DB
|
||||
SequenceNumber assigned_seqno = 0;
|
||||
// Level inside the DB we picked for the external file.
|
||||
int picked_level = 0;
|
||||
|
||||
InternalKey smallest_internal_key() const {
|
||||
return InternalKey(smallest_user_key, assigned_seqno,
|
||||
ValueType::kTypeValue);
|
||||
}
|
||||
|
||||
InternalKey largest_internal_key() const {
|
||||
return InternalKey(largest_user_key, assigned_seqno, ValueType::kTypeValue);
|
||||
}
|
||||
};
|
||||
|
||||
class ExternalSstFileIngestionJob {
|
||||
public:
|
||||
ExternalSstFileIngestionJob(
|
||||
Env* env, VersionSet* versions, ColumnFamilyData* cfd,
|
||||
const ImmutableDBOptions& db_options, const EnvOptions& env_options,
|
||||
SnapshotList* db_snapshots,
|
||||
const IngestExternalFileOptions& ingestion_options)
|
||||
: env_(env),
|
||||
versions_(versions),
|
||||
cfd_(cfd),
|
||||
db_options_(db_options),
|
||||
env_options_(env_options),
|
||||
db_snapshots_(db_snapshots),
|
||||
ingestion_options_(ingestion_options),
|
||||
job_start_time_(env_->NowMicros()) {}
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths);
|
||||
|
||||
// Check if we need to flush the memtable before running the ingestion job
|
||||
// This will be true if the files we are ingesting are overlapping with any
|
||||
// key range in the memtable.
|
||||
// REQUIRES: Mutex held
|
||||
Status NeedsFlush(bool* flush_needed);
|
||||
|
||||
// Will execute the ingestion job and prepare edit() to be applied.
|
||||
// REQUIRES: Mutex held
|
||||
Status Run();
|
||||
|
||||
// Update column family stats.
|
||||
// REQUIRES: Mutex held
|
||||
void UpdateStats();
|
||||
|
||||
// Cleanup after successfull/failed job
|
||||
void Cleanup(const Status& status);
|
||||
|
||||
VersionEdit* edit() { return &edit_; }
|
||||
|
||||
private:
|
||||
// Open the external file and populate `file_to_ingest` with all the
|
||||
// external information we need to ingest this file.
|
||||
Status GetIngestedFileInfo(const std::string& external_file,
|
||||
IngestedFileInfo* file_to_ingest);
|
||||
|
||||
// Check if the files we are ingesting overlap with any memtable.
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFilesOverlapWithMemtables(SuperVersion* sv, bool* overlap);
|
||||
|
||||
// Assign `file_to_ingest` the lowest possible level that it can
|
||||
// be ingested to.
|
||||
// REQUIRES: Mutex held
|
||||
Status AssignLevelForIngestedFile(SuperVersion* sv,
|
||||
IngestedFileInfo* file_to_ingest,
|
||||
bool* overlap_with_db);
|
||||
|
||||
// Set the file global sequence number to `seqno`
|
||||
Status AssignGlobalSeqnoForIngestedFile(IngestedFileInfo* file_to_ingest,
|
||||
SequenceNumber seqno);
|
||||
|
||||
// Check if `file_to_ingest` key range overlap with the range `iter` represent
|
||||
// REQUIRES: Mutex held
|
||||
Status IngestedFileOverlapWithIteratorRange(
|
||||
const IngestedFileInfo* file_to_ingest, InternalIterator* iter,
|
||||
bool* overlap);
|
||||
|
||||
// Check if `file_to_ingest` can fit in level `level`
|
||||
// REQUIRES: Mutex held
|
||||
bool IngestedFileFitInLevel(const IngestedFileInfo* file_to_ingest,
|
||||
int level);
|
||||
|
||||
Env* env_;
|
||||
VersionSet* versions_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const EnvOptions& env_options_;
|
||||
SnapshotList* db_snapshots_;
|
||||
autovector<IngestedFileInfo> files_to_ingest_;
|
||||
const IngestExternalFileOptions& ingestion_options_;
|
||||
VersionEdit edit_;
|
||||
uint64_t job_start_time_;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
File diff suppressed because it is too large.
Load diff
@@ -228,6 +228,11 @@ class FaultInjectionTest : public testing::Test,
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
__attribute__((__no_sanitize__("undefined")))
|
||||
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
|
||||
__attribute__((__no_sanitize_undefined__))
|
||||
#endif
|
||||
// Return the ith key
|
||||
Slice Key(int i, std::string* storage) const {
|
||||
int num = i;
|
||||
@@ -405,6 +410,7 @@ TEST_P(FaultInjectionTest, WriteOptionSyncTest) {
|
||||
env_->SetFilesystemActive(false);
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
|
||||
ASSERT_OK(OpenDB());
|
||||
std::string val;
|
||||
@@ -489,6 +495,7 @@ TEST_P(FaultInjectionTest, ManualLogSyncTest) {
|
||||
env_->SetFilesystemActive(false);
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
|
||||
ASSERT_OK(OpenDB());
|
||||
std::string val;
|
||||
@@ -501,6 +508,30 @@ TEST_P(FaultInjectionTest, ManualLogSyncTest) {
|
||||
ASSERT_EQ(value_space, val);
|
||||
}
|
||||
|
||||
TEST_P(FaultInjectionTest, WriteBatchWalTerminationTest) {
|
||||
ReadOptions ro;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
|
||||
WriteOptions wo;
|
||||
wo.sync = true;
|
||||
wo.disableWAL = false;
|
||||
WriteBatch batch;
|
||||
batch.Put("cats", "dogs");
|
||||
batch.MarkWalTerminationPoint();
|
||||
batch.Put("boys", "girls");
|
||||
ASSERT_OK(db_->Write(wo, &batch));
|
||||
|
||||
env_->SetFilesystemActive(false);
|
||||
NoWriteTestReopenWithFault(kResetDropAndDeleteUnsynced);
|
||||
ASSERT_OK(OpenDB());
|
||||
|
||||
std::string val;
|
||||
ASSERT_OK(db_->Get(ro, "cats", &val));
|
||||
ASSERT_EQ("dogs", val);
|
||||
ASSERT_EQ(db_->Get(ro, "boys", &val), Status::NotFound());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FaultTest, FaultInjectionTest, ::testing::Bool());
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
+2
-2
@@ -386,10 +386,10 @@ Status SetIdentityFile(Env* env, const std::string& dbname) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status SyncManifest(Env* env, const DBOptions* db_options,
|
||||
Status SyncManifest(Env* env, const ImmutableDBOptions* db_options,
|
||||
WritableFileWriter* file) {
|
||||
TEST_KILL_RANDOM("SyncManifest:0", rocksdb_kill_odds * REDUCE_ODDS2);
|
||||
if (db_options->disableDataSync) {
|
||||
if (db_options->disable_data_sync) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
StopWatch sw(env, db_options->statistics.get(), MANIFEST_FILE_SYNC_MICROS);
|
||||
|
||||
+2
-1
@@ -20,6 +20,7 @@
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "util/db_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -159,7 +160,7 @@ extern Status SetCurrentFile(Env* env, const std::string& dbname,
|
||||
extern Status SetIdentityFile(Env* env, const std::string& dbname);
|
||||
|
||||
// Sync manifest file `file`.
|
||||
extern Status SyncManifest(Env* env, const DBOptions* db_options,
|
||||
extern Status SyncManifest(Env* env, const ImmutableDBOptions* db_options,
|
||||
WritableFileWriter* file);
|
||||
|
||||
} // namespace rocksdb
|
||||
+64
-54
@@ -56,7 +56,7 @@
|
||||
namespace rocksdb {
|
||||
|
||||
FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const DBOptions& db_options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex,
|
||||
@@ -84,7 +84,8 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
output_compression_(output_compression),
|
||||
stats_(stats),
|
||||
event_logger_(event_logger),
|
||||
measure_io_stats_(measure_io_stats) {
|
||||
measure_io_stats_(measure_io_stats),
|
||||
pick_memtable_called(false) {
|
||||
// Update the thread status to indicate flush.
|
||||
ReportStartedFlush();
|
||||
TEST_SYNC_POINT("FlushJob::FlushJob()");
|
||||
@@ -96,7 +97,7 @@ FlushJob::~FlushJob() {
|
||||
|
||||
void FlushJob::ReportStartedFlush() {
|
||||
ThreadStatusUtil::SetColumnFamily(cfd_, cfd_->ioptions()->env,
|
||||
cfd_->options()->enable_thread_tracking);
|
||||
db_options_.enable_thread_tracking);
|
||||
ThreadStatusUtil::SetThreadOperation(ThreadStatus::OP_FLUSH);
|
||||
ThreadStatusUtil::SetThreadOperationProperty(
|
||||
ThreadStatus::COMPACTION_JOB_ID,
|
||||
@@ -121,9 +122,47 @@ void FlushJob::RecordFlushIOStats() {
|
||||
IOSTATS_RESET(bytes_written);
|
||||
}
|
||||
|
||||
void FlushJob::PickMemTable() {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(!pick_memtable_called);
|
||||
pick_memtable_called = true;
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
cfd_->imm()->PickMemtablesToFlush(&mems_);
|
||||
if (mems_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReportFlushInputSize(mems_);
|
||||
|
||||
// entries mems are (implicitly) sorted in ascending order by their created
|
||||
// time. We will use the first memtable's `edit` to keep the meta info for
|
||||
// this flush.
|
||||
MemTable* m = mems_[0];
|
||||
edit_ = m->GetEdits();
|
||||
edit_->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit_->SetLogNumber(mems_.back()->GetNextLogNumber());
|
||||
edit_->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// path 0 for level 0 file.
|
||||
meta_.fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
|
||||
base_ = cfd_->current();
|
||||
base_->Ref(); // it is likely that we do not need this reference
|
||||
}
|
||||
|
||||
Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
db_mutex_->AssertHeld();
|
||||
assert(pick_memtable_called);
|
||||
AutoThreadOperationStageUpdater stage_run(
|
||||
ThreadStatus::STAGE_FLUSH_RUN);
|
||||
if (mems_.empty()) {
|
||||
LogToBuffer(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// I/O measurement variables
|
||||
PerfLevel prev_perf_level = PerfLevel::kEnableTime;
|
||||
uint64_t prev_write_nanos = 0;
|
||||
@@ -139,31 +178,8 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
prev_prepare_write_nanos = IOSTATS(prepare_write_nanos);
|
||||
}
|
||||
|
||||
// Save the contents of the earliest memtable as a new Table
|
||||
FileMetaData meta;
|
||||
autovector<MemTable*> mems;
|
||||
cfd_->imm()->PickMemtablesToFlush(&mems);
|
||||
if (mems.empty()) {
|
||||
LogToBuffer(log_buffer_, "[%s] Nothing in memtable to flush",
|
||||
cfd_->GetName().c_str());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
ReportFlushInputSize(mems);
|
||||
|
||||
// entries mems are (implicitly) sorted in ascending order by their created
|
||||
// time. We will use the first memtable's `edit` to keep the meta info for
|
||||
// this flush.
|
||||
MemTable* m = mems[0];
|
||||
VersionEdit* edit = m->GetEdits();
|
||||
edit->SetPrevLogNumber(0);
|
||||
// SetLogNumber(log_num) indicates logs with number smaller than log_num
|
||||
// will no longer be picked up for recovery.
|
||||
edit->SetLogNumber(mems.back()->GetNextLogNumber());
|
||||
edit->SetColumnFamily(cfd_->GetID());
|
||||
|
||||
// This will release and re-acquire the mutex.
|
||||
Status s = WriteLevel0Table(mems, edit, &meta);
|
||||
Status s = WriteLevel0Table();
|
||||
|
||||
if (s.ok() &&
|
||||
(shutting_down_->load(std::memory_order_acquire) || cfd_->IsDropped())) {
|
||||
@@ -172,18 +188,18 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
cfd_->imm()->RollbackMemtableFlush(mems, meta.fd.GetNumber());
|
||||
cfd_->imm()->RollbackMemtableFlush(mems_, meta_.fd.GetNumber());
|
||||
} else {
|
||||
TEST_SYNC_POINT("FlushJob::InstallResults");
|
||||
// Replace immutable memtable with the generated Table
|
||||
s = cfd_->imm()->InstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems, versions_, db_mutex_,
|
||||
meta.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
cfd_, mutable_cf_options_, mems_, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_);
|
||||
}
|
||||
|
||||
if (s.ok() && file_meta != nullptr) {
|
||||
*file_meta = meta;
|
||||
*file_meta = meta_;
|
||||
}
|
||||
RecordFlushIOStats();
|
||||
|
||||
@@ -214,17 +230,11 @@ Status FlushJob::Run(FileMetaData* file_meta) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
VersionEdit* edit, FileMetaData* meta) {
|
||||
Status FlushJob::WriteLevel0Table() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
db_mutex_->AssertHeld();
|
||||
const uint64_t start_micros = db_options_.env->NowMicros();
|
||||
// path 0 for level 0 file.
|
||||
meta->fd = FileDescriptor(versions_->NewFileNumber(), 0, 0);
|
||||
|
||||
Version* base = cfd_->current();
|
||||
base->Ref(); // it is likely that we do not need this reference
|
||||
Status s;
|
||||
{
|
||||
db_mutex_->Unlock();
|
||||
@@ -237,7 +247,7 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
Arena arena;
|
||||
uint64_t total_num_entries = 0, total_num_deletes = 0;
|
||||
size_t total_memory_usage = 0;
|
||||
for (MemTable* m : mems) {
|
||||
for (MemTable* m : mems_) {
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Flushing memtable with next log file: %" PRIu64 "\n",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, m->GetNextLogNumber());
|
||||
@@ -249,7 +259,7 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
|
||||
event_logger_->Log() << "job" << job_context_->job_id << "event"
|
||||
<< "flush_started"
|
||||
<< "num_memtables" << mems.size() << "num_entries"
|
||||
<< "num_memtables" << mems_.size() << "num_entries"
|
||||
<< total_num_entries << "num_deletes"
|
||||
<< total_num_deletes << "memory_usage"
|
||||
<< total_memory_usage;
|
||||
@@ -260,13 +270,13 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
Log(InfoLogLevel::INFO_LEVEL, db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta->fd.GetNumber());
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta_.fd.GetNumber());
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table:output_compression",
|
||||
&output_compression_);
|
||||
s = BuildTable(
|
||||
dbname_, db_options_.env, *cfd_->ioptions(), mutable_cf_options_,
|
||||
env_options_, cfd_->table_cache(), iter.get(), meta,
|
||||
env_options_, cfd_->table_cache(), iter.get(), &meta_,
|
||||
cfd_->internal_comparator(), cfd_->int_tbl_prop_collector_factories(),
|
||||
cfd_->GetID(), cfd_->GetName(), existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, output_compression_,
|
||||
@@ -280,39 +290,39 @@ Status FlushJob::WriteLevel0Table(const autovector<MemTable*>& mems,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": %" PRIu64
|
||||
" bytes %s"
|
||||
"%s",
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta->fd.GetNumber(),
|
||||
meta->fd.GetFileSize(), s.ToString().c_str(),
|
||||
meta->marked_for_compaction ? " (needs compaction)" : "");
|
||||
cfd_->GetName().c_str(), job_context_->job_id, meta_.fd.GetNumber(),
|
||||
meta_.fd.GetFileSize(), s.ToString().c_str(),
|
||||
meta_.marked_for_compaction ? " (needs compaction)" : "");
|
||||
|
||||
if (!db_options_.disableDataSync && output_file_directory_ != nullptr) {
|
||||
if (!db_options_.disable_data_sync && output_file_directory_ != nullptr) {
|
||||
output_file_directory_->Fsync();
|
||||
}
|
||||
TEST_SYNC_POINT("FlushJob::WriteLevel0Table");
|
||||
db_mutex_->Lock();
|
||||
}
|
||||
base->Unref();
|
||||
base_->Unref();
|
||||
|
||||
// Note that if file_size is zero, the file has been deleted and
|
||||
// should not be added to the manifest.
|
||||
if (s.ok() && meta->fd.GetFileSize() > 0) {
|
||||
if (s.ok() && meta_.fd.GetFileSize() > 0) {
|
||||
// if we have more than 1 background thread, then we cannot
|
||||
// insert files directly into higher levels because some other
|
||||
// threads could be concurrently producing compacted files for
|
||||
// that key range.
|
||||
// Add file to L0
|
||||
edit->AddFile(0 /* level */, meta->fd.GetNumber(), meta->fd.GetPathId(),
|
||||
meta->fd.GetFileSize(), meta->smallest, meta->largest,
|
||||
meta->smallest_seqno, meta->largest_seqno,
|
||||
meta->marked_for_compaction);
|
||||
edit_->AddFile(0 /* level */, meta_.fd.GetNumber(), meta_.fd.GetPathId(),
|
||||
meta_.fd.GetFileSize(), meta_.smallest, meta_.largest,
|
||||
meta_.smallest_seqno, meta_.largest_seqno,
|
||||
meta_.marked_for_compaction);
|
||||
}
|
||||
|
||||
// Note that here we treat flush as level 0 compaction in internal stats
|
||||
InternalStats::CompactionStats stats(1);
|
||||
stats.micros = db_options_.env->NowMicros() - start_micros;
|
||||
stats.bytes_written = meta->fd.GetFileSize();
|
||||
stats.bytes_written = meta_.fd.GetFileSize();
|
||||
cfd_->internal_stats()->AddCompactionStats(0 /* level */, stats);
|
||||
cfd_->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
meta->fd.GetFileSize());
|
||||
meta_.fd.GetFileSize());
|
||||
RecordFlushIOStats();
|
||||
return s;
|
||||
}
|
||||
|
||||
+19
-10
@@ -16,12 +16,17 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/column_family.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/job_context.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -29,15 +34,11 @@
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/db_options.h"
|
||||
#include "util/event_logger.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/thread_local.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/write_controller.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "db/job_context.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -53,7 +54,7 @@ class FlushJob {
|
||||
// TODO(icanadi) make effort to reduce number of parameters here
|
||||
// IMPORTANT: mutable_cf_options needs to be alive while FlushJob is alive
|
||||
FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
const DBOptions& db_options,
|
||||
const ImmutableDBOptions& db_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const EnvOptions& env_options, VersionSet* versions,
|
||||
InstrumentedMutex* db_mutex, std::atomic<bool>* shutting_down,
|
||||
@@ -66,6 +67,8 @@ class FlushJob {
|
||||
|
||||
~FlushJob();
|
||||
|
||||
// Require db_mutex held
|
||||
void PickMemTable();
|
||||
Status Run(FileMetaData* file_meta = nullptr);
|
||||
TableProperties GetTableProperties() const { return table_properties_; }
|
||||
|
||||
@@ -73,11 +76,10 @@ class FlushJob {
|
||||
void ReportStartedFlush();
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
void RecordFlushIOStats();
|
||||
Status WriteLevel0Table(const autovector<MemTable*>& mems, VersionEdit* edit,
|
||||
FileMetaData* meta);
|
||||
Status WriteLevel0Table();
|
||||
const std::string& dbname_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const DBOptions& db_options_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
const EnvOptions& env_options_;
|
||||
VersionSet* versions_;
|
||||
@@ -94,6 +96,13 @@ class FlushJob {
|
||||
EventLogger* event_logger_;
|
||||
TableProperties table_properties_;
|
||||
bool measure_io_stats_;
|
||||
|
||||
// Variables below are set by PickMemTable():
|
||||
FileMetaData meta_;
|
||||
autovector<MemTable*> mems_;
|
||||
VersionEdit* edit_;
|
||||
Version* base_;
|
||||
bool pick_memtable_called;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
+11
-2
@@ -28,6 +28,8 @@ class FlushJobTest : public testing::Test {
|
||||
FlushJobTest()
|
||||
: env_(Env::Default()),
|
||||
dbname_(test::TmpDir() + "/flush_job_test"),
|
||||
options_(),
|
||||
db_options_(options_),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
@@ -74,9 +76,10 @@ class FlushJobTest : public testing::Test {
|
||||
Env* env_;
|
||||
std::string dbname_;
|
||||
EnvOptions env_options_;
|
||||
Options options_;
|
||||
ImmutableDBOptions db_options_;
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
WriteController write_controller_;
|
||||
DBOptions db_options_;
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
@@ -94,7 +97,11 @@ TEST_F(FlushJobTest, Empty) {
|
||||
env_options_, versions_.get(), &mutex_, &shutting_down_,
|
||||
{}, kMaxSequenceNumber, &job_context, nullptr, nullptr,
|
||||
nullptr, kNoCompression, nullptr, &event_logger, false);
|
||||
ASSERT_OK(flush_job.Run());
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
|
||||
@@ -135,6 +142,7 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
FileMetaData fd;
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run(&fd));
|
||||
mutex_.Unlock();
|
||||
ASSERT_EQ(ToString(0), fd.smallest.user_key().ToString());
|
||||
@@ -198,6 +206,7 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
&shutting_down_, snapshots, kMaxSequenceNumber, &job_context, nullptr,
|
||||
nullptr, nullptr, kNoCompression, nullptr, &event_logger, true);
|
||||
mutex_.Lock();
|
||||
flush_job.PickMemTable();
|
||||
ASSERT_OK(flush_job.Run());
|
||||
mutex_.Unlock();
|
||||
mock_table_factory_->AssertSingleFile(inserted_keys);
|
||||
|
||||
+160
-27
@@ -32,10 +32,24 @@ namespace rocksdb {
|
||||
class LevelIterator : public InternalIterator {
|
||||
public:
|
||||
LevelIterator(const ColumnFamilyData* const cfd,
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files)
|
||||
: cfd_(cfd), read_options_(read_options), files_(files), valid_(false),
|
||||
file_index_(std::numeric_limits<uint32_t>::max()) {}
|
||||
const ReadOptions& read_options,
|
||||
const std::vector<FileMetaData*>& files)
|
||||
: cfd_(cfd),
|
||||
read_options_(read_options),
|
||||
files_(files),
|
||||
valid_(false),
|
||||
file_index_(std::numeric_limits<uint32_t>::max()),
|
||||
file_iter_(nullptr),
|
||||
pinned_iters_mgr_(nullptr) {}
|
||||
|
||||
~LevelIterator() {
|
||||
// Reset current pointer
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(file_iter_);
|
||||
} else {
|
||||
delete file_iter_;
|
||||
}
|
||||
}
|
||||
|
||||
void SetFileIndex(uint32_t file_index) {
|
||||
assert(file_index < files_.size());
|
||||
@@ -47,10 +61,20 @@ class LevelIterator : public InternalIterator {
|
||||
}
|
||||
void Reset() {
|
||||
assert(file_index_ < files_.size());
|
||||
file_iter_.reset(cfd_->table_cache()->NewIterator(
|
||||
|
||||
// Reset current pointer
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(file_iter_);
|
||||
} else {
|
||||
delete file_iter_;
|
||||
}
|
||||
|
||||
file_iter_ = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *(cfd_->soptions()), cfd_->internal_comparator(),
|
||||
files_[file_index_]->fd, nullptr /* table_reader_ptr */, nullptr,
|
||||
false));
|
||||
false);
|
||||
|
||||
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
void SeekToLast() override {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekToLast()");
|
||||
@@ -73,6 +97,10 @@ class LevelIterator : public InternalIterator {
|
||||
file_iter_->Seek(internal_key);
|
||||
valid_ = file_iter_->Valid();
|
||||
}
|
||||
void SeekForPrev(const Slice& internal_key) override {
|
||||
status_ = Status::NotSupported("LevelIterator::SeekForPrev()");
|
||||
valid_ = false;
|
||||
}
|
||||
void Next() override {
|
||||
assert(valid_);
|
||||
file_iter_->Next();
|
||||
@@ -105,6 +133,20 @@ class LevelIterator : public InternalIterator {
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
bool IsKeyPinned() const override {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
file_iter_->IsKeyPinned();
|
||||
}
|
||||
bool IsValuePinned() const override {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
file_iter_->IsValuePinned();
|
||||
}
|
||||
void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override {
|
||||
pinned_iters_mgr_ = pinned_iters_mgr;
|
||||
if (file_iter_) {
|
||||
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const ColumnFamilyData* const cfd_;
|
||||
@@ -114,7 +156,8 @@ class LevelIterator : public InternalIterator {
|
||||
bool valid_;
|
||||
uint32_t file_index_;
|
||||
Status status_;
|
||||
std::unique_ptr<InternalIterator> file_iter_;
|
||||
InternalIterator* file_iter_;
|
||||
PinnedIteratorsManager* pinned_iters_mgr_;
|
||||
};
|
||||
|
||||
ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
@@ -135,7 +178,8 @@ ForwardIterator::ForwardIterator(DBImpl* db, const ReadOptions& read_options,
|
||||
has_iter_trimmed_for_upper_bound_(false),
|
||||
current_over_upper_bound_(false),
|
||||
is_prev_set_(false),
|
||||
is_prev_inclusive_(false) {
|
||||
is_prev_inclusive_(false),
|
||||
pinned_iters_mgr_(nullptr) {
|
||||
if (sv_) {
|
||||
RebuildIterators(false);
|
||||
}
|
||||
@@ -145,6 +189,13 @@ ForwardIterator::~ForwardIterator() {
|
||||
Cleanup(true);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Used in PinnedIteratorsManager to release pinned SuperVersion
|
||||
static void ReleaseSuperVersionFunc(void* sv) {
|
||||
delete reinterpret_cast<SuperVersion*>(sv);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void ForwardIterator::SVCleanup() {
|
||||
if (sv_ != nullptr && sv_->Unref()) {
|
||||
// Job id == 0 means that this is not our background process, but rather
|
||||
@@ -153,10 +204,18 @@ void ForwardIterator::SVCleanup() {
|
||||
db_->mutex_.Lock();
|
||||
sv_->Cleanup();
|
||||
db_->FindObsoleteFiles(&job_context, false, true);
|
||||
if (read_options_.background_purge_on_iterator_cleanup) {
|
||||
db_->ScheduleBgLogWriterClose(&job_context);
|
||||
}
|
||||
db_->mutex_.Unlock();
|
||||
delete sv_;
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinPtr(sv_, &ReleaseSuperVersionFunc);
|
||||
} else {
|
||||
delete sv_;
|
||||
}
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
db_->PurgeObsoleteFiles(job_context);
|
||||
db_->PurgeObsoleteFiles(
|
||||
job_context, read_options_.background_purge_on_iterator_cleanup);
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
@@ -164,18 +223,21 @@ void ForwardIterator::SVCleanup() {
|
||||
|
||||
void ForwardIterator::Cleanup(bool release_sv) {
|
||||
if (mutable_iter_ != nullptr) {
|
||||
mutable_iter_->~InternalIterator();
|
||||
DeleteIterator(mutable_iter_, true /* is_arena */);
|
||||
}
|
||||
|
||||
for (auto* m : imm_iters_) {
|
||||
m->~InternalIterator();
|
||||
DeleteIterator(m, true /* is_arena */);
|
||||
}
|
||||
imm_iters_.clear();
|
||||
|
||||
for (auto* f : l0_iters_) {
|
||||
delete f;
|
||||
DeleteIterator(f);
|
||||
}
|
||||
l0_iters_.clear();
|
||||
|
||||
for (auto* l : level_iters_) {
|
||||
delete l;
|
||||
DeleteIterator(l);
|
||||
}
|
||||
level_iters_.clear();
|
||||
|
||||
@@ -234,9 +296,15 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
// an option to turn it off.
|
||||
if (seek_to_first || NeedToSeekImmutable(internal_key)) {
|
||||
immutable_status_ = Status::OK();
|
||||
if ((has_iter_trimmed_for_upper_bound_) &&
|
||||
(cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||
prev_key_.GetKey(), internal_key) > 0)) {
|
||||
if (has_iter_trimmed_for_upper_bound_ &&
|
||||
(
|
||||
// prev_ is not set yet
|
||||
is_prev_set_ == false ||
|
||||
// We are doing SeekToFirst() and internal_key.size() = 0
|
||||
seek_to_first ||
|
||||
// prev_key_ > internal_key
|
||||
cfd_->internal_comparator().InternalKeyComparator::Compare(
|
||||
prev_key_.GetKey(), internal_key) > 0)) {
|
||||
// Some iterators are trimmed. Need to rebuild.
|
||||
RebuildIterators(true);
|
||||
// Already seeked mutable iter, so seek again
|
||||
@@ -276,7 +344,7 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
l0[i]->largest.user_key()) > 0) {
|
||||
if (read_options_.iterate_upper_bound != nullptr) {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
delete l0_iters_[i];
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = nullptr;
|
||||
}
|
||||
continue;
|
||||
@@ -291,7 +359,7 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
immutable_min_heap_.push(l0_iters_[i]);
|
||||
} else {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
delete l0_iters_[i];
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -368,7 +436,7 @@ void ForwardIterator::SeekInternal(const Slice& internal_key,
|
||||
} else {
|
||||
// Nothing in this level is interesting. Remove.
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
delete level_iters_[level - 1];
|
||||
DeleteIterator(level_iters_[level - 1]);
|
||||
level_iters_[level - 1] = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -481,6 +549,50 @@ Status ForwardIterator::GetProperty(std::string prop_name, std::string* prop) {
|
||||
return Status::InvalidArgument();
|
||||
}
|
||||
|
||||
void ForwardIterator::SetPinnedItersMgr(
|
||||
PinnedIteratorsManager* pinned_iters_mgr) {
|
||||
pinned_iters_mgr_ = pinned_iters_mgr;
|
||||
UpdateChildrenPinnedItersMgr();
|
||||
}
|
||||
|
||||
void ForwardIterator::UpdateChildrenPinnedItersMgr() {
|
||||
// Set PinnedIteratorsManager for mutable memtable iterator.
|
||||
if (mutable_iter_) {
|
||||
mutable_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
// Set PinnedIteratorsManager for immutable memtable iterators.
|
||||
for (InternalIterator* child_iter : imm_iters_) {
|
||||
if (child_iter) {
|
||||
child_iter->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
}
|
||||
|
||||
// Set PinnedIteratorsManager for L0 files iterators.
|
||||
for (InternalIterator* child_iter : l0_iters_) {
|
||||
if (child_iter) {
|
||||
child_iter->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
}
|
||||
|
||||
// Set PinnedIteratorsManager for L1+ levels iterators.
|
||||
for (LevelIterator* child_iter : level_iters_) {
|
||||
if (child_iter) {
|
||||
child_iter->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ForwardIterator::IsKeyPinned() const {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
current_->IsKeyPinned();
|
||||
}
|
||||
|
||||
bool ForwardIterator::IsValuePinned() const {
|
||||
return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
|
||||
current_->IsValuePinned();
|
||||
}
|
||||
|
||||
void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
// Clean up
|
||||
Cleanup(refresh_sv);
|
||||
@@ -509,6 +621,8 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
BuildLevelIterators(vstorage);
|
||||
current_ = nullptr;
|
||||
is_prev_set_ = false;
|
||||
|
||||
UpdateChildrenPinnedItersMgr();
|
||||
}
|
||||
|
||||
void ForwardIterator::RenewIterators() {
|
||||
@@ -517,10 +631,10 @@ void ForwardIterator::RenewIterators() {
|
||||
svnew = cfd_->GetReferencedSuperVersion(&(db_->mutex_));
|
||||
|
||||
if (mutable_iter_ != nullptr) {
|
||||
mutable_iter_->~InternalIterator();
|
||||
DeleteIterator(mutable_iter_, true /* is_arena */);
|
||||
}
|
||||
for (auto* m : imm_iters_) {
|
||||
m->~InternalIterator();
|
||||
DeleteIterator(m, true /* is_arena */);
|
||||
}
|
||||
imm_iters_.clear();
|
||||
|
||||
@@ -561,13 +675,13 @@ void ForwardIterator::RenewIterators() {
|
||||
}
|
||||
|
||||
for (auto* f : l0_iters_) {
|
||||
delete f;
|
||||
DeleteIterator(f);
|
||||
}
|
||||
l0_iters_.clear();
|
||||
l0_iters_ = l0_iters_new;
|
||||
|
||||
for (auto* l : level_iters_) {
|
||||
delete l;
|
||||
DeleteIterator(l);
|
||||
}
|
||||
level_iters_.clear();
|
||||
BuildLevelIterators(vstorage_new);
|
||||
@@ -575,6 +689,8 @@ void ForwardIterator::RenewIterators() {
|
||||
is_prev_set_ = false;
|
||||
SVCleanup();
|
||||
sv_ = svnew;
|
||||
|
||||
UpdateChildrenPinnedItersMgr();
|
||||
}
|
||||
|
||||
void ForwardIterator::BuildLevelIterators(const VersionStorageInfo* vstorage) {
|
||||
@@ -604,10 +720,11 @@ void ForwardIterator::ResetIncompleteIterators() {
|
||||
if (!l0_iters_[i] || !l0_iters_[i]->status().IsIncomplete()) {
|
||||
continue;
|
||||
}
|
||||
delete l0_iters_[i];
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = cfd_->table_cache()->NewIterator(
|
||||
read_options_, *cfd_->soptions(), cfd_->internal_comparator(),
|
||||
l0_files[i]->fd);
|
||||
l0_iters_[i]->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
for (auto* level_iter : level_iters_) {
|
||||
@@ -696,7 +813,7 @@ void ForwardIterator::DeleteCurrentIter() {
|
||||
}
|
||||
if (l0_iters_[i] == current_) {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
delete l0_iters_[i];
|
||||
DeleteIterator(l0_iters_[i]);
|
||||
l0_iters_[i] = nullptr;
|
||||
return;
|
||||
}
|
||||
@@ -708,7 +825,7 @@ void ForwardIterator::DeleteCurrentIter() {
|
||||
}
|
||||
if (level_iters_[level - 1] == current_) {
|
||||
has_iter_trimmed_for_upper_bound_ = true;
|
||||
delete level_iters_[level - 1];
|
||||
DeleteIterator(level_iters_[level - 1]);
|
||||
level_iters_[level - 1] = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -772,6 +889,22 @@ uint32_t ForwardIterator::FindFileInRange(
|
||||
return right;
|
||||
}
|
||||
|
||||
void ForwardIterator::DeleteIterator(InternalIterator* iter, bool is_arena) {
|
||||
if (iter == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled()) {
|
||||
pinned_iters_mgr_->PinIterator(iter, is_arena);
|
||||
} else {
|
||||
if (is_arena) {
|
||||
iter->~InternalIterator();
|
||||
} else {
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -55,6 +55,10 @@ class ForwardIterator : public InternalIterator {
|
||||
ColumnFamilyData* cfd, SuperVersion* current_sv = nullptr);
|
||||
virtual ~ForwardIterator();
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
status_ = Status::NotSupported("ForwardIterator::SeekForPrev()");
|
||||
valid_ = false;
|
||||
}
|
||||
void SeekToLast() override {
|
||||
status_ = Status::NotSupported("ForwardIterator::SeekToLast()");
|
||||
valid_ = false;
|
||||
@@ -72,6 +76,10 @@ class ForwardIterator : public InternalIterator {
|
||||
virtual Slice value() const override;
|
||||
virtual Status status() const override;
|
||||
virtual Status GetProperty(std::string prop_name, std::string* prop) override;
|
||||
virtual void SetPinnedItersMgr(
|
||||
PinnedIteratorsManager* pinned_iters_mgr) override;
|
||||
virtual bool IsKeyPinned() const override;
|
||||
virtual bool IsValuePinned() const override;
|
||||
|
||||
bool TEST_CheckDeletedIters(int* deleted_iters, int* num_iters);
|
||||
|
||||
@@ -92,6 +100,14 @@ class ForwardIterator : public InternalIterator {
|
||||
|
||||
bool IsOverUpperBound(const Slice& internal_key) const;
|
||||
|
||||
// Set PinnedIteratorsManager for all children Iterators, this function should
|
||||
// be called whenever we update children Iterators or pinned_iters_mgr_.
|
||||
void UpdateChildrenPinnedItersMgr();
|
||||
|
||||
// A helper function that will release iter in the proper manner, or pass it
|
||||
// to pinned_iters_mgr_ to release it later if pinning is enabled.
|
||||
void DeleteIterator(InternalIterator* iter, bool is_arena = false);
|
||||
|
||||
DBImpl* const db_;
|
||||
const ReadOptions read_options_;
|
||||
ColumnFamilyData* const cfd_;
|
||||
@@ -129,6 +145,7 @@ class ForwardIterator : public InternalIterator {
|
||||
bool is_prev_set_;
|
||||
bool is_prev_inclusive_;
|
||||
|
||||
PinnedIteratorsManager* pinned_iters_mgr_;
|
||||
Arena arena_;
|
||||
};
|
||||
|
||||
|
||||
@@ -116,6 +116,9 @@ class InlineSkipList {
|
||||
// Advance to the first entry with a key >= target
|
||||
void Seek(const char* target);
|
||||
|
||||
// Retreat to the last entry with a key <= target
|
||||
void SeekForPrev(const char* target);
|
||||
|
||||
// Position at the first entry in list.
|
||||
// Final state of iterator is Valid() iff list is not empty.
|
||||
void SeekToFirst();
|
||||
@@ -167,6 +170,10 @@ class InlineSkipList {
|
||||
return (compare_(a, b) == 0);
|
||||
}
|
||||
|
||||
bool LessThan(const char* a, const char* b) const {
|
||||
return (compare_(a, b) < 0);
|
||||
}
|
||||
|
||||
// Return true if key is greater than the data stored in "n". Null n
|
||||
// is considered infinite.
|
||||
bool KeyIsAfterNode(const char* key, Node* n) const;
|
||||
@@ -310,6 +317,18 @@ inline void InlineSkipList<Comparator>::Iterator::Seek(const char* target) {
|
||||
node_ = list_->FindGreaterOrEqual(target);
|
||||
}
|
||||
|
||||
template <class Comparator>
|
||||
inline void InlineSkipList<Comparator>::Iterator::SeekForPrev(
|
||||
const char* target) {
|
||||
Seek(target);
|
||||
if (!Valid()) {
|
||||
SeekToLast();
|
||||
}
|
||||
while (Valid() && list_->LessThan(target, key())) {
|
||||
Prev();
|
||||
}
|
||||
}
|
||||
|
||||
template <class Comparator>
|
||||
inline void InlineSkipList<Comparator>::Iterator::SeekToFirst() {
|
||||
node_ = list_->head_->Next(0);
|
||||
|
||||
@@ -58,6 +58,8 @@ TEST_F(InlineSkipTest, Empty) {
|
||||
key = 100;
|
||||
iter.Seek(Encode(&key));
|
||||
ASSERT_TRUE(!iter.Valid());
|
||||
iter.SeekForPrev(Encode(&key));
|
||||
ASSERT_TRUE(!iter.Valid());
|
||||
iter.SeekToLast();
|
||||
ASSERT_TRUE(!iter.Valid());
|
||||
}
|
||||
@@ -97,6 +99,11 @@ TEST_F(InlineSkipTest, InsertAndLookup) {
|
||||
ASSERT_TRUE(iter.Valid());
|
||||
ASSERT_EQ(*(keys.begin()), Decode(iter.key()));
|
||||
|
||||
uint64_t max_key = R - 1;
|
||||
iter.SeekForPrev(Encode(&max_key));
|
||||
ASSERT_TRUE(iter.Valid());
|
||||
ASSERT_EQ(*(keys.rbegin()), Decode(iter.key()));
|
||||
|
||||
iter.SeekToFirst();
|
||||
ASSERT_TRUE(iter.Valid());
|
||||
ASSERT_EQ(*(keys.begin()), Decode(iter.key()));
|
||||
@@ -127,18 +134,22 @@ TEST_F(InlineSkipTest, InsertAndLookup) {
|
||||
}
|
||||
|
||||
// Backward iteration test
|
||||
{
|
||||
for (Key i = 0; i < R; i++) {
|
||||
InlineSkipList<TestComparator>::Iterator iter(&list);
|
||||
iter.SeekToLast();
|
||||
iter.SeekForPrev(Encode(&i));
|
||||
|
||||
// Compare against model iterator
|
||||
for (std::set<Key>::reverse_iterator model_iter = keys.rbegin();
|
||||
model_iter != keys.rend(); ++model_iter) {
|
||||
ASSERT_TRUE(iter.Valid());
|
||||
ASSERT_EQ(*model_iter, Decode(iter.key()));
|
||||
iter.Prev();
|
||||
std::set<Key>::iterator model_iter = keys.upper_bound(i);
|
||||
for (int j = 0; j < 3; j++) {
|
||||
if (model_iter == keys.begin()) {
|
||||
ASSERT_TRUE(!iter.Valid());
|
||||
break;
|
||||
} else {
|
||||
ASSERT_TRUE(iter.Valid());
|
||||
ASSERT_EQ(*--model_iter, Decode(iter.key()));
|
||||
iter.Prev();
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(!iter.Valid());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-11
@@ -615,15 +615,15 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
// Data
|
||||
// writes: total number of write requests.
|
||||
// keys: total number of key updates issued by all the write requests
|
||||
// batches: number of group commits issued to the DB. Each group can contain
|
||||
// one or more writes.
|
||||
// commit groups: number of group commits issued to the DB. Each group can
|
||||
// contain one or more writes.
|
||||
// so writes/keys is the average number of put in multi-put or put
|
||||
// writes/batches is the average group commit size.
|
||||
// writes/groups is the average group commit size.
|
||||
//
|
||||
// The format is the same for interval stats.
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Cumulative writes: %s writes, %s keys, %s batches, "
|
||||
"%.1f writes per batch, ingest: %.2f GB, %.2f MB/s\n",
|
||||
"Cumulative writes: %s writes, %s keys, %s commit groups, "
|
||||
"%.1f writes per commit group, ingest: %.2f GB, %.2f MB/s\n",
|
||||
NumberToHumanString(write_other + write_self).c_str(),
|
||||
NumberToHumanString(num_keys_written).c_str(),
|
||||
NumberToHumanString(write_self).c_str(),
|
||||
@@ -654,8 +654,8 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
uint64_t interval_num_keys_written =
|
||||
num_keys_written - db_stats_snapshot_.num_keys_written;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Interval writes: %s writes, %s keys, %s batches, "
|
||||
"%.1f writes per batch, ingest: %.2f MB, %.2f MB/s\n",
|
||||
"Interval writes: %s writes, %s keys, %s commit groups, "
|
||||
"%.1f writes per commit group, ingest: %.2f MB, %.2f MB/s\n",
|
||||
NumberToHumanString(
|
||||
interval_write_other + interval_write_self).c_str(),
|
||||
NumberToHumanString(interval_num_keys_written).c_str(),
|
||||
@@ -769,10 +769,13 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
value->append(buf);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t flush_ingest = cf_stats_value_[BYTES_FLUSHED];
|
||||
uint64_t add_file_ingest = cf_stats_value_[BYTES_INGESTED_ADD_FILE];
|
||||
uint64_t curr_ingest = flush_ingest + add_file_ingest;
|
||||
uint64_t ingest_files_addfile = cf_stats_value_[INGESTED_NUM_FILES_TOTAL];
|
||||
uint64_t ingest_l0_files_addfile =
|
||||
cf_stats_value_[INGESTED_LEVEL0_NUM_FILES_TOTAL];
|
||||
uint64_t ingest_keys_addfile = cf_stats_value_[INGESTED_NUM_KEYS_TOTAL];
|
||||
// Cumulative summary
|
||||
double w_amp = stats_sum.bytes_written / static_cast<double>(curr_ingest + 1);
|
||||
uint64_t total_stall_count =
|
||||
@@ -790,7 +793,7 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
uint64_t interval_flush_ingest =
|
||||
flush_ingest - cf_stats_snapshot_.ingest_bytes_flush;
|
||||
uint64_t interval_add_file_inget =
|
||||
add_file_ingest - cf_stats_snapshot_.ingest_bytes_add_file;
|
||||
add_file_ingest - cf_stats_snapshot_.ingest_bytes_addfile;
|
||||
uint64_t interval_ingest =
|
||||
interval_flush_ingest + interval_add_file_inget + 1;
|
||||
CompactionStats interval_stats(stats_sum);
|
||||
@@ -804,13 +807,34 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
snprintf(buf, sizeof(buf), "Uptime(secs): %.1f total, %.1f interval\n",
|
||||
seconds_up, interval_seconds_up);
|
||||
value->append(buf);
|
||||
|
||||
snprintf(buf, sizeof(buf), "Flush(GB): cumulative %.3f, interval %.3f\n",
|
||||
flush_ingest / kGB, interval_flush_ingest / kGB);
|
||||
value->append(buf);
|
||||
snprintf(buf, sizeof(buf), "AddFile(GB): cumulative %.3f, interval %.3f\n",
|
||||
add_file_ingest / kGB, interval_add_file_inget / kGB);
|
||||
value->append(buf);
|
||||
|
||||
uint64_t interval_ingest_files_addfile =
|
||||
ingest_files_addfile - cf_stats_snapshot_.ingest_files_addfile;
|
||||
snprintf(buf, sizeof(buf), "AddFile(Total Files): cumulative %" PRIu64
|
||||
", interval %" PRIu64 "\n",
|
||||
ingest_files_addfile, interval_ingest_files_addfile);
|
||||
value->append(buf);
|
||||
|
||||
uint64_t interval_ingest_l0_files_addfile =
|
||||
ingest_l0_files_addfile - cf_stats_snapshot_.ingest_l0_files_addfile;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"AddFile(L0 Files): cumulative %" PRIu64 ", interval %" PRIu64 "\n",
|
||||
ingest_l0_files_addfile, interval_ingest_l0_files_addfile);
|
||||
value->append(buf);
|
||||
|
||||
uint64_t interval_ingest_keys_addfile =
|
||||
ingest_keys_addfile - cf_stats_snapshot_.ingest_keys_addfile;
|
||||
snprintf(buf, sizeof(buf),
|
||||
"AddFile(Keys): cumulative %" PRIu64 ", interval %" PRIu64 "\n",
|
||||
ingest_keys_addfile, interval_ingest_keys_addfile);
|
||||
value->append(buf);
|
||||
|
||||
// Compact
|
||||
uint64_t compact_bytes_read = 0;
|
||||
uint64_t compact_bytes_write = 0;
|
||||
@@ -881,7 +905,10 @@ void InternalStats::DumpCFStats(std::string* value) {
|
||||
value->append(buf);
|
||||
|
||||
cf_stats_snapshot_.ingest_bytes_flush = flush_ingest;
|
||||
cf_stats_snapshot_.ingest_bytes_add_file = add_file_ingest;
|
||||
cf_stats_snapshot_.ingest_bytes_addfile = add_file_ingest;
|
||||
cf_stats_snapshot_.ingest_files_addfile = ingest_files_addfile;
|
||||
cf_stats_snapshot_.ingest_l0_files_addfile = ingest_l0_files_addfile;
|
||||
cf_stats_snapshot_.ingest_keys_addfile = ingest_keys_addfile;
|
||||
cf_stats_snapshot_.comp_stats = stats_sum;
|
||||
cf_stats_snapshot_.stall_count = total_stall_count;
|
||||
}
|
||||
|
||||
+17
-3
@@ -60,6 +60,9 @@ class InternalStats {
|
||||
WRITE_STALLS_ENUM_MAX,
|
||||
BYTES_FLUSHED,
|
||||
BYTES_INGESTED_ADD_FILE,
|
||||
INGESTED_NUM_FILES_TOTAL,
|
||||
INGESTED_LEVEL0_NUM_FILES_TOTAL,
|
||||
INGESTED_NUM_KEYS_TOTAL,
|
||||
INTERNAL_CF_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
@@ -246,7 +249,6 @@ class InternalStats {
|
||||
// ColumnFamily-level stats
|
||||
CompactionStats comp_stats;
|
||||
uint64_t ingest_bytes_flush; // Bytes written to L0 (Flush)
|
||||
uint64_t ingest_bytes_add_file; // Bytes written to L0 (AddFile)
|
||||
uint64_t stall_count; // Stall count
|
||||
// Stats from compaction jobs - bytes written, bytes read, duration.
|
||||
uint64_t compact_bytes_write;
|
||||
@@ -254,15 +256,24 @@ class InternalStats {
|
||||
uint64_t compact_micros;
|
||||
double seconds_up;
|
||||
|
||||
// AddFile specific stats
|
||||
uint64_t ingest_bytes_addfile; // Total Bytes ingested
|
||||
uint64_t ingest_files_addfile; // Total number of files ingested
|
||||
uint64_t ingest_l0_files_addfile; // Total number of files ingested to L0
|
||||
uint64_t ingest_keys_addfile; // Total number of keys ingested
|
||||
|
||||
CFStatsSnapshot()
|
||||
: comp_stats(0),
|
||||
ingest_bytes_flush(0),
|
||||
ingest_bytes_add_file(0),
|
||||
stall_count(0),
|
||||
compact_bytes_write(0),
|
||||
compact_bytes_read(0),
|
||||
compact_micros(0),
|
||||
seconds_up(0) {}
|
||||
seconds_up(0),
|
||||
ingest_bytes_addfile(0),
|
||||
ingest_files_addfile(0),
|
||||
ingest_l0_files_addfile(0),
|
||||
ingest_keys_addfile(0) {}
|
||||
} cf_stats_snapshot_;
|
||||
|
||||
struct DBStatsSnapshot {
|
||||
@@ -376,6 +387,9 @@ class InternalStats {
|
||||
WRITE_STALLS_ENUM_MAX,
|
||||
BYTES_FLUSHED,
|
||||
BYTES_INGESTED_ADD_FILE,
|
||||
INGESTED_NUM_FILES_TOTAL,
|
||||
INGESTED_LEVEL0_NUM_FILES_TOTAL,
|
||||
INGESTED_NUM_KEYS_TOTAL,
|
||||
INTERNAL_CF_STATS_ENUM_MAX,
|
||||
};
|
||||
|
||||
|
||||
+4
-1
@@ -12,7 +12,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/log_writer.h"
|
||||
|
||||
namespace rocksdb {
|
||||
@@ -56,6 +55,10 @@ struct JobContext {
|
||||
// a list of log files that we need to delete
|
||||
std::vector<uint64_t> log_delete_files;
|
||||
|
||||
// a list of log files that we need to preserve during full purge since they
|
||||
// will be reused later
|
||||
std::vector<uint64_t> log_recycle_files;
|
||||
|
||||
// a list of manifest files that we need to delete
|
||||
std::vector<std::string> manifest_delete_files;
|
||||
|
||||
|
||||
+36
-1
@@ -754,8 +754,43 @@ TEST_F(EventListenerTest, MemTableSealedListenerTest) {
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
} // namespace rocksdb
|
||||
|
||||
class ColumnFamilyHandleDeletionStartedListener : public EventListener {
|
||||
private:
|
||||
std::vector<std::string> cfs_;
|
||||
int counter;
|
||||
|
||||
public:
|
||||
explicit ColumnFamilyHandleDeletionStartedListener(
|
||||
const std::vector<std::string>& cfs)
|
||||
: cfs_(cfs), counter(0) {
|
||||
cfs_.insert(cfs_.begin(), kDefaultColumnFamilyName);
|
||||
}
|
||||
void OnColumnFamilyHandleDeletionStarted(
|
||||
ColumnFamilyHandle* handle) override {
|
||||
ASSERT_EQ(cfs_[handle->GetID()], handle->GetName());
|
||||
counter++;
|
||||
}
|
||||
int getCounter() { return counter; }
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, ColumnFamilyHandleDeletionStartedListenerTest) {
|
||||
std::vector<std::string> cfs{"pikachu", "eevee", "Mewtwo"};
|
||||
auto listener =
|
||||
std::make_shared<ColumnFamilyHandleDeletionStartedListener>(cfs);
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.listeners.push_back(listener);
|
||||
CreateAndReopenWithCF(cfs, options);
|
||||
ASSERT_EQ(handles_.size(), 4);
|
||||
delete handles_[3];
|
||||
delete handles_[2];
|
||||
delete handles_[1];
|
||||
handles_.resize(1);
|
||||
ASSERT_EQ(listener->getCounter(), 3);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
|
||||
@@ -81,6 +81,8 @@ class Writer {
|
||||
WritableFileWriter* file() { return dest_.get(); }
|
||||
const WritableFileWriter* file() const { return dest_.get(); }
|
||||
|
||||
uint64_t get_log_number() const { return log_number_; }
|
||||
|
||||
private:
|
||||
unique_ptr<WritableFileWriter> dest_;
|
||||
size_t block_offset_; // Current offset in block
|
||||
|
||||
@@ -122,6 +122,16 @@ void ManagedIterator::Seek(const Slice& user_key) {
|
||||
SeekInternal(user_key, false);
|
||||
}
|
||||
|
||||
void ManagedIterator::SeekForPrev(const Slice& user_key) {
|
||||
MILock l(&in_use_, this);
|
||||
if (NeedToRebuild()) {
|
||||
RebuildIterator();
|
||||
}
|
||||
assert(mutable_iter_ != nullptr);
|
||||
mutable_iter_->SeekForPrev(user_key);
|
||||
UpdateCurrent();
|
||||
}
|
||||
|
||||
void ManagedIterator::SeekInternal(const Slice& user_key, bool seek_to_first) {
|
||||
if (NeedToRebuild()) {
|
||||
RebuildIterator();
|
||||
|
||||
@@ -43,6 +43,7 @@ class ManagedIterator : public Iterator {
|
||||
virtual bool Valid() const override;
|
||||
void SeekToFirst() override;
|
||||
virtual void Seek(const Slice& target) override;
|
||||
virtual void SeekForPrev(const Slice& target) override;
|
||||
virtual void Next() override;
|
||||
virtual Slice key() const override;
|
||||
virtual Slice value() const override;
|
||||
|
||||
+67
-18
@@ -44,8 +44,7 @@ MemTableOptions::MemTableOptions(const ImmutableCFOptions& ioptions,
|
||||
static_cast<double>(mutable_cf_options.write_buffer_size) *
|
||||
mutable_cf_options.memtable_prefix_bloom_size_ratio) *
|
||||
8u),
|
||||
memtable_prefix_bloom_huge_page_tlb_size(
|
||||
mutable_cf_options.memtable_prefix_bloom_huge_page_tlb_size),
|
||||
memtable_huge_page_size(mutable_cf_options.memtable_huge_page_size),
|
||||
inplace_update_support(ioptions.inplace_update_support),
|
||||
inplace_update_num_locks(mutable_cf_options.inplace_update_num_locks),
|
||||
inplace_callback(ioptions.inplace_callback),
|
||||
@@ -63,11 +62,15 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
moptions_(ioptions, mutable_cf_options),
|
||||
refs_(0),
|
||||
kArenaBlockSize(OptimizeBlockSize(moptions_.arena_block_size)),
|
||||
arena_(moptions_.arena_block_size, 0),
|
||||
arena_(moptions_.arena_block_size,
|
||||
mutable_cf_options.memtable_huge_page_size),
|
||||
allocator_(&arena_, write_buffer_manager),
|
||||
table_(ioptions.memtable_factory->CreateMemTableRep(
|
||||
comparator_, &allocator_, ioptions.prefix_extractor,
|
||||
ioptions.info_log)),
|
||||
range_del_table_(SkipListFactory().CreateMemTableRep(
|
||||
comparator_, &allocator_, nullptr /* transform */,
|
||||
ioptions.info_log)),
|
||||
data_size_(0),
|
||||
num_entries_(0),
|
||||
num_deletes_(0),
|
||||
@@ -92,7 +95,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
prefix_bloom_.reset(new DynamicBloom(
|
||||
&allocator_, moptions_.memtable_prefix_bloom_bits,
|
||||
ioptions.bloom_locality, 6 /* hard coded 6 probes */, nullptr,
|
||||
moptions_.memtable_prefix_bloom_huge_page_tlb_size, ioptions.info_log));
|
||||
moptions_.memtable_huge_page_size, ioptions.info_log));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +104,7 @@ MemTable::~MemTable() { assert(refs_ == 0); }
|
||||
size_t MemTable::ApproximateMemoryUsage() {
|
||||
size_t arena_usage = arena_.ApproximateMemoryUsage();
|
||||
size_t table_usage = table_->ApproximateMemoryUsage();
|
||||
table_usage += range_del_table_->ApproximateMemoryUsage();
|
||||
// let MAX_USAGE = std::numeric_limits<size_t>::max()
|
||||
// then if arena_usage + total_usage >= MAX_USAGE, return MAX_USAGE.
|
||||
// the following variation is to avoid numeric overflow.
|
||||
@@ -122,8 +126,9 @@ bool MemTable::ShouldFlushNow() const {
|
||||
|
||||
// If arena still have room for new block allocation, we can safely say it
|
||||
// shouldn't flush.
|
||||
auto allocated_memory =
|
||||
table_->ApproximateMemoryUsage() + arena_.MemoryAllocatedBytes();
|
||||
auto allocated_memory = table_->ApproximateMemoryUsage() +
|
||||
range_del_table_->ApproximateMemoryUsage() +
|
||||
arena_.MemoryAllocatedBytes();
|
||||
|
||||
// if we can still allocate one more block without exceeding the
|
||||
// over-allocation ratio, then we should not flush.
|
||||
@@ -218,13 +223,17 @@ const char* EncodeKey(std::string* scratch, const Slice& target) {
|
||||
|
||||
class MemTableIterator : public InternalIterator {
|
||||
public:
|
||||
MemTableIterator(
|
||||
const MemTable& mem, const ReadOptions& read_options, Arena* arena)
|
||||
MemTableIterator(const MemTable& mem, const ReadOptions& read_options,
|
||||
Arena* arena, bool use_range_del_table = false)
|
||||
: bloom_(nullptr),
|
||||
prefix_extractor_(mem.prefix_extractor_),
|
||||
comparator_(mem.comparator_),
|
||||
valid_(false),
|
||||
arena_mode_(arena != nullptr) {
|
||||
if (prefix_extractor_ != nullptr && !read_options.total_order_seek) {
|
||||
arena_mode_(arena != nullptr),
|
||||
value_pinned_(!mem.GetMemTableOptions()->inplace_update_support) {
|
||||
if (use_range_del_table) {
|
||||
iter_ = mem.range_del_table_->GetIterator(arena);
|
||||
} else if (prefix_extractor_ != nullptr && !read_options.total_order_seek) {
|
||||
bloom_ = mem.prefix_bloom_.get();
|
||||
iter_ = mem.table_->GetDynamicPrefixIterator(arena);
|
||||
} else {
|
||||
@@ -271,6 +280,28 @@ class MemTableIterator : public InternalIterator {
|
||||
iter_->Seek(k, nullptr);
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
virtual void SeekForPrev(const Slice& k) override {
|
||||
PERF_TIMER_GUARD(seek_on_memtable_time);
|
||||
PERF_COUNTER_ADD(seek_on_memtable_count, 1);
|
||||
if (bloom_ != nullptr) {
|
||||
if (!bloom_->MayContain(
|
||||
prefix_extractor_->Transform(ExtractUserKey(k)))) {
|
||||
PERF_COUNTER_ADD(bloom_memtable_miss_count, 1);
|
||||
valid_ = false;
|
||||
return;
|
||||
} else {
|
||||
PERF_COUNTER_ADD(bloom_memtable_hit_count, 1);
|
||||
}
|
||||
}
|
||||
iter_->Seek(k, nullptr);
|
||||
valid_ = iter_->Valid();
|
||||
if (!Valid()) {
|
||||
SeekToLast();
|
||||
}
|
||||
while (Valid() && comparator_.comparator.Compare(k, key()) < 0) {
|
||||
Prev();
|
||||
}
|
||||
}
|
||||
virtual void SeekToFirst() override {
|
||||
iter_->SeekToFirst();
|
||||
valid_ = iter_->Valid();
|
||||
@@ -306,12 +337,19 @@ class MemTableIterator : public InternalIterator {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool IsValuePinned() const override {
|
||||
// memtable value is always pinned, except if we allow inplace update.
|
||||
return value_pinned_;
|
||||
}
|
||||
|
||||
private:
|
||||
DynamicBloom* bloom_;
|
||||
const SliceTransform* const prefix_extractor_;
|
||||
const MemTable::KeyComparator comparator_;
|
||||
MemTableRep::Iterator* iter_;
|
||||
bool valid_;
|
||||
bool arena_mode_;
|
||||
bool value_pinned_;
|
||||
|
||||
// No copying allowed
|
||||
MemTableIterator(const MemTableIterator&);
|
||||
@@ -325,6 +363,14 @@ InternalIterator* MemTable::NewIterator(const ReadOptions& read_options,
|
||||
return new (mem) MemTableIterator(*this, read_options, arena);
|
||||
}
|
||||
|
||||
InternalIterator* MemTable::NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options, Arena* arena) {
|
||||
assert(arena != nullptr);
|
||||
auto mem = arena->AllocateAligned(sizeof(MemTableIterator));
|
||||
return new (mem) MemTableIterator(*this, read_options, arena,
|
||||
true /* use_range_del_table */);
|
||||
}
|
||||
|
||||
port::RWMutex* MemTable::GetLock(const Slice& key) {
|
||||
static murmur_hash hash;
|
||||
return &locks_[hash(key) % locks_.size()];
|
||||
@@ -333,6 +379,7 @@ port::RWMutex* MemTable::GetLock(const Slice& key) {
|
||||
uint64_t MemTable::ApproximateSize(const Slice& start_ikey,
|
||||
const Slice& end_ikey) {
|
||||
uint64_t entry_count = table_->ApproximateNumEntries(start_ikey, end_ikey);
|
||||
entry_count += range_del_table_->ApproximateNumEntries(start_ikey, end_ikey);
|
||||
if (entry_count == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -341,9 +388,9 @@ uint64_t MemTable::ApproximateSize(const Slice& start_ikey,
|
||||
return 0;
|
||||
}
|
||||
if (entry_count > n) {
|
||||
// table_->ApproximateNumEntries() is just an estimate so it can be larger
|
||||
// than actual entries we have. Cap it to entries we have to limit the
|
||||
// inaccuracy.
|
||||
// (range_del_)table_->ApproximateNumEntries() is just an estimate so it can
|
||||
// be larger than actual entries we have. Cap it to entries we have to limit
|
||||
// the inaccuracy.
|
||||
entry_count = n;
|
||||
}
|
||||
uint64_t data_size = data_size_.load(std::memory_order_relaxed);
|
||||
@@ -366,7 +413,9 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
internal_key_size + VarintLength(val_size) +
|
||||
val_size;
|
||||
char* buf = nullptr;
|
||||
KeyHandle handle = table_->Allocate(encoded_len, &buf);
|
||||
std::unique_ptr<MemTableRep>& table =
|
||||
type == kTypeRangeDeletion ? range_del_table_ : table_;
|
||||
KeyHandle handle = table->Allocate(encoded_len, &buf);
|
||||
|
||||
char* p = EncodeVarint32(buf, internal_key_size);
|
||||
memcpy(p, key.data(), key_size);
|
||||
@@ -378,7 +427,7 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
memcpy(p, value.data(), val_size);
|
||||
assert((unsigned)(p + val_size - buf) == (unsigned)encoded_len);
|
||||
if (!allow_concurrent) {
|
||||
table_->Insert(handle);
|
||||
table->Insert(handle);
|
||||
|
||||
// this is a bit ugly, but is the way to avoid locked instructions
|
||||
// when incrementing an atomic
|
||||
@@ -410,7 +459,7 @@ void MemTable::Add(SequenceNumber s, ValueType type,
|
||||
assert(post_process_info == nullptr);
|
||||
UpdateFlushState();
|
||||
} else {
|
||||
table_->InsertConcurrently(handle);
|
||||
table->InsertConcurrently(handle);
|
||||
|
||||
assert(post_process_info != nullptr);
|
||||
post_process_info->num_entries++;
|
||||
@@ -508,7 +557,6 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion: {
|
||||
if (*(s->merge_in_progress)) {
|
||||
*(s->status) = Status::OK();
|
||||
*(s->status) = MergeHelper::TimedFullMerge(
|
||||
merge_operator, s->key->user_key(), nullptr,
|
||||
merge_context->GetOperands(), s->value, s->logger, s->statistics,
|
||||
@@ -532,7 +580,8 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
}
|
||||
Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
|
||||
*(s->merge_in_progress) = true;
|
||||
merge_context->PushOperand(v);
|
||||
merge_context->PushOperand(
|
||||
v, s->inplace_update_support == false /* operand_pinned */);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
||||
+11
-6
@@ -15,17 +15,16 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "db/memtable_allocator.h"
|
||||
#include "db/skiplist.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/memtablerep.h"
|
||||
#include "rocksdb/immutable_options.h"
|
||||
#include "db/memtable_allocator.h"
|
||||
#include "util/cf_options.h"
|
||||
#include "util/concurrent_arena.h"
|
||||
#include "util/dynamic_bloom.h"
|
||||
#include "util/instrumented_mutex.h"
|
||||
#include "util/mutable_cf_options.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
@@ -41,7 +40,7 @@ struct MemTableOptions {
|
||||
size_t write_buffer_size;
|
||||
size_t arena_block_size;
|
||||
uint32_t memtable_prefix_bloom_bits;
|
||||
size_t memtable_prefix_bloom_huge_page_tlb_size;
|
||||
size_t memtable_huge_page_size;
|
||||
bool inplace_update_support;
|
||||
size_t inplace_update_num_locks;
|
||||
UpdateStatus (*inplace_callback)(char* existing_value,
|
||||
@@ -159,6 +158,9 @@ class MemTable {
|
||||
// those allocated in arena.
|
||||
InternalIterator* NewIterator(const ReadOptions& read_options, Arena* arena);
|
||||
|
||||
InternalIterator* NewRangeTombstoneIterator(const ReadOptions& read_options,
|
||||
Arena* arena);
|
||||
|
||||
// Add an entry into memtable that maps key to value at the
|
||||
// specified sequence number and with the specified type.
|
||||
// Typically value will be empty if type==kTypeDeletion.
|
||||
@@ -232,8 +234,10 @@ class MemTable {
|
||||
num_entries_.fetch_add(update_counters.num_entries,
|
||||
std::memory_order_relaxed);
|
||||
data_size_.fetch_add(update_counters.data_size, std::memory_order_relaxed);
|
||||
num_deletes_.fetch_add(update_counters.num_deletes,
|
||||
std::memory_order_relaxed);
|
||||
if (update_counters.num_deletes != 0) {
|
||||
num_deletes_.fetch_add(update_counters.num_deletes,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
UpdateFlushState();
|
||||
}
|
||||
|
||||
@@ -343,6 +347,7 @@ class MemTable {
|
||||
ConcurrentArena arena_;
|
||||
MemTableAllocator allocator_;
|
||||
unique_ptr<MemTableRep> table_;
|
||||
unique_ptr<MemTableRep> range_del_table_;
|
||||
|
||||
// Total data size of all data inserted
|
||||
std::atomic<uint64_t> data_size_;
|
||||
|
||||
+22
-25
@@ -57,14 +57,16 @@ class MemTableListTest : public testing::Test {
|
||||
|
||||
// Create a mock VersionSet
|
||||
DBOptions db_options;
|
||||
ImmutableDBOptions immutable_db_options(db_options);
|
||||
EnvOptions env_options;
|
||||
shared_ptr<Cache> table_cache(NewLRUCache(50000, 16));
|
||||
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size);
|
||||
WriteController write_controller(10000000u);
|
||||
|
||||
CreateDB();
|
||||
VersionSet versions(dbname, &db_options, env_options, table_cache.get(),
|
||||
&write_buffer_manager, &write_controller);
|
||||
VersionSet versions(dbname, &immutable_db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller);
|
||||
|
||||
// Create mock default ColumnFamilyData
|
||||
ColumnFamilyOptions cf_options;
|
||||
@@ -127,9 +129,8 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
ImmutableCFOptions ioptions(options);
|
||||
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
MemTable* mem =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb,
|
||||
kMaxSequenceNumber);
|
||||
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber);
|
||||
mem->Ref();
|
||||
|
||||
// Write some keys to this memtable.
|
||||
@@ -164,9 +165,8 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
|
||||
// Create another memtable and write some keys to it
|
||||
WriteBufferManager wb2(options.db_write_buffer_size);
|
||||
MemTable* mem2 =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb2,
|
||||
kMaxSequenceNumber);
|
||||
MemTable* mem2 = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb2,
|
||||
kMaxSequenceNumber);
|
||||
mem2->Ref();
|
||||
|
||||
mem2->Add(++seq, kTypeDeletion, "key1", "");
|
||||
@@ -229,9 +229,8 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
ImmutableCFOptions ioptions(options);
|
||||
|
||||
WriteBufferManager wb(options.db_write_buffer_size);
|
||||
MemTable* mem =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb,
|
||||
kMaxSequenceNumber);
|
||||
MemTable* mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber);
|
||||
mem->Ref();
|
||||
|
||||
// Write some keys to this memtable.
|
||||
@@ -272,8 +271,8 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
list.PickMemtablesToFlush(&to_flush);
|
||||
ASSERT_EQ(1, to_flush.size());
|
||||
|
||||
s = Mock_InstallMemtableFlushResults(
|
||||
&list, MutableCFOptions(options, ioptions), to_flush, &to_delete);
|
||||
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options),
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(0, list.NumNotFlushed());
|
||||
ASSERT_EQ(1, list.NumFlushed());
|
||||
@@ -304,9 +303,8 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
|
||||
// Create another memtable and write some keys to it
|
||||
WriteBufferManager wb2(options.db_write_buffer_size);
|
||||
MemTable* mem2 =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb2,
|
||||
kMaxSequenceNumber);
|
||||
MemTable* mem2 = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb2,
|
||||
kMaxSequenceNumber);
|
||||
mem2->Ref();
|
||||
|
||||
mem2->Add(++seq, kTypeDeletion, "key1", "");
|
||||
@@ -321,8 +319,8 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
ASSERT_EQ(1, to_flush.size());
|
||||
|
||||
// Flush second memtable
|
||||
s = Mock_InstallMemtableFlushResults(
|
||||
&list, MutableCFOptions(options, ioptions), to_flush, &to_delete);
|
||||
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options),
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(0, list.NumNotFlushed());
|
||||
ASSERT_EQ(2, list.NumFlushed());
|
||||
@@ -330,9 +328,8 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
|
||||
// Add a third memtable to push the first memtable out of the history
|
||||
WriteBufferManager wb3(options.db_write_buffer_size);
|
||||
MemTable* mem3 =
|
||||
new MemTable(cmp, ioptions, MutableCFOptions(options, ioptions), &wb3,
|
||||
kMaxSequenceNumber);
|
||||
MemTable* mem3 = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb3,
|
||||
kMaxSequenceNumber);
|
||||
mem3->Ref();
|
||||
list.Add(mem3, &to_delete);
|
||||
ASSERT_EQ(1, list.NumNotFlushed());
|
||||
@@ -401,7 +398,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
|
||||
// Create some MemTables
|
||||
std::vector<MemTable*> tables;
|
||||
MutableCFOptions mutable_cf_options(options, ioptions);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
for (int i = 0; i < num_tables; i++) {
|
||||
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb,
|
||||
kMaxSequenceNumber);
|
||||
@@ -539,8 +536,8 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire));
|
||||
|
||||
// Flush the 4 memtables that were picked in to_flush
|
||||
s = Mock_InstallMemtableFlushResults(
|
||||
&list, MutableCFOptions(options, ioptions), to_flush, &to_delete);
|
||||
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options),
|
||||
to_flush, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Note: now to_flush contains tables[0,1,2,4]. to_flush2 contains
|
||||
@@ -560,7 +557,7 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
|
||||
// Flush the 1 memtable that was picked in to_flush2
|
||||
s = MemTableListTest::Mock_InstallMemtableFlushResults(
|
||||
&list, MutableCFOptions(options, ioptions), to_flush2, &to_delete);
|
||||
&list, MutableCFOptions(options), to_flush2, &to_delete);
|
||||
ASSERT_OK(s);
|
||||
|
||||
// This will actually install 2 tables. The 1 we told it to flush, and also
|
||||
|
||||
+72
-29
@@ -4,70 +4,113 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include <string>
|
||||
#include <deque>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
const std::deque<std::string> empty_operand_list;
|
||||
const std::vector<Slice> empty_operand_list;
|
||||
|
||||
// The merge context for merging a user key.
|
||||
// When doing a Get(), DB will create such a class and pass it when
|
||||
// issuing Get() operation to memtables and version_set. The operands
|
||||
// will be fetched from the context when issuing partial of full merge.
|
||||
class MergeContext {
|
||||
public:
|
||||
public:
|
||||
// Clear all the operands
|
||||
void Clear() {
|
||||
if (operand_list) {
|
||||
operand_list->clear();
|
||||
if (operand_list_) {
|
||||
operand_list_->clear();
|
||||
copied_operands_->clear();
|
||||
}
|
||||
}
|
||||
// Replace all operands with merge_result, which are expected to be the
|
||||
// merge result of them.
|
||||
void PushPartialMergeResult(std::string& merge_result) {
|
||||
assert (operand_list);
|
||||
operand_list->clear();
|
||||
operand_list->push_front(std::move(merge_result));
|
||||
}
|
||||
|
||||
// Push a merge operand
|
||||
void PushOperand(const Slice& operand_slice) {
|
||||
void PushOperand(const Slice& operand_slice, bool operand_pinned = false) {
|
||||
Initialize();
|
||||
operand_list->push_front(operand_slice.ToString());
|
||||
SetDirectionBackward();
|
||||
|
||||
if (operand_pinned) {
|
||||
operand_list_->push_back(operand_slice);
|
||||
} else {
|
||||
// We need to have our own copy of the operand since it's not pinned
|
||||
copied_operands_->emplace_back(
|
||||
new std::string(operand_slice.data(), operand_slice.size()));
|
||||
operand_list_->push_back(*copied_operands_->back());
|
||||
}
|
||||
}
|
||||
|
||||
// Push back a merge operand
|
||||
void PushOperandBack(const Slice& operand_slice) {
|
||||
void PushOperandBack(const Slice& operand_slice,
|
||||
bool operand_pinned = false) {
|
||||
Initialize();
|
||||
operand_list->push_back(operand_slice.ToString());
|
||||
SetDirectionForward();
|
||||
|
||||
if (operand_pinned) {
|
||||
operand_list_->push_back(operand_slice);
|
||||
} else {
|
||||
// We need to have our own copy of the operand since it's not pinned
|
||||
copied_operands_->emplace_back(
|
||||
new std::string(operand_slice.data(), operand_slice.size()));
|
||||
operand_list_->push_back(*copied_operands_->back());
|
||||
}
|
||||
}
|
||||
|
||||
// return total number of operands in the list
|
||||
size_t GetNumOperands() const {
|
||||
if (!operand_list) {
|
||||
if (!operand_list_) {
|
||||
return 0;
|
||||
}
|
||||
return operand_list->size();
|
||||
return operand_list_->size();
|
||||
}
|
||||
|
||||
// Get the operand at the index.
|
||||
Slice GetOperand(int index) const {
|
||||
assert (operand_list);
|
||||
return (*operand_list)[index];
|
||||
Slice GetOperand(int index) {
|
||||
assert(operand_list_);
|
||||
|
||||
SetDirectionForward();
|
||||
return (*operand_list_)[index];
|
||||
}
|
||||
|
||||
// Return all the operands.
|
||||
const std::deque<std::string>& GetOperands() const {
|
||||
if (!operand_list) {
|
||||
const std::vector<Slice>& GetOperands() {
|
||||
if (!operand_list_) {
|
||||
return empty_operand_list;
|
||||
}
|
||||
return *operand_list;
|
||||
|
||||
SetDirectionForward();
|
||||
return *operand_list_;
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
void Initialize() {
|
||||
if (!operand_list) {
|
||||
operand_list.reset(new std::deque<std::string>());
|
||||
if (!operand_list_) {
|
||||
operand_list_.reset(new std::vector<Slice>());
|
||||
copied_operands_.reset(new std::vector<std::unique_ptr<std::string>>());
|
||||
}
|
||||
}
|
||||
std::unique_ptr<std::deque<std::string>> operand_list;
|
||||
|
||||
void SetDirectionForward() {
|
||||
if (operands_reversed_ == true) {
|
||||
std::reverse(operand_list_->begin(), operand_list_->end());
|
||||
operands_reversed_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SetDirectionBackward() {
|
||||
if (operands_reversed_ == false) {
|
||||
std::reverse(operand_list_->begin(), operand_list_->end());
|
||||
operands_reversed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// List of operands
|
||||
std::unique_ptr<std::vector<Slice>> operand_list_;
|
||||
// Copy of operands that are not pinned.
|
||||
std::unique_ptr<std::vector<std::unique_ptr<std::string>>> copied_operands_;
|
||||
bool operands_reversed_ = true;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
Loaded 100 of 554 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user