mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
116
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
169fce294f | ||
|
|
526728ab85 | ||
|
|
1cdc065c93 | ||
|
|
24d16c2e94 | ||
|
|
97f6319e22 | ||
|
|
a27cadfc93 | ||
|
|
7343eb4a74 | ||
|
|
d904233d2f | ||
|
|
cf14cb3e29 | ||
|
|
ec76f03168 | ||
|
|
4bc9df9459 | ||
|
|
b0fd1cc45a | ||
|
|
c9878baa87 | ||
|
|
ad25b1afb9 | ||
|
|
69877ac4f2 | ||
|
|
251143f8fb | ||
|
|
0c2d71edba | ||
|
|
8a05c21e32 | ||
|
|
eacb14a10a | ||
|
|
59ba104e4a | ||
|
|
6a85aea5b1 | ||
|
|
dab4fe5bcd | ||
|
|
0743eba0c4 | ||
|
|
ba8008c870 | ||
|
|
ea8bb82fc7 | ||
|
|
9df78a94f1 | ||
|
|
00519187a6 | ||
|
|
d1c510baec | ||
|
|
e4f1e64c30 | ||
|
|
14fbb43f3e | ||
|
|
7ebde3da45 | ||
|
|
170dffac83 | ||
|
|
c16d5a4fda | ||
|
|
65487da1f5 | ||
|
|
48669be618 | ||
|
|
91e2c151d2 | ||
|
|
cf160b98e1 | ||
|
|
974458891c | ||
|
|
e3183eae77 | ||
|
|
8d2bbdd04f | ||
|
|
4fba83b4c2 | ||
|
|
ee79a28963 | ||
|
|
cbf38af705 | ||
|
|
3feee6db17 | ||
|
|
a9f8f1fe38 | ||
|
|
0288bdbc53 | ||
|
|
108e6b6354 | ||
|
|
21218316da | ||
|
|
bf6795aea0 | ||
|
|
87983d442c | ||
|
|
e5311a8ea4 | ||
|
|
78ee8564ad | ||
|
|
4a09d632c4 | ||
|
|
0a9a05ae12 | ||
|
|
c696f27432 | ||
|
|
95013df278 | ||
|
|
19210d5715 | ||
|
|
8944a3bf27 | ||
|
|
36963dc2ca | ||
|
|
c6ff4c0b70 | ||
|
|
4ee991b1e6 | ||
|
|
9425acacce | ||
|
|
f9a30e0a5a | ||
|
|
12f1137355 | ||
|
|
1d226018af | ||
|
|
431e8afba7 | ||
|
|
19076c95aa | ||
|
|
12a8be1d44 | ||
|
|
e18a4df62a | ||
|
|
d5f5d6579a | ||
|
|
1a6438cff5 | ||
|
|
2d37830e44 | ||
|
|
b0c43e7081 | ||
|
|
cdd8b09b9b | ||
|
|
e44948295e | ||
|
|
928dea0e32 | ||
|
|
4aa1a19db8 | ||
|
|
4db58bcfb2 | ||
|
|
5b748b9e68 | ||
|
|
acc9679cda | ||
|
|
ffe4906192 | ||
|
|
d76a8eeef7 | ||
|
|
1001bc01c9 | ||
|
|
2fb6d9337f | ||
|
|
ac956f2bea | ||
|
|
7189ea8fb7 | ||
|
|
3e6ee9f82e | ||
|
|
0426d4a4ee | ||
|
|
8e7b068ecc | ||
|
|
77b4bfe511 | ||
|
|
fdbebdf484 | ||
|
|
e4bdf9e0cb | ||
|
|
edfcce39da | ||
|
|
0a9f78d2c8 | ||
|
|
eccc47e81c | ||
|
|
a3066ee75c | ||
|
|
4926b33742 | ||
|
|
8ed680bdb0 | ||
|
|
b2e30bdb67 | ||
|
|
c22e619f7e | ||
|
|
48c0843e69 | ||
|
|
cc2a180d00 | ||
|
|
8f7b6c8339 | ||
|
|
6e0f62f2b6 | ||
|
|
5792b73fdc | ||
|
|
e628f59e87 | ||
|
|
c1a65a4de4 | ||
|
|
58660bf21a | ||
|
|
225abffd8f | ||
|
|
159ea4702c | ||
|
|
b8c01ed38a | ||
|
|
0bad2b4308 | ||
|
|
61e324422e | ||
|
|
bb0f781da0 | ||
|
|
81367a4616 | ||
|
|
30cd38c687 |
No files matched your search
+59
-7
@@ -62,6 +62,14 @@ commands:
|
||||
path: /tmp/test-results
|
||||
- store_artifacts: # store LOG for debugging if there's any
|
||||
path: LOG
|
||||
- run: # on fail, compress Test Logs for diagnosing the issue
|
||||
name: Compress Test Logs
|
||||
command: tar -cvzf t.tar.gz t
|
||||
when: on_fail
|
||||
- store_artifacts: # on fail, store Test Logs for diagnosing the issue
|
||||
path: t.tar.gz
|
||||
destination: test_logs
|
||||
when: on_fail
|
||||
|
||||
install-clang-10:
|
||||
steps:
|
||||
@@ -95,6 +103,13 @@ commands:
|
||||
git clone --single-branch --branch master --depth 1 https://github.com/google/gtest-parallel.git ~/gtest-parallel
|
||||
echo 'export PATH=$HOME/gtest-parallel:$PATH' >> $BASH_ENV
|
||||
|
||||
install-compression-libs:
|
||||
steps:
|
||||
- run:
|
||||
name: Install compression libs
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev liblz4-dev libzstd-dev
|
||||
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
machine:
|
||||
@@ -356,9 +371,13 @@ jobs:
|
||||
- run:
|
||||
name: "Setup VS"
|
||||
command: |
|
||||
if [[ "${VS_YEAR}" == "2017" ]]; then
|
||||
if [[ "${VS_YEAR}" == "2019" ]]; then
|
||||
echo "VS2019 already present."
|
||||
elif [[ "${VS_YEAR}" == "2017" ]]; then
|
||||
echo "Installing VS2017..."
|
||||
powershell .circleci/vs2017_install.ps1
|
||||
elif [[ "${VS_YEAR}" == "2015" ]]; then
|
||||
echo "Installing VS2015..."
|
||||
powershell .circleci/vs2015_install.ps1
|
||||
fi
|
||||
- run:
|
||||
@@ -384,6 +403,7 @@ jobs:
|
||||
cd build
|
||||
${CMAKE_BIN} -G "${CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DJNI=1 << parameters.extra_cmake_opt >> ..
|
||||
cd ..
|
||||
echo "Building with VS version: ${CMAKE_GENERATOR}"
|
||||
msbuild.exe build/rocksdb.sln -maxCpuCount -property:Configuration=Debug -property:Platform=x64
|
||||
- run:
|
||||
name: "Test RocksDB"
|
||||
@@ -552,7 +572,25 @@ jobs:
|
||||
gtest-parallel $(</tmp/test_list) --output_dir=/tmp | cat # pipe to cat to continuously output status on circleci UI. Otherwise, no status will be printed while the job is running.
|
||||
- post-steps
|
||||
|
||||
build-format-compatible:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-compression-libs
|
||||
- run:
|
||||
name: "test"
|
||||
command: |
|
||||
export TEST_TMPDIR=/dev/shm/rocksdb
|
||||
rm -rf /dev/shm/rocksdb
|
||||
mkdir /dev/shm/rocksdb
|
||||
tools/check_format_compatible.sh
|
||||
- post-steps
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-linux:
|
||||
jobs:
|
||||
- build-linux
|
||||
@@ -592,23 +630,27 @@ workflows:
|
||||
build-linux-unity:
|
||||
jobs:
|
||||
- build-linux-unity
|
||||
build-windows:
|
||||
build-windows-vs2019:
|
||||
jobs:
|
||||
- build-windows
|
||||
- build-windows:
|
||||
name: "build-windows-vs2019"
|
||||
build-windows-vs2019-cxx20:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2019-cxx20"
|
||||
extra_cmake_opt: -DCMAKE_CXX_STANDARD=20
|
||||
build-windows-vs2017:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2017"
|
||||
vs_year: "2017"
|
||||
cmake_generator: "Visual Studio 15 Win64"
|
||||
build-windows-vs2015:
|
||||
jobs:
|
||||
- build-windows:
|
||||
name: "build-windows-vs2015"
|
||||
vs_year: "2015"
|
||||
cmake_generator: "Visual Studio 14 Win64"
|
||||
build-windows-cxx20:
|
||||
jobs:
|
||||
- build-windows:
|
||||
extra_cmake_opt: -DCMAKE_CXX_STANDARD=20
|
||||
build-java:
|
||||
jobs:
|
||||
- build-linux-java
|
||||
@@ -649,3 +691,13 @@ workflows:
|
||||
build-cmake-mingw:
|
||||
jobs:
|
||||
- build-cmake-mingw
|
||||
nightly:
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
jobs:
|
||||
- build-format-compatible
|
||||
@@ -21,3 +21,4 @@ if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
Copy-Item -Path "C:\Users\circleci\AppData\Local\Temp\vslogs.zip" -Destination "C:\w\build-results\"
|
||||
exit 1
|
||||
}
|
||||
echo "VS 2015 installed."
|
||||
@@ -32,3 +32,4 @@ if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
Copy-Item -Path "C:\Users\circleci\AppData\Local\Temp\vslogs.zip" -Destination "C:\w\build-results\"
|
||||
exit 1
|
||||
}
|
||||
echo "VS 2017 installed."
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/master/clang/tools/clang-format/clang-format-diff.py
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
@@ -93,3 +93,5 @@ clang-format-diff.py
|
||||
|
||||
fuzz/proto/gen/
|
||||
fuzz/crash-*
|
||||
|
||||
cmake-build-*
|
||||
+12
-84
@@ -2,20 +2,18 @@ dist: xenial
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
arch:
|
||||
- amd64
|
||||
- arm64
|
||||
- ppc64le
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
osx_image: xcode9.4
|
||||
cache:
|
||||
- ccache
|
||||
|
||||
addons:
|
||||
apt:
|
||||
update: true
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
@@ -26,15 +24,6 @@ addons:
|
||||
- liblzma-dev # xv
|
||||
- libzstd-dev
|
||||
- zlib1g-dev
|
||||
homebrew:
|
||||
update: true
|
||||
packages:
|
||||
- ccache
|
||||
- gflags
|
||||
- lz4
|
||||
- snappy
|
||||
- xz
|
||||
- zstd
|
||||
|
||||
env:
|
||||
- TEST_GROUP=platform_dependent # 16-18 minutes
|
||||
@@ -58,20 +47,6 @@ env:
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: osx
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: osx
|
||||
arch: ppc64le
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-mingw
|
||||
@@ -86,28 +61,6 @@ matrix:
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
compiler: clang
|
||||
# With migration to CircleCI, exclude Linux/amd64 for pull requests
|
||||
# (but build in branches for now)
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: amd64
|
||||
# Exclude most osx, arm64 and ppc64le tests for pull requests, but build in branches
|
||||
# Temporarily disable ppc64le cmake test while snapd is broken
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake
|
||||
# Exclude all osx since it should be covered by CircleCI
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=platform_dependent
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: JOB_NAME=cmake
|
||||
# NB: the cmake build is a partial java test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -116,9 +69,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -127,9 +77,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -138,9 +85,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: osx
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -149,9 +93,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os : osx
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -160,9 +101,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : osx
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -171,9 +109,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : osx
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -206,9 +141,6 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : osx
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -219,9 +151,6 @@ matrix:
|
||||
env: JOB_NAME=status_checked
|
||||
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
PATH=$PATH:/usr/local/opt/ccache/libexec;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == cmake-gcc8 ]; then
|
||||
sudo apt-get install -y g++-8 || exit $?;
|
||||
CC=gcc-8 && CXX=g++-8;
|
||||
@@ -237,22 +166,21 @@ install:
|
||||
sudo apt-get install -y g++-4.8 || exit $?;
|
||||
CC=gcc-4.8 && CXX=g++-4.8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
sudo apt-get install snapd && sudo snap install cmake --beta --classic || exit $?;
|
||||
export PATH=/snap/bin:$PATH;
|
||||
- |
|
||||
if [[ "${JOB_NAME}" == cmake* ]]; then
|
||||
sudo apt-get remove -y cmake cmake-data
|
||||
export CMAKE_DEB="cmake-3.14.5-Linux-$(uname -m).deb"
|
||||
export CMAKE_DEB_URL="https://rocksdb-deps.s3-us-west-2.amazonaws.com/cmake/${CMAKE_DEB}"
|
||||
curl --silent --fail --show-error --location --output "${CMAKE_DEB}" "${CMAKE_DEB_URL}" || exit $?
|
||||
sudo dpkg -i "${CMAKE_DEB}" || exit $?
|
||||
which cmake && cmake --version
|
||||
fi
|
||||
- |
|
||||
if [[ "${JOB_NAME}" == java_test || "${JOB_NAME}" == cmake* ]]; then
|
||||
# Ensure JDK 8
|
||||
if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
brew tap AdoptOpenJDK/openjdk || exit $?
|
||||
brew cask install adoptopenjdk8 || exit $?
|
||||
export JAVA_HOME=$(/usr/libexec/java_home)
|
||||
else
|
||||
sudo apt-get install -y openjdk-8-jdk || exit $?
|
||||
export PATH=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)/bin:$PATH
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)
|
||||
fi
|
||||
sudo apt-get install -y openjdk-8-jdk || exit $?
|
||||
export PATH=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)/bin:$PATH
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-$(dpkg --print-architecture)
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
|
||||
+19
-24
@@ -170,23 +170,25 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(TIMESTAMP TS "%Y/%m/%d %H:%M:%S" UTC)
|
||||
set(GIT_DATE_TIME "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
string(TIMESTAMP TS "%Y-%m-%d %H:%M:%S" UTC)
|
||||
set(BUILD_DATE "${TS}" CACHE STRING "the time we first built rocksdb")
|
||||
|
||||
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)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_SHA COMMAND "${GIT_EXECUTABLE}" rev-parse HEAD )
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE GIT_MOD COMMAND "${GIT_EXECUTABLE}" diff-index HEAD --quiet)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_DATE COMMAND "${GIT_EXECUTABLE}" log -1 --date=format:"%Y-%m-%d %T" --format="%ad")
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG RESULT_VARIABLE rv COMMAND "${GIT_EXECUTABLE}" symbolic-ref -q --short HEAD OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (rv AND NOT rv EQUAL 0)
|
||||
execute_process(WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG COMMAND "${GIT_EXECUTABLE}" describe --tags --exact-match OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endif()
|
||||
else()
|
||||
set(GIT_SHA 0)
|
||||
set(GIT_MOD 1)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-f]+" "" GIT_SHA "${GIT_SHA}")
|
||||
|
||||
string(REGEX REPLACE "[^0-9a-fA-F]+" "" GIT_SHA "${GIT_SHA}")
|
||||
string(REGEX REPLACE "[^0-9: /-]+" "" GIT_DATE "${GIT_DATE}")
|
||||
|
||||
option(WITH_MD_LIBRARY "build with MD" ON)
|
||||
if(WIN32 AND MSVC)
|
||||
@@ -199,9 +201,7 @@ endif()
|
||||
|
||||
set(BUILD_VERSION_CC ${CMAKE_BINARY_DIR}/build_version.cc)
|
||||
configure_file(util/build_version.cc.in ${BUILD_VERSION_CC} @ONLY)
|
||||
add_library(build_version OBJECT ${BUILD_VERSION_CC})
|
||||
target_include_directories(build_version PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
@@ -243,14 +243,14 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
endif(HAS_ALTIVEC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|AARCH64")
|
||||
CHECK_C_COMPILER_FLAG("-march=armv8-a+crc+crypto" HAS_ARMV8_CRC)
|
||||
if(HAS_ARMV8_CRC)
|
||||
message(STATUS " HAS_ARMV8_CRC yes")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a+crc+crypto -Wno-unused-function")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a+crc+crypto -Wno-unused-function")
|
||||
endif(HAS_ARMV8_CRC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|AARCH64")
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|AARCH64")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
@@ -466,11 +466,6 @@ 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")
|
||||
@@ -830,8 +825,7 @@ set(SOURCES
|
||||
utilities/transactions/write_unprepared_txn_db.cc
|
||||
utilities/ttl/db_ttl_impl.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc)
|
||||
|
||||
list(APPEND SOURCES
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/concurrent_tree.cc
|
||||
@@ -922,12 +916,12 @@ else()
|
||||
set(SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
|
||||
endif()
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES} ${BUILD_VERSION_CC})
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
@@ -945,7 +939,7 @@ if(ROCKSDB_BUILD_SHARED)
|
||||
LINKER_LANGUAGE CXX
|
||||
VERSION ${rocksdb_VERSION}
|
||||
SOVERSION ${rocksdb_VERSION_MAJOR}
|
||||
OUTPUT_NAME "rocksdb")
|
||||
OUTPUT_NAME "rocksdb${ARTIFACT_SUFFIX}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1088,6 +1082,7 @@ if(WITH_TESTS)
|
||||
db/db_iter_test.cc
|
||||
db/db_iter_stress_test.cc
|
||||
db/db_iterator_test.cc
|
||||
db/db_kv_checksum_test.cc
|
||||
db/db_log_iter_test.cc
|
||||
db/db_memtable_test.cc
|
||||
db/db_merge_operator_test.cc
|
||||
|
||||
+44
-1
@@ -1,4 +1,42 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
|
||||
## 6.18.0 (02/19/2021)
|
||||
### Behavior Changes
|
||||
* When retryable IO error occurs during compaction, it is mapped to soft error and set the BG error. However, auto resume is not called to clean the soft error since compaction will reschedule by itself. In this change, When retryable IO error occurs during compaction, BG error is not set. User will be informed the error via EventHelper.
|
||||
* Introduce a new trace file format for query tracing and replay and trace file version is bump up to 0.2. A payload map is added as the first portion of the payload. We will not have backward compatible issues when adding new entries to trace records. Added the iterator_upper_bound and iterator_lower_bound in Seek and SeekForPrev tracing function. Added them as the new payload member for iterator tracing.
|
||||
|
||||
### New Features
|
||||
* Add support for key-value integrity protection in live updates from the user buffers provided to `WriteBatch` through the write to RocksDB's in-memory update buffer (memtable). This is intended to detect some cases of in-memory data corruption, due to either software or hardware errors. Users can enable protection by constructing their `WriteBatch` with `protection_bytes_per_key == 8`.
|
||||
* Add support for updating `full_history_ts_low` option in manual compaction, which is for old timestamp data GC.
|
||||
* Add a mechanism for using Makefile to build external plugin code into the RocksDB libraries/binaries. This intends to simplify compatibility and distribution for plugins (e.g., special-purpose `FileSystem`s) whose source code resides outside the RocksDB repo. See "plugin/README.md" for developer details, and "PLUGINS.md" for a listing of available plugins.
|
||||
* Added memory pre-fetching for experimental Ribbon filter, which especially optimizes performance with batched MultiGet.
|
||||
* A new, experimental version of BlobDB (key-value separation) is now available. The new implementation is integrated into the RocksDB core, i.e. it is accessible via the usual `rocksdb::DB` API, as opposed to the separate `rocksdb::blob_db::BlobDB` interface used by the earlier version, and can be configured on a per-column family basis using the configuration options `enable_blob_files`, `min_blob_size`, `blob_file_size`, `blob_compression_type`, `enable_blob_garbage_collection`, and `blob_garbage_collection_age_cutoff`. It extends RocksDB's consistency guarantees to blobs, and offers more features and better performance. Note that some features, most notably `Merge`, compaction filters, and backup/restore are not yet supported, and there is no support for migrating a database created by the old implementation.
|
||||
|
||||
### Bug Fixes
|
||||
* Since 6.15.0, `TransactionDB` returns error `Status`es from calls to `DeleteRange()` and calls to `Write()` where the `WriteBatch` contains a range deletion. Previously such operations may have succeeded while not providing the expected transactional guarantees. There are certain cases where range deletion can still be used on such DBs; see the API doc on `TransactionDB::DeleteRange()` for details.
|
||||
* `OptimisticTransactionDB` now returns error `Status`es from calls to `DeleteRange()` and calls to `Write()` where the `WriteBatch` contains a range deletion. Previously such operations may have succeeded while not providing the expected transactional guarantees.
|
||||
* Fix `WRITE_PREPARED`, `WRITE_UNPREPARED` TransactionDB `MultiGet()` may return uncommitted data with snapshot.
|
||||
* In DB::OpenForReadOnly, if any error happens while checking Manifest file path, it was overridden by Status::NotFound. It has been fixed and now actual error is returned.
|
||||
|
||||
### Public API Change
|
||||
* Added a "only_mutable_options" flag to the ConfigOptions. When this flag is "true", the Configurable functions and convenience methods (such as GetDBOptionsFromString) will only deal with options that are marked as mutable. When this flag is true, only options marked as mutable can be configured (a Status::InvalidArgument will be returned) and options not marked as mutable will not be returned or compared. The default is "false", meaning to compare all options.
|
||||
* Add new Append and PositionedAppend APIs to FileSystem to bring the data verification information (data checksum information) from upper layer (e.g., WritableFileWriter) to the storage layer. In this way, the customized FileSystem is able to verify the correctness of data being written to the storage on time. Add checksum_handoff_file_types to DBOptions. User can use this option to control which file types (Currently supported file tyes: kWALFile, kTableFile, kDescriptorFile.) should use the new Append and PositionedAppend APIs to handoff the verification information. Currently, RocksDB only use crc32c to calculate the checksum for write handoff.
|
||||
* Add an option, `CompressionOptions::max_dict_buffer_bytes`, to limit the in-memory buffering for selecting samples for generating/training a dictionary. The limit is currently loosely adhered to.
|
||||
|
||||
## 6.17.0 (01/15/2021)
|
||||
### Behavior Changes
|
||||
* When verifying full file checksum with `DB::VerifyFileChecksums()`, we now fail with `Status::InvalidArgument` if the name of the checksum generator used for verification does not match the name of the checksum generator used for protecting the file when it was created.
|
||||
* Since RocksDB does not continue write the same file if a file write fails for any reason, the file scope write IO error is treated the same as retryable IO error. More information about error handling of file scope IO error is included in `ErrorHandler::SetBGError`.
|
||||
|
||||
### Bug Fixes
|
||||
* Version older than 6.15 cannot decode VersionEdits `WalAddition` and `WalDeletion`, fixed this by changing the encoded format of them to be ignorable by older versions.
|
||||
* Fix a race condition between DB startups and shutdowns in managing the periodic background worker threads. One effect of this race condition could be the process being terminated.
|
||||
|
||||
### Public API Change
|
||||
* Add a public API WriteBufferManager::dummy_entries_in_cache_usage() which reports the size of dummy entries stored in cache (passed to WriteBufferManager). Dummy entries are used to account for DataBlocks.
|
||||
* Add a public API GetRocksBuildProperties and GetRocksBuildInfoAsString to get properties about the current build. These properties may include settings related to the GIT settings (branch, timestamp). This change also sets the "build date" based on the GIT properties, rather than the actual build time, thereby enabling more reproducible builds.
|
||||
|
||||
## 6.16.0 (12/18/2020)
|
||||
### Behavior Changes
|
||||
* Attempting to write a merge operand without explicitly configuring `merge_operator` now fails immediately, causing the DB to enter read-only mode. Previously, failure was deferred until the `merge_operator` was needed by a user read or a background operation.
|
||||
@@ -10,6 +48,8 @@
|
||||
* Fixed the logic of populating native data structure for `read_amp_bytes_per_bit` during OPTIONS file parsing on big-endian architecture. Without this fix, original code introduced in PR7659, when running on big-endian machine, can mistakenly store read_amp_bytes_per_bit (an uint32) in little endian format. Future access to `read_amp_bytes_per_bit` will give wrong values. Little endian architecture is not affected.
|
||||
* Fixed prefix extractor with timestamp issues.
|
||||
* Fixed a bug in atomic flush: in two-phase commit mode, the minimum WAL log number to keep is incorrect.
|
||||
* Fixed a bug related to checkpoint in PR7789: if there are multiple column families, and the checkpoint is not opened as read only, then in rare cases, data loss may happen in the checkpoint. Since backup engine relies on checkpoint, it may also be affected.
|
||||
* When ldb --try_load_options is used with the --column_family option, the ColumnFamilyOptions for the specified column family was not loaded from the OPTIONS file. Fix it so its loaded from OPTIONS and then overridden with command line overrides.
|
||||
|
||||
### New Features
|
||||
* User defined timestamp feature supports `CompactRange` and `GetApproximateSizes`.
|
||||
@@ -18,6 +58,10 @@
|
||||
|
||||
### Public API Change
|
||||
* Deprecated public but rarely-used FilterBitsBuilder::CalculateNumEntry, which is replaced with ApproximateNumEntries taking a size_t parameter and returning size_t.
|
||||
* To improve portability the functions `Env::GetChildren` and `Env::GetChildrenFileAttributes` will no longer return entries for the special directories `.` or `..`.
|
||||
* Added a new option `track_and_verify_wals_in_manifest`. If `true`, the log numbers and sizes of the synced WALs are tracked in MANIFEST, then during DB recovery, if a synced WAL is missing from disk, or the WAL's size does not match the recorded size in MANIFEST, an error will be reported and the recovery will be aborted. Note that this option does not work with secondary instance.
|
||||
* `rocksdb_approximate_sizes` and `rocksdb_approximate_sizes_cf` in the C API now requires an error pointer (`char** errptr`) for receiving any error.
|
||||
* All overloads of DB::GetApproximateSizes now return Status, so that any failure to obtain the sizes is indicated to the caller.
|
||||
|
||||
## 6.15.0 (11/13/2020)
|
||||
### Bug Fixes
|
||||
@@ -39,7 +83,6 @@
|
||||
### Public API Change
|
||||
* Deprecate `BlockBasedTableOptions::pin_l0_filter_and_index_blocks_in_cache` and `BlockBasedTableOptions::pin_top_level_index_and_filter`. These options still take effect until users migrate to the replacement APIs in `BlockBasedTableOptions::metadata_cache_options`. Migration guidance can be found in the API comments on the deprecated options.
|
||||
* Add new API `DB::VerifyFileChecksums` to verify SST file checksum with corresponding entries in the MANIFEST if present. Current implementation requires scanning and recomputing file checksums.
|
||||
* Added a new option `track_and_verify_wals_in_manifest`. If `true`, the log numbers and sizes of the synced WALs are tracked in MANIFEST, then during DB recovery, if a synced WAL is missing from disk, or the WAL's size does not match the recorded size in MANIFEST, an error will be reported and the recovery will be aborted. Note that this option does not work with secondary instance.
|
||||
|
||||
### Behavior Changes
|
||||
* The dictionary compression settings specified in `ColumnFamilyOptions::compression_opts` now additionally affect files generated by flush and compaction to non-bottommost level. Previously those settings at most affected files generated by compaction to bottommost level, depending on whether `ColumnFamilyOptions::bottommost_compression_opts` overrode them. Users who relied on dictionary compression settings in `ColumnFamilyOptions::compression_opts` affecting only the bottommost level can keep the behavior by moving their dictionary settings to `ColumnFamilyOptions::bottommost_compression_opts` and setting its `enabled` flag.
|
||||
|
||||
@@ -269,6 +269,12 @@ dummy := $(shell (export ROCKSDB_ROOT="$(CURDIR)"; \
|
||||
# this file is generated by the previous line to set build flags and sources
|
||||
include make_config.mk
|
||||
|
||||
ROCKSDB_PLUGIN_MKS = $(foreach plugin, $(ROCKSDB_PLUGINS), plugin/$(plugin)/*.mk)
|
||||
include $(ROCKSDB_PLUGIN_MKS)
|
||||
ROCKSDB_PLUGIN_SOURCES = $(foreach plugin, $(ROCKSDB_PLUGINS), $(foreach source, $($(plugin)_SOURCES), plugin/$(plugin)/$(source)))
|
||||
ROCKSDB_PLUGIN_HEADERS = $(foreach plugin, $(ROCKSDB_PLUGINS), $(foreach header, $($(plugin)_HEADERS), plugin/$(plugin)/$(header)))
|
||||
PLATFORM_LDFLAGS += $(foreach plugin, $(ROCKSDB_PLUGINS), $($(plugin)_LDFLAGS))
|
||||
|
||||
export JAVAC_ARGS
|
||||
CLEAN_FILES += make_config.mk rocksdb.pc
|
||||
|
||||
@@ -479,33 +485,9 @@ CXXFLAGS += $(WARNING_FLAGS) -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT) -Woverl
|
||||
|
||||
LDFLAGS += $(PLATFORM_LDFLAGS)
|
||||
|
||||
# If NO_UPDATE_BUILD_VERSION is set we don't update util/build_version.cc, but
|
||||
# the file needs to already exist or else the build will fail
|
||||
ifndef NO_UPDATE_BUILD_VERSION
|
||||
date := $(shell date +%F)
|
||||
ifdef FORCE_GIT_SHA
|
||||
git_sha := $(FORCE_GIT_SHA)
|
||||
else
|
||||
git_sha := $(shell git rev-parse HEAD 2>/dev/null)
|
||||
endif
|
||||
gen_build_version = sed -e s/@@GIT_SHA@@/$(git_sha)/ -e s/@@GIT_DATE_TIME@@/$(date)/ util/build_version.cc.in
|
||||
|
||||
# Record the version of the source that we are compiling.
|
||||
# We keep a record of the git revision in this file. It is then built
|
||||
# as a regular source file as part of the compilation process.
|
||||
# One can run "strings executable_filename | grep _build_" to find
|
||||
# the version of the source that we used to build the executable file.
|
||||
FORCE:
|
||||
util/build_version.cc: FORCE
|
||||
$(AM_V_GEN)rm -f $@-t
|
||||
$(AM_V_at)$(gen_build_version) > $@-t
|
||||
$(AM_V_at)if test -f $@; then \
|
||||
cmp -s $@-t $@ && rm -f $@-t || mv -f $@-t $@; \
|
||||
else mv -f $@-t $@; fi
|
||||
endif
|
||||
|
||||
OBJ_DIR?=.
|
||||
LIB_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(LIB_SOURCES))
|
||||
LIB_OBJECTS += $(patsubst %.cc, $(OBJ_DIR)/%.o, $(ROCKSDB_PLUGIN_SOURCES))
|
||||
ifeq ($(HAVE_POWER8),1)
|
||||
LIB_OBJECTS += $(patsubst %.c, $(OBJ_DIR)/%.o, $(LIB_SOURCES_C))
|
||||
LIB_OBJECTS += $(patsubst %.S, $(OBJ_DIR)/%.o, $(LIB_SOURCES_ASM))
|
||||
@@ -515,6 +497,12 @@ ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
LIB_OBJECTS += $(patsubst %.cpp, $(OBJ_DIR)/%.o, $(FOLLY_SOURCES))
|
||||
endif
|
||||
|
||||
# range_tree is not compatible with non GNU libc on ppc64
|
||||
# see https://jira.percona.com/browse/PS-7559
|
||||
ifneq ($(PPC_LIBC_IS_GNU),0)
|
||||
LIB_OBJECTS += $(patsubst %.cc, $(OBJ_DIR)/%.o, $(RANGE_TREE_SOURCES))
|
||||
endif
|
||||
|
||||
GTEST = $(OBJ_DIR)/$(GTEST_DIR)/gtest/gtest-all.o
|
||||
TESTUTIL = $(OBJ_DIR)/test_util/testutil.o
|
||||
TESTHARNESS = $(OBJ_DIR)/test_util/testharness.o $(TESTUTIL) $(GTEST)
|
||||
@@ -612,8 +600,14 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
db_blob_basic_test \
|
||||
db_blob_index_test \
|
||||
db_block_cache_test \
|
||||
db_compaction_test \
|
||||
db_compaction_filter_test \
|
||||
db_dynamic_level_test \
|
||||
db_flush_test \
|
||||
db_inplace_update_test \
|
||||
db_io_failure_test \
|
||||
db_iterator_test \
|
||||
db_kv_checksum_test \
|
||||
db_logical_block_size_cache_test \
|
||||
db_memtable_test \
|
||||
db_merge_operand_test \
|
||||
@@ -629,6 +623,7 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
deletefile_test \
|
||||
external_sst_file_test \
|
||||
options_file_test \
|
||||
db_sst_test \
|
||||
db_statistics_test \
|
||||
db_table_properties_test \
|
||||
db_tailing_iter_test \
|
||||
@@ -653,6 +648,7 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
external_sst_file_basic_test \
|
||||
auto_roll_logger_test \
|
||||
file_indexer_test \
|
||||
delete_scheduler_test \
|
||||
flush_job_test \
|
||||
hash_table_test \
|
||||
hash_test \
|
||||
@@ -741,6 +737,9 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
cuckoo_table_reader_test \
|
||||
memory_test \
|
||||
table_test \
|
||||
backupable_db_test \
|
||||
blob_db_test \
|
||||
ttl_test \
|
||||
write_batch_test \
|
||||
write_batch_with_index_test \
|
||||
|
||||
@@ -853,6 +852,37 @@ ROCKSDB_MAJOR = $(shell egrep "ROCKSDB_MAJOR.[0-9]" include/rocksdb/version.h |
|
||||
ROCKSDB_MINOR = $(shell egrep "ROCKSDB_MINOR.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
ROCKSDB_PATCH = $(shell egrep "ROCKSDB_PATCH.[0-9]" include/rocksdb/version.h | cut -d ' ' -f 3)
|
||||
|
||||
# If NO_UPDATE_BUILD_VERSION is set we don't update util/build_version.cc, but
|
||||
# the file needs to already exist or else the build will fail
|
||||
ifndef NO_UPDATE_BUILD_VERSION
|
||||
|
||||
# By default, use the current date-time as the date. If there are no changes,
|
||||
# we will use the last commit date instead.
|
||||
build_date := $(shell date "+%Y-%m-%d %T")
|
||||
|
||||
ifdef FORCE_GIT_SHA
|
||||
git_sha := $(FORCE_GIT_SHA)
|
||||
git_mod := 1
|
||||
git_date := $(build_date)
|
||||
else
|
||||
git_sha := $(shell git rev-parse HEAD 2>/dev/null)
|
||||
git_tag := $(shell git symbolic-ref -q --short HEAD 2> /dev/null || git describe --tags --exact-match 2>/dev/null)
|
||||
git_mod := $(shell git diff-index HEAD --quiet 2>/dev/null; echo $$?)
|
||||
git_date := $(shell git log -1 --date=format:"%Y-%m-%d %T" --format="%ad" 2>/dev/null)
|
||||
endif
|
||||
gen_build_version = sed -e s/@GIT_SHA@/$(git_sha)/ -e s:@GIT_TAG@:"$(git_tag)": -e s/@GIT_MOD@/"$(git_mod)"/ -e s/@BUILD_DATE@/"$(build_date)"/ -e s/@GIT_DATE@/"$(git_date)"/ util/build_version.cc.in
|
||||
|
||||
# Record the version of the source that we are compiling.
|
||||
# We keep a record of the git revision in this file. It is then built
|
||||
# as a regular source file as part of the compilation process.
|
||||
# One can run "strings executable_filename | grep _build_" to find
|
||||
# the version of the source that we used to build the executable file.
|
||||
util/build_version.cc: $(filter-out $(OBJ_DIR)/util/build_version.o, $(LIB_OBJECTS)) util/build_version.cc.in
|
||||
$(AM_V_GEN)rm -f $@-t
|
||||
$(AM_V_at)$(gen_build_version) > $@
|
||||
endif
|
||||
CLEAN_FILES += util/build_version.cc
|
||||
|
||||
default: all
|
||||
|
||||
#-----------------------------------------------
|
||||
@@ -1395,7 +1425,7 @@ $(STATIC_TEST_LIBRARY): $(TEST_OBJECTS)
|
||||
$(AM_V_AR)rm -f $@ $(SHARED_TEST_LIBRARY)
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
$(STATIC_TOOLS_LIBRARY): $(BENCH_OBJECTS) $(TOOL_OBJECTS)
|
||||
$(STATIC_TOOLS_LIBRARY): $(TOOL_OBJECTS)
|
||||
$(AM_V_AR)rm -f $@ $(SHARED_TOOLS_LIBRARY)
|
||||
$(AM_V_at)$(AR) $(ARFLAGS) $@ $^
|
||||
|
||||
@@ -1592,6 +1622,9 @@ db_inplace_update_test: $(OBJ_DIR)/db/db_inplace_update_test.o $(TEST_LIBRARY) $
|
||||
db_iterator_test: $(OBJ_DIR)/db/db_iterator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_kv_checksum_test: $(OBJ_DIR)/db/db_kv_checksum_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_memtable_test: $(OBJ_DIR)/db/db_memtable_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2056,6 +2089,10 @@ install-headers: gen-pc
|
||||
for header in `$(FIND) "include/rocksdb" -type f -name *.h`; do \
|
||||
install -C -m 644 $$header $(DESTDIR)/$(PREFIX)/$$header; \
|
||||
done
|
||||
for header in $(ROCKSDB_PLUGIN_HEADERS); do \
|
||||
install -d $(DESTDIR)/$(PREFIX)/include/rocksdb/`dirname $$header`; \
|
||||
install -C -m 644 $$header $(DESTDIR)/$(PREFIX)/include/rocksdb/$$header; \
|
||||
done
|
||||
install -C -m 644 rocksdb.pc $(INSTALL_LIBDIR)/pkgconfig/rocksdb.pc
|
||||
|
||||
install-static: install-headers $(LIBRARY)
|
||||
@@ -2139,11 +2176,11 @@ BZIP2_DOWNLOAD_BASE ?= https://sourceware.org/pub/bzip2
|
||||
SNAPPY_VER ?= 1.1.8
|
||||
SNAPPY_SHA256 ?= 16b677f07832a612b0836178db7f374e414f94657c138e6993cbfc5dcc58651f
|
||||
SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.2
|
||||
LZ4_SHA256 ?= 658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc
|
||||
LZ4_VER ?= 1.9.3
|
||||
LZ4_SHA256 ?= 030644df4611007ff7dc962d981f390361e6c97a34e5cbc393ddfbe019ffe2c1
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.4.5
|
||||
ZSTD_SHA256 ?= 734d1f565c42f691f8420c8d06783ad818060fc390dee43ae0a89f86d0a4f8c2
|
||||
ZSTD_VER ?= 1.4.9
|
||||
ZSTD_SHA256 ?= acf714d98e3db7b876e5b540cbf6dee298f60eb3c0723104f6d3f065cd60d6a8
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -2272,7 +2309,7 @@ endif
|
||||
$(MAKE) rocksdbjavastatic_javalib
|
||||
|
||||
rocksdbjavastatic_javalib:
|
||||
cd java;$(MAKE) javalib
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib
|
||||
rm -f java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC \
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) \
|
||||
@@ -2366,7 +2403,7 @@ rocksdbjava: $(LIB_OBJECTS)
|
||||
ifeq ($(JAVA_HOME),)
|
||||
$(error JAVA_HOME is not set)
|
||||
endif
|
||||
$(AM_V_GEN)cd java;$(MAKE) javalib;
|
||||
$(AM_V_GEN)cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(AM_V_at)$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIB_OBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
$(AM_V_at)cd java;jar -cf target/$(ROCKSDB_JAR) HISTORY*.md
|
||||
@@ -2378,13 +2415,13 @@ jclean:
|
||||
cd java;$(MAKE) clean;
|
||||
|
||||
jtest_compile: rocksdbjava
|
||||
cd java;$(MAKE) java_test
|
||||
cd java; SHA256_CMD='$(SHA256_CMD)' $(MAKE) java_test
|
||||
|
||||
jtest_run:
|
||||
cd java;$(MAKE) run_test
|
||||
|
||||
jtest: rocksdbjava
|
||||
cd java;$(MAKE) sample;$(MAKE) test;
|
||||
cd java;$(MAKE) sample; SHA256_CMD='$(SHA256_CMD)' $(MAKE) test;
|
||||
$(PYTHON) tools/check_all_python.py # TODO peterd: find a better place for this check in CI targets
|
||||
|
||||
jdb_bench:
|
||||
@@ -2494,6 +2531,8 @@ build_subset_tests: $(ROCKSDBTESTS_SUBSET)
|
||||
# working solution.
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),format)
|
||||
ifneq ($(MAKECMDGOALS),check-format)
|
||||
ifneq ($(MAKECMDGOALS),check-buck-targets)
|
||||
ifneq ($(MAKECMDGOALS),jclean)
|
||||
ifneq ($(MAKECMDGOALS),jtest)
|
||||
ifneq ($(MAKECMDGOALS),rocksdbjavastatic)
|
||||
@@ -2509,3 +2548,5 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -0,0 +1,3 @@
|
||||
This is the list of all known third-party plugins for RocksDB. If something is missing, please open a pull request to add it.
|
||||
|
||||
* [Dedupfs](https://github.com/ajkr/dedupfs): an example for plugin developers to reference
|
||||
@@ -1,4 +1,5 @@
|
||||
# This file @generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# This file @generated by:
|
||||
#$ python3 buckifier/buckify_rocksdb.py
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -24,7 +25,7 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("zlib", None, "z"),
|
||||
("gflags", None, "gflags"),
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("zstd", None, "zstd"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -1237,6 +1238,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_kv_checksum_test",
|
||||
"db/db_kv_checksum_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_log_iter_test",
|
||||
"db/db_log_iter_test.cc",
|
||||
|
||||
@@ -26,6 +26,9 @@ Learn more about those use cases in a Tech Talk by Ankit Gupta and Naveen Somasu
|
||||
## Yahoo
|
||||
Yahoo is using RocksDB as a storage engine for their biggest distributed data store Sherpa. Learn more about it here: http://yahooeng.tumblr.com/post/120730204806/sherpa-scales-new-heights
|
||||
|
||||
## Baidu
|
||||
[Apache Doris](http://doris.apache.org/master/en/) is a MPP analytical database engine released by Baidu. It [uses RocksDB](http://doris.apache.org/master/en/administrator-guide/operation/tablet-meta-tool.html) to manage its tablet's metadata.
|
||||
|
||||
## CockroachDB
|
||||
CockroachDB is an open-source geo-replicated transactional database. They are using RocksDB as their storage engine. Check out their github: https://github.com/cockroachdb/cockroach
|
||||
|
||||
@@ -67,7 +70,7 @@ Pinterest's Object Retrieval System uses RocksDB for storage: https://www.youtub
|
||||
[VWO's](https://vwo.com/) Smart Code checker and URL helper uses RocksDB to store all the URLs where VWO's Smart Code is installed.
|
||||
|
||||
## quasardb
|
||||
[quasardb](https://www.quasardb.net) is a high-performance, distributed, transactional key-value database that integrates well with in-memory analytics engines such as Apache Spark.
|
||||
[quasardb](https://www.quasardb.net) is a high-performance, distributed, transactional key-value database that integrates well with in-memory analytics engines such as Apache Spark.
|
||||
quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
|
||||
## Netflix
|
||||
@@ -96,10 +99,10 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
|
||||
## IOTA Foundation
|
||||
[IOTA Foundation](https://www.iota.org/) is using RocksDB in the [IOTA Reference Implementation (IRI)](https://github.com/iotaledger/iri) to store the local state of the Tangle. The Tangle is the first open-source distributed ledger powering the future of the Internet of Things.
|
||||
|
||||
|
||||
## Avrio Project
|
||||
[Avrio Project](http://avrio-project.github.io/avrio.network/) is using RocksDB in [Avrio ](https://github.com/avrio-project/avrio) to store blocks, account balances and data and other blockchain-releated data. Avrio is a multiblockchain decentralized cryptocurrency empowering monetary transactions.
|
||||
|
||||
|
||||
## Crux
|
||||
[Crux](https://github.com/juxt/crux) is a document database that uses RocksDB for local [EAV](https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model) index storage to enable point-in-time bitemporal Datalog queries. The "unbundled" architecture uses Kafka to provide horizontal scalability.
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ from util import ColorString
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
# tests.)
|
||||
# $python3 buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": { \
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"], \
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"], \
|
||||
# } \
|
||||
# '{"fake": {
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"],
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"]
|
||||
# }
|
||||
# }'
|
||||
# (Generated TARGETS file has test_dep and mock1 as dependencies for RocksDB
|
||||
# unit tests, and will use the extra_compiler_flags to compile the unit test
|
||||
@@ -129,17 +129,27 @@ def generate_targets(repo_path, deps_map):
|
||||
if src_mk is None or cc_files is None or parallel_tests is None:
|
||||
return False
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path)
|
||||
extra_argv = ""
|
||||
if len(sys.argv) >= 2:
|
||||
# Heuristically quote and canonicalize whitespace for inclusion
|
||||
# in how the file was generated.
|
||||
extra_argv = " '{0}'".format(" ".join(sys.argv[1].split()))
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path, extra_argv)
|
||||
|
||||
# rocksdb_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"])
|
||||
# rocksdb_whole_archive_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_whole_archive_lib",
|
||||
src_mk["LIB_SOURCES"] +
|
||||
# always add range_tree, it's only excluded on ppc64, which we don't use internally
|
||||
src_mk["RANGE_TREE_SOURCES"] +
|
||||
src_mk["TOOL_LIB_SOURCES"],
|
||||
deps=None,
|
||||
headers=None,
|
||||
|
||||
@@ -25,10 +25,11 @@ def pretty_list(lst, indent=8):
|
||||
|
||||
|
||||
class TARGETSBuilder(object):
|
||||
def __init__(self, path):
|
||||
def __init__(self, path, extra_argv):
|
||||
self.path = path
|
||||
self.targets_file = open(path, 'wb')
|
||||
header = targets_cfg.rocksdb_target_header_template
|
||||
header = targets_cfg.rocksdb_target_header_template.format(
|
||||
extra_argv=extra_argv)
|
||||
self.targets_file.write(header.encode("utf-8"))
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
|
||||
@@ -5,7 +5,8 @@ from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
rocksdb_target_header_template = \
|
||||
"""# This file \100generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
"""# This file \100generated by:
|
||||
#$ python3 buckifier/buckify_rocksdb.py{extra_argv}
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -31,7 +32,7 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("zlib", None, "z"),
|
||||
("gflags", None, "gflags"),
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("zstd", None, "zstd"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -99,11 +100,11 @@ ROCKSDB_PREPROCESSOR_FLAGS = [
|
||||
"-I" + REPO_PATH,
|
||||
]
|
||||
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {
|
||||
ROCKSDB_ARCH_PREPROCESSOR_FLAGS = {{
|
||||
"x86_64": [
|
||||
"-DHAVE_PCLMUL",
|
||||
],
|
||||
}
|
||||
}}
|
||||
|
||||
build_mode = read_config("fbcode", "build_mode")
|
||||
|
||||
|
||||
@@ -668,6 +668,23 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^ppc64`"; then
|
||||
# check for GNU libc on ppc64
|
||||
$CXX -x c++ - -o /dev/null 2>/dev/null <<EOF
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <gnu/libc-version.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
printf("GNU libc version: %s\n", gnu_get_libc_version());
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
if [ "$?" != 0 ]; then
|
||||
PPC_LIBC_IS_GNU=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TRY_SSE_ETC"; then
|
||||
# The USE_SSE flag now means "attempt to compile with widely-available
|
||||
# Intel architecture extensions utilized by specific optimizations in the
|
||||
@@ -861,3 +878,6 @@ echo "LUA_PATH=$LUA_PATH" >> "$OUTPUT"
|
||||
if test -n "$USE_FOLLY_DISTRIBUTED_MUTEX"; then
|
||||
echo "USE_FOLLY_DISTRIBUTED_MUTEX=$USE_FOLLY_DISTRIBUTED_MUTEX" >> "$OUTPUT"
|
||||
fi
|
||||
if test -n "$PPC_LIBC_IS_GNU"; then
|
||||
echo "PPC_LIBC_IS_GNU=$PPC_LIBC_IS_GNU" >> "$OUTPUT"
|
||||
fi
|
||||
@@ -52,7 +52,8 @@ else
|
||||
else
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location https://tinyurl.com/y2kvokof -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo " curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo "You should make sure the downloaded script is not compromised."
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
@@ -82,7 +83,8 @@ else
|
||||
echo "You have clang-format-diff.py for Python 2 but are using a Python 3"
|
||||
echo "interpreter (${PYTHON:-python3})."
|
||||
echo "You can download clang-format-diff.py for Python 3 by running: "
|
||||
echo " curl --location https://tinyurl.com/y2kvokof -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo " curl --location https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo "You should make sure the downloaded script is not compromised."
|
||||
exit 130
|
||||
fi
|
||||
CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# to determine next steps to run
|
||||
|
||||
# Usage:
|
||||
# EMAIL=<email> ONCALL=<email> TRIGGER=<trigger> SUBSCRIBER=<email> rocks_ci.py <test-name>
|
||||
# EMAIL=<email> ONCALL=<email> TRIGGER=<trigger> SUBSCRIBER=<email> WORKINGDIR=<working_dir> rocksdb-lego-determinator <test-name>
|
||||
#
|
||||
# Input Value
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -11,7 +11,7 @@
|
||||
# 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
|
||||
#
|
||||
# WORKINGDIR Working directory
|
||||
|
||||
#
|
||||
# Report configuration
|
||||
@@ -53,13 +53,19 @@ if [[ ! -z $REPORT_EMAIL || ! -z $CREATE_TASK ]]; then
|
||||
]"
|
||||
fi
|
||||
|
||||
# Working directory for the following command, default to current directory
|
||||
WORKING_DIR=.
|
||||
if [ ! -z $WORKINGDIR ]; then
|
||||
WORKING_DIR=$WORKINGDIR
|
||||
fi
|
||||
|
||||
#
|
||||
# Helper variables
|
||||
#
|
||||
CLEANUP_ENV="
|
||||
{
|
||||
'name':'Cleanup environment',
|
||||
'shell':'rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && (chmod +t /dev/shm || true) && make clean',
|
||||
'shell':'cd $WORKING_DIR; rm -rf /dev/shm/rocksdb && mkdir /dev/shm/rocksdb && (chmod +t /dev/shm || true) && make clean',
|
||||
'user':'root'
|
||||
}"
|
||||
|
||||
@@ -112,6 +118,7 @@ SETUP_JAVA_ENV="export $HTTP_PROXY; export JAVA_HOME=/usr/local/jdk-8u60-64/; ex
|
||||
PARSER="'parser':'python build_tools/error_filter.py $1'"
|
||||
|
||||
CONTRUN_NAME="ROCKSDB_CONTRUN_NAME"
|
||||
SKIP_FORMAT_CHECKS="SKIP_FORMAT_BUCK_CHECKS=1"
|
||||
|
||||
# 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
|
||||
@@ -153,7 +160,7 @@ UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -176,7 +183,7 @@ UNIT_TEST_NON_SHM_COMMANDS="[
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'timeout': 86400,
|
||||
'shell':'$NON_SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $NON_SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=non_shm_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -197,7 +204,7 @@ RELEASE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; make $PARALLEL_j release || $CONTRUN_NAME=release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -218,7 +225,7 @@ UNIT_TEST_COMMANDS_481="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug version',
|
||||
'shell':'$SHM $GCC_481 $DEBUG make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $GCC_481 $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=unit_gcc_481_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -239,7 +246,7 @@ RELEASE_BUILD_COMMANDS_481="[
|
||||
$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',
|
||||
'shell':'cd $WORKING_DIR; $GCC_481 make $PARALLEL_j release || $CONTRUN_NAME=release_gcc481 $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -260,7 +267,7 @@ CLANG_UNIT_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and test RocksDB debug',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $CLANG $SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=clang_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -281,7 +288,7 @@ CLANG_RELEASE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB release',
|
||||
'shell':'$CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $CLANG make $PARALLEL_j release|| $CONTRUN_NAME=clang_release $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -302,7 +309,7 @@ CLANG_ANALYZE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'RocksDB build and analyze',
|
||||
'shell':'$CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $CLANG $SHM $DEBUG make $PARALLEL_j analyze || $CONTRUN_NAME=clang_analyze $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -323,7 +330,7 @@ CODE_COV_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test and collect code coverage info',
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM coverage || $CONTRUN_NAME=coverage $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -344,7 +351,7 @@ UNITY_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build, test unity test',
|
||||
'shell':'$SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG V=1 make J=1 unity_test || $CONTRUN_NAME=unity_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -365,7 +372,7 @@ LITE_BUILD_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB debug version',
|
||||
'shell':'make J=1 LITE=1 all check || $CONTRUN_NAME=lite $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SKIP_FORMAT_CHECKS make J=1 LITE=1 all check || $CONTRUN_NAME=lite $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -385,7 +392,7 @@ REPORT_LITE_BINARY_SIZE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Report RocksDB Lite binary size to scuba',
|
||||
'shell':'tools/report_lite_binary_size.sh',
|
||||
'shell':'cd $WORKING_DIR; tools/report_lite_binary_size.sh',
|
||||
'user':'root',
|
||||
},
|
||||
],
|
||||
@@ -404,14 +411,14 @@ STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH 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 $NON_TSAN_CRASH make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test || $CONTRUN_NAME=crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -434,14 +441,14 @@ BLACKBOX_STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug blackbox crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 blackbox_crash_test || $CONTRUN_NAME=blackbox_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 blackbox_crash_test || $CONTRUN_NAME=blackbox_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -464,14 +471,14 @@ WHITEBOX_STRESS_CRASH_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug whitebox crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 whitebox_crash_test || $CONTRUN_NAME=whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 whitebox_crash_test || $CONTRUN_NAME=whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -494,14 +501,14 @@ STRESS_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests with atomic flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -524,14 +531,14 @@ STRESS_CRASH_TEST_WITH_TXN_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build and run RocksDB debug stress tests',
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 db_stress || $CONTRUN_NAME=db_stress $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build and run RocksDB debug crash tests with txn',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_txn || $CONTRUN_NAME=crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH make J=1 crash_test_with_txn || $CONTRUN_NAME=crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -553,7 +560,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 || $CONTRUN_NAME=write_stress $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; 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
|
||||
}
|
||||
@@ -576,7 +583,7 @@ ASAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under ASAN',
|
||||
'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',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && ($SHM $ASAN $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM asan_check || $CONTRUN_NAME=asan_check $TASK_CREATION_TOOL) |& /usr/facebook/ops/scripts/asan_symbolize.py -d',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -599,7 +606,7 @@ ASAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make J=1 asan_crash_test || $CONTRUN_NAME=asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -623,7 +630,7 @@ ASAN_BLACKBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug blackbox asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 blackbox_asan_crash_test || $CONTRUN_NAME=blackbox_asan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make J=1 blackbox_asan_crash_test || $CONTRUN_NAME=blackbox_asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -647,7 +654,7 @@ ASAN_WHITEBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug whitebox asan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 whitebox_asan_crash_test || $CONTRUN_NAME=whitebox_asan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make J=1 whitebox_asan_crash_test || $CONTRUN_NAME=whitebox_asan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -671,7 +678,7 @@ ASAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make J=1 asan_crash_test_with_atomic_flush || $CONTRUN_NAME=asan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -695,7 +702,7 @@ ASAN_CRASH_TEST_WITH_TXN_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug asan_crash_test_with_txn',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH make J=1 asan_crash_test_with_txn || $CONTRUN_NAME=asan_crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $SKIP_FORMAT_CHECKS make J=1 asan_crash_test_with_txn || $CONTRUN_NAME=asan_crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -717,7 +724,7 @@ UBSAN_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Test RocksDB debug under UBSAN',
|
||||
'shell':'set -o pipefail && $SHM $UBSAN $CLANG $DEBUG make $PARALLELISM ubsan_check || $CONTRUN_NAME=ubsan_check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $UBSAN $CLANG $DEBUG $SKIP_FORMAT_CHECKS make $PARALLELISM ubsan_check || $CONTRUN_NAME=ubsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
}
|
||||
@@ -740,7 +747,7 @@ UBSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH $CLANG make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make J=1 ubsan_crash_test || $CONTRUN_NAME=ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -764,7 +771,7 @@ UBSAN_BLACKBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug blackbox ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH $CLANG make J=1 blackbox_ubsan_crash_test || $CONTRUN_NAME=blackbox_ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make J=1 blackbox_ubsan_crash_test || $CONTRUN_NAME=blackbox_ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -788,7 +795,7 @@ UBSAN_WHITEBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug whitebox ubsan_crash_test',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH $CLANG make J=1 whitebox_ubsan_crash_test || $CONTRUN_NAME=whitebox_ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make J=1 whitebox_ubsan_crash_test || $CONTRUN_NAME=whitebox_ubsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -812,7 +819,7 @@ UBSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test_with_atomic_flush',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH $CLANG make J=1 ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make J=1 ubsan_crash_test_with_atomic_flush || $CONTRUN_NAME=ubsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -836,7 +843,7 @@ UBSAN_CRASH_TEST_WITH_TXN_COMMANDS="[
|
||||
{
|
||||
'name':'Build and run RocksDB debug ubsan_crash_test_with_txn',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG $NON_TSAN_CRASH $CLANG make J=1 ubsan_crash_test_with_txn || $CONTRUN_NAME=ubsan_crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG $NON_TSAN_CRASH $CLANG $SKIP_FORMAT_CHECKS make J=1 ubsan_crash_test_with_txn || $CONTRUN_NAME=ubsan_crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -860,7 +867,7 @@ VALGRIND_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Run RocksDB debug unit tests',
|
||||
'timeout': 86400,
|
||||
'shell':'$SHM $DEBUG make $PARALLELISM valgrind_test || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SHM $DEBUG make $PARALLELISM valgrind_test || $CONTRUN_NAME=valgrind_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -883,7 +890,7 @@ TSAN_UNIT_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $SKIP_FORMAT_CHECKS make $PARALLELISM check || $CONTRUN_NAME=tsan_check $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -906,7 +913,7 @@ TSAN_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test || $CONTRUN_NAME=tsan_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -930,7 +937,7 @@ TSAN_BLACKBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 blackbox_crash_test || $CONTRUN_NAME=tsan_blackbox_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 blackbox_crash_test || $CONTRUN_NAME=tsan_blackbox_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -954,7 +961,7 @@ TSAN_WHITEBOX_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 whitebox_crash_test || $CONTRUN_NAME=tsan_whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 whitebox_crash_test || $CONTRUN_NAME=tsan_whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -978,7 +985,7 @@ TSAN_CRASH_TEST_WITH_ATOMIC_FLUSH_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=tsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_atomic_flush || $CONTRUN_NAME=tsan_crash_test_with_atomic_flush $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -1002,7 +1009,7 @@ TSAN_CRASH_TEST_WITH_TXN_COMMANDS="[
|
||||
{
|
||||
'name':'Compile and run',
|
||||
'timeout': 86400,
|
||||
'shell':'set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_txn || $CONTRUN_NAME=tsan_crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; set -o pipefail && $SHM $DEBUG $TSAN $TSAN_CRASH CRASH_TEST_KILL_ODD=1887 make J=1 crash_test_with_txn || $CONTRUN_NAME=tsan_crash_test_with_txn $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -1036,7 +1043,7 @@ FORMAT_COMPATIBLE_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; build_tools/rocksdb-lego-determinator run_format_compatible || $CONTRUN_NAME=run_format_compatible $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -1058,6 +1065,7 @@ run_no_compression()
|
||||
mv .tmp.fbcode_config.sh build_tools/fbcode_config.sh
|
||||
cat Makefile | grep -v tools/ldb_test.py > .tmp.Makefile
|
||||
mv .tmp.Makefile Makefile
|
||||
export $SKIP_FORMAT_CHECKS
|
||||
make $DEBUG J=1 check
|
||||
}
|
||||
|
||||
@@ -1070,7 +1078,7 @@ NO_COMPRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Run RocksDB debug unit test',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; build_tools/rocksdb-lego-determinator run_no_compression || $CONTRUN_NAME=run_no_compression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -1129,7 +1137,7 @@ REGRESSION_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Make and run script',
|
||||
'shell':'build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; build_tools/rocksdb-lego-determinator run_regression || $CONTRUN_NAME=run_regression $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
@@ -1150,7 +1158,7 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
$CLEANUP_ENV,
|
||||
{
|
||||
'name':'Build RocksDB for Java',
|
||||
'shell':'$SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
|
||||
'shell':'cd $WORKING_DIR; $SETUP_JAVA_ENV; $SHM make rocksdbjava || $CONTRUN_NAME=rocksdbjava $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "logging/logging.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "trace_replay/io_tracer.h"
|
||||
#include "util/compression.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -31,12 +33,13 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: BlobFileBuilder([versions]() { return versions->NewFileNumber(); }, env,
|
||||
fs, immutable_cf_options, mutable_cf_options,
|
||||
file_options, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
column_family_name, io_priority, write_hint, io_tracer,
|
||||
blob_file_paths, blob_file_additions) {}
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
@@ -46,6 +49,7 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: file_number_generator_(std::move(file_number_generator)),
|
||||
@@ -61,6 +65,7 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
column_family_name_(column_family_name),
|
||||
io_priority_(io_priority),
|
||||
write_hint_(write_hint),
|
||||
io_tracer_(io_tracer),
|
||||
blob_file_paths_(blob_file_paths),
|
||||
blob_file_additions_(blob_file_additions),
|
||||
blob_count_(0),
|
||||
@@ -157,11 +162,12 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
|
||||
{
|
||||
TEST_SYNC_POINT("BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile");
|
||||
|
||||
assert(file_options_);
|
||||
const Status s =
|
||||
NewWritableFile(fs_, blob_file_path, &file, *file_options_);
|
||||
Status s = NewWritableFile(fs_, blob_file_path, &file, *file_options_);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:NewWritableFile", &s);
|
||||
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -176,17 +182,19 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
assert(file);
|
||||
file->SetIOPriority(io_priority_);
|
||||
file->SetWriteLifeTimeHint(write_hint_);
|
||||
|
||||
FileTypeSet tmp_set = immutable_cf_options_->checksum_handoff_file_types;
|
||||
Statistics* const statistics = immutable_cf_options_->statistics;
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_paths_->back(), *file_options_, env_,
|
||||
nullptr /*IOTracer*/, statistics, immutable_cf_options_->listeners,
|
||||
immutable_cf_options_->file_checksum_gen_factory));
|
||||
io_tracer_, statistics, immutable_cf_options_->listeners,
|
||||
immutable_cf_options_->file_checksum_gen_factory,
|
||||
tmp_set.Contains(FileType::kBlobFile)));
|
||||
|
||||
std::unique_ptr<BlobLogWriter> blob_log_writer(
|
||||
new BlobLogWriter(std::move(file_writer), env_, statistics,
|
||||
blob_file_number, immutable_cf_options_->use_fsync));
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
std::unique_ptr<BlobLogWriter> blob_log_writer(new BlobLogWriter(
|
||||
std::move(file_writer), env_, statistics, blob_file_number,
|
||||
immutable_cf_options_->use_fsync, do_flush));
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
@@ -195,9 +203,11 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
expiration_range);
|
||||
|
||||
{
|
||||
TEST_SYNC_POINT("BlobFileBuilder::OpenBlobFileIfNeeded:WriteHeader");
|
||||
Status s = blob_log_writer->WriteHeader(header);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"BlobFileBuilder::OpenBlobFileIfNeeded:WriteHeader", &s);
|
||||
|
||||
const Status s = blob_log_writer->WriteHeader(header);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -247,9 +257,10 @@ Status BlobFileBuilder::WriteBlobToFile(const Slice& key, const Slice& blob,
|
||||
|
||||
uint64_t key_offset = 0;
|
||||
|
||||
TEST_SYNC_POINT("BlobFileBuilder::WriteBlobToFile:AddRecord");
|
||||
Status s = writer_->AddRecord(key, blob, &key_offset, blob_offset);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileBuilder::WriteBlobToFile:AddRecord", &s);
|
||||
|
||||
const Status s = writer_->AddRecord(key, blob, &key_offset, blob_offset);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -271,10 +282,10 @@ Status BlobFileBuilder::CloseBlobFile() {
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
|
||||
TEST_SYNC_POINT("BlobFileBuilder::WriteBlobToFile:AppendFooter");
|
||||
Status s = writer_->AppendFooter(footer, &checksum_method, &checksum_value);
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileBuilder::WriteBlobToFile:AppendFooter", &s);
|
||||
|
||||
const Status s =
|
||||
writer_->AppendFooter(footer, &checksum_method, &checksum_value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class BlobFileAddition;
|
||||
class Status;
|
||||
class Slice;
|
||||
class BlobLogWriter;
|
||||
class IOTracer;
|
||||
|
||||
class BlobFileBuilder {
|
||||
public:
|
||||
@@ -36,6 +37,7 @@ class BlobFileBuilder {
|
||||
const std::string& column_family_name,
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions);
|
||||
|
||||
@@ -48,6 +50,7 @@ class BlobFileBuilder {
|
||||
const std::string& column_family_name,
|
||||
Env::IOPriority io_priority,
|
||||
Env::WriteLifeTimeHint write_hint,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions);
|
||||
|
||||
@@ -81,6 +84,7 @@ class BlobFileBuilder {
|
||||
std::string column_family_name_;
|
||||
Env::IOPriority io_priority_;
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
std::vector<std::string>* blob_file_paths_;
|
||||
std::vector<BlobFileAddition>* blob_file_additions_;
|
||||
std::unique_ptr<BlobLogWriter> writer_;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_log_sequential_reader.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
@@ -40,7 +39,8 @@ class TestFileNumberGenerator {
|
||||
|
||||
class BlobFileBuilderTest : public testing::Test {
|
||||
protected:
|
||||
BlobFileBuilderTest() : mock_env_(Env::Default()), fs_(&mock_env_) {}
|
||||
BlobFileBuilderTest()
|
||||
: mock_env_(Env::Default()), fs_(mock_env_.GetFileSystem().get()) {}
|
||||
|
||||
void VerifyBlobFile(uint64_t blob_file_number,
|
||||
const std::string& blob_file_path,
|
||||
@@ -54,7 +54,7 @@ class BlobFileBuilderTest : public testing::Test {
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
ASSERT_OK(
|
||||
fs_.NewRandomAccessFile(blob_file_path, file_options_, &file, dbg));
|
||||
fs_->NewRandomAccessFile(blob_file_path, file_options_, &file, dbg));
|
||||
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
new RandomAccessFileReader(std::move(file), blob_file_path,
|
||||
@@ -108,7 +108,7 @@ class BlobFileBuilderTest : public testing::Test {
|
||||
}
|
||||
|
||||
MockEnv mock_env_;
|
||||
LegacyFileSystemWrapper fs_;
|
||||
FileSystem* fs_;
|
||||
FileOptions file_options_;
|
||||
};
|
||||
|
||||
@@ -138,11 +138,11 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &mock_env_, &fs_,
|
||||
&immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
@@ -221,11 +221,11 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &mock_env_, &fs_,
|
||||
&immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
@@ -306,11 +306,11 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &mock_env_, &fs_,
|
||||
&immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
const std::string key = std::to_string(i);
|
||||
@@ -358,11 +358,11 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &mock_env_, &fs_,
|
||||
&immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string uncompressed_value(value_size, 'x');
|
||||
@@ -440,11 +440,11 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &mock_env_, &fs_,
|
||||
&immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack("CompressData:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
@@ -517,11 +517,11 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &mock_env_, &fs_,
|
||||
&immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string value("deadbeef");
|
||||
@@ -570,13 +570,11 @@ class BlobFileBuilderIOErrorTest
|
||||
protected:
|
||||
BlobFileBuilderIOErrorTest()
|
||||
: mock_env_(Env::Default()),
|
||||
fault_injection_env_(&mock_env_),
|
||||
fs_(&fault_injection_env_),
|
||||
fs_(mock_env_.GetFileSystem().get()),
|
||||
sync_point_(GetParam()) {}
|
||||
|
||||
MockEnv mock_env_;
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
LegacyFileSystemWrapper fs_;
|
||||
FileSystem* fs_;
|
||||
FileOptions file_options_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
@@ -597,11 +595,11 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
|
||||
Options options;
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&fault_injection_env_,
|
||||
"BlobFileBuilderIOErrorTest_IOError"),
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileBuilderIOErrorTest_IOError"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = value_size;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
@@ -615,15 +613,17 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), &fault_injection_env_,
|
||||
&fs_, &immutable_cf_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), &mock_env_, fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id, column_family_id,
|
||||
column_family_name, io_priority, write_hint, nullptr /*IOTracer*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
|
||||
Status* const s = static_cast<Status*>(arg);
|
||||
assert(s);
|
||||
|
||||
(*s) = Status::IOError(sync_point_);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "trace_replay/io_tracer.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -21,13 +22,15 @@ BlobFileCache::BlobFileCache(Cache* cache,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const FileOptions* file_options,
|
||||
uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist)
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: cache_(cache),
|
||||
mutex_(kNumberOfMutexStripes, GetSliceNPHash64),
|
||||
mutex_(kNumberOfMutexStripes, kGetSliceNPHash64UnseededFnPtr),
|
||||
immutable_cf_options_(immutable_cf_options),
|
||||
file_options_(file_options),
|
||||
column_family_id_(column_family_id),
|
||||
blob_file_read_hist_(blob_file_read_hist) {
|
||||
blob_file_read_hist_(blob_file_read_hist),
|
||||
io_tracer_(io_tracer) {
|
||||
assert(cache_);
|
||||
assert(immutable_cf_options_);
|
||||
assert(file_options_);
|
||||
@@ -71,7 +74,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
assert(file_options_);
|
||||
const Status s = BlobFileReader::Create(
|
||||
*immutable_cf_options_, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, &reader);
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_, &reader);
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
return s;
|
||||
|
||||
@@ -20,12 +20,14 @@ class HistogramImpl;
|
||||
class Status;
|
||||
class BlobFileReader;
|
||||
class Slice;
|
||||
class IOTracer;
|
||||
|
||||
class BlobFileCache {
|
||||
public:
|
||||
BlobFileCache(Cache* cache, const ImmutableCFOptions* immutable_cf_options,
|
||||
const FileOptions* file_options, uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist);
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
|
||||
BlobFileCache(const BlobFileCache&) = delete;
|
||||
BlobFileCache& operator=(const BlobFileCache&) = delete;
|
||||
@@ -42,6 +44,7 @@ class BlobFileCache {
|
||||
const FileOptions* file_options_;
|
||||
uint32_t column_family_id_;
|
||||
HistogramImpl* blob_file_read_hist_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
|
||||
static constexpr size_t kNumberOfMutexStripes = 1 << 7;
|
||||
};
|
||||
|
||||
@@ -46,10 +46,11 @@ void WriteBlobFile(uint32_t column_family_id,
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
blob_file_number, use_fsync);
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
@@ -113,7 +114,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
// First try: reader should be opened and put in cache
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
@@ -158,7 +159,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
@@ -206,7 +207,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_IOError) {
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
// Note: there is no blob file with the below number
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
@@ -247,7 +248,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
// Insert into cache should fail since it has zero capacity and
|
||||
// strict_capacity_limit is set
|
||||
|
||||
@@ -24,6 +24,7 @@ Status BlobFileReader::Create(
|
||||
const ImmutableCFOptions& immutable_cf_options,
|
||||
const FileOptions& file_options, uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist, uint64_t blob_file_number,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader) {
|
||||
assert(blob_file_reader);
|
||||
assert(!*blob_file_reader);
|
||||
@@ -34,7 +35,7 @@ Status BlobFileReader::Create(
|
||||
{
|
||||
const Status s =
|
||||
OpenFile(immutable_cf_options, file_options, blob_file_read_hist,
|
||||
blob_file_number, &file_size, &file_reader);
|
||||
blob_file_number, io_tracer, &file_size, &file_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -68,8 +69,8 @@ Status BlobFileReader::Create(
|
||||
Status BlobFileReader::OpenFile(
|
||||
const ImmutableCFOptions& immutable_cf_options,
|
||||
const FileOptions& file_opts, HistogramImpl* blob_file_read_hist,
|
||||
uint64_t blob_file_number, uint64_t* file_size,
|
||||
std::unique_ptr<RandomAccessFileReader>* file_reader) {
|
||||
uint64_t blob_file_number, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t* file_size, std::unique_ptr<RandomAccessFileReader>* file_reader) {
|
||||
assert(file_size);
|
||||
assert(file_reader);
|
||||
|
||||
@@ -117,10 +118,11 @@ Status BlobFileReader::OpenFile(
|
||||
}
|
||||
|
||||
file_reader->reset(new RandomAccessFileReader(
|
||||
std::move(file), blob_file_path, immutable_cf_options.env,
|
||||
std::shared_ptr<IOTracer>(), immutable_cf_options.statistics,
|
||||
BLOB_DB_BLOB_FILE_READ_MICROS, blob_file_read_hist,
|
||||
immutable_cf_options.rate_limiter, immutable_cf_options.listeners));
|
||||
std::move(file), blob_file_path,
|
||||
immutable_cf_options.env, io_tracer,
|
||||
immutable_cf_options.statistics, BLOB_DB_BLOB_FILE_READ_MICROS,
|
||||
blob_file_read_hist, immutable_cf_options.rate_limiter,
|
||||
immutable_cf_options.listeners));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ class BlobFileReader {
|
||||
uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
uint64_t blob_file_number,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::unique_ptr<BlobFileReader>* reader);
|
||||
|
||||
BlobFileReader(const BlobFileReader&) = delete;
|
||||
@@ -47,7 +48,9 @@ class BlobFileReader {
|
||||
static Status OpenFile(const ImmutableCFOptions& immutable_cf_options,
|
||||
const FileOptions& file_opts,
|
||||
HistogramImpl* blob_file_read_hist,
|
||||
uint64_t blob_file_number, uint64_t* file_size,
|
||||
uint64_t blob_file_number,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
uint64_t* file_size,
|
||||
std::unique_ptr<RandomAccessFileReader>* file_reader);
|
||||
|
||||
static Status ReadHeader(const RandomAccessFileReader* file_reader,
|
||||
|
||||
@@ -54,10 +54,11 @@ void WriteBlobFile(const ImmutableCFOptions& immutable_cf_options,
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
blob_file_number, use_fsync);
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, compression_type, has_ttl,
|
||||
expiration_range_header);
|
||||
@@ -141,9 +142,9 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader));
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
// Make sure the blob can be retrieved with and without checksum verification
|
||||
ReadOptions read_options;
|
||||
@@ -263,10 +264,11 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
blob_file_number, use_fsync);
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, kNoCompression, has_ttl,
|
||||
expiration_range);
|
||||
@@ -280,7 +282,8 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader)
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -313,7 +316,8 @@ TEST_F(BlobFileReaderTest, TTL) {
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader)
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -351,7 +355,8 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInHeader) {
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader)
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -389,7 +394,8 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInFooter) {
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader)
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -427,7 +433,7 @@ TEST_F(BlobFileReaderTest, IncorrectColumnFamily) {
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
incorrect_column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
&reader)
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
@@ -458,9 +464,9 @@ TEST_F(BlobFileReaderTest, BlobCRCError) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader));
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::VerifyBlob:CheckBlobCRC", [](void* arg) {
|
||||
@@ -514,9 +520,9 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader));
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
// Make sure the blob can be retrieved with and without checksum verification
|
||||
ReadOptions read_options;
|
||||
@@ -574,9 +580,9 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader));
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::UncompressBlobIfNeeded:TamperWithResult", [](void* arg) {
|
||||
@@ -659,9 +665,9 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const Status s = BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader);
|
||||
const Status s = BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
|
||||
const bool fail_during_create =
|
||||
(sync_point_ != "BlobFileReader::GetBlob:ReadFromFile");
|
||||
@@ -739,9 +745,9 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const Status s = BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, &reader);
|
||||
const Status s = BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
|
||||
const bool fail_during_create =
|
||||
sync_point_ != "BlobFileReader::GetBlob:TamperWithResult";
|
||||
|
||||
@@ -20,13 +20,15 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobLogWriter::BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest,
|
||||
Env* env, Statistics* statistics,
|
||||
uint64_t log_number, bool use_fs, uint64_t boffset)
|
||||
uint64_t log_number, bool use_fs, bool do_flush,
|
||||
uint64_t boffset)
|
||||
: dest_(std::move(dest)),
|
||||
env_(env),
|
||||
statistics_(statistics),
|
||||
log_number_(log_number),
|
||||
block_offset_(boffset),
|
||||
use_fsync_(use_fs),
|
||||
do_flush_(do_flush),
|
||||
last_elem_type_(kEtNone) {}
|
||||
|
||||
BlobLogWriter::~BlobLogWriter() = default;
|
||||
@@ -49,7 +51,9 @@ Status BlobLogWriter::WriteHeader(BlobLogHeader& header) {
|
||||
Status s = dest_->Append(Slice(str));
|
||||
if (s.ok()) {
|
||||
block_offset_ += str.size();
|
||||
s = dest_->Flush();
|
||||
if (do_flush_) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
}
|
||||
last_elem_type_ = kEtFileHdr;
|
||||
RecordTick(statistics_, BLOB_DB_BLOB_FILE_BYTES_WRITTEN,
|
||||
@@ -152,7 +156,7 @@ Status BlobLogWriter::EmitPhysicalRecord(const std::string& headerbuf,
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(val);
|
||||
}
|
||||
if (s.ok()) {
|
||||
if (do_flush_ && s.ok()) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class BlobLogWriter {
|
||||
// "*dest" must remain live while this BlobLogWriter is in use.
|
||||
BlobLogWriter(std::unique_ptr<WritableFileWriter>&& dest, Env* env,
|
||||
Statistics* statistics, uint64_t log_number, bool use_fsync,
|
||||
uint64_t boffset = 0);
|
||||
bool do_flush, uint64_t boffset = 0);
|
||||
// No copying allowed
|
||||
BlobLogWriter(const BlobLogWriter&) = delete;
|
||||
BlobLogWriter& operator=(const BlobLogWriter&) = delete;
|
||||
@@ -74,6 +74,7 @@ class BlobLogWriter {
|
||||
uint64_t log_number_;
|
||||
uint64_t block_offset_; // Current offset in block
|
||||
bool use_fsync_;
|
||||
bool do_flush_;
|
||||
|
||||
public:
|
||||
enum ElemType { kEtNone, kEtFileHdr, kEtRecord, kEtFileFooter };
|
||||
|
||||
@@ -210,6 +210,63 @@ TEST_F(DBBlobBasicTest, GetBlob_IndexWithInvalidFileNumber) {
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBlobBasicTest, GenerateIOTracing) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
std::string trace_file = dbname_ + "/io_trace_file";
|
||||
|
||||
Reopen(options);
|
||||
{
|
||||
// Create IO trace file
|
||||
std::unique_ptr<TraceWriter> trace_writer;
|
||||
ASSERT_OK(
|
||||
NewFileTraceWriter(env_, EnvOptions(), trace_file, &trace_writer));
|
||||
ASSERT_OK(db_->StartIOTrace(env_, TraceOptions(), std::move(trace_writer)));
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob_value[] = "blob_value";
|
||||
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(Get(key), blob_value);
|
||||
|
||||
ASSERT_OK(db_->EndIOTrace());
|
||||
ASSERT_OK(env_->FileExists(trace_file));
|
||||
}
|
||||
{
|
||||
// Parse trace file to check file opertions related to blob files are
|
||||
// recorded.
|
||||
std::unique_ptr<TraceReader> trace_reader;
|
||||
ASSERT_OK(
|
||||
NewFileTraceReader(env_, EnvOptions(), trace_file, &trace_reader));
|
||||
IOTraceReader reader(std::move(trace_reader));
|
||||
|
||||
IOTraceHeader header;
|
||||
ASSERT_OK(reader.ReadHeader(&header));
|
||||
ASSERT_EQ(kMajorVersion, static_cast<int>(header.rocksdb_major_version));
|
||||
ASSERT_EQ(kMinorVersion, static_cast<int>(header.rocksdb_minor_version));
|
||||
|
||||
// Read records.
|
||||
int blob_files_op_count = 0;
|
||||
Status status;
|
||||
while (true) {
|
||||
IOTraceRecord record;
|
||||
status = reader.ReadIOOp(&record);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
if (record.file_name.find("blob") != std::string::npos) {
|
||||
blob_files_op_count++;
|
||||
}
|
||||
}
|
||||
// Assuming blob files will have Append, Close and then Read operations.
|
||||
ASSERT_GT(blob_files_op_count, 2);
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
class DBBlobBasicIOErrorTest : public DBBlobBasicTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
protected:
|
||||
|
||||
+6
-4
@@ -28,6 +28,7 @@
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/iterator.h"
|
||||
@@ -147,13 +148,14 @@ Status BuildTable(
|
||||
file_checksum, file_checksum_func_name);
|
||||
return s;
|
||||
}
|
||||
FileTypeSet tmp_set = ioptions.checksum_handoff_file_types;
|
||||
file->SetIOPriority(io_priority);
|
||||
file->SetWriteLifeTimeHint(write_hint);
|
||||
|
||||
file_writer.reset(new WritableFileWriter(
|
||||
std::move(file), fname, file_options, env, io_tracer,
|
||||
ioptions.statistics, ioptions.listeners,
|
||||
ioptions.file_checksum_gen_factory));
|
||||
ioptions.file_checksum_gen_factory,
|
||||
tmp_set.Contains(FileType::kTableFile)));
|
||||
|
||||
builder = NewTableBuilder(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
@@ -175,8 +177,8 @@ Status BuildTable(
|
||||
? new BlobFileBuilder(versions, env, fs, &ioptions,
|
||||
&mutable_cf_options, &file_options, job_id,
|
||||
column_family_id, column_family_name,
|
||||
io_priority, write_hint, &blob_file_paths,
|
||||
blob_file_additions)
|
||||
io_priority, write_hint, io_tracer,
|
||||
&blob_file_paths, blob_file_additions)
|
||||
: nullptr);
|
||||
|
||||
CompactionIterator c_iter(
|
||||
|
||||
@@ -1388,34 +1388,39 @@ char* rocksdb_property_value_cf(
|
||||
}
|
||||
}
|
||||
|
||||
void rocksdb_approximate_sizes(
|
||||
rocksdb_t* db,
|
||||
int num_ranges,
|
||||
const char* const* range_start_key, const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key, const size_t* range_limit_key_len,
|
||||
uint64_t* sizes) {
|
||||
void rocksdb_approximate_sizes(rocksdb_t* db, int num_ranges,
|
||||
const char* const* range_start_key,
|
||||
const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key,
|
||||
const size_t* range_limit_key_len,
|
||||
uint64_t* sizes, char** errptr) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
|
||||
}
|
||||
db->rep->GetApproximateSizes(ranges, num_ranges, sizes);
|
||||
Status s = db->rep->GetApproximateSizes(ranges, num_ranges, sizes);
|
||||
if (!s.ok()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
void rocksdb_approximate_sizes_cf(
|
||||
rocksdb_t* db,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges,
|
||||
const char* const* range_start_key, const size_t* range_start_key_len,
|
||||
const char* const* range_limit_key, const size_t* range_limit_key_len,
|
||||
uint64_t* sizes) {
|
||||
rocksdb_t* db, rocksdb_column_family_handle_t* column_family,
|
||||
int num_ranges, const char* const* range_start_key,
|
||||
const size_t* range_start_key_len, const char* const* range_limit_key,
|
||||
const size_t* range_limit_key_len, uint64_t* sizes, char** errptr) {
|
||||
Range* ranges = new Range[num_ranges];
|
||||
for (int i = 0; i < num_ranges; i++) {
|
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]);
|
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]);
|
||||
}
|
||||
db->rep->GetApproximateSizes(column_family->rep, ranges, num_ranges, sizes);
|
||||
Status s = db->rep->GetApproximateSizes(column_family->rep, ranges,
|
||||
num_ranges, sizes);
|
||||
if (!s.ok()) {
|
||||
SaveError(errptr, s);
|
||||
}
|
||||
delete[] ranges;
|
||||
}
|
||||
|
||||
@@ -2769,6 +2774,14 @@ void rocksdb_options_set_bottommost_compression_options_zstd_max_train_bytes(
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression_options_max_dict_buffer_bytes(
|
||||
rocksdb_options_t* opt, uint64_t max_dict_buffer_bytes,
|
||||
unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.max_dict_buffer_bytes =
|
||||
max_dict_buffer_bytes;
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
int level, int strategy,
|
||||
int max_dict_bytes) {
|
||||
@@ -2783,6 +2796,11 @@ void rocksdb_options_set_compression_options_zstd_max_train_bytes(
|
||||
opt->rep.compression_opts.zstd_max_train_bytes = zstd_max_train_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options_max_dict_buffer_bytes(
|
||||
rocksdb_options_t* opt, uint64_t max_dict_buffer_bytes) {
|
||||
opt->rep.compression_opts.max_dict_buffer_bytes = max_dict_buffer_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_prefix_extractor(
|
||||
rocksdb_options_t* opt, rocksdb_slicetransform_t* prefix_extractor) {
|
||||
opt->rep.prefix_extractor.reset(prefix_extractor);
|
||||
@@ -3931,6 +3949,25 @@ unsigned char rocksdb_readoptions_get_ignore_range_deletions(
|
||||
return opt->rep.ignore_range_deletions;
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_deadline(rocksdb_readoptions_t* opt,
|
||||
uint64_t microseconds) {
|
||||
opt->rep.deadline = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
uint64_t rocksdb_readoptions_get_deadline(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.deadline.count();
|
||||
}
|
||||
|
||||
void rocksdb_readoptions_set_io_timeout(rocksdb_readoptions_t* opt,
|
||||
uint64_t microseconds) {
|
||||
opt->rep.io_timeout = std::chrono::microseconds(microseconds);
|
||||
}
|
||||
|
||||
extern ROCKSDB_LIBRARY_API uint64_t
|
||||
rocksdb_readoptions_get_io_timeout(rocksdb_readoptions_t* opt) {
|
||||
return opt->rep.io_timeout.count();
|
||||
}
|
||||
|
||||
rocksdb_writeoptions_t* rocksdb_writeoptions_create() {
|
||||
return new rocksdb_writeoptions_t;
|
||||
}
|
||||
|
||||
+9
-1
@@ -988,7 +988,9 @@ int main(int argc, char** argv) {
|
||||
&err);
|
||||
CheckNoError(err);
|
||||
}
|
||||
rocksdb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes);
|
||||
rocksdb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes,
|
||||
&err);
|
||||
CheckNoError(err);
|
||||
CheckCondition(sizes[0] > 0);
|
||||
CheckCondition(sizes[1] > 0);
|
||||
}
|
||||
@@ -2279,6 +2281,12 @@ int main(int argc, char** argv) {
|
||||
rocksdb_readoptions_set_ignore_range_deletions(ro, 1);
|
||||
CheckCondition(1 == rocksdb_readoptions_get_ignore_range_deletions(ro));
|
||||
|
||||
rocksdb_readoptions_set_deadline(ro, 300);
|
||||
CheckCondition(300 == rocksdb_readoptions_get_deadline(ro));
|
||||
|
||||
rocksdb_readoptions_set_io_timeout(ro, 400);
|
||||
CheckCondition(400 == rocksdb_readoptions_get_io_timeout(ro));
|
||||
|
||||
rocksdb_readoptions_destroy(ro);
|
||||
}
|
||||
|
||||
|
||||
+11
-10
@@ -33,6 +33,7 @@
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
@@ -562,7 +563,7 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
block_cache_tracer, io_tracer));
|
||||
blob_file_cache_.reset(
|
||||
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
|
||||
internal_stats_->GetBlobFileReadHist()));
|
||||
internal_stats_->GetBlobFileReadHist(), io_tracer));
|
||||
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
@@ -1355,19 +1356,19 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Status ColumnFamilyData::SetOptions(
|
||||
const DBOptions& db_options,
|
||||
const DBOptions& db_opts,
|
||||
const std::unordered_map<std::string, std::string>& options_map) {
|
||||
MutableCFOptions new_mutable_cf_options;
|
||||
Status s =
|
||||
GetMutableOptionsFromStrings(mutable_cf_options_, options_map,
|
||||
ioptions_.info_log, &new_mutable_cf_options);
|
||||
ColumnFamilyOptions cf_opts =
|
||||
BuildColumnFamilyOptions(initial_cf_options_, mutable_cf_options_);
|
||||
ConfigOptions config_opts;
|
||||
config_opts.mutable_options_only = true;
|
||||
Status s = GetColumnFamilyOptionsFromMap(config_opts, cf_opts, options_map,
|
||||
&cf_opts);
|
||||
if (s.ok()) {
|
||||
ColumnFamilyOptions cf_options =
|
||||
BuildColumnFamilyOptions(initial_cf_options_, new_mutable_cf_options);
|
||||
s = ValidateOptions(db_options, cf_options);
|
||||
s = ValidateOptions(db_opts, cf_opts);
|
||||
}
|
||||
if (s.ok()) {
|
||||
mutable_cf_options_ = new_mutable_cf_options;
|
||||
mutable_cf_options_ = MutableCFOptions(cf_opts);
|
||||
mutable_cf_options_.RefreshDerivedOptions(ioptions_);
|
||||
}
|
||||
return s;
|
||||
|
||||
@@ -187,8 +187,8 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
names_.clear();
|
||||
for (size_t i = 0; i < cf.size(); ++i) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
cf[i], options.size() == 0 ? column_family_options_ : options[i]));
|
||||
column_families.emplace_back(
|
||||
cf[i], options.size() == 0 ? column_family_options_ : options[i]);
|
||||
names_.push_back(cf[i]);
|
||||
}
|
||||
return DB::Open(db_options_, dbname_, column_families, &handles_, &db_);
|
||||
@@ -199,8 +199,8 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
names_.clear();
|
||||
for (size_t i = 0; i < cf.size(); ++i) {
|
||||
column_families.push_back(ColumnFamilyDescriptor(
|
||||
cf[i], options.size() == 0 ? column_family_options_ : options[i]));
|
||||
column_families.emplace_back(
|
||||
cf[i], options.size() == 0 ? column_family_options_ : options[i]);
|
||||
names_.push_back(cf[i]);
|
||||
}
|
||||
return DB::OpenForReadOnly(db_options_, dbname_, column_families, &handles_,
|
||||
@@ -898,9 +898,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
std::vector<std::string> old_files;
|
||||
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
||||
for (auto& file : old_files) {
|
||||
if (file != "." && file != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||
}
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||
}
|
||||
|
||||
column_family_options_.merge_operator =
|
||||
@@ -929,9 +927,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
std::vector<std::string> logs;
|
||||
ASSERT_OK(env_->GetChildren(db_options_.wal_dir, &logs));
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(db_options_.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||
}
|
||||
CopyFile(db_options_.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||
}
|
||||
|
||||
// recover the DB
|
||||
@@ -956,9 +952,7 @@ TEST_P(ColumnFamilyTest, IgnoreRecoveredLog) {
|
||||
if (iter == 0) {
|
||||
// copy the logs from backup back to wal dir
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(backup_logs + "/" + log, db_options_.wal_dir + "/" + log);
|
||||
}
|
||||
CopyFile(backup_logs + "/" + log, db_options_.wal_dir + "/" + log);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,8 +422,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// In the previous iteration we encountered a single delete that we could
|
||||
// not compact out. We will keep this Put, but can drop it's data.
|
||||
// (See Optimization 3, below.)
|
||||
assert(ikey_.type == kTypeValue);
|
||||
if (ikey_.type != kTypeValue) {
|
||||
assert(ikey_.type == kTypeValue || ikey_.type == kTypeBlobIndex);
|
||||
if (ikey_.type != kTypeValue && ikey_.type != kTypeBlobIndex) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"Unexpected key type %d for compaction output",
|
||||
ikey_.type);
|
||||
@@ -436,6 +436,11 @@ void CompactionIterator::NextFromInput() {
|
||||
current_user_key_snapshot_, last_snapshot);
|
||||
}
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
|
||||
value_.clear();
|
||||
valid_ = true;
|
||||
clear_and_output_next_key_ = false;
|
||||
|
||||
@@ -970,6 +970,21 @@ TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
2 /*earliest_write_conflict_snapshot*/);
|
||||
}
|
||||
|
||||
// Same as above but with a blob index. In addition to the value getting
|
||||
// trimmed, the type of the KV is changed to kTypeValue.
|
||||
TEST_F(CompactionIteratorWithSnapshotCheckerTest,
|
||||
KeepSingleDeletionForWriteConflictChecking_BlobIndex) {
|
||||
AddSnapshot(2, 0);
|
||||
RunTest({test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeBlobIndex)},
|
||||
{"", "fake_blob_index"},
|
||||
{test::KeyStr("a", 2, kTypeSingleDeletion),
|
||||
test::KeyStr("a", 1, kTypeValue)},
|
||||
{"", ""}, 2 /*last_committed_seq*/, nullptr /*merge_operator*/,
|
||||
nullptr /*compaction_filter*/, false /*bottommost_level*/,
|
||||
2 /*earliest_write_conflict_snapshot*/);
|
||||
}
|
||||
|
||||
// Compaction filter should keep uncommitted key as-is, and
|
||||
// * Convert the latest velue to deletion, and/or
|
||||
// * if latest value is a merge, apply filter to all suequent merges.
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "monitoring/iostats_context_imp.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -976,7 +977,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
sub_compact->compaction->immutable_cf_options(),
|
||||
mutable_cf_options, &file_options_, job_id_, cfd->GetID(),
|
||||
cfd->GetName(), Env::IOPriority::IO_LOW, write_hint_,
|
||||
&blob_file_paths, &sub_compact->blob_file_additions)
|
||||
io_tracer_, &blob_file_paths, &sub_compact->blob_file_additions)
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
@@ -1449,8 +1450,9 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
} else {
|
||||
sub_compact->builder->Abandon();
|
||||
}
|
||||
IOStatus io_s = sub_compact->builder->io_status();
|
||||
if (s.ok()) {
|
||||
s = sub_compact->builder->io_status();
|
||||
s = io_s;
|
||||
}
|
||||
const uint64_t current_bytes = sub_compact->builder->FileSize();
|
||||
if (s.ok()) {
|
||||
@@ -1461,7 +1463,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
|
||||
// Finish and check for file errors
|
||||
IOStatus io_s;
|
||||
if (s.ok()) {
|
||||
StopWatch sw(env_, stats_, COMPACTION_OUTFILE_SYNC_MICROS);
|
||||
io_s = sub_compact->outfile->Sync(db_options_.use_fsync);
|
||||
@@ -1733,6 +1734,7 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
|
||||
writable_file->SetIOPriority(Env::IOPriority::IO_LOW);
|
||||
writable_file->SetWriteLifeTimeHint(write_hint_);
|
||||
FileTypeSet tmp_set = db_options_.checksum_handoff_file_types;
|
||||
writable_file->SetPreallocationBlockSize(static_cast<size_t>(
|
||||
sub_compact->compaction->OutputFilePreallocationSize()));
|
||||
const auto& listeners =
|
||||
@@ -1740,7 +1742,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->outfile.reset(new WritableFileWriter(
|
||||
std::move(writable_file), fname, file_options_, env_, io_tracer_,
|
||||
db_options_.statistics.get(), listeners,
|
||||
db_options_.file_checksum_gen_factory.get()));
|
||||
db_options_.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile)));
|
||||
|
||||
// If the Column family flag is to only optimize filters for hits,
|
||||
// we can skip creating filters if this is the bottommost_level where
|
||||
|
||||
@@ -297,15 +297,14 @@ class CompactionJobStatsTest : public testing::Test,
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t Size(const Slice& start, const Slice& limit, int cf = 0) {
|
||||
Status Size(uint64_t* size, const Slice& start, const Slice& limit,
|
||||
int cf = 0) {
|
||||
Range r(start, limit);
|
||||
uint64_t size;
|
||||
if (cf == 0) {
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
return db_->GetApproximateSizes(&r, 1, size);
|
||||
} else {
|
||||
db_->GetApproximateSizes(handles_[1], &r, 1, &size);
|
||||
return db_->GetApproximateSizes(handles_[1], &r, 1, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void Compact(int cf, const Slice& start, const Slice& limit,
|
||||
|
||||
@@ -72,7 +72,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
CompactionJobTestBase(std::string dbname, const Comparator* ucmp,
|
||||
std::function<std::string(uint64_t)> encode_u64_ts)
|
||||
: env_(Env::Default()),
|
||||
fs_(std::make_shared<LegacyFileSystemWrapper>(env_)),
|
||||
fs_(env_->GetFileSystem()),
|
||||
dbname_(std::move(dbname)),
|
||||
ucmp_(ucmp),
|
||||
db_options_(),
|
||||
|
||||
+27
-1
@@ -21,6 +21,7 @@
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
#include "util/file_checksum_helper.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
@@ -3273,7 +3274,9 @@ class DeadlineFS : public FileSystemWrapper {
|
||||
|
||||
// Increment the IO counter and return a delay in microseconds
|
||||
IOStatus ShouldDelay(const IOOptions& opts) {
|
||||
if (!deadline_.count() && !io_timeout_.count()) {
|
||||
if (timedout_) {
|
||||
return IOStatus::TimedOut();
|
||||
} else if (!deadline_.count() && !io_timeout_.count()) {
|
||||
return IOStatus::OK();
|
||||
}
|
||||
if (!ignore_deadline_ && delay_trigger_ == io_count_++) {
|
||||
@@ -3580,6 +3583,29 @@ TEST_F(DBBasicTest, VerifyFileChecksums) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
|
||||
|
||||
// Does the right thing but with the wrong name -- using it should lead to an
|
||||
// error.
|
||||
class MisnamedFileChecksumGenerator : public FileChecksumGenCrc32c {
|
||||
public:
|
||||
MisnamedFileChecksumGenerator(const FileChecksumGenContext& context)
|
||||
: FileChecksumGenCrc32c(context) {}
|
||||
|
||||
const char* Name() const override { return "sha1"; }
|
||||
};
|
||||
|
||||
class MisnamedFileChecksumGenFactory : public FileChecksumGenCrc32cFactory {
|
||||
public:
|
||||
std::unique_ptr<FileChecksumGenerator> CreateFileChecksumGenerator(
|
||||
const FileChecksumGenContext& context) override {
|
||||
return std::unique_ptr<FileChecksumGenerator>(
|
||||
new MisnamedFileChecksumGenerator(context));
|
||||
}
|
||||
};
|
||||
|
||||
options.file_checksum_gen_factory.reset(new MisnamedFileChecksumGenFactory());
|
||||
Reopen(options);
|
||||
ASSERT_TRUE(db_->VerifyFileChecksums(ReadOptions()).IsInvalidArgument());
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class DBTestCompactionFilterWithCompactParam
|
||||
option_config_ == kUniversalSubcompactions) {
|
||||
assert(options.max_subcompactions > 1);
|
||||
}
|
||||
TryReopen(options);
|
||||
Reopen(options);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -276,7 +276,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
}
|
||||
ASSERT_OK(Flush(1));
|
||||
|
||||
@@ -284,10 +284,10 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
// the compaction is each level invokes the filter for
|
||||
// all the keys in that level.
|
||||
cfilter_count = 0;
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(cfilter_count, 100000);
|
||||
cfilter_count = 0;
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(cfilter_count, 100000);
|
||||
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
|
||||
@@ -321,6 +321,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
}
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
ASSERT_EQ(total, 100000);
|
||||
ASSERT_EQ(count, 0);
|
||||
@@ -337,10 +338,10 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
// means that all keys should pass at least once
|
||||
// via the compaction filter
|
||||
cfilter_count = 0;
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(cfilter_count, 100000);
|
||||
cfilter_count = 0;
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(cfilter_count, 100000);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 1), 0);
|
||||
@@ -369,10 +370,10 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
// verify that at the end of the compaction process,
|
||||
// nothing is left.
|
||||
cfilter_count = 0;
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(cfilter_count, 100000);
|
||||
cfilter_count = 0;
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_EQ(cfilter_count, 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
|
||||
ASSERT_EQ(NumTableFilesAtLevel(1, 1), 0);
|
||||
@@ -387,6 +388,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
count++;
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_EQ(count, 0);
|
||||
}
|
||||
|
||||
@@ -427,9 +429,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilterDeletesAll) {
|
||||
// put some data
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
Put(ToString(table * 100 + i), "val");
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// this will produce empty file (delete compaction filter)
|
||||
@@ -440,6 +442,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterDeletesAll) {
|
||||
|
||||
Iterator* itr = db_->NewIterator(ReadOptions());
|
||||
itr->SeekToFirst();
|
||||
ASSERT_OK(itr->status());
|
||||
// empty db
|
||||
ASSERT_TRUE(!itr->Valid());
|
||||
|
||||
@@ -463,25 +466,25 @@ TEST_P(DBTestCompactionFilterWithCompactParam,
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
}
|
||||
|
||||
// push all files to lower levels
|
||||
ASSERT_OK(Flush(1));
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]));
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), handles_[1],
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
// re-write all data again
|
||||
for (int i = 0; i < 100001; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%010d", i);
|
||||
Put(1, key, value);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
}
|
||||
|
||||
// push all files to lower levels. This should
|
||||
@@ -489,11 +492,11 @@ TEST_P(DBTestCompactionFilterWithCompactParam,
|
||||
ASSERT_OK(Flush(1));
|
||||
if (option_config_ != kUniversalCompactionMultiLevel &&
|
||||
option_config_ != kUniversalSubcompactions) {
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]);
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]));
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(1, nullptr, nullptr, handles_[1]));
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), handles_[1], nullptr,
|
||||
nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), handles_[1],
|
||||
nullptr, nullptr));
|
||||
}
|
||||
|
||||
// verify that all keys now have the new value that
|
||||
@@ -531,7 +534,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterWithMergeOperator) {
|
||||
ASSERT_OK(Flush());
|
||||
std::string newvalue = Get("foo");
|
||||
ASSERT_EQ(newvalue, three);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
newvalue = Get("foo");
|
||||
ASSERT_EQ(newvalue, three);
|
||||
|
||||
@@ -539,12 +542,12 @@ TEST_F(DBTestCompactionFilter, CompactionFilterWithMergeOperator) {
|
||||
// merge keys.
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "bar", two));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
newvalue = Get("bar");
|
||||
ASSERT_EQ("NOT_FOUND", newvalue);
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "bar", two));
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
newvalue = Get("bar");
|
||||
ASSERT_EQ(two, two);
|
||||
|
||||
@@ -555,7 +558,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterWithMergeOperator) {
|
||||
ASSERT_OK(Flush());
|
||||
newvalue = Get("foobar");
|
||||
ASSERT_EQ(newvalue, three);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
newvalue = Get("foobar");
|
||||
ASSERT_EQ(newvalue, three);
|
||||
|
||||
@@ -568,7 +571,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterWithMergeOperator) {
|
||||
ASSERT_OK(Flush());
|
||||
newvalue = Get("barfoo");
|
||||
ASSERT_EQ(newvalue, four);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
newvalue = Get("barfoo");
|
||||
ASSERT_EQ(newvalue, four);
|
||||
}
|
||||
@@ -590,21 +593,21 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
for (int i = 0; i < num_keys_per_file; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%08d%02d", i, j);
|
||||
Put(key, value);
|
||||
ASSERT_OK(Put(key, value));
|
||||
}
|
||||
dbfull()->TEST_FlushMemTable();
|
||||
ASSERT_OK(dbfull()->TEST_FlushMemTable());
|
||||
// Make sure next file is much smaller so automatic compaction will not
|
||||
// be triggered.
|
||||
num_keys_per_file /= 2;
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
// Force a manual compaction
|
||||
cfilter_count = 0;
|
||||
filter->expect_manual_compaction_.store(true);
|
||||
filter->expect_full_compaction_.store(true);
|
||||
filter->expect_cf_id_.store(0);
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(cfilter_count, 700);
|
||||
ASSERT_EQ(NumSortedRuns(0), 1);
|
||||
ASSERT_TRUE(filter->compaction_filter_created());
|
||||
@@ -654,14 +657,14 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextCfId) {
|
||||
for (int i = 0; i < num_keys_per_file; i++) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "B%08d%02d", i, j);
|
||||
Put(1, key, value);
|
||||
ASSERT_OK(Put(1, key, value));
|
||||
}
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
// Make sure next file is much smaller so automatic compaction will not
|
||||
// be triggered.
|
||||
num_keys_per_file /= 2;
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_TRUE(filter->compaction_filter_created());
|
||||
}
|
||||
@@ -680,9 +683,9 @@ TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
const Snapshot* snapshot = nullptr;
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Put(ToString(table * 100 + i), "val");
|
||||
ASSERT_OK(Put(ToString(table * 100 + i), "val"));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
if (table == 0) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
@@ -702,6 +705,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
read_options.snapshot = snapshot;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_OK(iter->status());
|
||||
int count = 0;
|
||||
while (iter->Valid()) {
|
||||
count++;
|
||||
@@ -710,6 +714,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterIgnoreSnapshot) {
|
||||
ASSERT_EQ(count, 6);
|
||||
read_options.snapshot = nullptr;
|
||||
std::unique_ptr<Iterator> iter1(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter1->status());
|
||||
iter1->SeekToFirst();
|
||||
count = 0;
|
||||
while (iter1->Valid()) {
|
||||
@@ -740,9 +745,9 @@ TEST_F(DBTestCompactionFilter, SkipUntil) {
|
||||
for (int i = table * 6; i < 39 + table * 11; ++i) {
|
||||
char key[100];
|
||||
snprintf(key, sizeof(key), "%010d", table * 100 + i);
|
||||
Put(key, std::to_string(table * 1000 + i));
|
||||
ASSERT_OK(Put(key, std::to_string(table * 1000 + i)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
cfilter_skips = 0;
|
||||
@@ -781,10 +786,10 @@ TEST_F(DBTestCompactionFilter, SkipUntilWithBloomFilter) {
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put("0000000010", "v10");
|
||||
Put("0000000020", "v20"); // skipped
|
||||
Put("0000000050", "v50");
|
||||
Flush();
|
||||
ASSERT_OK(Put("0000000010", "v10"));
|
||||
ASSERT_OK(Put("0000000020", "v20")); // skipped
|
||||
ASSERT_OK(Put("0000000050", "v50"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
cfilter_skips = 0;
|
||||
EXPECT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
@@ -822,13 +827,13 @@ TEST_F(DBTestCompactionFilter, IgnoreSnapshotsFalse) {
|
||||
options.compaction_filter = new TestNotSupportedFilter();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Put("a", "v10");
|
||||
Put("z", "v20");
|
||||
Flush();
|
||||
ASSERT_OK(Put("a", "v10"));
|
||||
ASSERT_OK(Put("z", "v20"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Put("a", "v10");
|
||||
Put("z", "v20");
|
||||
Flush();
|
||||
ASSERT_OK(Put("a", "v10"));
|
||||
ASSERT_OK(Put("z", "v20"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Comapction should fail because IgnoreSnapshots() = false
|
||||
EXPECT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
|
||||
+609
-319
File diff suppressed because it is too large.
Load diff
+21
-20
@@ -102,7 +102,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
||||
}
|
||||
|
||||
// Test compact range works
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
// All data should be in the last level.
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta);
|
||||
@@ -166,8 +167,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(4U, int_prop);
|
||||
|
||||
@@ -184,8 +185,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(3U, int_prop);
|
||||
ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level1", &str_prop));
|
||||
@@ -205,8 +206,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
ASSERT_OK(dbfull()->SetOptions({
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(3U, int_prop);
|
||||
|
||||
@@ -234,8 +235,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
}));
|
||||
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:0");
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
ASSERT_EQ(2U, int_prop);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -264,7 +265,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
}
|
||||
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:2");
|
||||
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
thread.join();
|
||||
|
||||
@@ -302,7 +303,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Compact against empty DB
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
uint64_t int_prop;
|
||||
std::string str_prop;
|
||||
@@ -316,13 +317,13 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
ASSERT_OK(
|
||||
Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))), rnd.RandomString(80)));
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
if (NumTableFilesAtLevel(0) == 0) {
|
||||
// Make sure level 0 is not empty
|
||||
ASSERT_OK(
|
||||
Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))), rnd.RandomString(80)));
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
||||
@@ -343,7 +344,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(output_levels.size(), 2);
|
||||
ASSERT_TRUE(output_levels.find(3) != output_levels.end());
|
||||
ASSERT_TRUE(output_levels.find(4) != output_levels.end());
|
||||
@@ -389,8 +390,8 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBaseInc) {
|
||||
PutFixed32(&value, static_cast<uint32_t>(i));
|
||||
ASSERT_OK(Put(Key(i), value));
|
||||
}
|
||||
Flush();
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ASSERT_EQ(non_trivial, 0);
|
||||
@@ -449,7 +450,7 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
ASSERT_OK(Delete(Key(i / 10)));
|
||||
}
|
||||
verify_func(total_keys, false);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
options.level_compaction_dynamic_level_bytes = true;
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -464,7 +465,7 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = options.num_levels - 1;
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
||||
compaction_finished.store(true);
|
||||
});
|
||||
do {
|
||||
@@ -484,7 +485,7 @@ TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
||||
}
|
||||
|
||||
verify_func(total_keys2, false);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
verify_func(total_keys2, false);
|
||||
|
||||
// Base level is not level 1
|
||||
|
||||
@@ -44,7 +44,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
Env* target = GetTargetEnv();
|
||||
int hits = 0;
|
||||
for (auto it = fileNames.begin() ; it != fileNames.end(); ++it) {
|
||||
if ((*it == "..") || (*it == ".") || (*it == "LOCK")) {
|
||||
if (*it == "LOCK") {
|
||||
continue;
|
||||
}
|
||||
auto filePath = dbname_ + "/" + *it;
|
||||
|
||||
+206
-13
@@ -19,6 +19,7 @@
|
||||
#include "util/cast_util.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -451,7 +452,6 @@ TEST_F(DBFlushTest, FlushWithBlob) {
|
||||
constexpr uint64_t min_blob_size = 10;
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = min_blob_size;
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -525,14 +525,209 @@ TEST_F(DBFlushTest, FlushWithBlob) {
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, FlushWithChecksumHandoff1) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_fs_env.get();
|
||||
options.checksum_handoff_file_types.Add(FileType::kTableFile);
|
||||
Reopen(options);
|
||||
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
|
||||
// The hash does not match, write fails
|
||||
// fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
|
||||
// Since the file system returns IOStatus::Corruption, it is an
|
||||
// unrecoverable error.
|
||||
SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) {
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
|
||||
});
|
||||
ASSERT_OK(Put("key3", "value3"));
|
||||
ASSERT_OK(Put("key4", "value4"));
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = Flush();
|
||||
ASSERT_EQ(s.severity(),
|
||||
ROCKSDB_NAMESPACE::Status::Severity::kUnrecoverableError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Destroy(options);
|
||||
Reopen(options);
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
|
||||
ASSERT_OK(Put("key5", "value5"));
|
||||
ASSERT_OK(Put("key6", "value6"));
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
|
||||
// Each write will be similated as corrupted.
|
||||
// Since the file system returns IOStatus::Corruption, it is an
|
||||
// unrecoverable error.
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) {
|
||||
fault_fs->IngestDataCorruptionBeforeWrite();
|
||||
});
|
||||
ASSERT_OK(Put("key7", "value7"));
|
||||
ASSERT_OK(Put("key8", "value8"));
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(),
|
||||
ROCKSDB_NAMESPACE::Status::Severity::kUnrecoverableError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, FlushWithChecksumHandoff2) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_fs_env.get();
|
||||
Reopen(options);
|
||||
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// options is not set, the checksum handoff will not be triggered
|
||||
SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) {
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
|
||||
});
|
||||
ASSERT_OK(Put("key3", "value3"));
|
||||
ASSERT_OK(Put("key4", "value4"));
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Destroy(options);
|
||||
Reopen(options);
|
||||
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
|
||||
ASSERT_OK(Put("key5", "value5"));
|
||||
ASSERT_OK(Put("key6", "value6"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// options is not set, the checksum handoff will not be triggered
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
SyncPoint::GetInstance()->SetCallBack("FlushJob::Start", [&](void*) {
|
||||
fault_fs->IngestDataCorruptionBeforeWrite();
|
||||
});
|
||||
ASSERT_OK(Put("key7", "value7"));
|
||||
ASSERT_OK(Put("key8", "value8"));
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, FlushWithChecksumHandoffManifest1) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_fs_env.get();
|
||||
options.checksum_handoff_file_types.Add(FileType::kDescriptorFile);
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("key1", "value1"));
|
||||
ASSERT_OK(Put("key2", "value2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// The hash does not match, write fails
|
||||
// fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
|
||||
// Since the file system returns IOStatus::Corruption, it is mapped to
|
||||
// kFatalError error.
|
||||
ASSERT_OK(Put("key3", "value3"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest", [&](void*) {
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
|
||||
});
|
||||
ASSERT_OK(Put("key3", "value3"));
|
||||
ASSERT_OK(Put("key4", "value4"));
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, FlushWithChecksumHandoffManifest2) {
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100;
|
||||
options.max_write_buffer_number = 4;
|
||||
options.min_write_buffer_number_to_merge = 3;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = fault_fs_env.get();
|
||||
options.checksum_handoff_file_types.Add(FileType::kDescriptorFile);
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
|
||||
Reopen(options);
|
||||
// The file system does not support checksum handoff. The check
|
||||
// will be ignored.
|
||||
ASSERT_OK(Put("key5", "value5"));
|
||||
ASSERT_OK(Put("key6", "value6"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Each write will be similated as corrupted.
|
||||
// Since the file system returns IOStatus::Corruption, it is mapped to
|
||||
// kFatalError error.
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest",
|
||||
[&](void*) { fault_fs->IngestDataCorruptionBeforeWrite(); });
|
||||
ASSERT_OK(Put("key7", "value7"));
|
||||
ASSERT_OK(Put("key8", "value8"));
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
class DBFlushTestBlobError : public DBFlushTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBFlushTestBlobError()
|
||||
: fault_injection_env_(env_), sync_point_(GetParam()) {}
|
||||
~DBFlushTestBlobError() { Close(); }
|
||||
DBFlushTestBlobError() : sync_point_(GetParam()) {}
|
||||
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
@@ -545,20 +740,18 @@ TEST_P(DBFlushTestBlobError, FlushError) {
|
||||
Options options;
|
||||
options.enable_blob_files = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = &fault_injection_env_;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("key", "blob"));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
|
||||
Status* const s = static_cast<Status*>(arg);
|
||||
assert(s);
|
||||
|
||||
(*s) = Status::IOError(sync_point_);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile", [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(true);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_NOK(Flush());
|
||||
|
||||
+61
-29
@@ -53,7 +53,6 @@
|
||||
#include "db/version_set.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "db/write_callback.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
@@ -83,6 +82,7 @@
|
||||
#include "rocksdb/stats_history.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/table.h"
|
||||
#include "rocksdb/version.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/block_based/block.h"
|
||||
#include "table/block_based/block_based_table_factory.h"
|
||||
@@ -94,7 +94,6 @@
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/build_version.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/compression.h"
|
||||
@@ -307,18 +306,21 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
mutex_.AssertHeld();
|
||||
WaitForBackgroundWork();
|
||||
|
||||
Status bg_error = error_handler_.GetBGError();
|
||||
Status s;
|
||||
if (shutdown_initiated_) {
|
||||
// Returning shutdown status to SFM during auto recovery will cause it
|
||||
// to abort the recovery and allow the shutdown to progress
|
||||
s = Status::ShutdownInProgress();
|
||||
}
|
||||
if (s.ok() && bg_error.severity() > Status::Severity::kHardError) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"DB resume requested but failed due to Fatal/Unrecoverable error");
|
||||
s = bg_error;
|
||||
|
||||
if (s.ok()) {
|
||||
Status bg_error = error_handler_.GetBGError();
|
||||
if (bg_error.severity() > Status::Severity::kHardError) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"DB resume requested but failed due to Fatal/Unrecoverable error");
|
||||
s = bg_error;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the IO Status stored in version set is set to OK.
|
||||
@@ -393,6 +395,11 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
FindObsoleteFiles(&job_context, true);
|
||||
if (s.ok()) {
|
||||
s = error_handler_.ClearBGError();
|
||||
} else {
|
||||
// NOTE: this is needed to pass ASSERT_STATUS_CHECKED
|
||||
// in the DBSSTTest.DBWithMaxSpaceAllowedRandomized test.
|
||||
// See https://github.com/facebook/rocksdb/pull/7715#issuecomment-754947952
|
||||
error_handler_.GetRecoveryError().PermitUncheckedError();
|
||||
}
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -409,6 +416,12 @@ Status DBImpl::ResumeImpl(DBRecoverContext context) {
|
||||
if (file_deletion_disabled) {
|
||||
// Always return ok
|
||||
s = EnableFileDeletions(/*force=*/true);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"DB resume requested but could not enable file deletions [%s]",
|
||||
s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log, "Successfully resumed DB");
|
||||
}
|
||||
@@ -690,6 +703,18 @@ void DBImpl::PrintStatistics() {
|
||||
|
||||
void DBImpl::StartPeriodicWorkScheduler() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#ifndef NDEBUG
|
||||
// It only used by test to disable scheduler
|
||||
bool disable_scheduler = false;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::StartPeriodicWorkScheduler:DisableScheduler",
|
||||
&disable_scheduler);
|
||||
if (disable_scheduler) {
|
||||
return;
|
||||
}
|
||||
#endif // !NDEBUG
|
||||
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
periodic_work_scheduler_ = PeriodicWorkScheduler::Default();
|
||||
@@ -1693,7 +1718,9 @@ Status DBImpl::GetImpl(const ReadOptions& read_options, const Slice& key,
|
||||
}
|
||||
// If timestamp is used, we use read callback to ensure <key,t,s> is returned
|
||||
// only if t <= read_opts.timestamp and s <= snapshot.
|
||||
if (ts_sz > 0 && !get_impl_options.callback) {
|
||||
if (ts_sz > 0) {
|
||||
assert(!get_impl_options
|
||||
.callback); // timestamp with callback is not supported
|
||||
read_cb.Refresh(snapshot);
|
||||
get_impl_options.callback = &read_cb;
|
||||
}
|
||||
@@ -2369,8 +2396,9 @@ void DBImpl::MultiGetWithCallback(
|
||||
}
|
||||
|
||||
GetWithTimestampReadCallback timestamp_read_callback(0);
|
||||
ReadCallback* read_callback = nullptr;
|
||||
ReadCallback* read_callback = callback;
|
||||
if (read_options.timestamp && read_options.timestamp->size() > 0) {
|
||||
assert(!read_callback); // timestamp with callback is not supported
|
||||
timestamp_read_callback.Refresh(consistent_seqnum);
|
||||
read_callback = ×tamp_read_callback;
|
||||
}
|
||||
@@ -3099,8 +3127,8 @@ const std::string& DBImpl::GetName() const { return dbname_; }
|
||||
Env* DBImpl::GetEnv() const { return env_; }
|
||||
|
||||
FileSystem* DB::GetFileSystem() const {
|
||||
static LegacyFileSystemWrapper fs_wrap(GetEnv());
|
||||
return &fs_wrap;
|
||||
const auto& fs = GetEnv()->GetFileSystem();
|
||||
return fs.get();
|
||||
}
|
||||
|
||||
FileSystem* DBImpl::GetFileSystem() const {
|
||||
@@ -3574,7 +3602,7 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
const RangePtr* ranges, size_t n,
|
||||
bool include_end) {
|
||||
Status status;
|
||||
Status status = Status::OK();
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
VersionEdit edit;
|
||||
@@ -3633,7 +3661,7 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
}
|
||||
if (edit.GetDeletedFiles().empty()) {
|
||||
job_context.Clean();
|
||||
return Status::OK();
|
||||
return status;
|
||||
}
|
||||
input_version->Ref();
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
@@ -4180,16 +4208,17 @@ void DBImpl::EraseThreadStatusDbInfo() const {}
|
||||
//
|
||||
// A global method that can dump out the build version
|
||||
void DumpRocksDBBuildVersion(Logger* log) {
|
||||
#if !defined(IOS_CROSS_COMPILE)
|
||||
// if we compile with Xcode, we don't run build_detect_version, so we don't
|
||||
// generate util/build_version.cc
|
||||
ROCKS_LOG_HEADER(log, "RocksDB version: %d.%d.%d\n", ROCKSDB_MAJOR,
|
||||
ROCKSDB_MINOR, ROCKSDB_PATCH);
|
||||
ROCKS_LOG_HEADER(log, "Git sha %s", rocksdb_build_git_sha);
|
||||
ROCKS_LOG_HEADER(log, "Compile date %s", rocksdb_build_compile_date);
|
||||
#else
|
||||
(void)log; // ignore "-Wunused-parameter"
|
||||
#endif
|
||||
ROCKS_LOG_HEADER(log, "RocksDB version: %s\n",
|
||||
GetRocksVersionAsString().c_str());
|
||||
const auto& props = GetRocksBuildProperties();
|
||||
const auto& sha = props.find("rocksdb_build_git_sha");
|
||||
if (sha != props.end()) {
|
||||
ROCKS_LOG_HEADER(log, "Git sha %s", sha->second.c_str());
|
||||
}
|
||||
const auto date = props.find("rocksdb_build_date");
|
||||
if (date != props.end()) {
|
||||
ROCKS_LOG_HEADER(log, "Compile date %s", date->second.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -4944,24 +4973,27 @@ Status DBImpl::EndBlockCacheTrace() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status DBImpl::TraceIteratorSeek(const uint32_t& cf_id, const Slice& key) {
|
||||
Status DBImpl::TraceIteratorSeek(const uint32_t& cf_id, const Slice& key,
|
||||
const Slice& lower_bound,
|
||||
const Slice upper_bound) {
|
||||
Status s;
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
s = tracer_->IteratorSeek(cf_id, key);
|
||||
s = tracer_->IteratorSeek(cf_id, key, lower_bound, upper_bound);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::TraceIteratorSeekForPrev(const uint32_t& cf_id,
|
||||
const Slice& key) {
|
||||
Status DBImpl::TraceIteratorSeekForPrev(const uint32_t& cf_id, const Slice& key,
|
||||
const Slice& lower_bound,
|
||||
const Slice upper_bound) {
|
||||
Status s;
|
||||
if (tracer_) {
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
s = tracer_->IteratorSeekForPrev(cf_id, key);
|
||||
s = tracer_->IteratorSeekForPrev(cf_id, key, lower_bound, upper_bound);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
||||
+10
-2
@@ -595,8 +595,11 @@ class DBImpl : public DB {
|
||||
bool* found_record_for_key,
|
||||
bool* is_blob_index = nullptr);
|
||||
|
||||
Status TraceIteratorSeek(const uint32_t& cf_id, const Slice& key);
|
||||
Status TraceIteratorSeekForPrev(const uint32_t& cf_id, const Slice& key);
|
||||
Status TraceIteratorSeek(const uint32_t& cf_id, const Slice& key,
|
||||
const Slice& lower_bound, const Slice upper_bound);
|
||||
Status TraceIteratorSeekForPrev(const uint32_t& cf_id, const Slice& key,
|
||||
const Slice& lower_bound,
|
||||
const Slice upper_bound);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// Similar to GetSnapshot(), but also lets the db know that this snapshot
|
||||
@@ -954,6 +957,9 @@ class DBImpl : public DB {
|
||||
// is only for the special test of CancelledCompactions
|
||||
Status TEST_WaitForCompact(bool waitUnscheduled = false);
|
||||
|
||||
// Get the background error status
|
||||
Status TEST_GetBGError();
|
||||
|
||||
// Return the maximum overlapping data (in bytes) at next level for any
|
||||
// file at a level >= 1.
|
||||
int64_t TEST_MaxNextLevelOverlappingBytes(
|
||||
@@ -1870,6 +1876,8 @@ class DBImpl : public DB {
|
||||
|
||||
Status DisableFileDeletionsWithLock();
|
||||
|
||||
Status IncreaseFullHistoryTsLow(ColumnFamilyData* cfd, std::string ts_low);
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
std::shared_ptr<Cache> table_cache_;
|
||||
|
||||
|
||||
@@ -35,8 +35,10 @@ bool DBImpl::EnoughRoomForCompaction(
|
||||
// Pass the current bg_error_ to SFM so it can decide what checks to
|
||||
// perform. If this DB instance hasn't seen any error yet, the SFM can be
|
||||
// optimistic and not do disk space checks
|
||||
enough_room =
|
||||
sfm->EnoughRoomForCompaction(cfd, inputs, error_handler_.GetBGError());
|
||||
Status bg_error = error_handler_.GetBGError();
|
||||
enough_room = sfm->EnoughRoomForCompaction(cfd, inputs, bg_error);
|
||||
bg_error.PermitUncheckedError(); // bg_error is just a copy of the Status
|
||||
// from the error_handler_
|
||||
if (enough_room) {
|
||||
*sfm_reserved_compact_space = true;
|
||||
}
|
||||
@@ -284,7 +286,10 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// Notify sst_file_manager that a new file was added
|
||||
std::string file_path = MakeTableFileName(
|
||||
cfd->ioptions()->cf_paths[0].path, file_meta.fd.GetNumber());
|
||||
s = sfm->OnAddFile(file_path);
|
||||
// TODO (PR7798). We should only add the file to the FileManager if it
|
||||
// exists. Otherwise, some tests may fail. Ignore the error in the
|
||||
// interim.
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
if (sfm->IsMaxAllowedSpaceReached()) {
|
||||
Status new_bg_error =
|
||||
Status::SpaceLimit("Max allowed space was reached");
|
||||
@@ -627,7 +632,10 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
if (sfm) {
|
||||
std::string file_path = MakeTableFileName(
|
||||
cfds[i]->ioptions()->cf_paths[0].path, file_meta[i].fd.GetNumber());
|
||||
s = sfm->OnAddFile(file_path);
|
||||
// TODO (PR7798). We should only add the file to the FileManager if it
|
||||
// exists. Otherwise, some tests may fail. Ignore the error in the
|
||||
// interim.
|
||||
sfm->OnAddFile(file_path).PermitUncheckedError();
|
||||
if (sfm->IsMaxAllowedSpaceReached() &&
|
||||
error_handler_.GetBGError().ok()) {
|
||||
Status new_bg_error =
|
||||
@@ -798,6 +806,25 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
end_with_ts);
|
||||
}
|
||||
|
||||
Status DBImpl::IncreaseFullHistoryTsLow(ColumnFamilyData* cfd,
|
||||
std::string ts_low) {
|
||||
VersionEdit edit;
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
edit.SetFullHistoryTsLow(ts_low);
|
||||
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
std::string current_ts_low = cfd->GetFullHistoryTsLow();
|
||||
const Comparator* ucmp = cfd->user_comparator();
|
||||
if (!current_ts_low.empty() &&
|
||||
ucmp->CompareTimestamp(ts_low, current_ts_low) < 0) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot decrease full_history_timestamp_low");
|
||||
}
|
||||
|
||||
return versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(), &edit,
|
||||
&mutex_);
|
||||
}
|
||||
|
||||
Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
@@ -809,6 +836,22 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
}
|
||||
|
||||
bool flush_needed = true;
|
||||
|
||||
// Update full_history_ts_low if it's set
|
||||
if (options.full_history_ts_low != nullptr &&
|
||||
!options.full_history_ts_low->empty()) {
|
||||
std::string ts_low = options.full_history_ts_low->ToString();
|
||||
if (begin != nullptr || end != nullptr) {
|
||||
return Status::InvalidArgument(
|
||||
"Cannot specify compaction range with full_history_ts_low");
|
||||
}
|
||||
Status s = IncreaseFullHistoryTsLow(cfd, ts_low);
|
||||
if (!s.ok()) {
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
Status s;
|
||||
if (begin != nullptr && end != nullptr) {
|
||||
// TODO(ajkr): We could also optimize away the flush in certain cases where
|
||||
|
||||
@@ -177,6 +177,11 @@ Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_GetBGError() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
void DBImpl::TEST_LockMutex() { mutex_.Lock(); }
|
||||
|
||||
void DBImpl::TEST_UnlockMutex() { mutex_.Unlock(); }
|
||||
|
||||
@@ -289,11 +289,13 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
FileTypeSet tmp_set = immutable_db_options_.checksum_handoff_file_types;
|
||||
file->SetPreallocationBlockSize(
|
||||
immutable_db_options_.manifest_preallocation_size);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, file_options, env_, io_tracer_,
|
||||
nullptr /* stats */, immutable_db_options_.listeners));
|
||||
nullptr /* stats */, immutable_db_options_.listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
@@ -1391,7 +1393,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.oldest_ancester_time, meta.file_creation_time,
|
||||
meta.file_checksum, meta.file_checksum_func_name);
|
||||
|
||||
edit->SetBlobFileAdditions(std::move(blob_file_additions));
|
||||
for (const auto& blob : blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
}
|
||||
}
|
||||
|
||||
InternalStats::CompactionStats stats(CompactionReason::kFlush, 1);
|
||||
@@ -1485,9 +1489,11 @@ IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
lfile->SetPreallocationBlockSize(preallocate_block_size);
|
||||
|
||||
const auto& listeners = immutable_db_options_.listeners;
|
||||
FileTypeSet tmp_set = immutable_db_options_.checksum_handoff_file_types;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_file_options, env_, io_tracer_,
|
||||
nullptr /* stats */, listeners));
|
||||
nullptr /* stats */, listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kWalFile)));
|
||||
*new_log = new log::Writer(std::move(file_writer), log_file_num,
|
||||
immutable_db_options_.recycle_log_file_num > 0,
|
||||
immutable_db_options_.manual_wal_flush);
|
||||
|
||||
@@ -143,9 +143,6 @@ Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
|
||||
uint64_t manifest_file_number;
|
||||
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), &manifest_path,
|
||||
&manifest_file_number);
|
||||
if (!s.ok()) {
|
||||
return Status::NotFound(CurrentFileName(dbname), "does not exist");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -64,9 +64,10 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
break;
|
||||
case kWalFile:
|
||||
if (env->GetFileSize(dbname + "/" + file, &file_size).ok()) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
wal_info.append(file).append(" size: ").append(str).append(" ; ");
|
||||
wal_info.append(file)
|
||||
.append(" size: ")
|
||||
.append(std::to_string(file_size))
|
||||
.append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file: %s/%s\n",
|
||||
dbname.c_str(), file.c_str());
|
||||
@@ -120,9 +121,10 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kWalFile) {
|
||||
if (env->GetFileSize(options.wal_dir + "/" + file, &file_size).ok()) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
wal_info.append(file).append(" size: ").append(str).append(" ; ");
|
||||
wal_info.append(file)
|
||||
.append(" size: ")
|
||||
.append(std::to_string(file_size))
|
||||
.append(" ; ");
|
||||
} else {
|
||||
Error(options.info_log, "Error when reading LOG file %s/%s\n",
|
||||
options.wal_dir.c_str(), file.c_str());
|
||||
|
||||
+68
-46
@@ -43,11 +43,15 @@ TEST_F(DBIOFailureTest, DropWrites) {
|
||||
if (level > 0 && level == dbfull()->NumberLevels() - 1) {
|
||||
break;
|
||||
}
|
||||
dbfull()->TEST_CompactRange(level, nullptr, nullptr, nullptr,
|
||||
true /* disallow trivial move */);
|
||||
Status s =
|
||||
dbfull()->TEST_CompactRange(level, nullptr, nullptr, nullptr,
|
||||
true /* disallow trivial move */);
|
||||
ASSERT_TRUE(s.ok() || s.IsCorruption());
|
||||
}
|
||||
} else {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
Status s =
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(s.ok() || s.IsCorruption());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +60,8 @@ TEST_F(DBIOFailureTest, DropWrites) {
|
||||
ASSERT_EQ("5", property_value);
|
||||
|
||||
env_->drop_writes_.store(false, std::memory_order_release);
|
||||
ASSERT_LT(CountFiles(), num_files + 3);
|
||||
const size_t count = CountFiles();
|
||||
ASSERT_LT(count, num_files + 3);
|
||||
|
||||
// Check that compaction attempts slept after errors
|
||||
// TODO @krad: Figure out why ASSERT_EQ 5 keeps failing in certain compiler
|
||||
@@ -82,7 +87,8 @@ TEST_F(DBIOFailureTest, DropWritesFlush) {
|
||||
ASSERT_TRUE(db_->GetProperty("rocksdb.background-errors", &property_value));
|
||||
ASSERT_EQ("0", property_value);
|
||||
|
||||
dbfull()->TEST_FlushMemTable(true);
|
||||
// ASSERT file is too short
|
||||
ASSERT_TRUE(dbfull()->TEST_FlushMemTable(true).IsCorruption());
|
||||
|
||||
ASSERT_TRUE(db_->GetProperty("rocksdb.background-errors", &property_value));
|
||||
ASSERT_EQ("1", property_value);
|
||||
@@ -166,7 +172,7 @@ TEST_F(DBIOFailureTest, ManifestWriteError) {
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
|
||||
// Memtable compaction (will succeed)
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
const int last = 2;
|
||||
MoveFilesToLevel(2);
|
||||
@@ -174,7 +180,8 @@ TEST_F(DBIOFailureTest, ManifestWriteError) {
|
||||
|
||||
// Merging compaction (will fail)
|
||||
error_type->store(true, std::memory_order_release);
|
||||
dbfull()->TEST_CompactRange(last, nullptr, nullptr); // Should fail
|
||||
ASSERT_NOK(
|
||||
dbfull()->TEST_CompactRange(last, nullptr, nullptr)); // Should fail
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
|
||||
error_type->store(false, std::memory_order_release);
|
||||
@@ -192,7 +199,13 @@ TEST_F(DBIOFailureTest, ManifestWriteError) {
|
||||
|
||||
// Merging compaction (will fail)
|
||||
error_type->store(true, std::memory_order_release);
|
||||
dbfull()->TEST_CompactRange(last, nullptr, nullptr); // Should fail
|
||||
Status s =
|
||||
dbfull()->TEST_CompactRange(last, nullptr, nullptr); // Should fail
|
||||
if (iter == 0) {
|
||||
ASSERT_OK(s);
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
|
||||
// Recovery: should not lose data
|
||||
@@ -220,18 +233,15 @@ TEST_F(DBIOFailureTest, PutFailsParanoid) {
|
||||
options.paranoid_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo1", "bar1"));
|
||||
// simulate error
|
||||
env_->log_write_error_.store(true, std::memory_order_release);
|
||||
s = Put(1, "foo2", "bar2");
|
||||
ASSERT_TRUE(!s.ok());
|
||||
ASSERT_NOK(Put(1, "foo2", "bar2"));
|
||||
env_->log_write_error_.store(false, std::memory_order_release);
|
||||
s = Put(1, "foo3", "bar3");
|
||||
// the next put should fail, too
|
||||
ASSERT_TRUE(!s.ok());
|
||||
ASSERT_NOK(Put(1, "foo3", "bar3"));
|
||||
// but we're still able to read
|
||||
ASSERT_EQ("bar", Get(1, "foo"));
|
||||
|
||||
@@ -244,12 +254,10 @@ TEST_F(DBIOFailureTest, PutFailsParanoid) {
|
||||
ASSERT_OK(Put(1, "foo1", "bar1"));
|
||||
// simulate error
|
||||
env_->log_write_error_.store(true, std::memory_order_release);
|
||||
s = Put(1, "foo2", "bar2");
|
||||
ASSERT_TRUE(!s.ok());
|
||||
ASSERT_NOK(Put(1, "foo2", "bar2"));
|
||||
env_->log_write_error_.store(false, std::memory_order_release);
|
||||
s = Put(1, "foo3", "bar3");
|
||||
// the next put should NOT fail
|
||||
ASSERT_TRUE(s.ok());
|
||||
ASSERT_OK(Put(1, "foo3", "bar3"));
|
||||
}
|
||||
#if !(defined NDEBUG) || !defined(OS_WIN)
|
||||
TEST_F(DBIOFailureTest, FlushSstRangeSyncError) {
|
||||
@@ -269,14 +277,14 @@ TEST_F(DBIOFailureTest, FlushSstRangeSyncError) {
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
const char* io_error_msg = "range sync dummy error";
|
||||
std::atomic<int> range_sync_called(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::RangeSync", [&](void* arg) {
|
||||
if (range_sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("range sync dummy error");
|
||||
*st = Status::IOError(io_error_msg);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -298,7 +306,9 @@ TEST_F(DBIOFailureTest, FlushSstRangeSyncError) {
|
||||
ASSERT_OK(Put(1, "foo3_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_3", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo4", "bar"));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
Status s = dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_STREQ(s.getState(), io_error_msg);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
@@ -328,7 +338,6 @@ TEST_F(DBIOFailureTest, CompactSstRangeSyncError) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
Random rnd(301);
|
||||
std::string rnd_str =
|
||||
@@ -342,21 +351,22 @@ TEST_F(DBIOFailureTest, CompactSstRangeSyncError) {
|
||||
ASSERT_OK(Put(1, "foo1_1", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo1_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo1_3", rnd_str));
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo3_1", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_2", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo3_3", rnd_str));
|
||||
ASSERT_OK(Put(1, "foo4", "bar"));
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
|
||||
|
||||
const char* io_error_msg = "range sync dummy error";
|
||||
std::atomic<int> range_sync_called(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::RangeSync", [&](void* arg) {
|
||||
if (range_sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("range sync dummy error");
|
||||
*st = Status::IOError(io_error_msg);
|
||||
}
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -365,7 +375,9 @@ TEST_F(DBIOFailureTest, CompactSstRangeSyncError) {
|
||||
{
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
Status s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_STREQ(s.getState(), io_error_msg);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
@@ -389,13 +401,14 @@ TEST_F(DBIOFailureTest, FlushSstCloseError) {
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
const char* io_error_msg = "close dummy error";
|
||||
std::atomic<int> close_called(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Close", [&](void* arg) {
|
||||
if (close_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("close dummy error");
|
||||
*st = Status::IOError(io_error_msg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -404,7 +417,9 @@ TEST_F(DBIOFailureTest, FlushSstCloseError) {
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo1", "bar1"));
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
Status s = dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_STREQ(s.getState(), io_error_msg);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
@@ -429,25 +444,25 @@ TEST_F(DBIOFailureTest, CompactionSstCloseError) {
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", "bar3"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
const char* io_error_msg = "close dummy error";
|
||||
std::atomic<int> close_called(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Close", [&](void* arg) {
|
||||
if (close_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("close dummy error");
|
||||
*st = Status::IOError(io_error_msg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -456,7 +471,9 @@ TEST_F(DBIOFailureTest, CompactionSstCloseError) {
|
||||
{
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
Status s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_STREQ(s.getState(), io_error_msg);
|
||||
|
||||
// Following writes should fail as compaction failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
@@ -480,13 +497,14 @@ TEST_F(DBIOFailureTest, FlushSstSyncError) {
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
const char* io_error_msg = "sync dummy error";
|
||||
std::atomic<int> sync_called(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Sync", [&](void* arg) {
|
||||
if (sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("sync dummy error");
|
||||
*st = Status::IOError(io_error_msg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -495,7 +513,9 @@ TEST_F(DBIOFailureTest, FlushSstSyncError) {
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo1", "bar1"));
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
Status s = dbfull()->TEST_WaitForFlushMemTable(handles_[1]);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_STREQ(s.getState(), io_error_msg);
|
||||
|
||||
// Following writes should fail as flush failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
@@ -521,25 +541,25 @@ TEST_F(DBIOFailureTest, CompactionSstSyncError) {
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Status s;
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", "bar2"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(1, "foo", "bar3"));
|
||||
ASSERT_OK(Put(1, "foo2", "bar"));
|
||||
Flush(1);
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
const char* io_error_msg = "sync dummy error";
|
||||
std::atomic<int> sync_called(0);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"SpecialEnv::SStableFile::Sync", [&](void* arg) {
|
||||
if (sync_called.fetch_add(1) == 0) {
|
||||
Status* st = static_cast<Status*>(arg);
|
||||
*st = Status::IOError("close dummy error");
|
||||
*st = Status::IOError(io_error_msg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -548,7 +568,9 @@ TEST_F(DBIOFailureTest, CompactionSstSyncError) {
|
||||
{
|
||||
{"disable_auto_compactions", "false"},
|
||||
}));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
Status s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
ASSERT_STREQ(s.getState(), io_error_msg);
|
||||
|
||||
// Following writes should fail as compaction failed.
|
||||
ASSERT_NOK(Put(1, "foo2", "bar3"));
|
||||
|
||||
+27
-2
@@ -1254,7 +1254,19 @@ void DBIter::Seek(const Slice& target) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
// TODO: What do we do if this returns an error?
|
||||
db_impl_->TraceIteratorSeek(cfd_->GetID(), target).PermitUncheckedError();
|
||||
Slice lower_bound, upper_bound;
|
||||
if (iterate_lower_bound_ != nullptr) {
|
||||
lower_bound = *iterate_lower_bound_;
|
||||
} else {
|
||||
lower_bound = Slice("");
|
||||
}
|
||||
if (iterate_upper_bound_ != nullptr) {
|
||||
upper_bound = *iterate_upper_bound_;
|
||||
} else {
|
||||
upper_bound = Slice("");
|
||||
}
|
||||
db_impl_->TraceIteratorSeek(cfd_->GetID(), target, lower_bound, upper_bound)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -1316,7 +1328,20 @@ void DBIter::SeekForPrev(const Slice& target) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (db_impl_ != nullptr && cfd_ != nullptr) {
|
||||
// TODO: What do we do if this returns an error?
|
||||
db_impl_->TraceIteratorSeekForPrev(cfd_->GetID(), target)
|
||||
Slice lower_bound, upper_bound;
|
||||
if (iterate_lower_bound_ != nullptr) {
|
||||
lower_bound = *iterate_lower_bound_;
|
||||
} else {
|
||||
lower_bound = Slice("");
|
||||
}
|
||||
if (iterate_upper_bound_ != nullptr) {
|
||||
upper_bound = *iterate_upper_bound_;
|
||||
} else {
|
||||
upper_bound = Slice("");
|
||||
}
|
||||
db_impl_
|
||||
->TraceIteratorSeekForPrev(cfd_->GetID(), target, lower_bound,
|
||||
upper_bound)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2020-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
enum class WriteBatchOpType {
|
||||
kPut = 0,
|
||||
kDelete,
|
||||
kSingleDelete,
|
||||
kDeleteRange,
|
||||
kMerge,
|
||||
kBlobIndex,
|
||||
kNum,
|
||||
};
|
||||
|
||||
// Integer addition is needed for `::testing::Range()` to take the enum type.
|
||||
WriteBatchOpType operator+(WriteBatchOpType lhs, const int rhs) {
|
||||
using T = std::underlying_type<WriteBatchOpType>::type;
|
||||
return static_cast<WriteBatchOpType>(static_cast<T>(lhs) + rhs);
|
||||
}
|
||||
|
||||
class DbKvChecksumTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<WriteBatchOpType, char>> {
|
||||
public:
|
||||
DbKvChecksumTest()
|
||||
: DBTestBase("/db_kv_checksum_test", /*env_do_fsync=*/false) {
|
||||
op_type_ = std::get<0>(GetParam());
|
||||
corrupt_byte_addend_ = std::get<1>(GetParam());
|
||||
}
|
||||
|
||||
std::pair<WriteBatch, Status> GetWriteBatch(size_t ts_sz,
|
||||
ColumnFamilyHandle* cf_handle) {
|
||||
Status s;
|
||||
WriteBatch wb(0 /* reserved_bytes */, 0 /* max_bytes */, ts_sz,
|
||||
8 /* protection_bytes_per_entry */);
|
||||
switch (op_type_) {
|
||||
case WriteBatchOpType::kPut:
|
||||
s = wb.Put(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kDelete:
|
||||
s = wb.Delete(cf_handle, "key");
|
||||
break;
|
||||
case WriteBatchOpType::kSingleDelete:
|
||||
s = wb.SingleDelete(cf_handle, "key");
|
||||
break;
|
||||
case WriteBatchOpType::kDeleteRange:
|
||||
s = wb.DeleteRange(cf_handle, "begin", "end");
|
||||
break;
|
||||
case WriteBatchOpType::kMerge:
|
||||
s = wb.Merge(cf_handle, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kBlobIndex:
|
||||
// TODO(ajkr): use public API once available.
|
||||
uint32_t cf_id;
|
||||
if (cf_handle == nullptr) {
|
||||
cf_id = 0;
|
||||
} else {
|
||||
cf_id = cf_handle->GetID();
|
||||
}
|
||||
s = WriteBatchInternal::PutBlobIndex(&wb, cf_id, "key", "val");
|
||||
break;
|
||||
case WriteBatchOpType::kNum:
|
||||
assert(false);
|
||||
}
|
||||
return {std::move(wb), std::move(s)};
|
||||
}
|
||||
|
||||
void CorruptNextByteCallBack(void* arg) {
|
||||
Slice encoded = *static_cast<Slice*>(arg);
|
||||
if (entry_len_ == port::kMaxSizet) {
|
||||
// We learn the entry size on the first attempt
|
||||
entry_len_ = encoded.size();
|
||||
}
|
||||
// All entries should be the same size
|
||||
assert(entry_len_ == encoded.size());
|
||||
char* buf = const_cast<char*>(encoded.data());
|
||||
buf[corrupt_byte_offset_] += corrupt_byte_addend_;
|
||||
++corrupt_byte_offset_;
|
||||
}
|
||||
|
||||
bool MoreBytesToCorrupt() { return corrupt_byte_offset_ < entry_len_; }
|
||||
|
||||
protected:
|
||||
WriteBatchOpType op_type_;
|
||||
char corrupt_byte_addend_;
|
||||
size_t corrupt_byte_offset_ = 0;
|
||||
size_t entry_len_ = port::kMaxSizet;
|
||||
};
|
||||
|
||||
std::string GetTestNameSuffix(
|
||||
::testing::TestParamInfo<std::tuple<WriteBatchOpType, char>> info) {
|
||||
std::ostringstream oss;
|
||||
switch (std::get<0>(info.param)) {
|
||||
case WriteBatchOpType::kPut:
|
||||
oss << "Put";
|
||||
break;
|
||||
case WriteBatchOpType::kDelete:
|
||||
oss << "Delete";
|
||||
break;
|
||||
case WriteBatchOpType::kSingleDelete:
|
||||
oss << "SingleDelete";
|
||||
break;
|
||||
case WriteBatchOpType::kDeleteRange:
|
||||
oss << "DeleteRange";
|
||||
break;
|
||||
case WriteBatchOpType::kMerge:
|
||||
oss << "Merge";
|
||||
break;
|
||||
case WriteBatchOpType::kBlobIndex:
|
||||
oss << "BlobIndex";
|
||||
break;
|
||||
case WriteBatchOpType::kNum:
|
||||
assert(false);
|
||||
}
|
||||
oss << "Add"
|
||||
<< static_cast<int>(static_cast<unsigned char>(std::get<1>(info.param)));
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DbKvChecksumTest, DbKvChecksumTest,
|
||||
::testing::Combine(::testing::Range(static_cast<WriteBatchOpType>(0),
|
||||
WriteBatchOpType::kNum),
|
||||
::testing::Values(2, 103, 251)),
|
||||
GetTestNameSuffix);
|
||||
|
||||
TEST_P(DbKvChecksumTest, MemTableAddCorrupted) {
|
||||
// This test repeatedly attempts to write `WriteBatch`es containing a single
|
||||
// entry of type `op_type_`. Each attempt has one byte corrupted in its
|
||||
// memtable entry by adding `corrupt_byte_addend_` to its original value. The
|
||||
// test repeats until an attempt has been made on each byte in the encoded
|
||||
// memtable entry. All attempts are expected to fail with `Status::Corruption`
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTable::Add:Encoded",
|
||||
std::bind(&DbKvChecksumTest::CorruptNextByteCallBack, this,
|
||||
std::placeholders::_1));
|
||||
|
||||
while (MoreBytesToCorrupt()) {
|
||||
// Failed memtable insert always leads to read-only mode, so we have to
|
||||
// reopen for every attempt.
|
||||
Options options = CurrentOptions();
|
||||
if (op_type_ == WriteBatchOpType::kMerge) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
auto batch_and_status =
|
||||
GetWriteBatch(0 /* ts_sz */, nullptr /* cf_handle */);
|
||||
ASSERT_OK(batch_and_status.second);
|
||||
ASSERT_TRUE(
|
||||
db_->Write(WriteOptions(), &batch_and_status.first).IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DbKvChecksumTest, MemTableAddWithColumnFamilyCorrupted) {
|
||||
// This test repeatedly attempts to write `WriteBatch`es containing a single
|
||||
// entry of type `op_type_` to a non-default column family. Each attempt has
|
||||
// one byte corrupted in its memtable entry by adding `corrupt_byte_addend_`
|
||||
// to its original value. The test repeats until an attempt has been made on
|
||||
// each byte in the encoded memtable entry. All attempts are expected to fail
|
||||
// with `Status::Corruption`.
|
||||
Options options = CurrentOptions();
|
||||
if (op_type_ == WriteBatchOpType::kMerge) {
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
}
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"MemTable::Add:Encoded",
|
||||
std::bind(&DbKvChecksumTest::CorruptNextByteCallBack, this,
|
||||
std::placeholders::_1));
|
||||
|
||||
while (MoreBytesToCorrupt()) {
|
||||
// Failed memtable insert always leads to read-only mode, so we have to
|
||||
// reopen for every attempt.
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, "pikachu"}, options);
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
auto batch_and_status = GetWriteBatch(0 /* ts_sz */, handles_[1]);
|
||||
ASSERT_OK(batch_and_status.second);
|
||||
ASSERT_TRUE(
|
||||
db_->Write(WriteOptions(), &batch_and_status.first).IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+33
-19
@@ -145,15 +145,25 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
|
||||
// Write some keys and make sure it returns false on duplicates
|
||||
ASSERT_OK(mem->Add(seq, kTypeValue, "key", "value2"));
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeValue, "key", "value2").IsTryAgain());
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeValue, "key", "value2", nullptr /* kv_prot_info */));
|
||||
ASSERT_TRUE(
|
||||
mem->Add(seq, kTypeValue, "key", "value2", nullptr /* kv_prot_info */)
|
||||
.IsTryAgain());
|
||||
// Changing the type should still cause the duplicatae key
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeMerge, "key", "value2").IsTryAgain());
|
||||
ASSERT_TRUE(
|
||||
mem->Add(seq, kTypeMerge, "key", "value2", nullptr /* kv_prot_info */)
|
||||
.IsTryAgain());
|
||||
// Changing the seq number will make the key fresh
|
||||
ASSERT_OK(mem->Add(seq + 1, kTypeMerge, "key", "value2"));
|
||||
ASSERT_OK(mem->Add(seq + 1, kTypeMerge, "key", "value2",
|
||||
nullptr /* kv_prot_info */));
|
||||
// Test with different types for duplicate keys
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeDeletion, "key", "").IsTryAgain());
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeSingleDeletion, "key", "").IsTryAgain());
|
||||
ASSERT_TRUE(
|
||||
mem->Add(seq, kTypeDeletion, "key", "", nullptr /* kv_prot_info */)
|
||||
.IsTryAgain());
|
||||
ASSERT_TRUE(
|
||||
mem->Add(seq, kTypeSingleDeletion, "key", "", nullptr /* kv_prot_info */)
|
||||
.IsTryAgain());
|
||||
|
||||
// Test the duplicate keys under stress
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
@@ -161,7 +171,8 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
if (!insert_dup) {
|
||||
seq++;
|
||||
}
|
||||
Status s = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq));
|
||||
Status s = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq),
|
||||
nullptr /* kv_prot_info */);
|
||||
if (insert_dup) {
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
} else {
|
||||
@@ -177,8 +188,11 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
// Insert a duplicate key with _ in it
|
||||
ASSERT_OK(mem->Add(seq, kTypeValue, "key_1", "value"));
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeValue, "key_1", "value").IsTryAgain());
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeValue, "key_1", "value", nullptr /* kv_prot_info */));
|
||||
ASSERT_TRUE(
|
||||
mem->Add(seq, kTypeValue, "key_1", "value", nullptr /* kv_prot_info */)
|
||||
.IsTryAgain());
|
||||
delete mem;
|
||||
|
||||
// Test when InsertConcurrently will be invoked
|
||||
@@ -187,11 +201,11 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
MemTablePostProcessInfo post_process_info;
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info));
|
||||
ASSERT_TRUE(
|
||||
mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info)
|
||||
.IsTryAgain());
|
||||
ASSERT_OK(mem->Add(seq, kTypeValue, "key", "value",
|
||||
nullptr /* kv_prot_info */, true, &post_process_info));
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeValue, "key", "value",
|
||||
nullptr /* kv_prot_info */, true, &post_process_info)
|
||||
.IsTryAgain());
|
||||
delete mem;
|
||||
}
|
||||
|
||||
@@ -217,7 +231,7 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
|
||||
// Put 0 as the base
|
||||
PutFixed64(&value, static_cast<uint64_t>(0));
|
||||
ASSERT_OK(mem->Add(0, kTypeValue, "key", value));
|
||||
ASSERT_OK(mem->Add(0, kTypeValue, "key", value, nullptr /* kv_prot_info */));
|
||||
value.clear();
|
||||
|
||||
// Write Merge concurrently
|
||||
@@ -226,8 +240,8 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
std::string v1;
|
||||
for (int seq = 1; seq < num_ops / 2; seq++) {
|
||||
PutFixed64(&v1, seq);
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeMerge, "key", v1, true, &post_process_info1));
|
||||
ASSERT_OK(mem->Add(seq, kTypeMerge, "key", v1, nullptr /* kv_prot_info */,
|
||||
true, &post_process_info1));
|
||||
v1.clear();
|
||||
}
|
||||
});
|
||||
@@ -236,8 +250,8 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
std::string v2;
|
||||
for (int seq = num_ops / 2; seq < num_ops; seq++) {
|
||||
PutFixed64(&v2, seq);
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeMerge, "key", v2, true, &post_process_info2));
|
||||
ASSERT_OK(mem->Add(seq, kTypeMerge, "key", v2, nullptr /* kv_prot_info */,
|
||||
true, &post_process_info2));
|
||||
v2.clear();
|
||||
}
|
||||
});
|
||||
|
||||
+80
-2
@@ -129,6 +129,83 @@ TEST_F(DBOptionsTest, GetLatestCFOptions) {
|
||||
GetMutableCFOptionsMap(dbfull()->GetOptions(handles_[1])));
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.blob_file_size = 16384;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.no_block_cache = true;
|
||||
bbto.block_size = 8192;
|
||||
bbto.block_restart_interval = 7;
|
||||
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
Options c_opts = dbfull()->GetOptions(cfh);
|
||||
const auto* c_bbto =
|
||||
c_opts.table_factory->GetOptions<BlockBasedTableOptions>();
|
||||
ASSERT_NE(c_bbto, nullptr);
|
||||
ASSERT_EQ(c_opts.blob_file_size, 16384);
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 8192);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 7);
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.block_size", "16384"},
|
||||
{"table_factory.block_restart_interval", "11"}}));
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
|
||||
// Now set an option that is not mutable - options should not change
|
||||
ASSERT_NOK(
|
||||
dbfull()->SetOptions(cfh, {{"table_factory.no_block_cache", "false"}}));
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
|
||||
// Set some that are mutable and some that are not - options should not change
|
||||
ASSERT_NOK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.no_block_cache", "false"},
|
||||
{"table_factory.block_size", "8192"},
|
||||
{"table_factory.block_restart_interval", "7"}}));
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
|
||||
// Set some that are mutable and some that do not exist - options should not
|
||||
// change
|
||||
ASSERT_NOK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.block_size", "8192"},
|
||||
{"table_factory.does_not_exist", "true"},
|
||||
{"table_factory.block_restart_interval", "7"}}));
|
||||
ASSERT_EQ(c_bbto->no_block_cache, true);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 11);
|
||||
|
||||
// Trying to change the table factory fails
|
||||
ASSERT_NOK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory", TableFactory::kPlainTableName()}}));
|
||||
|
||||
// Set some on the table and some on the Column Family
|
||||
ASSERT_OK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.block_size", "16384"},
|
||||
{"blob_file_size", "32768"},
|
||||
{"table_factory.block_restart_interval", "13"}}));
|
||||
c_opts = dbfull()->GetOptions(cfh);
|
||||
ASSERT_EQ(c_opts.blob_file_size, 32768);
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 13);
|
||||
// Set some on the table and a bad one on the ColumnFamily - options should
|
||||
// not change
|
||||
ASSERT_NOK(dbfull()->SetOptions(
|
||||
cfh, {{"table_factory.block_size", "1024"},
|
||||
{"no_such_option", "32768"},
|
||||
{"table_factory.block_restart_interval", "7"}}));
|
||||
ASSERT_EQ(c_bbto->block_size, 16384);
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 13);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBytesPerSync) {
|
||||
const size_t kValueSize = 1024 * 1024; // 1MB
|
||||
Options options;
|
||||
@@ -190,10 +267,11 @@ TEST_F(DBOptionsTest, SetWalBytesPerSync) {
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(512, dbfull()->GetDBOptions().wal_bytes_per_sync);
|
||||
int counter = 0;
|
||||
std::atomic_int counter{0};
|
||||
int low_bytes_per_sync = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"WritableFileWriter::RangeSync:0", [&](void* /*arg*/) { counter++; });
|
||||
"WritableFileWriter::RangeSync:0",
|
||||
[&](void* /*arg*/) { counter.fetch_add(1); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
const std::string kValue(kValueSize, 'v');
|
||||
int i = 0;
|
||||
|
||||
+42
-20
@@ -98,7 +98,7 @@ TEST_F(DBSSTTest, SSTsWithLdbSuffixHandling) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
GenerateNewFile(&rnd, &key_id, false);
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
int const num_files = GetSstFileCount(dbname_);
|
||||
ASSERT_GT(num_files, 0);
|
||||
@@ -374,6 +374,14 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
*abs_time_us = Env::Default()->NowMicros();
|
||||
});
|
||||
|
||||
// Disable PeriodicWorkScheduler as it also has TimedWait, which could update
|
||||
// the simulated sleep time
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::StartPeriodicWorkScheduler:DisableScheduler", [&](void* arg) {
|
||||
bool* disable_scheduler = static_cast<bool*>(arg);
|
||||
*disable_scheduler = true;
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
@@ -393,7 +401,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
|
||||
WriteOptions wo;
|
||||
wo.disableWAL = true;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
Reopen(options);
|
||||
// Create 4 files in L0
|
||||
for (char v = 'a'; v <= 'd'; v++) {
|
||||
ASSERT_OK(Put("Key2", DummyString(1024, v), wo));
|
||||
@@ -540,7 +548,7 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(options.sst_file_manager.get());
|
||||
sfm->delete_scheduler()->SetMaxTrashDBRatio(3.1);
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
Reopen(options);
|
||||
|
||||
// Create 4 files in L0
|
||||
for (char v = 'a'; v <= 'd'; v++) {
|
||||
@@ -567,11 +575,11 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
if (!wal_dir_same_as_dbname_) {
|
||||
// Forcibly create some trash log files
|
||||
std::unique_ptr<WritableFile> result;
|
||||
env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result,
|
||||
EnvOptions());
|
||||
ASSERT_OK(env->NewWritableFile(options.wal_dir + "/1000.log.trash", &result,
|
||||
EnvOptions()));
|
||||
result.reset();
|
||||
}
|
||||
env->GetChildren(options.wal_dir, &filenames);
|
||||
ASSERT_OK(env->GetChildren(options.wal_dir, &filenames));
|
||||
for (const std::string& fname : filenames) {
|
||||
if (fname.find(".log.trash") != std::string::npos) {
|
||||
trash_log_count++;
|
||||
@@ -580,11 +588,11 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
ASSERT_GE(trash_log_count, 1);
|
||||
|
||||
env->set_fake_log_delete(false);
|
||||
ASSERT_OK(TryReopen(options));
|
||||
Reopen(options);
|
||||
|
||||
filenames.clear();
|
||||
trash_log_count = 0;
|
||||
env->GetChildren(options.wal_dir, &filenames);
|
||||
ASSERT_OK(env->GetChildren(options.wal_dir, &filenames));
|
||||
for (const std::string& fname : filenames) {
|
||||
if (fname.find(".log.trash") != std::string::npos) {
|
||||
trash_log_count++;
|
||||
@@ -614,7 +622,7 @@ TEST_F(DBSSTTest, OpenDBWithExistingTrash) {
|
||||
ASSERT_OK(WriteStringToFile(env_, "abc", dbname_ + "/" + "003.sst.trash"));
|
||||
|
||||
// Reopen the DB and verify that it deletes existing trash files
|
||||
ASSERT_OK(TryReopen(options));
|
||||
Reopen(options);
|
||||
sfm->WaitForEmptyTrash();
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "001.sst.trash"));
|
||||
ASSERT_NOK(env_->FileExists(dbname_ + "/" + "002.sst.trash"));
|
||||
@@ -872,10 +880,12 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// OK, now trigger a manual compaction
|
||||
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsCompactionTooLarge());
|
||||
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
@@ -885,10 +895,13 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
|
||||
// Now make sure CompactFiles also gets cancelled
|
||||
auto l0_files = collector->GetFlushedFiles();
|
||||
dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0);
|
||||
ASSERT_TRUE(
|
||||
dbfull()
|
||||
->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0)
|
||||
.IsCompactionTooLarge());
|
||||
|
||||
// Wait for manual compaction to get scheduled and finish
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
ASSERT_EQ(dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
COMPACTION_CANCELLED),
|
||||
@@ -903,8 +916,9 @@ TEST_F(DBSSTTest, CancellingManualCompactionsWorks) {
|
||||
"CompactFilesImpl:End", [&](void* /*arg*/) { completed_compactions++; });
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(), l0_files, 0);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
ASSERT_OK(dbfull()->CompactFiles(ROCKSDB_NAMESPACE::CompactionOptions(),
|
||||
l0_files, 0));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact(true));
|
||||
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
ASSERT_GT(completed_compactions, 0);
|
||||
@@ -1008,7 +1022,7 @@ TEST_F(DBSSTTest, OpenDBWithInfiniteMaxOpenFiles) {
|
||||
CompactRangeOptions compact_options;
|
||||
compact_options.change_level = true;
|
||||
compact_options.target_level = 2;
|
||||
db_->CompactRange(compact_options, nullptr, nullptr);
|
||||
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
|
||||
|
||||
// Create 12 Files in L0
|
||||
for (int i = 0; i < 12; i++) {
|
||||
@@ -1060,7 +1074,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
std::string val = "val_file_" + ToString(i);
|
||||
ASSERT_OK(Put(Key(j), val));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("5", FilesPerLevel(0));
|
||||
|
||||
@@ -1084,6 +1098,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
|
||||
// hold current version
|
||||
std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
|
||||
ASSERT_OK(iter1->status());
|
||||
|
||||
// Compact 5 files into 1 file in L0
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
@@ -1107,12 +1122,13 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
|
||||
// hold current version
|
||||
std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
|
||||
ASSERT_OK(iter2->status());
|
||||
|
||||
// Delete all keys and compact, this will delete all live files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
@@ -1126,6 +1142,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
// Total SST files = 6 (5 original files + compacted file)
|
||||
ASSERT_EQ(total_sst_files_size, 6 * single_file_size);
|
||||
|
||||
ASSERT_OK(iter1->status());
|
||||
iter1.reset();
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
|
||||
&total_sst_files_size));
|
||||
@@ -1133,6 +1150,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSize) {
|
||||
// Total SST files = 1 (compacted file)
|
||||
ASSERT_EQ(total_sst_files_size, 1 * single_file_size);
|
||||
|
||||
ASSERT_OK(iter2->status());
|
||||
iter2.reset();
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
|
||||
&total_sst_files_size));
|
||||
@@ -1151,7 +1169,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
// Generate 5 files in L0
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_OK(Put(Key(i), "val"));
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ("5", FilesPerLevel(0));
|
||||
|
||||
@@ -1176,6 +1194,7 @@ TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
|
||||
// hold current version
|
||||
std::unique_ptr<Iterator> iter1(dbfull()->NewIterator(ReadOptions()));
|
||||
ASSERT_OK(iter1->status());
|
||||
|
||||
// Compaction will do trivial move from L0 to L1
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
@@ -1199,12 +1218,13 @@ TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
|
||||
// hold current version
|
||||
std::unique_ptr<Iterator> iter2(dbfull()->NewIterator(ReadOptions()));
|
||||
ASSERT_OK(iter2->status());
|
||||
|
||||
// Delete all keys and compact, this will delete all live files
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ("", FilesPerLevel(0));
|
||||
|
||||
@@ -1218,7 +1238,9 @@ TEST_F(DBSSTTest, GetTotalSstFilesSizeVersionsFilesShared) {
|
||||
// Total SST files = 5 (used in 2 version)
|
||||
ASSERT_EQ(total_sst_files_size, 5 * single_file_size);
|
||||
|
||||
ASSERT_OK(iter1->status());
|
||||
iter1.reset();
|
||||
ASSERT_OK(iter2->status());
|
||||
iter2.reset();
|
||||
|
||||
ASSERT_TRUE(dbfull()->GetIntProperty("rocksdb.total-sst-files-size",
|
||||
|
||||
@@ -31,6 +31,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorSingle) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
// add a record and check that iter can see it
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "mirko", "fodor"));
|
||||
@@ -48,6 +49,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorKeepAdding) {
|
||||
read_options.tailing = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
const int num_records = 10000;
|
||||
@@ -70,7 +72,9 @@ TEST_F(DBTestTailingIterator, TailingIteratorSeekToNext) {
|
||||
read_options.tailing = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::unique_ptr<Iterator> itern(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(itern->status());
|
||||
std::string value(1024, 'a');
|
||||
|
||||
const int num_records = 1000;
|
||||
@@ -138,8 +142,11 @@ TEST_F(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
Slice keyu(bufe, 20);
|
||||
read_options.iterate_upper_bound = &keyu;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
std::unique_ptr<Iterator> itern(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(itern->status());
|
||||
std::unique_ptr<Iterator> iterh(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iterh->status());
|
||||
std::string value(1024, 'a');
|
||||
bool file_iters_deleted = false;
|
||||
bool file_iters_renewed_null = false;
|
||||
@@ -225,6 +232,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
read_options.read_tier = kBlockCacheTier;
|
||||
std::unique_ptr<Iterator> iteri(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iteri->status());
|
||||
char buf5[32];
|
||||
snprintf(buf5, sizeof(buf5), "00a0%016d", (num_records / 2) * 5 - 2);
|
||||
Slice target1(buf5, 20);
|
||||
@@ -236,6 +244,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorTrimSeekToNext) {
|
||||
options.table_factory.reset(NewBlockBasedTableFactory());
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
iter.reset(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
for (int i = 2 * num_records; i > 0; --i) {
|
||||
char buf1[32];
|
||||
char buf2[32];
|
||||
@@ -262,6 +271,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorDeletes) {
|
||||
read_options.tailing = true;
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
// write a single record, read it using the iterator, then delete it
|
||||
ASSERT_OK(Put(1, "0test", "test"));
|
||||
@@ -309,6 +319,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorPrefixSeek) {
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(Put(1, "0101", "test"));
|
||||
|
||||
ASSERT_OK(Flush(1));
|
||||
@@ -339,6 +350,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorIncomplete) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), key, value));
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
// we either see the entry or it's not in cache
|
||||
ASSERT_TRUE(iter->Valid() || iter->status().IsIncomplete());
|
||||
@@ -369,6 +381,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorSeekToSame) {
|
||||
}
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
// Seek to 00001. We expect to find 00002.
|
||||
std::string start_key = "00001";
|
||||
iter->Seek(start_key);
|
||||
@@ -404,6 +417,7 @@ TEST_F(DBTestTailingIterator, TailingIteratorUpperBound) {
|
||||
ASSERT_OK(Put(1, "21", "21"));
|
||||
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(read_options, handles_[1]));
|
||||
ASSERT_OK(it->status());
|
||||
it->Seek("12");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("12", it->key().ToString());
|
||||
@@ -479,6 +493,8 @@ TEST_F(DBTestTailingIterator, TailingIteratorGap) {
|
||||
it->Next();
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("40", it->key().ToString());
|
||||
|
||||
ASSERT_OK(it->status());
|
||||
}
|
||||
|
||||
TEST_F(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
@@ -497,6 +513,7 @@ TEST_F(DBTestTailingIterator, SeekWithUpperBoundBug) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
iter->Seek("aa");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -519,6 +536,7 @@ TEST_F(DBTestTailingIterator, SeekToFirstWithUpperBoundBug) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_options));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
|
||||
+75
-42
@@ -1337,17 +1337,19 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = true;
|
||||
size_approx_options.include_files = true;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
// Zero if not including mem table
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_OK(db_->GetApproximateSizes(&r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -1357,13 +1359,15 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(100);
|
||||
end = Key(1020);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
options.max_write_buffer_number = 8;
|
||||
@@ -1389,29 +1393,32 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(100);
|
||||
end = Key(300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_GT(size, 6000);
|
||||
|
||||
start = Key(2100);
|
||||
end = Key(2300);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
uint64_t size_with_mt, size_without_mt;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size_with_mt);
|
||||
ASSERT_OK(db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size_with_mt));
|
||||
ASSERT_GT(size_with_mt, 6000);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_OK(db_->GetApproximateSizes(&r, 1, &size_without_mt));
|
||||
ASSERT_EQ(size_without_mt, 0);
|
||||
|
||||
Flush();
|
||||
@@ -1423,15 +1430,16 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
start = Key(1050);
|
||||
end = Key(1080);
|
||||
r = Range(start, end);
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size_with_mt);
|
||||
db_->GetApproximateSizes(&r, 1, &size_without_mt);
|
||||
ASSERT_OK(db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size_with_mt));
|
||||
ASSERT_OK(db_->GetApproximateSizes(&r, 1, &size_without_mt));
|
||||
ASSERT_GT(size_with_mt, size_without_mt);
|
||||
ASSERT_GT(size_without_mt, 6000);
|
||||
|
||||
// Check that include_memtabtles flag works as expected
|
||||
size_approx_options.include_memtabtles = false;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, size_without_mt);
|
||||
|
||||
// Check that files_size_error_margin works as expected, when the heuristic
|
||||
@@ -1440,10 +1448,12 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
end = Key(1000 + N - 2);
|
||||
r = Range(start, end);
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
uint64_t size2;
|
||||
size_approx_options.files_size_error_margin = 0.5; // enabled, but not used
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2));
|
||||
ASSERT_EQ(size, size2);
|
||||
}
|
||||
|
||||
@@ -1494,14 +1504,16 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_OK(db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size));
|
||||
ASSERT_NE(size, 0);
|
||||
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_OK(db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1,
|
||||
&size2));
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
}
|
||||
@@ -1517,7 +1529,7 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
const std::string end = Key(i + 11); // overlap by 1 key
|
||||
const Range r(start, end);
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_OK(db_->GetApproximateSizes(&r, 1, &size));
|
||||
ASSERT_LE(size, 11 * 100);
|
||||
}
|
||||
}
|
||||
@@ -1585,9 +1597,12 @@ TEST_F(DBTest, ApproximateSizes) {
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_TRUE(Between(Size("", "xyz", 1), 0, 0));
|
||||
uint64_t size;
|
||||
ASSERT_OK(Size("", "xyz", 1, &size));
|
||||
ASSERT_TRUE(Between(size, 0, 0));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_TRUE(Between(Size("", "xyz", 1), 0, 0));
|
||||
ASSERT_OK(Size("", "xyz", 1, &size));
|
||||
ASSERT_TRUE(Between(size, 0, 0));
|
||||
|
||||
// Write 8MB (80 values, each 100K)
|
||||
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
|
||||
@@ -1600,7 +1615,8 @@ TEST_F(DBTest, ApproximateSizes) {
|
||||
}
|
||||
|
||||
// 0 because GetApproximateSizes() does not account for memtable space
|
||||
ASSERT_TRUE(Between(Size("", Key(50), 1), 0, 0));
|
||||
ASSERT_OK(Size("", Key(50), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 0, 0));
|
||||
|
||||
// Check sizes across recovery by reopening a few times
|
||||
for (int run = 0; run < 3; run++) {
|
||||
@@ -1608,14 +1624,17 @@ TEST_F(DBTest, ApproximateSizes) {
|
||||
|
||||
for (int compact_start = 0; compact_start < N; compact_start += 10) {
|
||||
for (int i = 0; i < N; i += 10) {
|
||||
ASSERT_TRUE(Between(Size("", Key(i), 1), S1 * i, S2 * i));
|
||||
ASSERT_TRUE(Between(Size("", Key(i) + ".suffix", 1), S1 * (i + 1),
|
||||
S2 * (i + 1)));
|
||||
ASSERT_TRUE(Between(Size(Key(i), Key(i + 10), 1), S1 * 10, S2 * 10));
|
||||
ASSERT_OK(Size("", Key(i), 1, &size));
|
||||
ASSERT_TRUE(Between(size, S1 * i, S2 * i));
|
||||
ASSERT_OK(Size("", Key(i) + ".suffix", 1, &size));
|
||||
ASSERT_TRUE(Between(size, S1 * (i + 1), S2 * (i + 1)));
|
||||
ASSERT_OK(Size(Key(i), Key(i + 10), 1, &size));
|
||||
ASSERT_TRUE(Between(size, S1 * 10, S2 * 10));
|
||||
}
|
||||
ASSERT_TRUE(Between(Size("", Key(50), 1), S1 * 50, S2 * 50));
|
||||
ASSERT_TRUE(
|
||||
Between(Size("", Key(50) + ".suffix", 1), S1 * 50, S2 * 50));
|
||||
ASSERT_OK(Size("", Key(50), 1, &size));
|
||||
ASSERT_TRUE(Between(size, S1 * 50, S2 * 50));
|
||||
ASSERT_OK(Size("", Key(50) + ".suffix", 1, &size));
|
||||
ASSERT_TRUE(Between(size, S1 * 50, S2 * 50));
|
||||
|
||||
std::string cstart_str = Key(compact_start);
|
||||
std::string cend_str = Key(compact_start + 9);
|
||||
@@ -1650,21 +1669,32 @@ TEST_F(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
||||
ASSERT_OK(Put(1, Key(7), rnd.RandomString(10000)));
|
||||
|
||||
// Check sizes across recovery by reopening a few times
|
||||
uint64_t size;
|
||||
for (int run = 0; run < 3; run++) {
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
|
||||
ASSERT_TRUE(Between(Size("", Key(0), 1), 0, 0));
|
||||
ASSERT_TRUE(Between(Size("", Key(1), 1), 10000, 11000));
|
||||
ASSERT_TRUE(Between(Size("", Key(2), 1), 20000, 21000));
|
||||
ASSERT_TRUE(Between(Size("", Key(3), 1), 120000, 121000));
|
||||
ASSERT_TRUE(Between(Size("", Key(4), 1), 130000, 131000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 232000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 242000));
|
||||
ASSERT_OK(Size("", Key(0), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 0, 0));
|
||||
ASSERT_OK(Size("", Key(1), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 10000, 11000));
|
||||
ASSERT_OK(Size("", Key(2), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 20000, 21000));
|
||||
ASSERT_OK(Size("", Key(3), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 120000, 121000));
|
||||
ASSERT_OK(Size("", Key(4), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 130000, 131000));
|
||||
ASSERT_OK(Size("", Key(5), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 230000, 232000));
|
||||
ASSERT_OK(Size("", Key(6), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 240000, 242000));
|
||||
// Ensure some overhead is accounted for, even without including all
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540500, 545000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550500, 555000));
|
||||
ASSERT_OK(Size("", Key(7), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 540500, 545000));
|
||||
ASSERT_OK(Size("", Key(8), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 550500, 555000));
|
||||
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110100, 111000));
|
||||
ASSERT_OK(Size(Key(3), Key(5), 1, &size));
|
||||
ASSERT_TRUE(Between(size, 110100, 111000));
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
}
|
||||
@@ -1748,6 +1778,7 @@ TEST_F(DBTest, Snapshot) {
|
||||
TEST_F(DBTest, HiddenValuesAreRemoved) {
|
||||
anon::OptionsOverride options_override;
|
||||
options_override.skip_policy = kSkipNoSnapshot;
|
||||
uint64_t size;
|
||||
do {
|
||||
Options options = CurrentOptions(options_override);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
@@ -1765,7 +1796,8 @@ TEST_F(DBTest, HiddenValuesAreRemoved) {
|
||||
ASSERT_GT(NumTableFilesAtLevel(0, 1), 0);
|
||||
|
||||
ASSERT_EQ(big, Get(1, "foo", snapshot));
|
||||
ASSERT_TRUE(Between(Size("", "pastfoo", 1), 50000, 60000));
|
||||
ASSERT_OK(Size("", "pastfoo", 1, &size));
|
||||
ASSERT_TRUE(Between(size, 50000, 60000));
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ tiny, " + big + " ]");
|
||||
Slice x("x");
|
||||
@@ -1776,7 +1808,8 @@ TEST_F(DBTest, HiddenValuesAreRemoved) {
|
||||
dbfull()->TEST_CompactRange(1, nullptr, &x, handles_[1]);
|
||||
ASSERT_EQ(AllEntriesFor("foo", 1), "[ tiny ]");
|
||||
|
||||
ASSERT_TRUE(Between(Size("", "pastfoo", 1), 0, 1000));
|
||||
ASSERT_OK(Size("", "pastfoo", 1, &size));
|
||||
ASSERT_TRUE(Between(size, 0, 1000));
|
||||
// ApproximateOffsetOf() is not yet implemented in plain table format,
|
||||
// which is used by Size().
|
||||
} while (ChangeOptions(kSkipUniversalCompaction | kSkipFIFOCompaction |
|
||||
|
||||
+119
-48
@@ -41,9 +41,7 @@ TEST_F(DBTest2, OpenForReadOnly) {
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||
for (auto& f : files) {
|
||||
if (f != "." && f != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
// <dbname> should be empty now and we should be able to delete it
|
||||
ASSERT_OK(env_->DeleteDir(dbname));
|
||||
@@ -75,9 +73,7 @@ TEST_F(DBTest2, OpenForReadOnlyWithColumnFamilies) {
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||
for (auto& f : files) {
|
||||
if (f != "." && f != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
// <dbname> should be empty now and we should be able to delete it
|
||||
ASSERT_OK(env_->DeleteDir(dbname));
|
||||
@@ -1414,67 +1410,94 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
TEST_P(PresetCompressionDictTest, Flush) {
|
||||
// Verifies that dictionary is generated and written during flush only when
|
||||
// `ColumnFamilyOptions::compression` enables dictionary.
|
||||
// `ColumnFamilyOptions::compression` enables dictionary. Also verifies the
|
||||
// size of the dictionary is within expectations according to the limit on
|
||||
// buffering set by `CompressionOptions::max_dict_buffer_bytes`.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 4 << 10;
|
||||
const size_t kDictLen = 16 << 10;
|
||||
const size_t kBlockLen = 4 << 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
if (bottommost_) {
|
||||
options.bottommost_compression = compression_type_;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
|
||||
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
}
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kKeysPerFile));
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = kBlockLen;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
uint64_t prev_compression_dict_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
Random rnd(301);
|
||||
for (size_t i = 0; i <= kKeysPerFile; ++i) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(i)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
|
||||
// If there's a compression dictionary, it should have been loaded when the
|
||||
// flush finished, incurring a cache miss.
|
||||
uint64_t expected_compression_dict_misses;
|
||||
// We can use `BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT` to detect whether a
|
||||
// compression dictionary exists since dictionaries would be preloaded when
|
||||
// the flush finishes.
|
||||
if (bottommost_) {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses;
|
||||
// Flush is never considered bottommost. This should change in the future
|
||||
// since flushed files may have nothing underneath them, like the one in
|
||||
// this test case.
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
} else {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses + 1;
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
0);
|
||||
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
|
||||
// number of bytes needs to be adjusted in case the cached block is in
|
||||
// ZSTD's digested dictionary format.
|
||||
if (compression_type_ != kZSTD &&
|
||||
compression_type_ != kZSTDNotFinalCompression) {
|
||||
// Although we limited buffering to `kBlockLen`, there may be up to two
|
||||
// blocks of data included in the dictionary since we only check limit
|
||||
// after each block is built.
|
||||
ASSERT_LE(TestGetTickerCount(options,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
2 * kBlockLen);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(expected_compression_dict_misses,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
}
|
||||
|
||||
TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
|
||||
// Verifies that dictionary is generated and written during compaction to
|
||||
// non-bottommost level only when `ColumnFamilyOptions::compression` enables
|
||||
// dictionary.
|
||||
// dictionary. Also verifies the size of the dictionary is within expectations
|
||||
// according to the limit on buffering set by
|
||||
// `CompressionOptions::max_dict_buffer_bytes`.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 4 << 10;
|
||||
const size_t kDictLen = 16 << 10;
|
||||
const size_t kBlockLen = 4 << 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
if (bottommost_) {
|
||||
options.bottommost_compression = compression_type_;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
|
||||
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
}
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = kBlockLen;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
@@ -1496,8 +1519,8 @@ TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
|
||||
ASSERT_EQ("2,0,1", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_compression_dict_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
uint64_t prev_compression_dict_bytes_inserted =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT);
|
||||
// This L0->L1 compaction merges the two L0 files into L1. The produced L1
|
||||
// file is not bottommost due to the existing L2 file covering the same key-
|
||||
// range.
|
||||
@@ -1505,38 +1528,58 @@ TEST_P(PresetCompressionDictTest, CompactNonBottommost) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,1,1", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
// If there's a compression dictionary, it should have been loaded when the
|
||||
// compaction finished, incurring a cache miss.
|
||||
uint64_t expected_compression_dict_misses;
|
||||
// We can use `BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT` to detect whether a
|
||||
// compression dictionary exists since dictionaries would be preloaded when
|
||||
// the compaction finishes.
|
||||
if (bottommost_) {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses;
|
||||
ASSERT_EQ(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted);
|
||||
} else {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses + 1;
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted);
|
||||
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
|
||||
// number of bytes needs to be adjusted in case the cached block is in
|
||||
// ZSTD's digested dictionary format.
|
||||
if (compression_type_ != kZSTD &&
|
||||
compression_type_ != kZSTDNotFinalCompression) {
|
||||
// Although we limited buffering to `kBlockLen`, there may be up to two
|
||||
// blocks of data included in the dictionary since we only check limit
|
||||
// after each block is built.
|
||||
ASSERT_LE(TestGetTickerCount(options,
|
||||
BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted + 2 * kBlockLen);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(expected_compression_dict_misses,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
}
|
||||
|
||||
TEST_P(PresetCompressionDictTest, CompactBottommost) {
|
||||
// Verifies that dictionary is generated and written during compaction to
|
||||
// non-bottommost level only when either `ColumnFamilyOptions::compression` or
|
||||
// `ColumnFamilyOptions::bottommost_compression` enables dictionary.
|
||||
// `ColumnFamilyOptions::bottommost_compression` enables dictionary. Also
|
||||
// verifies the size of the dictionary is within expectations according to the
|
||||
// limit on buffering set by `CompressionOptions::max_dict_buffer_bytes`.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 4 << 10;
|
||||
const size_t kDictLen = 16 << 10;
|
||||
const size_t kBlockLen = 4 << 10;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
if (bottommost_) {
|
||||
options.bottommost_compression = compression_type_;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.bottommost_compression_opts.max_dict_bytes = kDictLen;
|
||||
options.bottommost_compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
options.compression_opts.max_dict_buffer_bytes = kBlockLen;
|
||||
}
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = kBlockLen;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
@@ -1552,17 +1595,28 @@ TEST_P(PresetCompressionDictTest, CompactBottommost) {
|
||||
ASSERT_EQ("2", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_compression_dict_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
uint64_t prev_compression_dict_bytes_inserted =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT);
|
||||
CompactRangeOptions cro;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,1", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
// If there's a compression dictionary, it should have been loaded when the
|
||||
// compaction finished, incurring a cache miss.
|
||||
ASSERT_EQ(prev_compression_dict_misses + 1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS));
|
||||
ASSERT_GT(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted);
|
||||
// TODO(ajkr): fix the below assertion to work with ZSTD. The expectation on
|
||||
// number of bytes needs to be adjusted in case the cached block is in ZSTD's
|
||||
// digested dictionary format.
|
||||
if (compression_type_ != kZSTD &&
|
||||
compression_type_ != kZSTDNotFinalCompression) {
|
||||
// Although we limited buffering to `kBlockLen`, there may be up to two
|
||||
// blocks of data included in the dictionary since we only check limit after
|
||||
// each block is built.
|
||||
ASSERT_LE(
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_BYTES_INSERT),
|
||||
prev_compression_dict_bytes_inserted + 2 * kBlockLen);
|
||||
}
|
||||
}
|
||||
|
||||
class CompactionCompressionListener : public EventListener {
|
||||
@@ -3685,20 +3739,26 @@ TEST_F(DBTest2, LiveFilesOmitObsoleteFiles) {
|
||||
|
||||
TEST_F(DBTest2, TestNumPread) {
|
||||
Options options = CurrentOptions();
|
||||
bool prefetch_supported =
|
||||
test::IsPrefetchSupported(env_->GetFileSystem(), dbname_);
|
||||
// disable block cache
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
env_->count_random_reads_ = true;
|
||||
|
||||
env_->random_file_open_counter_.store(0);
|
||||
ASSERT_OK(Put("bar", "foo"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
// After flush, we'll open the file and read footer, meta block,
|
||||
// property block and index block.
|
||||
ASSERT_EQ(4, env_->random_read_counter_.Read());
|
||||
if (prefetch_supported) {
|
||||
// After flush, we'll open the file and read footer, meta block,
|
||||
// property block and index block.
|
||||
ASSERT_EQ(4, env_->random_read_counter_.Read());
|
||||
} else {
|
||||
// With prefetch not supported, we will do a single read into a buffer
|
||||
ASSERT_EQ(1, env_->random_read_counter_.Read());
|
||||
}
|
||||
ASSERT_EQ(1, env_->random_file_open_counter_.load());
|
||||
|
||||
// One pread per a normal data block read
|
||||
@@ -3714,19 +3774,30 @@ TEST_F(DBTest2, TestNumPread) {
|
||||
ASSERT_OK(Put("bar2", "foo2"));
|
||||
ASSERT_OK(Put("foo2", "bar2"));
|
||||
ASSERT_OK(Flush());
|
||||
// After flush, we'll open the file and read footer, meta block,
|
||||
// property block and index block.
|
||||
ASSERT_EQ(4, env_->random_read_counter_.Read());
|
||||
if (prefetch_supported) {
|
||||
// After flush, we'll open the file and read footer, meta block,
|
||||
// property block and index block.
|
||||
ASSERT_EQ(4, env_->random_read_counter_.Read());
|
||||
} else {
|
||||
// With prefetch not supported, we will do a single read into a buffer
|
||||
ASSERT_EQ(1, env_->random_read_counter_.Read());
|
||||
}
|
||||
ASSERT_EQ(1, env_->random_file_open_counter_.load());
|
||||
|
||||
// Compaction needs two input blocks, which requires 2 preads, and
|
||||
// generate a new SST file which needs 4 preads (footer, meta block,
|
||||
// property block and index block). In total 6.
|
||||
env_->random_file_open_counter_.store(0);
|
||||
env_->random_read_counter_.Reset();
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(6, env_->random_read_counter_.Read());
|
||||
// All compactin input files should have already been opened.
|
||||
if (prefetch_supported) {
|
||||
// Compaction needs two input blocks, which requires 2 preads, and
|
||||
// generate a new SST file which needs 4 preads (footer, meta block,
|
||||
// property block and index block). In total 6.
|
||||
ASSERT_EQ(6, env_->random_read_counter_.Read());
|
||||
} else {
|
||||
// With prefetch off, compaction needs two input blocks,
|
||||
// followed by a single buffered read. In total 3.
|
||||
ASSERT_EQ(3, env_->random_read_counter_.Read());
|
||||
}
|
||||
// All compaction input files should have already been opened.
|
||||
ASSERT_EQ(1, env_->random_file_open_counter_.load());
|
||||
|
||||
// One pread per a normal data block read
|
||||
|
||||
+33
-12
@@ -1128,27 +1128,48 @@ std::string DBTestBase::FilesPerLevel(int cf) {
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
size_t DBTestBase::CountFiles() {
|
||||
size_t count = 0;
|
||||
std::vector<std::string> files;
|
||||
EXPECT_OK(env_->GetChildren(dbname_, &files));
|
||||
|
||||
std::vector<std::string> logfiles;
|
||||
if (dbname_ != last_options_.wal_dir) {
|
||||
Status s = env_->GetChildren(last_options_.wal_dir, &logfiles);
|
||||
EXPECT_TRUE(s.ok() || s.IsNotFound());
|
||||
if (env_->GetChildren(dbname_, &files).ok()) {
|
||||
count += files.size();
|
||||
}
|
||||
|
||||
return files.size() + logfiles.size();
|
||||
if (dbname_ != last_options_.wal_dir) {
|
||||
if (env_->GetChildren(last_options_.wal_dir, &files).ok()) {
|
||||
count += files.size();
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
Status DBTestBase::CountFiles(size_t* count) {
|
||||
std::vector<std::string> files;
|
||||
Status s = env_->GetChildren(dbname_, &files);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
size_t files_count = files.size();
|
||||
|
||||
if (dbname_ != last_options_.wal_dir) {
|
||||
s = env_->GetChildren(last_options_.wal_dir, &files);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
*count = files_count + files.size();
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
uint64_t DBTestBase::Size(const Slice& start, const Slice& limit, int cf) {
|
||||
Status DBTestBase::Size(const Slice& start, const Slice& limit, int cf,
|
||||
uint64_t* size) {
|
||||
Range r(start, limit);
|
||||
uint64_t size;
|
||||
if (cf == 0) {
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
return db_->GetApproximateSizes(&r, 1, size);
|
||||
} else {
|
||||
db_->GetApproximateSizes(handles_[1], &r, 1, &size);
|
||||
return db_->GetApproximateSizes(handles_[1], &r, 1, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void DBTestBase::Compact(int cf, const Slice& start, const Slice& limit,
|
||||
|
||||
+7
-1
@@ -1070,7 +1070,13 @@ class DBTestBase : public testing::Test {
|
||||
|
||||
size_t CountFiles();
|
||||
|
||||
uint64_t Size(const Slice& start, const Slice& limit, int cf = 0);
|
||||
Status CountFiles(size_t* count);
|
||||
|
||||
Status Size(const Slice& start, const Slice& limit, uint64_t* size) {
|
||||
return Size(start, limit, 0, size);
|
||||
}
|
||||
|
||||
Status Size(const Slice& start, const Slice& limit, int cf, uint64_t* size);
|
||||
|
||||
void Compact(int cf, const Slice& start, const Slice& limit,
|
||||
uint32_t target_path_id);
|
||||
|
||||
+162
-29
@@ -14,6 +14,7 @@
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class DBWALTestBase : public DBTestBase {
|
||||
@@ -437,15 +438,159 @@ TEST_F(DBWALTest, RecoverWithBlob) {
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, RecoverWithBlobMultiSST) {
|
||||
// Write several large (4 KB) values without flushing. Note that blob files
|
||||
// are not actually enabled at this point.
|
||||
std::string large_value(1 << 12, 'a');
|
||||
|
||||
constexpr int num_keys = 64;
|
||||
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), large_value));
|
||||
}
|
||||
|
||||
// There should be no files just yet since we haven't flushed.
|
||||
{
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
ASSERT_NE(versions, nullptr);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
ASSERT_NE(cfd, nullptr);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
ASSERT_NE(current, nullptr);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
ASSERT_NE(storage_info, nullptr);
|
||||
|
||||
ASSERT_EQ(storage_info->num_non_empty_levels(), 0);
|
||||
ASSERT_TRUE(storage_info->GetBlobFiles().empty());
|
||||
}
|
||||
|
||||
// Reopen the database with blob files enabled and write buffer size set to a
|
||||
// smaller value. Multiple table files+blob files should be written and added
|
||||
// to the Version during recovery.
|
||||
Options options;
|
||||
options.write_buffer_size = 1 << 16; // 64 KB
|
||||
options.enable_blob_files = true;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
ASSERT_EQ(Get(Key(i)), large_value);
|
||||
}
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
ASSERT_NE(versions, nullptr);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
ASSERT_NE(cfd, nullptr);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
ASSERT_NE(current, nullptr);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
ASSERT_NE(storage_info, nullptr);
|
||||
|
||||
const auto& l0_files = storage_info->LevelFiles(0);
|
||||
ASSERT_GT(l0_files.size(), 1);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
ASSERT_GT(blob_files.size(), 1);
|
||||
|
||||
ASSERT_EQ(l0_files.size(), blob_files.size());
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, WALWithChecksumHandoff) {
|
||||
#ifndef ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
if (mem_env_ || encrypted_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
|
||||
return;
|
||||
}
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs(
|
||||
new FaultInjectionTestFS(FileSystem::Default()));
|
||||
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
|
||||
do {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.checksum_handoff_file_types.Add(FileType::kWalFile);
|
||||
options.env = fault_fs_env.get();
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
WriteOptions writeOpt = WriteOptions();
|
||||
writeOpt.disableWAL = true;
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v1"));
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v1"));
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("v1", Get(1, "foo"));
|
||||
ASSERT_EQ("v1", Get(1, "bar"));
|
||||
|
||||
writeOpt.disableWAL = false;
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v2"));
|
||||
writeOpt.disableWAL = true;
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v2"));
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// Both value's should be present.
|
||||
ASSERT_EQ("v2", Get(1, "bar"));
|
||||
ASSERT_EQ("v2", Get(1, "foo"));
|
||||
|
||||
writeOpt.disableWAL = true;
|
||||
// This put, data is persisted by Flush
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v3"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
writeOpt.disableWAL = false;
|
||||
// Data is persisted in the WAL
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "zoo", "v3"));
|
||||
// The hash does not match, write fails
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
|
||||
writeOpt.disableWAL = false;
|
||||
ASSERT_NOK(dbfull()->Put(writeOpt, handles_[1], "foo", "v3"));
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
// Due to the write failure, Get should not find
|
||||
ASSERT_NE("v3", Get(1, "foo"));
|
||||
ASSERT_EQ("v3", Get(1, "zoo"));
|
||||
ASSERT_EQ("v3", Get(1, "bar"));
|
||||
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
|
||||
// Each write will be similated as corrupted.
|
||||
fault_fs->IngestDataCorruptionBeforeWrite();
|
||||
writeOpt.disableWAL = true;
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v4"));
|
||||
writeOpt.disableWAL = false;
|
||||
ASSERT_NOK(dbfull()->Put(writeOpt, handles_[1], "foo", "v4"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_NE("v4", Get(1, "foo"));
|
||||
ASSERT_NE("v4", Get(1, "bar"));
|
||||
fault_fs->NoDataCorruptionBeforeWrite();
|
||||
|
||||
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
|
||||
// The file system does not provide checksum method and verification.
|
||||
writeOpt.disableWAL = true;
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "bar", "v5"));
|
||||
writeOpt.disableWAL = false;
|
||||
ASSERT_OK(dbfull()->Put(writeOpt, handles_[1], "foo", "v5"));
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("v5", Get(1, "foo"));
|
||||
ASSERT_EQ("v5", Get(1, "bar"));
|
||||
|
||||
Destroy(options);
|
||||
} while (ChangeWalOptions());
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
}
|
||||
|
||||
class DBRecoveryTestBlobError
|
||||
: public DBWALTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBRecoveryTestBlobError()
|
||||
: fault_injection_env_(env_), sync_point_(GetParam()) {}
|
||||
~DBRecoveryTestBlobError() { Close(); }
|
||||
DBRecoveryTestBlobError() : sync_point_(GetParam()) {}
|
||||
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
@@ -460,21 +605,19 @@ TEST_P(DBRecoveryTestBlobError, RecoverWithBlobError) {
|
||||
|
||||
// Reopen with blob files enabled but make blob file writing fail during
|
||||
// recovery.
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
|
||||
Status* const s = static_cast<Status*>(arg);
|
||||
assert(s);
|
||||
|
||||
(*s) = Status::IOError(sync_point_);
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeDeleteFile", [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(true);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options;
|
||||
options.enable_blob_files = true;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = &fault_injection_env_;
|
||||
options.env = env_;
|
||||
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
|
||||
@@ -506,9 +649,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
std::vector<std::string> old_files;
|
||||
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
||||
for (auto& file : old_files) {
|
||||
if (file != "." && file != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||
}
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -528,9 +669,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
std::vector<std::string> logs;
|
||||
ASSERT_OK(env_->GetChildren(options.wal_dir, &logs));
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(options.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||
}
|
||||
CopyFile(options.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||
}
|
||||
|
||||
// recover the DB
|
||||
@@ -541,9 +680,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
|
||||
// copy the logs from backup back to wal dir
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
}
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
}
|
||||
// this should ignore the log files, recovery should not happen again
|
||||
// if the recovery happens, the same merge operator would be called twice,
|
||||
@@ -559,9 +696,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
// copy the logs from backup back to wal dir
|
||||
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
}
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
}
|
||||
// assert that we successfully recovered only from logs, even though we
|
||||
// destroyed the DB
|
||||
@@ -574,11 +709,9 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
// copy the logs from backup back to wal dir
|
||||
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
// we won't be needing this file no more
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + log));
|
||||
}
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
// we won't be needing this file no more
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + log));
|
||||
}
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_NOK(s);
|
||||
|
||||
@@ -223,6 +223,111 @@ TEST_F(DBBasicTestWithTimestamp, CompactRangeWithSpecifiedRange) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLow) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const std::string kKey = "test kKey";
|
||||
|
||||
// Test set ts_low first and flush()
|
||||
int current_ts_low = 5;
|
||||
std::string ts_low_str = Timestamp(current_ts_low, 0);
|
||||
Slice ts_low = ts_low_str;
|
||||
CompactRangeOptions comp_opts;
|
||||
comp_opts.full_history_ts_low = &ts_low;
|
||||
comp_opts.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(comp_opts, nullptr, nullptr));
|
||||
|
||||
auto* cfd =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(db_->DefaultColumnFamily())
|
||||
->cfd();
|
||||
auto result_ts_low = cfd->GetFullHistoryTsLow();
|
||||
|
||||
ASSERT_TRUE(test_cmp.CompareTimestamp(ts_low, result_ts_low) == 0);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, kKey, Key(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ReadOptions read_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
Slice ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status status = db_->Get(read_opts, kKey, &value);
|
||||
if (i < current_ts_low) {
|
||||
ASSERT_TRUE(status.IsNotFound());
|
||||
} else {
|
||||
ASSERT_OK(status);
|
||||
ASSERT_TRUE(value.compare(Key(i)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Test set ts_low and then trigger compaction
|
||||
for (int i = 10; i < 20; i++) {
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, kKey, Key(i)));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
current_ts_low = 15;
|
||||
ts_low_str = Timestamp(current_ts_low, 0);
|
||||
ts_low = ts_low_str;
|
||||
comp_opts.full_history_ts_low = &ts_low;
|
||||
ASSERT_OK(db_->CompactRange(comp_opts, nullptr, nullptr));
|
||||
result_ts_low = cfd->GetFullHistoryTsLow();
|
||||
ASSERT_TRUE(test_cmp.CompareTimestamp(ts_low, result_ts_low) == 0);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
ReadOptions read_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
Slice ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status status = db_->Get(read_opts, kKey, &value);
|
||||
if (i < current_ts_low) {
|
||||
ASSERT_TRUE(status.IsNotFound());
|
||||
} else {
|
||||
ASSERT_OK(status);
|
||||
ASSERT_TRUE(value.compare(Key(i)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Test invalid compaction with range
|
||||
Slice start(kKey), end(kKey);
|
||||
Status s = db_->CompactRange(comp_opts, &start, &end);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
s = db_->CompactRange(comp_opts, &start, nullptr);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
s = db_->CompactRange(comp_opts, nullptr, &end);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
|
||||
// Test invalid compaction with the decreasing ts_low
|
||||
ts_low_str = Timestamp(current_ts_low - 1, 0);
|
||||
ts_low = ts_low_str;
|
||||
comp_opts.full_history_ts_low = &ts_low;
|
||||
s = db_->CompactRange(comp_opts, nullptr, nullptr);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, GetApproximateSizes) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000; // Large write buffer
|
||||
@@ -270,7 +375,7 @@ TEST_F(DBBasicTestWithTimestamp, GetApproximateSizes) {
|
||||
ASSERT_EQ(range_sizes[1], size);
|
||||
|
||||
// Zero if not including mem table
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_OK(db_->GetApproximateSizes(&r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(500);
|
||||
|
||||
+4
-2
@@ -146,8 +146,10 @@ inline void UnPackSequenceAndType(uint64_t packed, uint64_t* seq,
|
||||
*seq = packed >> 8;
|
||||
*t = static_cast<ValueType>(packed & 0xff);
|
||||
|
||||
assert(*seq <= kMaxSequenceNumber);
|
||||
assert(IsExtendedValueType(*t));
|
||||
// Commented the following two assertions in order to test key-value checksum
|
||||
// on corrupted keys without crashing ("DbKvChecksumTest").
|
||||
// assert(*seq <= kMaxSequenceNumber);
|
||||
// assert(IsExtendedValueType(*t));
|
||||
}
|
||||
|
||||
EntryType GetEntryType(ValueType value_type);
|
||||
|
||||
+27
-19
@@ -350,12 +350,17 @@ const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
|
||||
// This is the main function for looking at IO related error during the
|
||||
// background operations. The main logic is:
|
||||
// 1) File scope IO error is treated as retryable IO error in the write
|
||||
// path. In RocksDB, If a file has write IO error and it is at file scope,
|
||||
// RocksDB never write to the same file again. RocksDB will create a new
|
||||
// file and rewrite the whole content. Thus, it is retryable.
|
||||
// 1) if the error is caused by data loss, the error is mapped to
|
||||
// unrecoverable error. Application/user must take action to handle
|
||||
// this situation.
|
||||
// 2) if the error is a Retryable IO error, auto resume will be called and the
|
||||
// auto resume can be controlled by resume count and resume interval
|
||||
// options. There are three sub-cases:
|
||||
// this situation (File scope case is excluded).
|
||||
// 2) if the error is a Retryable IO error (i.e., it is a file scope IO error,
|
||||
// or its retryable flag is set and not a data loss error), auto resume
|
||||
// will be called and the auto resume can be controlled by resume count
|
||||
// and resume interval options. There are three sub-cases:
|
||||
// a) if the error happens during compaction, it is mapped to a soft error.
|
||||
// the compaction thread will reschedule a new compaction.
|
||||
// b) if the error happens during flush and also WAL is empty, it is mapped
|
||||
@@ -384,9 +389,10 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
|
||||
Status new_bg_io_err = bg_io_err;
|
||||
DBRecoverContext context;
|
||||
if (bg_io_err.GetDataLoss()) {
|
||||
// First, data loss is treated as unrecoverable error. So it can directly
|
||||
// overwrite any existing bg_error_.
|
||||
if (bg_io_err.GetScope() != IOStatus::IOErrorScope::kIOErrorScopeFile &&
|
||||
bg_io_err.GetDataLoss()) {
|
||||
// First, data loss (non file scope) is treated as unrecoverable error. So
|
||||
// it can directly overwrite any existing bg_error_.
|
||||
bool auto_recovery = false;
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kUnrecoverableError);
|
||||
bg_error_ = bg_err;
|
||||
@@ -397,23 +403,25 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
&bg_err, db_mutex_, &auto_recovery);
|
||||
recover_context_ = context;
|
||||
return bg_error_;
|
||||
} else if (bg_io_err.GetRetryable()) {
|
||||
// Second, check if the error is a retryable IO error or not. if it is
|
||||
// retryable error and its severity is higher than bg_error_, overwrite
|
||||
// the bg_error_ with new error.
|
||||
// In current stage, for retryable IO error of compaction, treat it as
|
||||
// soft error. In other cases, treat the retryable IO error as hard
|
||||
// error.
|
||||
} else if (bg_io_err.GetScope() ==
|
||||
IOStatus::IOErrorScope::kIOErrorScopeFile ||
|
||||
bg_io_err.GetRetryable()) {
|
||||
// Second, check if the error is a retryable IO error (file scope IO error
|
||||
// is also treated as retryable IO error in RocksDB write path). if it is
|
||||
// retryable error and its severity is higher than bg_error_, overwrite the
|
||||
// bg_error_ with new error. In current stage, for retryable IO error of
|
||||
// compaction, treat it as soft error. In other cases, treat the retryable
|
||||
// IO error as hard error.
|
||||
bool auto_recovery = false;
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
&auto_recovery);
|
||||
if (BackgroundErrorReason::kCompaction == reason) {
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kSoftError);
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
recover_context_ = context;
|
||||
// We map the retryable IO error during compaction to soft error. Since
|
||||
// compaction can reschedule by itself. We will not set the BG error in
|
||||
// this case
|
||||
// TODO: a better way to set or clean the retryable IO error which
|
||||
// happens during compaction SST file write.
|
||||
return bg_error_;
|
||||
} else if (BackgroundErrorReason::kFlushNoWAL == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
|
||||
+199
-7
@@ -9,9 +9,9 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/io_status.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/sst_file_manager.h"
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "test_util/sync_point.h"
|
||||
@@ -241,6 +241,90 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritFileScopeError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("File Scope Data Loss Error");
|
||||
error_msg.SetDataLoss(true);
|
||||
error_msg.SetScope(
|
||||
ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
error_msg.SetRetryable(false);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val1", Get(Key(1)));
|
||||
|
||||
ASSERT_OK(Put(Key(2), "val2"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeSyncTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val2", Get(Key(2)));
|
||||
|
||||
ASSERT_OK(Put(Key(3), "val3"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeCloseTableFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
// not file scope, but retyrable set
|
||||
error_msg.SetDataLoss(false);
|
||||
error_msg.SetScope(
|
||||
ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFileSystem);
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
ASSERT_OK(Put(Key(3), "val3"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeCloseTableFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val3", Get(Key(3)));
|
||||
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -453,6 +537,52 @@ TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableError) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteFileScopeError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
std::string old_manifest;
|
||||
std::string new_manifest;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
old_manifest = GetManifestNameFromLiveFiles();
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("File Scope Data Loss Error");
|
||||
error_msg.SetDataLoss(true);
|
||||
error_msg.SetScope(
|
||||
ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
error_msg.SetRetryable(false);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put(Key(1), "val"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionSet::LogAndApply:WriteManifest",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
|
||||
new_manifest = GetManifestNameFromLiveFiles();
|
||||
ASSERT_NE(new_manifest, old_manifest);
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_EQ("val", Get(Key(0)));
|
||||
ASSERT_EQ("val", Get(Key(1)));
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteNoWALRetryableError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -734,7 +864,7 @@ TEST_F(DBErrorHandlingFSTest, CompactionWriteError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, CompactionWriteRetryableError) {
|
||||
TEST_F(DBErrorHandlingFSTest, DISABLED_CompactionWriteRetryableError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
@@ -762,14 +892,67 @@ TEST_F(DBErrorHandlingFSTest, CompactionWriteRetryableError) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Finish",
|
||||
[&](void*) { CancelAllBackgroundWork(dbfull()); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, DISABLED_CompactionWriteFileScopeError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 0;
|
||||
Status s;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::IOError("File Scope Data Loss Error");
|
||||
error_msg.SetDataLoss(true);
|
||||
error_msg.SetScope(
|
||||
ROCKSDB_NAMESPACE::IOStatus::IOErrorScope::kIOErrorScopeFile);
|
||||
error_msg.SetRetryable(false);
|
||||
|
||||
ASSERT_OK(Put(Key(0), "va;"));
|
||||
ASSERT_OK(Put(Key(2), "va;"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
listener->OverrideBGError(Status(error_msg, Status::Severity::kHardError));
|
||||
listener->EnableAutoRecovery(false);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::OpenCompactionOutputFile",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:Finish",
|
||||
[&](void*) { CancelAllBackgroundWork(dbfull()); });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val"));
|
||||
s = Flush();
|
||||
ASSERT_OK(s);
|
||||
|
||||
s = dbfull()->TEST_GetBGError();
|
||||
ASSERT_OK(s);
|
||||
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
@@ -1213,6 +1396,10 @@ TEST_F(DBErrorHandlingFSTest, MultiDBCompactionError) {
|
||||
EXPECT_EQ(atoi(prop.c_str()), 1);
|
||||
}
|
||||
|
||||
SstFileManagerImpl* sfmImpl =
|
||||
static_cast_with_check<SstFileManagerImpl>(sfm.get());
|
||||
sfmImpl->Close();
|
||||
|
||||
for (auto i = 0; i < kNumDbInstances; ++i) {
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "_%d", i);
|
||||
@@ -1346,6 +1533,10 @@ TEST_F(DBErrorHandlingFSTest, MultiDBVariousErrors) {
|
||||
EXPECT_EQ(atoi(prop.c_str()), 1);
|
||||
}
|
||||
|
||||
SstFileManagerImpl* sfmImpl =
|
||||
static_cast_with_check<SstFileManagerImpl>(sfm.get());
|
||||
sfmImpl->Close();
|
||||
|
||||
for (auto i = 0; i < kNumDbInstances; ++i) {
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "_%d", i);
|
||||
@@ -2012,8 +2203,7 @@ TEST_F(DBErrorHandlingFSTest, CompactionWriteRetryableErrorAutoRecover) {
|
||||
ASSERT_OK(s);
|
||||
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kSoftError);
|
||||
|
||||
ASSERT_OK(s);
|
||||
TEST_SYNC_POINT("CompactionWriteRetryableErrorAutoRecover0");
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -2062,7 +2252,8 @@ TEST_F(DBErrorHandlingFSTest, WALWriteRetryableErrorAutoRecover1) {
|
||||
ASSERT_OK(batch.Put(Key(i), rnd.RandomString(1024)));
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"RecoverFromRetryableBGIOError:BeforeResume0", "WALWriteError1:0"},
|
||||
{{"WALWriteErrorDone", "RecoverFromRetryableBGIOError:BeforeStart"},
|
||||
{"RecoverFromRetryableBGIOError:BeforeResume0", "WALWriteError1:0"},
|
||||
{"WALWriteError1:1", "RecoverFromRetryableBGIOError:BeforeResume1"},
|
||||
{"RecoverFromRetryableBGIOError:RecoverSuccess", "WALWriteError1:2"}});
|
||||
|
||||
@@ -2078,6 +2269,7 @@ TEST_F(DBErrorHandlingFSTest, WALWriteRetryableErrorAutoRecover1) {
|
||||
wopts.sync = true;
|
||||
s = dbfull()->Write(wopts, &batch);
|
||||
ASSERT_EQ(true, s.IsIOError());
|
||||
TEST_SYNC_POINT("WALWriteErrorDone");
|
||||
|
||||
TEST_SYNC_POINT("WALWriteError1:0");
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
|
||||
+8
-9
@@ -213,17 +213,16 @@ void EventHelpers::NotifyOnErrorRecoveryCompleted(
|
||||
const std::vector<std::shared_ptr<EventListener>>& listeners,
|
||||
Status old_bg_error, InstrumentedMutex* db_mutex) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (listeners.size() == 0U) {
|
||||
return;
|
||||
}
|
||||
db_mutex->AssertHeld();
|
||||
// release lock while notifying events
|
||||
db_mutex->Unlock();
|
||||
for (auto& listener : listeners) {
|
||||
listener->OnErrorRecoveryCompleted(old_bg_error);
|
||||
if (listeners.size() > 0) {
|
||||
db_mutex->AssertHeld();
|
||||
// release lock while notifying events
|
||||
db_mutex->Unlock();
|
||||
for (auto& listener : listeners) {
|
||||
listener->OnErrorRecoveryCompleted(old_bg_error);
|
||||
}
|
||||
db_mutex->Lock();
|
||||
}
|
||||
old_bg_error.PermitUncheckedError();
|
||||
db_mutex->Lock();
|
||||
#else
|
||||
(void)listeners;
|
||||
(void)old_bg_error;
|
||||
|
||||
@@ -810,7 +810,8 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
fs_->NewRandomRWFile(file_to_ingest->internal_file_path, env_options_,
|
||||
&rwfile, nullptr);
|
||||
if (status.ok()) {
|
||||
FSRandomRWFilePtr fsptr(std::move(rwfile), io_tracer_);
|
||||
FSRandomRWFilePtr fsptr(std::move(rwfile), io_tracer_,
|
||||
file_to_ingest->internal_file_path);
|
||||
std::string seqno_val;
|
||||
PutFixed64(&seqno_val, seqno);
|
||||
status = fsptr->Write(file_to_ingest->global_seqno_offset, seqno_val,
|
||||
|
||||
+13
-8
@@ -32,7 +32,7 @@ class FlushJobTestBase : public testing::Test {
|
||||
protected:
|
||||
FlushJobTestBase(std::string dbname, const Comparator* ucmp)
|
||||
: env_(Env::Default()),
|
||||
fs_(std::make_shared<LegacyFileSystemWrapper>(env_)),
|
||||
fs_(env_->GetFileSystem()),
|
||||
dbname_(std::move(dbname)),
|
||||
ucmp_(ucmp),
|
||||
options_(),
|
||||
@@ -188,7 +188,8 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
for (int i = 1; i < 10000; ++i) {
|
||||
std::string key(ToString((i + 1000) % 10000));
|
||||
std::string value("value" + key);
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(i), kTypeValue, key, value));
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(i), kTypeValue, key, value,
|
||||
nullptr /* kv_prot_info */));
|
||||
if ((i + 1000) % 10000 < 9995) {
|
||||
InternalKey internal_key(key, SequenceNumber(i), kTypeValue);
|
||||
inserted_keys.push_back({internal_key.Encode().ToString(), value});
|
||||
@@ -197,7 +198,7 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
|
||||
{
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(10000), kTypeRangeDeletion, "9995",
|
||||
"9999a"));
|
||||
"9999a", nullptr /* kv_prot_info */));
|
||||
InternalKey internal_key("9995", SequenceNumber(10000), kTypeRangeDeletion);
|
||||
inserted_keys.push_back({internal_key.Encode().ToString(), "9999a"});
|
||||
}
|
||||
@@ -224,7 +225,8 @@ TEST_F(FlushJobTest, NonEmpty) {
|
||||
}
|
||||
|
||||
const SequenceNumber seq(i + 10001);
|
||||
ASSERT_OK(new_mem->Add(seq, kTypeBlobIndex, key, blob_index));
|
||||
ASSERT_OK(new_mem->Add(seq, kTypeBlobIndex, key, blob_index,
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
InternalKey internal_key(key, seq, kTypeBlobIndex);
|
||||
inserted_keys.push_back({internal_key.Encode().ToString(), blob_index});
|
||||
@@ -286,7 +288,7 @@ TEST_F(FlushJobTest, FlushMemTablesSingleColumnFamily) {
|
||||
std::string key(ToString(j + i * num_keys_per_table));
|
||||
std::string value("value" + key);
|
||||
ASSERT_OK(mem->Add(SequenceNumber(j + i * num_keys_per_table), kTypeValue,
|
||||
key, value));
|
||||
key, value, nullptr /* kv_prot_info */));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +360,8 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
for (size_t j = 0; j != num_keys_per_memtable; ++j) {
|
||||
std::string key(ToString(j + i * num_keys_per_memtable));
|
||||
std::string value("value" + key);
|
||||
ASSERT_OK(mem->Add(curr_seqno++, kTypeValue, key, value));
|
||||
ASSERT_OK(mem->Add(curr_seqno++, kTypeValue, key, value,
|
||||
nullptr /* kv_prot_info */));
|
||||
}
|
||||
|
||||
cfd->imm()->Add(mem, &to_delete);
|
||||
@@ -469,7 +472,8 @@ TEST_F(FlushJobTest, Snapshots) {
|
||||
for (int j = 0; j < insertions; ++j) {
|
||||
std::string value(rnd.HumanReadableString(10));
|
||||
auto seqno = ++current_seqno;
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(seqno), kTypeValue, key, value));
|
||||
ASSERT_OK(new_mem->Add(SequenceNumber(seqno), kTypeValue, key, value,
|
||||
nullptr /* kv_prot_info */));
|
||||
// a key is visible only if:
|
||||
// 1. it's the last one written (j == insertions - 1)
|
||||
// 2. there's a snapshot pointing at it
|
||||
@@ -521,7 +525,8 @@ class FlushJobTimestampTest : public FlushJobTestBase {
|
||||
Slice value) {
|
||||
std::string key_str(std::move(key));
|
||||
PutFixed64(&key_str, ts);
|
||||
ASSERT_OK(memtable->Add(seq, value_type, key_str, value));
|
||||
ASSERT_OK(memtable->Add(seq, value_type, key_str, value,
|
||||
nullptr /* kv_prot_info */));
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -0,0 +1,424 @@
|
||||
// Copyright (c) 2020-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
//
|
||||
// This file contains classes containing fields to protect individual entries.
|
||||
// The classes are named "ProtectionInfo<suffix>", where <suffix> indicates the
|
||||
// combination of fields that are covered. Each field has a single letter
|
||||
// abbreviation as follows.
|
||||
//
|
||||
// K = key
|
||||
// V = value
|
||||
// O = optype aka value type
|
||||
// T = timestamp
|
||||
// S = seqno
|
||||
// C = CF ID
|
||||
//
|
||||
// Then, for example, a class that protects an entry consisting of key, value,
|
||||
// optype, timestamp, and CF ID (i.e., a `WriteBatch` entry) would be named
|
||||
// `ProtectionInfoKVOTC`.
|
||||
//
|
||||
// The `ProtectionInfo.*` classes are templated on the integer type used to hold
|
||||
// the XOR of hashes for each field. Only unsigned integer types are supported,
|
||||
// and the maximum supported integer width is 64 bits. When the integer type is
|
||||
// narrower than the hash values, we lop off the most significant bits to make
|
||||
// them fit.
|
||||
//
|
||||
// The `ProtectionInfo.*` classes are all intended to be non-persistent. We do
|
||||
// not currently make the byte order consistent for integer fields before
|
||||
// hashing them, so the resulting values are endianness-dependent.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
template <typename T>
|
||||
class ProtectionInfo;
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOT;
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOTC;
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOTS;
|
||||
|
||||
// Aliases for 64-bit protection infos.
|
||||
typedef ProtectionInfo<uint64_t> ProtectionInfo64;
|
||||
typedef ProtectionInfoKVOT<uint64_t> ProtectionInfoKVOT64;
|
||||
typedef ProtectionInfoKVOTC<uint64_t> ProtectionInfoKVOTC64;
|
||||
typedef ProtectionInfoKVOTS<uint64_t> ProtectionInfoKVOTS64;
|
||||
|
||||
template <typename T>
|
||||
class ProtectionInfo {
|
||||
public:
|
||||
ProtectionInfo<T>() = default;
|
||||
|
||||
Status GetStatus() const;
|
||||
ProtectionInfoKVOT<T> ProtectKVOT(const Slice& key, const Slice& value,
|
||||
ValueType op_type,
|
||||
const Slice& timestamp) const;
|
||||
ProtectionInfoKVOT<T> ProtectKVOT(const SliceParts& key,
|
||||
const SliceParts& value, ValueType op_type,
|
||||
const Slice& timestamp) const;
|
||||
|
||||
private:
|
||||
friend class ProtectionInfoKVOT<T>;
|
||||
friend class ProtectionInfoKVOTS<T>;
|
||||
friend class ProtectionInfoKVOTC<T>;
|
||||
|
||||
// Each field is hashed with an independent value so we can catch fields being
|
||||
// swapped. Per the `NPHash64()` docs, using consecutive seeds is a pitfall,
|
||||
// and we should instead vary our seeds by a large odd number. This value by
|
||||
// which we increment (0xD28AAD72F49BD50B) was taken from
|
||||
// `head -c8 /dev/urandom | hexdump`, run repeatedly until it yielded an odd
|
||||
// number. The values are computed manually since the Windows C++ compiler
|
||||
// complains about the overflow when adding constants.
|
||||
static const uint64_t kSeedK = 0;
|
||||
static const uint64_t kSeedV = 0xD28AAD72F49BD50B;
|
||||
static const uint64_t kSeedO = 0xA5155AE5E937AA16;
|
||||
static const uint64_t kSeedT = 0x77A00858DDD37F21;
|
||||
static const uint64_t kSeedS = 0x4A2AB5CBD26F542C;
|
||||
static const uint64_t kSeedC = 0x1CB5633EC70B2937;
|
||||
|
||||
ProtectionInfo<T>(T val) : val_(val) {
|
||||
static_assert(sizeof(ProtectionInfo<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
T GetVal() const { return val_; }
|
||||
void SetVal(T val) { val_ = val; }
|
||||
|
||||
T val_ = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOT {
|
||||
public:
|
||||
ProtectionInfoKVOT<T>() = default;
|
||||
|
||||
ProtectionInfo<T> StripKVOT(const Slice& key, const Slice& value,
|
||||
ValueType op_type, const Slice& timestamp) const;
|
||||
ProtectionInfo<T> StripKVOT(const SliceParts& key, const SliceParts& value,
|
||||
ValueType op_type, const Slice& timestamp) const;
|
||||
|
||||
ProtectionInfoKVOTC<T> ProtectC(ColumnFamilyId column_family_id) const;
|
||||
ProtectionInfoKVOTS<T> ProtectS(SequenceNumber sequence_number) const;
|
||||
|
||||
void UpdateK(const Slice& old_key, const Slice& new_key);
|
||||
void UpdateK(const SliceParts& old_key, const SliceParts& new_key);
|
||||
void UpdateV(const Slice& old_value, const Slice& new_value);
|
||||
void UpdateV(const SliceParts& old_value, const SliceParts& new_value);
|
||||
void UpdateO(ValueType old_op_type, ValueType new_op_type);
|
||||
void UpdateT(const Slice& old_timestamp, const Slice& new_timestamp);
|
||||
|
||||
private:
|
||||
friend class ProtectionInfo<T>;
|
||||
friend class ProtectionInfoKVOTS<T>;
|
||||
friend class ProtectionInfoKVOTC<T>;
|
||||
|
||||
ProtectionInfoKVOT<T>(T val) : info_(val) {
|
||||
static_assert(sizeof(ProtectionInfoKVOT<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
T GetVal() const { return info_.GetVal(); }
|
||||
void SetVal(T val) { info_.SetVal(val); }
|
||||
|
||||
ProtectionInfo<T> info_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOTC {
|
||||
public:
|
||||
ProtectionInfoKVOTC<T>() = default;
|
||||
|
||||
ProtectionInfoKVOT<T> StripC(ColumnFamilyId column_family_id) const;
|
||||
|
||||
void UpdateK(const Slice& old_key, const Slice& new_key) {
|
||||
kvot_.UpdateK(old_key, new_key);
|
||||
}
|
||||
void UpdateK(const SliceParts& old_key, const SliceParts& new_key) {
|
||||
kvot_.UpdateK(old_key, new_key);
|
||||
}
|
||||
void UpdateV(const Slice& old_value, const Slice& new_value) {
|
||||
kvot_.UpdateV(old_value, new_value);
|
||||
}
|
||||
void UpdateV(const SliceParts& old_value, const SliceParts& new_value) {
|
||||
kvot_.UpdateV(old_value, new_value);
|
||||
}
|
||||
void UpdateO(ValueType old_op_type, ValueType new_op_type) {
|
||||
kvot_.UpdateO(old_op_type, new_op_type);
|
||||
}
|
||||
void UpdateT(const Slice& old_timestamp, const Slice& new_timestamp) {
|
||||
kvot_.UpdateT(old_timestamp, new_timestamp);
|
||||
}
|
||||
void UpdateC(ColumnFamilyId old_column_family_id,
|
||||
ColumnFamilyId new_column_family_id);
|
||||
|
||||
private:
|
||||
friend class ProtectionInfoKVOT<T>;
|
||||
|
||||
ProtectionInfoKVOTC<T>(T val) : kvot_(val) {
|
||||
static_assert(sizeof(ProtectionInfoKVOTC<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
T GetVal() const { return kvot_.GetVal(); }
|
||||
void SetVal(T val) { kvot_.SetVal(val); }
|
||||
|
||||
ProtectionInfoKVOT<T> kvot_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOTS {
|
||||
public:
|
||||
ProtectionInfoKVOTS<T>() = default;
|
||||
|
||||
ProtectionInfoKVOT<T> StripS(SequenceNumber sequence_number) const;
|
||||
|
||||
void UpdateK(const Slice& old_key, const Slice& new_key) {
|
||||
kvot_.UpdateK(old_key, new_key);
|
||||
}
|
||||
void UpdateK(const SliceParts& old_key, const SliceParts& new_key) {
|
||||
kvot_.UpdateK(old_key, new_key);
|
||||
}
|
||||
void UpdateV(const Slice& old_value, const Slice& new_value) {
|
||||
kvot_.UpdateV(old_value, new_value);
|
||||
}
|
||||
void UpdateV(const SliceParts& old_value, const SliceParts& new_value) {
|
||||
kvot_.UpdateV(old_value, new_value);
|
||||
}
|
||||
void UpdateO(ValueType old_op_type, ValueType new_op_type) {
|
||||
kvot_.UpdateO(old_op_type, new_op_type);
|
||||
}
|
||||
void UpdateT(const Slice& old_timestamp, const Slice& new_timestamp) {
|
||||
kvot_.UpdateT(old_timestamp, new_timestamp);
|
||||
}
|
||||
void UpdateS(SequenceNumber old_sequence_number,
|
||||
SequenceNumber new_sequence_number);
|
||||
|
||||
private:
|
||||
friend class ProtectionInfoKVOT<T>;
|
||||
|
||||
ProtectionInfoKVOTS<T>(T val) : kvot_(val) {
|
||||
static_assert(sizeof(ProtectionInfoKVOTS<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
T GetVal() const { return kvot_.GetVal(); }
|
||||
void SetVal(T val) { kvot_.SetVal(val); }
|
||||
|
||||
ProtectionInfoKVOT<T> kvot_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Status ProtectionInfo<T>::GetStatus() const {
|
||||
if (val_ != 0) {
|
||||
return Status::Corruption("ProtectionInfo mismatch");
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfoKVOT<T> ProtectionInfo<T>::ProtectKVOT(
|
||||
const Slice& key, const Slice& value, ValueType op_type,
|
||||
const Slice& timestamp) const {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(GetSliceNPHash64(key, ProtectionInfo<T>::kSeedK));
|
||||
val =
|
||||
val ^ static_cast<T>(GetSliceNPHash64(value, ProtectionInfo<T>::kSeedV));
|
||||
val = val ^
|
||||
static_cast<T>(NPHash64(reinterpret_cast<char*>(&op_type),
|
||||
sizeof(op_type), ProtectionInfo<T>::kSeedO));
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(timestamp, ProtectionInfo<T>::kSeedT));
|
||||
return ProtectionInfoKVOT<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfoKVOT<T> ProtectionInfo<T>::ProtectKVOT(
|
||||
const SliceParts& key, const SliceParts& value, ValueType op_type,
|
||||
const Slice& timestamp) const {
|
||||
T val = GetVal();
|
||||
val = val ^
|
||||
static_cast<T>(GetSlicePartsNPHash64(key, ProtectionInfo<T>::kSeedK));
|
||||
val = val ^
|
||||
static_cast<T>(GetSlicePartsNPHash64(value, ProtectionInfo<T>::kSeedV));
|
||||
val = val ^
|
||||
static_cast<T>(NPHash64(reinterpret_cast<char*>(&op_type),
|
||||
sizeof(op_type), ProtectionInfo<T>::kSeedO));
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(timestamp, ProtectionInfo<T>::kSeedT));
|
||||
return ProtectionInfoKVOT<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOT<T>::UpdateK(const Slice& old_key,
|
||||
const Slice& new_key) {
|
||||
T val = GetVal();
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(old_key, ProtectionInfo<T>::kSeedK));
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(new_key, ProtectionInfo<T>::kSeedK));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOT<T>::UpdateK(const SliceParts& old_key,
|
||||
const SliceParts& new_key) {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(
|
||||
GetSlicePartsNPHash64(old_key, ProtectionInfo<T>::kSeedK));
|
||||
val = val ^ static_cast<T>(
|
||||
GetSlicePartsNPHash64(new_key, ProtectionInfo<T>::kSeedK));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOT<T>::UpdateV(const Slice& old_value,
|
||||
const Slice& new_value) {
|
||||
T val = GetVal();
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(old_value, ProtectionInfo<T>::kSeedV));
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(new_value, ProtectionInfo<T>::kSeedV));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOT<T>::UpdateV(const SliceParts& old_value,
|
||||
const SliceParts& new_value) {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(
|
||||
GetSlicePartsNPHash64(old_value, ProtectionInfo<T>::kSeedV));
|
||||
val = val ^ static_cast<T>(
|
||||
GetSlicePartsNPHash64(new_value, ProtectionInfo<T>::kSeedV));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOT<T>::UpdateO(ValueType old_op_type,
|
||||
ValueType new_op_type) {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(reinterpret_cast<char*>(&old_op_type),
|
||||
sizeof(old_op_type),
|
||||
ProtectionInfo<T>::kSeedO));
|
||||
val = val ^ static_cast<T>(NPHash64(reinterpret_cast<char*>(&new_op_type),
|
||||
sizeof(new_op_type),
|
||||
ProtectionInfo<T>::kSeedO));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOT<T>::UpdateT(const Slice& old_timestamp,
|
||||
const Slice& new_timestamp) {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(
|
||||
GetSliceNPHash64(old_timestamp, ProtectionInfo<T>::kSeedT));
|
||||
val = val ^ static_cast<T>(
|
||||
GetSliceNPHash64(new_timestamp, ProtectionInfo<T>::kSeedT));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfo<T> ProtectionInfoKVOT<T>::StripKVOT(
|
||||
const Slice& key, const Slice& value, ValueType op_type,
|
||||
const Slice& timestamp) const {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(GetSliceNPHash64(key, ProtectionInfo<T>::kSeedK));
|
||||
val =
|
||||
val ^ static_cast<T>(GetSliceNPHash64(value, ProtectionInfo<T>::kSeedV));
|
||||
val = val ^
|
||||
static_cast<T>(NPHash64(reinterpret_cast<char*>(&op_type),
|
||||
sizeof(op_type), ProtectionInfo<T>::kSeedO));
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(timestamp, ProtectionInfo<T>::kSeedT));
|
||||
return ProtectionInfo<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfo<T> ProtectionInfoKVOT<T>::StripKVOT(
|
||||
const SliceParts& key, const SliceParts& value, ValueType op_type,
|
||||
const Slice& timestamp) const {
|
||||
T val = GetVal();
|
||||
val = val ^
|
||||
static_cast<T>(GetSlicePartsNPHash64(key, ProtectionInfo<T>::kSeedK));
|
||||
val = val ^
|
||||
static_cast<T>(GetSlicePartsNPHash64(value, ProtectionInfo<T>::kSeedV));
|
||||
val = val ^
|
||||
static_cast<T>(NPHash64(reinterpret_cast<char*>(&op_type),
|
||||
sizeof(op_type), ProtectionInfo<T>::kSeedO));
|
||||
val = val ^
|
||||
static_cast<T>(GetSliceNPHash64(timestamp, ProtectionInfo<T>::kSeedT));
|
||||
return ProtectionInfo<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfoKVOTC<T> ProtectionInfoKVOT<T>::ProtectC(
|
||||
ColumnFamilyId column_family_id) const {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(
|
||||
reinterpret_cast<char*>(&column_family_id),
|
||||
sizeof(column_family_id), ProtectionInfo<T>::kSeedC));
|
||||
return ProtectionInfoKVOTC<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfoKVOT<T> ProtectionInfoKVOTC<T>::StripC(
|
||||
ColumnFamilyId column_family_id) const {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(
|
||||
reinterpret_cast<char*>(&column_family_id),
|
||||
sizeof(column_family_id), ProtectionInfo<T>::kSeedC));
|
||||
return ProtectionInfoKVOT<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOTC<T>::UpdateC(ColumnFamilyId old_column_family_id,
|
||||
ColumnFamilyId new_column_family_id) {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(
|
||||
reinterpret_cast<char*>(&old_column_family_id),
|
||||
sizeof(old_column_family_id), ProtectionInfo<T>::kSeedC));
|
||||
val = val ^ static_cast<T>(NPHash64(
|
||||
reinterpret_cast<char*>(&new_column_family_id),
|
||||
sizeof(new_column_family_id), ProtectionInfo<T>::kSeedC));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfoKVOTS<T> ProtectionInfoKVOT<T>::ProtectS(
|
||||
SequenceNumber sequence_number) const {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(reinterpret_cast<char*>(&sequence_number),
|
||||
sizeof(sequence_number),
|
||||
ProtectionInfo<T>::kSeedS));
|
||||
return ProtectionInfoKVOTS<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ProtectionInfoKVOT<T> ProtectionInfoKVOTS<T>::StripS(
|
||||
SequenceNumber sequence_number) const {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(reinterpret_cast<char*>(&sequence_number),
|
||||
sizeof(sequence_number),
|
||||
ProtectionInfo<T>::kSeedS));
|
||||
return ProtectionInfoKVOT<T>(val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ProtectionInfoKVOTS<T>::UpdateS(SequenceNumber old_sequence_number,
|
||||
SequenceNumber new_sequence_number) {
|
||||
T val = GetVal();
|
||||
val = val ^ static_cast<T>(NPHash64(
|
||||
reinterpret_cast<char*>(&old_sequence_number),
|
||||
sizeof(old_sequence_number), ProtectionInfo<T>::kSeedS));
|
||||
val = val ^ static_cast<T>(NPHash64(
|
||||
reinterpret_cast<char*>(&new_sequence_number),
|
||||
sizeof(new_sequence_number), ProtectionInfo<T>::kSeedS));
|
||||
SetVal(val);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+61
-88
@@ -9,7 +9,6 @@
|
||||
|
||||
#include "db/log_reader.h"
|
||||
#include "db/log_writer.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/sequence_file_reader.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/env.h"
|
||||
@@ -50,7 +49,7 @@ static std::string RandomSkewedString(int i, Random* rnd) {
|
||||
// get<1>(tuple): true if allow retry after read EOF, false otherwise
|
||||
class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
private:
|
||||
class StringSource : public SequentialFile {
|
||||
class StringSource : public FSSequentialFile {
|
||||
public:
|
||||
Slice& contents_;
|
||||
bool force_error_;
|
||||
@@ -68,7 +67,8 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
returned_partial_(false),
|
||||
fail_after_read_partial_(fail_after_read_partial) {}
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
IOStatus Read(size_t n, const IOOptions& /*opts*/, Slice* result,
|
||||
char* scratch, IODebugContext* /*dbg*/) override {
|
||||
if (fail_after_read_partial_) {
|
||||
EXPECT_TRUE(!returned_partial_) << "must not Read() after eof/error";
|
||||
}
|
||||
@@ -81,7 +81,7 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
contents_.remove_prefix(force_error_position_);
|
||||
force_error_ = false;
|
||||
returned_partial_ = true;
|
||||
return Status::Corruption("read error");
|
||||
return IOStatus::Corruption("read error");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,28 +106,21 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
*result = Slice(scratch, n);
|
||||
|
||||
contents_.remove_prefix(n);
|
||||
return Status::OK();
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
Status Skip(uint64_t n) override {
|
||||
IOStatus Skip(uint64_t n) override {
|
||||
if (n > contents_.size()) {
|
||||
contents_.clear();
|
||||
return Status::NotFound("in-memory file skipepd past end");
|
||||
return IOStatus::NotFound("in-memory file skipepd past end");
|
||||
}
|
||||
|
||||
contents_.remove_prefix(n);
|
||||
|
||||
return Status::OK();
|
||||
return IOStatus::OK();
|
||||
}
|
||||
};
|
||||
|
||||
inline StringSource* GetStringSourceFromLegacyReader(
|
||||
SequentialFileReader* reader) {
|
||||
LegacySequentialFileWrapper* file =
|
||||
static_cast<LegacySequentialFileWrapper*>(reader->file());
|
||||
return static_cast<StringSource*>(file->target());
|
||||
}
|
||||
|
||||
class ReportCollector : public Reader::Reporter {
|
||||
public:
|
||||
size_t dropped_bytes_;
|
||||
@@ -140,29 +133,17 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
}
|
||||
};
|
||||
|
||||
std::string& dest_contents() {
|
||||
auto dest = test::GetStringSinkFromLegacyWriter(writer_.file());
|
||||
assert(dest);
|
||||
return dest->contents_;
|
||||
}
|
||||
std::string& dest_contents() { return sink_->contents_; }
|
||||
|
||||
const std::string& dest_contents() const {
|
||||
auto dest = test::GetStringSinkFromLegacyWriter(writer_.file());
|
||||
assert(dest);
|
||||
return dest->contents_;
|
||||
}
|
||||
const std::string& dest_contents() const { return sink_->contents_; }
|
||||
|
||||
void reset_source_contents() {
|
||||
auto src = GetStringSourceFromLegacyReader(reader_->file());
|
||||
assert(src);
|
||||
src->contents_ = dest_contents();
|
||||
}
|
||||
void reset_source_contents() { source_->contents_ = dest_contents(); }
|
||||
|
||||
Slice reader_contents_;
|
||||
std::unique_ptr<WritableFileWriter> dest_holder_;
|
||||
std::unique_ptr<SequentialFileReader> source_holder_;
|
||||
test::StringSink* sink_;
|
||||
StringSource* source_;
|
||||
ReportCollector report_;
|
||||
Writer writer_;
|
||||
std::unique_ptr<Writer> writer_;
|
||||
std::unique_ptr<Reader> reader_;
|
||||
|
||||
protected:
|
||||
@@ -171,19 +152,23 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
public:
|
||||
LogTest()
|
||||
: reader_contents_(),
|
||||
dest_holder_(test::GetWritableFileWriter(
|
||||
new test::StringSink(&reader_contents_), "" /* don't care */)),
|
||||
source_holder_(test::GetSequentialFileReader(
|
||||
new StringSource(reader_contents_, !std::get<1>(GetParam())),
|
||||
"" /* file name */)),
|
||||
writer_(std::move(dest_holder_), 123, std::get<0>(GetParam())),
|
||||
sink_(new test::StringSink(&reader_contents_)),
|
||||
source_(new StringSource(reader_contents_, !std::get<1>(GetParam()))),
|
||||
allow_retry_read_(std::get<1>(GetParam())) {
|
||||
std::unique_ptr<FSWritableFile> sink_holder(sink_);
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(sink_holder), "" /* don't care */, FileOptions()));
|
||||
writer_.reset(
|
||||
new Writer(std::move(file_writer), 123, std::get<0>(GetParam())));
|
||||
std::unique_ptr<FSSequentialFile> source_holder(source_);
|
||||
std::unique_ptr<SequentialFileReader> file_reader(
|
||||
new SequentialFileReader(std::move(source_holder), "" /* file name */));
|
||||
if (allow_retry_read_) {
|
||||
reader_.reset(new FragmentBufferedReader(
|
||||
nullptr, std::move(source_holder_), &report_, true /* checksum */,
|
||||
123 /* log_number */));
|
||||
reader_.reset(new FragmentBufferedReader(nullptr, std::move(file_reader),
|
||||
&report_, true /* checksum */,
|
||||
123 /* log_number */));
|
||||
} else {
|
||||
reader_.reset(new Reader(nullptr, std::move(source_holder_), &report_,
|
||||
reader_.reset(new Reader(nullptr, std::move(file_reader), &report_,
|
||||
true /* checksum */, 123 /* log_number */));
|
||||
}
|
||||
}
|
||||
@@ -191,7 +176,7 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
Slice* get_reader_contents() { return &reader_contents_; }
|
||||
|
||||
void Write(const std::string& msg) {
|
||||
ASSERT_OK(writer_.AddRecord(Slice(msg)));
|
||||
ASSERT_OK(writer_->AddRecord(Slice(msg)));
|
||||
}
|
||||
|
||||
size_t WrittenBytes() const {
|
||||
@@ -219,11 +204,7 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
dest_contents()[offset] = new_byte;
|
||||
}
|
||||
|
||||
void ShrinkSize(int bytes) {
|
||||
auto dest = test::GetStringSinkFromLegacyWriter(writer_.file());
|
||||
assert(dest);
|
||||
dest->Drop(bytes);
|
||||
}
|
||||
void ShrinkSize(int bytes) { sink_->Drop(bytes); }
|
||||
|
||||
void FixChecksum(int header_offset, int len, bool recyclable) {
|
||||
// Compute crc of type/len/data
|
||||
@@ -235,9 +216,8 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
}
|
||||
|
||||
void ForceError(size_t position = 0) {
|
||||
auto src = GetStringSourceFromLegacyReader(reader_->file());
|
||||
src->force_error_ = true;
|
||||
src->force_error_position_ = position;
|
||||
source_->force_error_ = true;
|
||||
source_->force_error_position_ = position;
|
||||
}
|
||||
|
||||
size_t DroppedBytes() const {
|
||||
@@ -249,14 +229,12 @@ class LogTest : public ::testing::TestWithParam<std::tuple<int, bool>> {
|
||||
}
|
||||
|
||||
void ForceEOF(size_t position = 0) {
|
||||
auto src = GetStringSourceFromLegacyReader(reader_->file());
|
||||
src->force_eof_ = true;
|
||||
src->force_eof_position_ = position;
|
||||
source_->force_eof_ = true;
|
||||
source_->force_eof_position_ = position;
|
||||
}
|
||||
|
||||
void UnmarkEOF() {
|
||||
auto src = GetStringSourceFromLegacyReader(reader_->file());
|
||||
src->returned_partial_ = false;
|
||||
source_->returned_partial_ = false;
|
||||
reader_->UnmarkEOF();
|
||||
}
|
||||
|
||||
@@ -685,9 +663,10 @@ TEST_P(LogTest, Recycle) {
|
||||
while (get_reader_contents()->size() < log::kBlockSize * 2) {
|
||||
Write("xxxxxxxxxxxxxxxx");
|
||||
}
|
||||
std::unique_ptr<WritableFileWriter> dest_holder(test::GetWritableFileWriter(
|
||||
new test::OverwritingStringSink(get_reader_contents()),
|
||||
"" /* don't care */));
|
||||
std::unique_ptr<FSWritableFile> sink(
|
||||
new test::OverwritingStringSink(get_reader_contents()));
|
||||
std::unique_ptr<WritableFileWriter> dest_holder(new WritableFileWriter(
|
||||
std::move(sink), "" /* don't care */, FileOptions()));
|
||||
Writer recycle_writer(std::move(dest_holder), 123, true);
|
||||
ASSERT_OK(recycle_writer.AddRecord(Slice("foooo")));
|
||||
ASSERT_OK(recycle_writer.AddRecord(Slice("bar")));
|
||||
@@ -718,10 +697,9 @@ class RetriableLogTest : public ::testing::TestWithParam<int> {
|
||||
};
|
||||
|
||||
Slice contents_;
|
||||
std::unique_ptr<WritableFileWriter> dest_holder_;
|
||||
test::StringSink* sink_;
|
||||
std::unique_ptr<Writer> log_writer_;
|
||||
Env* env_;
|
||||
EnvOptions env_options_;
|
||||
const std::string test_dir_;
|
||||
const std::string log_file_;
|
||||
std::unique_ptr<WritableFileWriter> writer_;
|
||||
@@ -732,55 +710,50 @@ class RetriableLogTest : public ::testing::TestWithParam<int> {
|
||||
public:
|
||||
RetriableLogTest()
|
||||
: contents_(),
|
||||
dest_holder_(nullptr),
|
||||
sink_(new test::StringSink(&contents_)),
|
||||
log_writer_(nullptr),
|
||||
env_(Env::Default()),
|
||||
test_dir_(test::PerThreadDBPath("retriable_log_test")),
|
||||
log_file_(test_dir_ + "/log"),
|
||||
writer_(nullptr),
|
||||
reader_(nullptr),
|
||||
log_reader_(nullptr) {}
|
||||
log_reader_(nullptr) {
|
||||
std::unique_ptr<FSWritableFile> sink_holder(sink_);
|
||||
std::unique_ptr<WritableFileWriter> wfw(new WritableFileWriter(
|
||||
std::move(sink_holder), "" /* file name */, FileOptions()));
|
||||
log_writer_.reset(new Writer(std::move(wfw), 123, GetParam()));
|
||||
}
|
||||
|
||||
Status SetupTestEnv() {
|
||||
dest_holder_.reset(test::GetWritableFileWriter(
|
||||
new test::StringSink(&contents_), "" /* file name */));
|
||||
assert(dest_holder_ != nullptr);
|
||||
log_writer_.reset(new Writer(std::move(dest_holder_), 123, GetParam()));
|
||||
assert(log_writer_ != nullptr);
|
||||
|
||||
Status s;
|
||||
s = env_->CreateDirIfMissing(test_dir_);
|
||||
std::unique_ptr<WritableFile> writable_file;
|
||||
FileOptions fopts;
|
||||
auto fs = env_->GetFileSystem();
|
||||
s = fs->CreateDirIfMissing(test_dir_, IOOptions(), nullptr);
|
||||
std::unique_ptr<FSWritableFile> writable_file;
|
||||
if (s.ok()) {
|
||||
s = env_->NewWritableFile(log_file_, &writable_file, env_options_);
|
||||
s = fs->NewWritableFile(log_file_, fopts, &writable_file, nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
writer_.reset(new WritableFileWriter(
|
||||
NewLegacyWritableFileWrapper(std::move(writable_file)), log_file_,
|
||||
env_options_));
|
||||
assert(writer_ != nullptr);
|
||||
writer_.reset(
|
||||
new WritableFileWriter(std::move(writable_file), log_file_, fopts));
|
||||
EXPECT_NE(writer_, nullptr);
|
||||
}
|
||||
std::unique_ptr<SequentialFile> seq_file;
|
||||
std::unique_ptr<FSSequentialFile> seq_file;
|
||||
if (s.ok()) {
|
||||
s = env_->NewSequentialFile(log_file_, &seq_file, env_options_);
|
||||
s = fs->NewSequentialFile(log_file_, fopts, &seq_file, nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
reader_.reset(new SequentialFileReader(
|
||||
NewLegacySequentialFileWrapper(seq_file), log_file_));
|
||||
assert(reader_ != nullptr);
|
||||
reader_.reset(new SequentialFileReader(std::move(seq_file), log_file_));
|
||||
EXPECT_NE(reader_, nullptr);
|
||||
log_reader_.reset(new FragmentBufferedReader(
|
||||
nullptr, std::move(reader_), &report_, true /* checksum */,
|
||||
123 /* log_number */));
|
||||
assert(log_reader_ != nullptr);
|
||||
EXPECT_NE(log_reader_, nullptr);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string contents() {
|
||||
auto file = test::GetStringSinkFromLegacyWriter(log_writer_->file());
|
||||
assert(file != nullptr);
|
||||
return file->contents_;
|
||||
}
|
||||
std::string contents() { return sink_->contents_; }
|
||||
|
||||
void Encode(const std::string& msg) {
|
||||
ASSERT_OK(log_writer_->AddRecord(Slice(msg)));
|
||||
|
||||
+90
-7
@@ -13,7 +13,9 @@
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/kv_checksum.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/merge_helper.h"
|
||||
#include "db/pinned_iterators_manager.h"
|
||||
@@ -484,9 +486,54 @@ MemTable::MemTableStats MemTable::ApproximateStats(const Slice& start_ikey,
|
||||
return {entry_count * (data_size / n), entry_count};
|
||||
}
|
||||
|
||||
Status MemTable::VerifyEncodedEntry(Slice encoded,
|
||||
const ProtectionInfoKVOTS64& kv_prot_info) {
|
||||
uint32_t ikey_len = 0;
|
||||
if (!GetVarint32(&encoded, &ikey_len)) {
|
||||
return Status::Corruption("Unable to parse internal key length");
|
||||
}
|
||||
size_t ts_sz = GetInternalKeyComparator().user_comparator()->timestamp_size();
|
||||
if (ikey_len < 8 + ts_sz) {
|
||||
return Status::Corruption("Internal key length too short");
|
||||
}
|
||||
if (ikey_len > encoded.size()) {
|
||||
return Status::Corruption("Internal key length too long");
|
||||
}
|
||||
uint32_t value_len = 0;
|
||||
const size_t key_without_ts_len = ikey_len - ts_sz - 8;
|
||||
Slice key(encoded.data(), key_without_ts_len);
|
||||
encoded.remove_prefix(key_without_ts_len);
|
||||
|
||||
Slice timestamp(encoded.data(), ts_sz);
|
||||
encoded.remove_prefix(ts_sz);
|
||||
|
||||
uint64_t packed = DecodeFixed64(encoded.data());
|
||||
ValueType value_type = kMaxValue;
|
||||
SequenceNumber sequence_number = kMaxSequenceNumber;
|
||||
UnPackSequenceAndType(packed, &sequence_number, &value_type);
|
||||
encoded.remove_prefix(8);
|
||||
|
||||
if (!GetVarint32(&encoded, &value_len)) {
|
||||
return Status::Corruption("Unable to parse value length");
|
||||
}
|
||||
if (value_len < encoded.size()) {
|
||||
return Status::Corruption("Value length too short");
|
||||
}
|
||||
if (value_len > encoded.size()) {
|
||||
return Status::Corruption("Value length too long");
|
||||
}
|
||||
Slice value(encoded.data(), value_len);
|
||||
|
||||
return kv_prot_info.StripS(sequence_number)
|
||||
.StripKVOT(key, value, value_type, timestamp)
|
||||
.GetStatus();
|
||||
}
|
||||
|
||||
Status MemTable::Add(SequenceNumber s, ValueType type,
|
||||
const Slice& key, /* user key */
|
||||
const Slice& value, bool allow_concurrent,
|
||||
const Slice& value,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info,
|
||||
bool allow_concurrent,
|
||||
MemTablePostProcessInfo* post_process_info, void** hint) {
|
||||
// Format of an entry is concatenation of:
|
||||
// key_size : varint32 of internal_key.size()
|
||||
@@ -514,6 +561,15 @@ Status MemTable::Add(SequenceNumber s, ValueType type,
|
||||
p = EncodeVarint32(p, val_size);
|
||||
memcpy(p, value.data(), val_size);
|
||||
assert((unsigned)(p + val_size - buf) == (unsigned)encoded_len);
|
||||
if (kv_prot_info != nullptr) {
|
||||
Slice encoded(buf, encoded_len);
|
||||
TEST_SYNC_POINT_CALLBACK("MemTable::Add:Encoded", &encoded);
|
||||
Status status = VerifyEncodedEntry(encoded, *kv_prot_info);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ts_sz = GetInternalKeyComparator().user_comparator()->timestamp_size();
|
||||
Slice key_without_ts = StripTimestampFromUserKey(key, ts_sz);
|
||||
|
||||
@@ -978,7 +1034,8 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
|
||||
Status MemTable::Update(SequenceNumber seq, const Slice& key,
|
||||
const Slice& value) {
|
||||
const Slice& value,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info) {
|
||||
LookupKey lkey(key, seq);
|
||||
Slice mem_key = lkey.memtable_key();
|
||||
|
||||
@@ -1022,6 +1079,13 @@ Status MemTable::Update(SequenceNumber seq, const Slice& key,
|
||||
(unsigned)(VarintLength(key_length) + key_length +
|
||||
VarintLength(value.size()) + value.size()));
|
||||
RecordTick(moptions_.statistics, NUMBER_KEYS_UPDATED);
|
||||
if (kv_prot_info != nullptr) {
|
||||
ProtectionInfoKVOTS64 updated_kv_prot_info(*kv_prot_info);
|
||||
// `seq` is swallowed and `existing_seq` prevails.
|
||||
updated_kv_prot_info.UpdateS(seq, existing_seq);
|
||||
Slice encoded(entry, p + value.size() - entry);
|
||||
return VerifyEncodedEntry(encoded, updated_kv_prot_info);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
@@ -1029,11 +1093,12 @@ Status MemTable::Update(SequenceNumber seq, const Slice& key,
|
||||
}
|
||||
|
||||
// The latest value is not `kTypeValue` or key doesn't exist
|
||||
return Add(seq, kTypeValue, key, value);
|
||||
return Add(seq, kTypeValue, key, value, kv_prot_info);
|
||||
}
|
||||
|
||||
Status MemTable::UpdateCallback(SequenceNumber seq, const Slice& key,
|
||||
const Slice& delta) {
|
||||
const Slice& delta,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info) {
|
||||
LookupKey lkey(key, seq);
|
||||
Slice memkey = lkey.memtable_key();
|
||||
|
||||
@@ -1059,8 +1124,8 @@ Status MemTable::UpdateCallback(SequenceNumber seq, const Slice& key,
|
||||
// Correct user key
|
||||
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
|
||||
ValueType type;
|
||||
uint64_t unused;
|
||||
UnPackSequenceAndType(tag, &unused, &type);
|
||||
uint64_t existing_seq;
|
||||
UnPackSequenceAndType(tag, &existing_seq, &type);
|
||||
switch (type) {
|
||||
case kTypeValue: {
|
||||
Slice prev_value = GetLengthPrefixedSlice(key_ptr + key_length);
|
||||
@@ -1087,9 +1152,27 @@ Status MemTable::UpdateCallback(SequenceNumber seq, const Slice& key,
|
||||
}
|
||||
RecordTick(moptions_.statistics, NUMBER_KEYS_UPDATED);
|
||||
UpdateFlushState();
|
||||
if (kv_prot_info != nullptr) {
|
||||
ProtectionInfoKVOTS64 updated_kv_prot_info(*kv_prot_info);
|
||||
// `seq` is swallowed and `existing_seq` prevails.
|
||||
updated_kv_prot_info.UpdateS(seq, existing_seq);
|
||||
updated_kv_prot_info.UpdateV(delta,
|
||||
Slice(prev_buffer, new_prev_size));
|
||||
Slice encoded(entry, prev_buffer + new_prev_size - entry);
|
||||
return VerifyEncodedEntry(encoded, updated_kv_prot_info);
|
||||
}
|
||||
return Status::OK();
|
||||
} else if (status == UpdateStatus::UPDATED) {
|
||||
Status s = Add(seq, kTypeValue, key, Slice(str_value));
|
||||
Status s;
|
||||
if (kv_prot_info != nullptr) {
|
||||
ProtectionInfoKVOTS64 updated_kv_prot_info(*kv_prot_info);
|
||||
updated_kv_prot_info.UpdateV(delta, str_value);
|
||||
s = Add(seq, kTypeValue, key, Slice(str_value),
|
||||
&updated_kv_prot_info);
|
||||
} else {
|
||||
s = Add(seq, kTypeValue, key, Slice(str_value),
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
RecordTick(moptions_.statistics, NUMBER_KEYS_WRITTEN);
|
||||
UpdateFlushState();
|
||||
return s;
|
||||
|
||||
+11
-3
@@ -15,7 +15,9 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/kv_checksum.h"
|
||||
#include "db/range_tombstone_fragmenter.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "db/version_edit.h"
|
||||
@@ -175,6 +177,9 @@ class MemTable {
|
||||
FragmentedRangeTombstoneIterator* NewRangeTombstoneIterator(
|
||||
const ReadOptions& read_options, SequenceNumber read_seq);
|
||||
|
||||
Status VerifyEncodedEntry(Slice encoded,
|
||||
const ProtectionInfoKVOTS64& kv_prot_info);
|
||||
|
||||
// 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.
|
||||
@@ -186,7 +191,8 @@ class MemTable {
|
||||
// in the memtable and `MemTableRepFactory::CanHandleDuplicatedKey()` is true.
|
||||
// The next attempt should try a larger value for `seq`.
|
||||
Status Add(SequenceNumber seq, ValueType type, const Slice& key,
|
||||
const Slice& value, bool allow_concurrent = false,
|
||||
const Slice& value, const ProtectionInfoKVOTS64* kv_prot_info,
|
||||
bool allow_concurrent = false,
|
||||
MemTablePostProcessInfo* post_process_info = nullptr,
|
||||
void** hint = nullptr);
|
||||
|
||||
@@ -250,7 +256,8 @@ class MemTable {
|
||||
//
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
Status Update(SequenceNumber seq, const Slice& key, const Slice& value);
|
||||
Status Update(SequenceNumber seq, const Slice& key, const Slice& value,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info);
|
||||
|
||||
// If `key` exists in current memtable with type `kTypeValue` and the existing
|
||||
// value is at least as large as the new value, updates it in-place. Otherwise
|
||||
@@ -267,7 +274,8 @@ class MemTable {
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable.
|
||||
Status UpdateCallback(SequenceNumber seq, const Slice& key,
|
||||
const Slice& delta);
|
||||
const Slice& delta,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info);
|
||||
|
||||
// Returns the number of successive merge entries starting from the newest
|
||||
// entry for the key up to the last non-merge entry or last entry for the
|
||||
|
||||
+42
-21
@@ -243,10 +243,14 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
mem->Ref();
|
||||
|
||||
// Write some keys to this memtable.
|
||||
ASSERT_OK(mem->Add(++seq, kTypeDeletion, "key1", ""));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key1", "value1"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2.2"));
|
||||
ASSERT_OK(
|
||||
mem->Add(++seq, kTypeDeletion, "key1", "", nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key1", "value1",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2.2",
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
// Fetch the newly written keys
|
||||
merge_context.Clear();
|
||||
@@ -284,8 +288,10 @@ TEST_F(MemTableListTest, GetTest) {
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
mem2->Ref();
|
||||
|
||||
ASSERT_OK(mem2->Add(++seq, kTypeDeletion, "key1", ""));
|
||||
ASSERT_OK(mem2->Add(++seq, kTypeValue, "key2", "value2.3"));
|
||||
ASSERT_OK(
|
||||
mem2->Add(++seq, kTypeDeletion, "key1", "", nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem2->Add(++seq, kTypeValue, "key2", "value2.3",
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
// Add second memtable to list
|
||||
list.Add(mem2, &to_delete);
|
||||
@@ -360,9 +366,12 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
mem->Ref();
|
||||
|
||||
// Write some keys to this memtable.
|
||||
ASSERT_OK(mem->Add(++seq, kTypeDeletion, "key1", ""));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2.2"));
|
||||
ASSERT_OK(
|
||||
mem->Add(++seq, kTypeDeletion, "key1", "", nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key2", "value2.2",
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
// Fetch the newly written keys
|
||||
merge_context.Clear();
|
||||
@@ -444,8 +453,10 @@ TEST_F(MemTableListTest, GetFromHistoryTest) {
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
mem2->Ref();
|
||||
|
||||
ASSERT_OK(mem2->Add(++seq, kTypeDeletion, "key1", ""));
|
||||
ASSERT_OK(mem2->Add(++seq, kTypeValue, "key3", "value3"));
|
||||
ASSERT_OK(
|
||||
mem2->Add(++seq, kTypeDeletion, "key1", "", nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem2->Add(++seq, kTypeValue, "key3", "value3",
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
// Add second memtable to list
|
||||
list.Add(mem2, &to_delete);
|
||||
@@ -555,11 +566,16 @@ TEST_F(MemTableListTest, FlushPendingTest) {
|
||||
std::string value;
|
||||
MergeContext merge_context;
|
||||
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key1", ToString(i)));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), ""));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key1", ToString(i),
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), "",
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
tables.push_back(mem);
|
||||
}
|
||||
@@ -824,11 +840,16 @@ TEST_F(MemTableListTest, AtomicFlusTest) {
|
||||
|
||||
std::string value;
|
||||
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key1", ToString(i)));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM"));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), ""));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "key1", ToString(i),
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM",
|
||||
nullptr /* kv_prot_info */));
|
||||
ASSERT_OK(mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), "",
|
||||
nullptr /* kv_prot_info */));
|
||||
|
||||
elem.push_back(mem);
|
||||
}
|
||||
|
||||
+3
-2
@@ -183,7 +183,6 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
|
||||
assert(IsValueType(ikey.type));
|
||||
if (ikey.type != kTypeMerge) {
|
||||
|
||||
// hit a put/delete/single delete
|
||||
// => merge the put value or a nullptr with operands_
|
||||
// => store result in operands_.back() (and update keys_.back())
|
||||
@@ -414,7 +413,9 @@ CompactionFilter::Decision MergeHelper::FilterMerge(const Slice& user_key,
|
||||
kValueTypeForSeek);
|
||||
}
|
||||
}
|
||||
total_filter_time_ += filter_timer_.ElapsedNanosSafe();
|
||||
if (stats_ != nullptr && ShouldReportDetailedTime(env_, stats_)) {
|
||||
total_filter_time_ += filter_timer_.ElapsedNanosSafe();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+33
-19
@@ -49,12 +49,8 @@ class CountMergeOperator : public AssociativeMergeOperator {
|
||||
return true;
|
||||
}
|
||||
|
||||
return mergeOperator_->PartialMerge(
|
||||
key,
|
||||
*existing_value,
|
||||
value,
|
||||
new_value,
|
||||
logger);
|
||||
return mergeOperator_->PartialMerge(key, *existing_value, value, new_value,
|
||||
logger);
|
||||
}
|
||||
|
||||
bool PartialMergeMulti(const Slice& key,
|
||||
@@ -73,6 +69,29 @@ class CountMergeOperator : public AssociativeMergeOperator {
|
||||
std::shared_ptr<MergeOperator> mergeOperator_;
|
||||
};
|
||||
|
||||
class EnvMergeTest : public EnvWrapper {
|
||||
public:
|
||||
EnvMergeTest() : EnvWrapper(Env::Default()) {}
|
||||
// ~EnvMergeTest() override {}
|
||||
|
||||
uint64_t NowNanos() override {
|
||||
++now_nanos_count_;
|
||||
return target()->NowNanos();
|
||||
}
|
||||
|
||||
static uint64_t now_nanos_count_;
|
||||
|
||||
static std::unique_ptr<EnvMergeTest> singleton_;
|
||||
|
||||
static EnvMergeTest* GetInstance() {
|
||||
if (nullptr == singleton_) singleton_.reset(new EnvMergeTest);
|
||||
return singleton_.get();
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t EnvMergeTest::now_nanos_count_{0};
|
||||
std::unique_ptr<EnvMergeTest> EnvMergeTest::singleton_;
|
||||
|
||||
std::shared_ptr<DB> OpenDb(const std::string& dbname, const bool ttl = false,
|
||||
const size_t max_successive_merges = 0) {
|
||||
DB* db;
|
||||
@@ -80,6 +99,7 @@ std::shared_ptr<DB> OpenDb(const std::string& dbname, const bool ttl = false,
|
||||
options.create_if_missing = true;
|
||||
options.merge_operator = std::make_shared<CountMergeOperator>();
|
||||
options.max_successive_merges = max_successive_merges;
|
||||
options.env = EnvMergeTest::GetInstance();
|
||||
EXPECT_OK(DestroyDB(dbname, Options()));
|
||||
Status s;
|
||||
// DBWithTTL is not supported in ROCKSDB_LITE
|
||||
@@ -106,7 +126,6 @@ std::shared_ptr<DB> OpenDb(const std::string& dbname, const bool ttl = false,
|
||||
// set, add, get and remove
|
||||
// This is a quick implementation without a Merge operation.
|
||||
class Counters {
|
||||
|
||||
protected:
|
||||
std::shared_ptr<DB> db_;
|
||||
|
||||
@@ -190,7 +209,6 @@ class Counters {
|
||||
return get(key, &base) && set(key, base + value);
|
||||
}
|
||||
|
||||
|
||||
// convenience functions for testing
|
||||
void assert_set(const std::string& key, uint64_t value) {
|
||||
assert(set(key, value));
|
||||
@@ -202,27 +220,25 @@ class Counters {
|
||||
uint64_t value = default_;
|
||||
int result = get(key, &value);
|
||||
assert(result);
|
||||
if (result == 0) exit(1); // Disable unused variable warning.
|
||||
if (result == 0) exit(1); // Disable unused variable warning.
|
||||
return value;
|
||||
}
|
||||
|
||||
void assert_add(const std::string& key, uint64_t value) {
|
||||
int result = add(key, value);
|
||||
assert(result);
|
||||
if (result == 0) exit(1); // Disable unused variable warning.
|
||||
if (result == 0) exit(1); // Disable unused variable warning.
|
||||
}
|
||||
};
|
||||
|
||||
// Implement 'add' directly with the new Merge operation
|
||||
class MergeBasedCounters : public Counters {
|
||||
private:
|
||||
WriteOptions merge_option_; // for merge
|
||||
WriteOptions merge_option_; // for merge
|
||||
|
||||
public:
|
||||
explicit MergeBasedCounters(std::shared_ptr<DB> db, uint64_t defaultCount = 0)
|
||||
: Counters(db, defaultCount),
|
||||
merge_option_() {
|
||||
}
|
||||
: Counters(db, defaultCount), merge_option_() {}
|
||||
|
||||
// mapped to a rocksdb Merge operation
|
||||
bool add(const std::string& key, uint64_t value) override {
|
||||
@@ -243,14 +259,13 @@ class MergeBasedCounters : public Counters {
|
||||
void dumpDb(DB* db) {
|
||||
auto it = std::unique_ptr<Iterator>(db->NewIterator(ReadOptions()));
|
||||
for (it->SeekToFirst(); it->Valid(); it->Next()) {
|
||||
//uint64_t value = DecodeFixed64(it->value().data());
|
||||
//std::cout << it->key().ToString() << ": " << value << std::endl;
|
||||
// uint64_t value = DecodeFixed64(it->value().data());
|
||||
// std::cout << it->key().ToString() << ": " << value << std::endl;
|
||||
}
|
||||
assert(it->status().ok()); // Check for any errors found during the scan
|
||||
}
|
||||
|
||||
void testCounters(Counters& counters, DB* db, bool test_compaction) {
|
||||
|
||||
FlushOptions o;
|
||||
o.wait = true;
|
||||
|
||||
@@ -392,7 +407,6 @@ void testCountersWithFlushAndCompaction(Counters& counters, DB* db) {
|
||||
|
||||
void testSuccessiveMerge(Counters& counters, size_t max_num_merges,
|
||||
size_t num_merges) {
|
||||
|
||||
counters.assert_remove("z");
|
||||
uint64_t sum = 0;
|
||||
|
||||
@@ -449,6 +463,7 @@ void testPartialMerge(Counters* counters, DB* db, size_t max_merge,
|
||||
ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_EQ(tmp_sum, counters->assert_get("c"));
|
||||
ASSERT_EQ(num_partial_merge_calls, 0U);
|
||||
ASSERT_EQ(EnvMergeTest::now_nanos_count_, 0U);
|
||||
}
|
||||
|
||||
void testSingleBatchSuccessiveMerge(DB* db, size_t max_num_merges,
|
||||
@@ -486,7 +501,6 @@ void testSingleBatchSuccessiveMerge(DB* db, size_t max_num_merges,
|
||||
}
|
||||
|
||||
void runTest(const std::string& dbname, const bool use_ttl = false) {
|
||||
|
||||
{
|
||||
auto db = OpenDb(dbname, use_ttl);
|
||||
|
||||
|
||||
@@ -10,13 +10,14 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
PeriodicWorkScheduler::PeriodicWorkScheduler(Env* env) {
|
||||
PeriodicWorkScheduler::PeriodicWorkScheduler(Env* env) : timer_mu_(env) {
|
||||
timer = std::unique_ptr<Timer>(new Timer(env));
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
unsigned int stats_dump_period_sec,
|
||||
unsigned int stats_persist_period_sec) {
|
||||
MutexLock l(&timer_mu_);
|
||||
static std::atomic<uint64_t> initial_delay(0);
|
||||
timer->Start();
|
||||
if (stats_dump_period_sec > 0) {
|
||||
@@ -41,6 +42,7 @@ void PeriodicWorkScheduler::Register(DBImpl* dbi,
|
||||
}
|
||||
|
||||
void PeriodicWorkScheduler::Unregister(DBImpl* dbi) {
|
||||
MutexLock l(&timer_mu_);
|
||||
timer->Cancel(GetTaskName(dbi, "dump_st"));
|
||||
timer->Cancel(GetTaskName(dbi, "pst_st"));
|
||||
timer->Cancel(GetTaskName(dbi, "flush_info_log"));
|
||||
@@ -78,7 +80,10 @@ PeriodicWorkTestScheduler* PeriodicWorkTestScheduler::Default(Env* env) {
|
||||
MutexLock l(&mutex);
|
||||
if (scheduler.timer.get() != nullptr &&
|
||||
scheduler.timer->TEST_GetPendingTaskNum() == 0) {
|
||||
scheduler.timer->Shutdown();
|
||||
{
|
||||
MutexLock timer_mu_guard(&scheduler.timer_mu_);
|
||||
scheduler.timer->Shutdown();
|
||||
}
|
||||
scheduler.timer.reset(new Timer(env));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ class PeriodicWorkScheduler {
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Timer> timer;
|
||||
// `timer_mu_` serves two purposes currently:
|
||||
// (1) to ensure calls to `Start()` and `Shutdown()` are serialized, as
|
||||
// they are currently not implemented in a thread-safe way; and
|
||||
// (2) to ensure the `Timer::Add()`s and `Timer::Start()` run atomically, and
|
||||
// the `Timer::Cancel()`s and `Timer::Shutdown()` run atomically.
|
||||
port::Mutex timer_mu_;
|
||||
|
||||
explicit PeriodicWorkScheduler(Env* env);
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ TEST_F(PlainTableKeyDecoderTest, ReadNonMmap) {
|
||||
Slice contents(tmp);
|
||||
test::StringSource* string_source =
|
||||
new test::StringSource(contents, 0, false);
|
||||
|
||||
std::unique_ptr<FSRandomAccessFile> holder(string_source);
|
||||
std::unique_ptr<RandomAccessFileReader> file_reader(
|
||||
test::GetRandomAccessFileReader(string_source));
|
||||
new RandomAccessFileReader(std::move(holder), "test"));
|
||||
std::unique_ptr<PlainTableReaderFileInfo> file_info(
|
||||
new PlainTableReaderFileInfo(std::move(file_reader), EnvOptions(),
|
||||
kLength));
|
||||
|
||||
@@ -439,7 +439,6 @@ class Repairer {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
|
||||
LegacyFileSystemWrapper fs(env_);
|
||||
IOStatus io_s;
|
||||
status = BuildTable(
|
||||
dbname_, /* versions */ nullptr, immutable_db_options_,
|
||||
|
||||
+4
-4
@@ -80,8 +80,8 @@ TEST_F(RepairTest, CorruptManifest) {
|
||||
Close();
|
||||
ASSERT_OK(env_->FileExists(manifest_path));
|
||||
|
||||
LegacyFileSystemWrapper fs(env_);
|
||||
ASSERT_OK(CreateFile(&fs, manifest_path, "blah", false /* use_fsync */));
|
||||
ASSERT_OK(CreateFile(env_->GetFileSystem(), manifest_path, "blah",
|
||||
false /* use_fsync */));
|
||||
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
||||
Reopen(CurrentOptions());
|
||||
|
||||
@@ -163,8 +163,8 @@ TEST_F(RepairTest, CorruptSst) {
|
||||
ASSERT_OK(GetFirstSstPath(&sst_path));
|
||||
ASSERT_FALSE(sst_path.empty());
|
||||
|
||||
LegacyFileSystemWrapper fs(env_);
|
||||
ASSERT_OK(CreateFile(&fs, sst_path, "blah", false /* use_fsync */));
|
||||
ASSERT_OK(CreateFile(env_->GetFileSystem(), sst_path, "blah",
|
||||
false /* use_fsync */));
|
||||
|
||||
Close();
|
||||
ASSERT_OK(RepairDB(dbname_, CurrentOptions()));
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ TableCache::TableCache(const ImmutableCFOptions& ioptions,
|
||||
cache_(cache),
|
||||
immortal_tables_(false),
|
||||
block_cache_tracer_(block_cache_tracer),
|
||||
loader_mutex_(kLoadConcurency, GetSliceNPHash64),
|
||||
loader_mutex_(kLoadConcurency, kGetSliceNPHash64UnseededFnPtr),
|
||||
io_tracer_(io_tracer) {
|
||||
if (ioptions_.row_cache) {
|
||||
// If the same cache is shared by multiple instances, we need to
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/sequence_file_reader.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -49,10 +48,9 @@ void MakeBuilder(const Options& options, const ImmutableCFOptions& ioptions,
|
||||
int_tbl_prop_collector_factories,
|
||||
std::unique_ptr<WritableFileWriter>* writable,
|
||||
std::unique_ptr<TableBuilder>* builder) {
|
||||
std::unique_ptr<WritableFile> wf(new test::StringSink);
|
||||
std::unique_ptr<FSWritableFile> wf(new test::StringSink);
|
||||
writable->reset(
|
||||
new WritableFileWriter(NewLegacyWritableFileWrapper(std::move(wf)),
|
||||
"" /* don't care */, EnvOptions()));
|
||||
new WritableFileWriter(std::move(wf), "" /* don't care */, EnvOptions()));
|
||||
int unknown_level = -1;
|
||||
builder->reset(NewTableBuilder(
|
||||
ioptions, moptions, internal_comparator, int_tbl_prop_collector_factories,
|
||||
@@ -286,12 +284,13 @@ void TestCustomizedTablePropertiesCollector(
|
||||
writer->Flush();
|
||||
|
||||
// -- Step 2: Read properties
|
||||
LegacyWritableFileWrapper* file =
|
||||
static_cast<LegacyWritableFileWrapper*>(writer->writable_file());
|
||||
test::StringSink* fwf = static_cast<test::StringSink*>(file->target());
|
||||
test::StringSink* fwf =
|
||||
static_cast<test::StringSink*>(writer->writable_file());
|
||||
std::unique_ptr<FSRandomAccessFile> source(
|
||||
new test::StringSource(fwf->contents()));
|
||||
std::unique_ptr<RandomAccessFileReader> fake_file_reader(
|
||||
test::GetRandomAccessFileReader(
|
||||
new test::StringSource(fwf->contents())));
|
||||
new RandomAccessFileReader(std::move(source), "test"));
|
||||
|
||||
TableProperties* props;
|
||||
Status s = ReadTableProperties(fake_file_reader.get(), fwf->contents().size(),
|
||||
magic_number, ioptions, &props,
|
||||
@@ -427,12 +426,13 @@ void TestInternalKeyPropertiesCollector(
|
||||
ASSERT_OK(builder->Finish());
|
||||
writable->Flush();
|
||||
|
||||
LegacyWritableFileWrapper* file =
|
||||
static_cast<LegacyWritableFileWrapper*>(writable->writable_file());
|
||||
test::StringSink* fwf = static_cast<test::StringSink*>(file->target());
|
||||
test::StringSink* fwf =
|
||||
static_cast<test::StringSink*>(writable->writable_file());
|
||||
std::unique_ptr<FSRandomAccessFile> source(
|
||||
new test::StringSource(fwf->contents()));
|
||||
std::unique_ptr<RandomAccessFileReader> reader(
|
||||
test::GetRandomAccessFileReader(
|
||||
new test::StringSource(fwf->contents())));
|
||||
new RandomAccessFileReader(std::move(source), "test"));
|
||||
|
||||
TableProperties* props;
|
||||
Status s =
|
||||
ReadTableProperties(reader.get(), fwf->contents().size(), magic_number,
|
||||
|
||||
+56
-6
@@ -226,13 +226,17 @@ bool VersionEdit::EncodeTo(std::string* dst) const {
|
||||
}
|
||||
|
||||
for (const auto& wal_addition : wal_additions_) {
|
||||
PutVarint32(dst, kWalAddition);
|
||||
wal_addition.EncodeTo(dst);
|
||||
PutVarint32(dst, kWalAddition2);
|
||||
std::string encoded;
|
||||
wal_addition.EncodeTo(&encoded);
|
||||
PutLengthPrefixedSlice(dst, encoded);
|
||||
}
|
||||
|
||||
if (!wal_deletion_.IsEmpty()) {
|
||||
PutVarint32(dst, kWalDeletion);
|
||||
wal_deletion_.EncodeTo(dst);
|
||||
PutVarint32(dst, kWalDeletion2);
|
||||
std::string encoded;
|
||||
wal_deletion_.EncodeTo(&encoded);
|
||||
PutLengthPrefixedSlice(dst, encoded);
|
||||
}
|
||||
|
||||
// 0 is default and does not need to be explicitly written
|
||||
@@ -375,6 +379,11 @@ const char* VersionEdit::DecodeNewFile4From(Slice* input) {
|
||||
|
||||
Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
Clear();
|
||||
#ifndef NDEBUG
|
||||
bool ignore_ignorable_tags = false;
|
||||
TEST_SYNC_POINT_CALLBACK("VersionEdit::EncodeTo:IgnoreIgnorableTags",
|
||||
&ignore_ignorable_tags);
|
||||
#endif
|
||||
Slice input = src;
|
||||
const char* msg = nullptr;
|
||||
uint32_t tag = 0;
|
||||
@@ -385,6 +394,11 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
Slice str;
|
||||
InternalKey key;
|
||||
while (msg == nullptr && GetVarint32(&input, &tag)) {
|
||||
#ifndef NDEBUG
|
||||
if (ignore_ignorable_tags && tag > kTagSafeIgnoreMask) {
|
||||
tag = kTagSafeIgnoreMask;
|
||||
}
|
||||
#endif
|
||||
switch (tag) {
|
||||
case kDbId:
|
||||
if (GetLengthPrefixedSlice(&input, &str)) {
|
||||
@@ -542,7 +556,8 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
break;
|
||||
}
|
||||
|
||||
case kBlobFileAddition: {
|
||||
case kBlobFileAddition:
|
||||
case kBlobFileAddition_DEPRECATED: {
|
||||
BlobFileAddition blob_file_addition;
|
||||
const Status s = blob_file_addition.DecodeFrom(&input);
|
||||
if (!s.ok()) {
|
||||
@@ -553,7 +568,8 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
break;
|
||||
}
|
||||
|
||||
case kBlobFileGarbage: {
|
||||
case kBlobFileGarbage:
|
||||
case kBlobFileGarbage_DEPRECATED: {
|
||||
BlobFileGarbage blob_file_garbage;
|
||||
const Status s = blob_file_garbage.DecodeFrom(&input);
|
||||
if (!s.ok()) {
|
||||
@@ -575,6 +591,23 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
break;
|
||||
}
|
||||
|
||||
case kWalAddition2: {
|
||||
Slice encoded;
|
||||
if (!GetLengthPrefixedSlice(&input, &encoded)) {
|
||||
msg = "WalAddition not prefixed by length";
|
||||
break;
|
||||
}
|
||||
|
||||
WalAddition wal_addition;
|
||||
const Status s = wal_addition.DecodeFrom(&encoded);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
wal_additions_.emplace_back(std::move(wal_addition));
|
||||
break;
|
||||
}
|
||||
|
||||
case kWalDeletion: {
|
||||
WalDeletion wal_deletion;
|
||||
const Status s = wal_deletion.DecodeFrom(&input);
|
||||
@@ -586,6 +619,23 @@ Status VersionEdit::DecodeFrom(const Slice& src) {
|
||||
break;
|
||||
}
|
||||
|
||||
case kWalDeletion2: {
|
||||
Slice encoded;
|
||||
if (!GetLengthPrefixedSlice(&input, &encoded)) {
|
||||
msg = "WalDeletion not prefixed by length";
|
||||
break;
|
||||
}
|
||||
|
||||
WalDeletion wal_deletion;
|
||||
const Status s = wal_deletion.DecodeFrom(&encoded);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
wal_deletion_ = std::move(wal_deletion);
|
||||
break;
|
||||
}
|
||||
|
||||
case kColumnFamily:
|
||||
if (!GetVarint32(&input, &column_family_)) {
|
||||
if (!msg) {
|
||||
|
||||
+9
-2
@@ -52,16 +52,21 @@ enum Tag : uint32_t {
|
||||
|
||||
kInAtomicGroup = 300,
|
||||
|
||||
kBlobFileAddition = 400,
|
||||
kBlobFileGarbage,
|
||||
|
||||
// Mask for an unidentified tag from the future which can be safely ignored.
|
||||
kTagSafeIgnoreMask = 1 << 13,
|
||||
|
||||
// Forward compatible (aka ignorable) records
|
||||
kDbId,
|
||||
kBlobFileAddition,
|
||||
kBlobFileGarbage,
|
||||
kBlobFileAddition_DEPRECATED,
|
||||
kBlobFileGarbage_DEPRECATED,
|
||||
kWalAddition,
|
||||
kWalDeletion,
|
||||
kFullHistoryTsLow,
|
||||
kWalAddition2,
|
||||
kWalDeletion2,
|
||||
};
|
||||
|
||||
enum NewFileCustomTag : uint32_t {
|
||||
@@ -426,6 +431,7 @@ class VersionEdit {
|
||||
}
|
||||
|
||||
void SetBlobFileAdditions(BlobFileAdditions blob_file_additions) {
|
||||
assert(blob_file_additions_.empty());
|
||||
blob_file_additions_ = std::move(blob_file_additions);
|
||||
}
|
||||
|
||||
@@ -449,6 +455,7 @@ class VersionEdit {
|
||||
}
|
||||
|
||||
void SetBlobFileGarbages(BlobFileGarbages blob_file_garbages) {
|
||||
assert(blob_file_garbages_.empty());
|
||||
blob_file_garbages_ = std::move(blob_file_garbages);
|
||||
}
|
||||
|
||||
|
||||
@@ -98,12 +98,18 @@ Status ListColumnFamiliesHandler::ApplyVersionEdit(
|
||||
Status FileChecksumRetriever::ApplyVersionEdit(VersionEdit& edit,
|
||||
ColumnFamilyData** /*unused*/) {
|
||||
for (const auto& deleted_file : edit.GetDeletedFiles()) {
|
||||
file_checksum_list_.RemoveOneFileChecksum(deleted_file.second);
|
||||
Status s = file_checksum_list_.RemoveOneFileChecksum(deleted_file.second);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
for (const auto& new_file : edit.GetNewFiles()) {
|
||||
file_checksum_list_.InsertOneFileChecksum(
|
||||
Status s = file_checksum_list_.InsertOneFileChecksum(
|
||||
new_file.second.fd.GetNumber(), new_file.second.file_checksum,
|
||||
new_file.second.file_checksum_func_name);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
+88
-14
@@ -324,14 +324,22 @@ TEST_F(VersionEditTest, AddWalEncodeDecode) {
|
||||
TestEncodeDecode(edit);
|
||||
}
|
||||
|
||||
static std::string PrefixEncodedWalAdditionWithLength(
|
||||
const std::string& encoded) {
|
||||
std::string ret;
|
||||
PutVarint32(&ret, Tag::kWalAddition2);
|
||||
PutLengthPrefixedSlice(&ret, encoded);
|
||||
return ret;
|
||||
}
|
||||
|
||||
TEST_F(VersionEditTest, AddWalDecodeBadLogNumber) {
|
||||
std::string encoded;
|
||||
PutVarint32(&encoded, Tag::kWalAddition);
|
||||
|
||||
{
|
||||
// No log number.
|
||||
std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(encoded);
|
||||
Status s = edit.DecodeFrom(encoded_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
|
||||
std::string::npos)
|
||||
@@ -345,8 +353,10 @@ TEST_F(VersionEditTest, AddWalDecodeBadLogNumber) {
|
||||
unsigned char* ptr = reinterpret_cast<unsigned char*>(&c);
|
||||
*ptr = 128;
|
||||
encoded.append(1, c);
|
||||
|
||||
std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(encoded);
|
||||
Status s = edit.DecodeFrom(encoded_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("Error decoding WAL log number") !=
|
||||
std::string::npos)
|
||||
@@ -358,14 +368,14 @@ TEST_F(VersionEditTest, AddWalDecodeBadTag) {
|
||||
constexpr WalNumber kLogNumber = 100;
|
||||
constexpr uint64_t kSizeInBytes = 100;
|
||||
|
||||
std::string encoded_without_tag;
|
||||
PutVarint32(&encoded_without_tag, Tag::kWalAddition);
|
||||
PutVarint64(&encoded_without_tag, kLogNumber);
|
||||
std::string encoded;
|
||||
PutVarint64(&encoded, kLogNumber);
|
||||
|
||||
{
|
||||
// No tag.
|
||||
std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(encoded_without_tag);
|
||||
Status s = edit.DecodeFrom(encoded_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
|
||||
<< s.ToString();
|
||||
@@ -373,12 +383,15 @@ TEST_F(VersionEditTest, AddWalDecodeBadTag) {
|
||||
|
||||
{
|
||||
// Only has size tag, no terminate tag.
|
||||
std::string encoded_with_size = encoded_without_tag;
|
||||
std::string encoded_with_size = encoded;
|
||||
PutVarint32(&encoded_with_size,
|
||||
static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
|
||||
PutVarint64(&encoded_with_size, kSizeInBytes);
|
||||
|
||||
std::string encoded_edit =
|
||||
PrefixEncodedWalAdditionWithLength(encoded_with_size);
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(encoded_with_size);
|
||||
Status s = edit.DecodeFrom(encoded_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
|
||||
<< s.ToString();
|
||||
@@ -386,11 +399,14 @@ TEST_F(VersionEditTest, AddWalDecodeBadTag) {
|
||||
|
||||
{
|
||||
// Only has terminate tag.
|
||||
std::string encoded_with_terminate = encoded_without_tag;
|
||||
std::string encoded_with_terminate = encoded;
|
||||
PutVarint32(&encoded_with_terminate,
|
||||
static_cast<uint32_t>(WalAdditionTag::kTerminate));
|
||||
|
||||
std::string encoded_edit =
|
||||
PrefixEncodedWalAdditionWithLength(encoded_with_terminate);
|
||||
VersionEdit edit;
|
||||
ASSERT_OK(edit.DecodeFrom(encoded_with_terminate));
|
||||
ASSERT_OK(edit.DecodeFrom(encoded_edit));
|
||||
auto& wal_addition = edit.GetWalAdditions()[0];
|
||||
ASSERT_EQ(wal_addition.GetLogNumber(), kLogNumber);
|
||||
ASSERT_FALSE(wal_addition.GetMetadata().HasSyncedSize());
|
||||
@@ -401,15 +417,15 @@ TEST_F(VersionEditTest, AddWalDecodeNoSize) {
|
||||
constexpr WalNumber kLogNumber = 100;
|
||||
|
||||
std::string encoded;
|
||||
PutVarint32(&encoded, Tag::kWalAddition);
|
||||
PutVarint64(&encoded, kLogNumber);
|
||||
PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kSyncedSize));
|
||||
// No real size after the size tag.
|
||||
|
||||
{
|
||||
// Without terminate tag.
|
||||
std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(encoded);
|
||||
Status s = edit.DecodeFrom(encoded_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(s.ToString().find("Error decoding WAL file size") !=
|
||||
std::string::npos)
|
||||
@@ -419,8 +435,10 @@ TEST_F(VersionEditTest, AddWalDecodeNoSize) {
|
||||
{
|
||||
// With terminate tag.
|
||||
PutVarint32(&encoded, static_cast<uint32_t>(WalAdditionTag::kTerminate));
|
||||
|
||||
std::string encoded_edit = PrefixEncodedWalAdditionWithLength(encoded);
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(encoded);
|
||||
Status s = edit.DecodeFrom(encoded_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
// The terminate tag is misunderstood as the size.
|
||||
ASSERT_TRUE(s.ToString().find("Error decoding tag") != std::string::npos)
|
||||
@@ -515,6 +533,62 @@ TEST_F(VersionEditTest, FullHistoryTsLow) {
|
||||
TestEncodeDecode(edit);
|
||||
}
|
||||
|
||||
// Tests that if RocksDB is downgraded, the new types of VersionEdits
|
||||
// that have a tag larger than kTagSafeIgnoreMask can be safely ignored.
|
||||
TEST_F(VersionEditTest, IgnorableTags) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionEdit::EncodeTo:IgnoreIgnorableTags", [&](void* arg) {
|
||||
bool* ignore = static_cast<bool*>(arg);
|
||||
*ignore = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr uint64_t kPrevLogNumber = 100;
|
||||
constexpr uint64_t kLogNumber = 200;
|
||||
constexpr uint64_t kNextFileNumber = 300;
|
||||
constexpr uint64_t kColumnFamilyId = 400;
|
||||
|
||||
VersionEdit edit;
|
||||
// Add some ignorable entries.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
edit.AddWal(i + 1, WalMetadata(i + 2));
|
||||
}
|
||||
edit.SetDBId("db_id");
|
||||
// Add unignorable entries.
|
||||
edit.SetPrevLogNumber(kPrevLogNumber);
|
||||
edit.SetLogNumber(kLogNumber);
|
||||
// Add more ignorable entries.
|
||||
edit.DeleteWalsBefore(100);
|
||||
// Add unignorable entry.
|
||||
edit.SetNextFile(kNextFileNumber);
|
||||
// Add more ignorable entries.
|
||||
edit.SetFullHistoryTsLow("ts");
|
||||
// Add unignorable entry.
|
||||
edit.SetColumnFamily(kColumnFamilyId);
|
||||
|
||||
std::string encoded;
|
||||
ASSERT_TRUE(edit.EncodeTo(&encoded));
|
||||
|
||||
VersionEdit decoded;
|
||||
ASSERT_OK(decoded.DecodeFrom(encoded));
|
||||
|
||||
// Check that all ignorable entries are ignored.
|
||||
ASSERT_FALSE(decoded.HasDbId());
|
||||
ASSERT_FALSE(decoded.HasFullHistoryTsLow());
|
||||
ASSERT_FALSE(decoded.IsWalAddition());
|
||||
ASSERT_FALSE(decoded.IsWalDeletion());
|
||||
ASSERT_TRUE(decoded.GetWalAdditions().empty());
|
||||
ASSERT_TRUE(decoded.GetWalDeletion().IsEmpty());
|
||||
|
||||
// Check that unignorable entries are still present.
|
||||
ASSERT_EQ(edit.GetPrevLogNumber(), kPrevLogNumber);
|
||||
ASSERT_EQ(edit.GetLogNumber(), kLogNumber);
|
||||
ASSERT_EQ(edit.GetNextFile(), kNextFileNumber);
|
||||
ASSERT_EQ(edit.GetColumnFamily(), kColumnFamilyId);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+4
-2
@@ -42,6 +42,7 @@
|
||||
#include "monitoring/file_read_sample.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/persistent_stats_history.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
@@ -4117,10 +4118,11 @@ Status VersionSet::ProcessManifestWrites(
|
||||
if (io_s.ok()) {
|
||||
descriptor_file->SetPreallocationBlockSize(
|
||||
db_options_->manifest_preallocation_size);
|
||||
|
||||
FileTypeSet tmp_set = db_options_->checksum_handoff_file_types;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(descriptor_file), descriptor_fname, opt_file_opts, env_,
|
||||
io_tracer_, nullptr, db_options_->listeners));
|
||||
io_tracer_, nullptr, db_options_->listeners, nullptr,
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
descriptor_log_.reset(
|
||||
new log::Writer(std::move(file_writer), 0, false));
|
||||
s = WriteCurrentStateToManifest(curr_state, wal_additions,
|
||||
|
||||
@@ -47,8 +47,7 @@ class WalManagerTest : public testing::Test {
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
db_options_.wal_dir = dbname_;
|
||||
db_options_.env = env_.get();
|
||||
fs_.reset(new LegacyFileSystemWrapper(env_.get()));
|
||||
db_options_.fs = fs_;
|
||||
db_options_.fs = env_->GetFileSystem();
|
||||
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
@@ -116,7 +115,6 @@ class WalManagerTest : public testing::Test {
|
||||
WriteBufferManager write_buffer_manager_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
std::unique_ptr<WalManager> wal_manager_;
|
||||
std::shared_ptr<LegacyFileSystemWrapper> fs_;
|
||||
|
||||
std::unique_ptr<log::Writer> current_log_writer_;
|
||||
uint64_t current_log_number_;
|
||||
|
||||
+324
-45
@@ -46,6 +46,7 @@
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/kv_checksum.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/merge_context.h"
|
||||
#include "db/snapshot_impl.h"
|
||||
@@ -140,10 +141,14 @@ struct BatchContentClassifier : public WriteBatch::Handler {
|
||||
|
||||
class TimestampAssigner : public WriteBatch::Handler {
|
||||
public:
|
||||
explicit TimestampAssigner(const Slice& ts)
|
||||
: timestamp_(ts), timestamps_(kEmptyTimestampList) {}
|
||||
explicit TimestampAssigner(const std::vector<Slice>& ts_list)
|
||||
: timestamps_(ts_list) {
|
||||
explicit TimestampAssigner(const Slice& ts,
|
||||
WriteBatch::ProtectionInfo* prot_info)
|
||||
: timestamp_(ts),
|
||||
timestamps_(kEmptyTimestampList),
|
||||
prot_info_(prot_info) {}
|
||||
explicit TimestampAssigner(const std::vector<Slice>& ts_list,
|
||||
WriteBatch::ProtectionInfo* prot_info)
|
||||
: timestamps_(ts_list), prot_info_(prot_info) {
|
||||
SanityCheck();
|
||||
}
|
||||
~TimestampAssigner() override {}
|
||||
@@ -167,9 +172,8 @@ class TimestampAssigner : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
Status DeleteRangeCF(uint32_t, const Slice& begin_key,
|
||||
const Slice& end_key) override {
|
||||
const Slice& /* end_key */) override {
|
||||
AssignTimestamp(begin_key);
|
||||
AssignTimestamp(end_key);
|
||||
++idx_;
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -221,12 +225,17 @@ class TimestampAssigner : public WriteBatch::Handler {
|
||||
const Slice& ts = timestamps_.empty() ? timestamp_ : timestamps_[idx_];
|
||||
size_t ts_sz = ts.size();
|
||||
char* ptr = const_cast<char*>(key.data() + key.size() - ts_sz);
|
||||
if (prot_info_ != nullptr) {
|
||||
Slice old_ts(ptr, ts_sz), new_ts(ts.data(), ts_sz);
|
||||
prot_info_->entries_[idx_].UpdateT(old_ts, new_ts);
|
||||
}
|
||||
memcpy(ptr, ts.data(), ts_sz);
|
||||
}
|
||||
|
||||
static const std::vector<Slice> kEmptyTimestampList;
|
||||
const Slice timestamp_;
|
||||
const std::vector<Slice>& timestamps_;
|
||||
WriteBatch::ProtectionInfo* const prot_info_;
|
||||
size_t idx_ = 0;
|
||||
|
||||
// No copy or move.
|
||||
@@ -258,6 +267,21 @@ WriteBatch::WriteBatch(size_t reserved_bytes, size_t max_bytes, size_t ts_sz)
|
||||
rep_.resize(WriteBatchInternal::kHeader);
|
||||
}
|
||||
|
||||
WriteBatch::WriteBatch(size_t reserved_bytes, size_t max_bytes, size_t ts_sz,
|
||||
size_t protection_bytes_per_key)
|
||||
: content_flags_(0), max_bytes_(max_bytes), rep_(), timestamp_size_(ts_sz) {
|
||||
// Currently `protection_bytes_per_key` can only be enabled at 8 bytes per
|
||||
// entry.
|
||||
assert(protection_bytes_per_key == 0 || protection_bytes_per_key == 8);
|
||||
if (protection_bytes_per_key != 0) {
|
||||
prot_info_.reset(new WriteBatch::ProtectionInfo());
|
||||
}
|
||||
rep_.reserve((reserved_bytes > WriteBatchInternal::kHeader)
|
||||
? reserved_bytes
|
||||
: WriteBatchInternal::kHeader);
|
||||
rep_.resize(WriteBatchInternal::kHeader);
|
||||
}
|
||||
|
||||
WriteBatch::WriteBatch(const std::string& rep)
|
||||
: content_flags_(ContentFlags::DEFERRED),
|
||||
max_bytes_(0),
|
||||
@@ -280,6 +304,10 @@ WriteBatch::WriteBatch(const WriteBatch& src)
|
||||
save_points_.reset(new SavePoints());
|
||||
save_points_->stack = src.save_points_->stack;
|
||||
}
|
||||
if (src.prot_info_ != nullptr) {
|
||||
prot_info_.reset(new WriteBatch::ProtectionInfo());
|
||||
prot_info_->entries_ = src.prot_info_->entries_;
|
||||
}
|
||||
}
|
||||
|
||||
WriteBatch::WriteBatch(WriteBatch&& src) noexcept
|
||||
@@ -287,6 +315,7 @@ WriteBatch::WriteBatch(WriteBatch&& src) noexcept
|
||||
wal_term_point_(std::move(src.wal_term_point_)),
|
||||
content_flags_(src.content_flags_.load(std::memory_order_relaxed)),
|
||||
max_bytes_(src.max_bytes_),
|
||||
prot_info_(std::move(src.prot_info_)),
|
||||
rep_(std::move(src.rep_)),
|
||||
timestamp_size_(src.timestamp_size_) {}
|
||||
|
||||
@@ -331,6 +360,9 @@ void WriteBatch::Clear() {
|
||||
}
|
||||
}
|
||||
|
||||
if (prot_info_ != nullptr) {
|
||||
prot_info_->entries_.clear();
|
||||
}
|
||||
wal_term_point_.clear();
|
||||
}
|
||||
|
||||
@@ -359,6 +391,13 @@ void WriteBatch::MarkWalTerminationPoint() {
|
||||
wal_term_point_.content_flags = content_flags_;
|
||||
}
|
||||
|
||||
size_t WriteBatch::GetProtectionBytesPerKey() const {
|
||||
if (prot_info_ != nullptr) {
|
||||
return prot_info_->GetBytesPerKey();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool WriteBatch::HasPut() const {
|
||||
return (ComputeContentFlags() & ContentFlags::HAS_PUT) != 0;
|
||||
}
|
||||
@@ -777,18 +816,31 @@ Status WriteBatchInternal::Put(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyValue));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
std::string timestamp(b->timestamp_size_, '\0');
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSlice(&b->rep_, key);
|
||||
} else {
|
||||
PutVarint32(&b->rep_,
|
||||
static_cast<uint32_t>(key.size() + b->timestamp_size_));
|
||||
b->rep_.append(key.data(), key.size());
|
||||
b->rep_.append(b->timestamp_size_, '\0');
|
||||
b->rep_.append(timestamp);
|
||||
}
|
||||
PutLengthPrefixedSlice(&b->rep_, value);
|
||||
b->content_flags_.store(
|
||||
b->content_flags_.load(std::memory_order_relaxed) | ContentFlags::HAS_PUT,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// Technically the optype could've been `kTypeColumnFamilyValue` with the
|
||||
// CF ID encoded in the `WriteBatch`. That distinction is unimportant
|
||||
// however since we verify CF ID is correct, as well as all other fields
|
||||
// (a missing/extra encoded CF ID would corrupt another field). It is
|
||||
// convenient to consolidate on `kTypeValue` here as that is what will be
|
||||
// inserted into memtable.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key, value, kTypeValue, timestamp)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -833,6 +885,7 @@ Status WriteBatchInternal::Put(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyValue));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
std::string timestamp(b->timestamp_size_, '\0');
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
} else {
|
||||
@@ -842,6 +895,14 @@ Status WriteBatchInternal::Put(WriteBatch* b, uint32_t column_family_id,
|
||||
b->content_flags_.store(
|
||||
b->content_flags_.load(std::memory_order_relaxed) | ContentFlags::HAS_PUT,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key, value, kTypeValue, timestamp)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -916,17 +977,26 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
std::string timestamp(b->timestamp_size_, '\0');
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSlice(&b->rep_, key);
|
||||
} else {
|
||||
PutVarint32(&b->rep_,
|
||||
static_cast<uint32_t>(key.size() + b->timestamp_size_));
|
||||
b->rep_.append(key.data(), key.size());
|
||||
b->rep_.append(b->timestamp_size_, '\0');
|
||||
b->rep_.append(timestamp);
|
||||
}
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key, "" /* value */, kTypeDeletion, timestamp)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -945,6 +1015,7 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
std::string timestamp(b->timestamp_size_, '\0');
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
} else {
|
||||
@@ -953,6 +1024,16 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key,
|
||||
SliceParts(nullptr /* _parts */, 0 /* _num_parts */),
|
||||
kTypeDeletion, timestamp)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -977,6 +1058,15 @@ Status WriteBatchInternal::SingleDelete(WriteBatch* b,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_SINGLE_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(ProtectionInfo64()
|
||||
.ProtectKVOT(key, "" /* value */,
|
||||
kTypeSingleDeletion,
|
||||
"" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1001,6 +1091,17 @@ Status WriteBatchInternal::SingleDelete(WriteBatch* b,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_SINGLE_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key,
|
||||
SliceParts(nullptr /* _parts */,
|
||||
0 /* _num_parts */) /* value */,
|
||||
kTypeSingleDeletion, "" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1026,6 +1127,16 @@ Status WriteBatchInternal::DeleteRange(WriteBatch* b, uint32_t column_family_id,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE_RANGE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
// In `DeleteRange()`, the end key is treated as the value.
|
||||
b->prot_info_->entries_.emplace_back(ProtectionInfo64()
|
||||
.ProtectKVOT(begin_key, end_key,
|
||||
kTypeRangeDeletion,
|
||||
"" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1051,6 +1162,16 @@ Status WriteBatchInternal::DeleteRange(WriteBatch* b, uint32_t column_family_id,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE_RANGE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
// In `DeleteRange()`, the end key is treated as the value.
|
||||
b->prot_info_->entries_.emplace_back(ProtectionInfo64()
|
||||
.ProtectKVOT(begin_key, end_key,
|
||||
kTypeRangeDeletion,
|
||||
"" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1083,6 +1204,14 @@ Status WriteBatchInternal::Merge(WriteBatch* b, uint32_t column_family_id,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_MERGE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key, value, kTypeMerge, "" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1113,6 +1242,14 @@ Status WriteBatchInternal::Merge(WriteBatch* b, uint32_t column_family_id,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_MERGE,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key, value, kTypeMerge, "" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1138,6 +1275,14 @@ Status WriteBatchInternal::PutBlobIndex(WriteBatch* b,
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_BLOB_INDEX,
|
||||
std::memory_order_relaxed);
|
||||
if (b->prot_info_ != nullptr) {
|
||||
// See comment in first `WriteBatchInternal::Put()` overload concerning the
|
||||
// `ValueType` argument passed to `ProtectKVOT()`.
|
||||
b->prot_info_->entries_.emplace_back(
|
||||
ProtectionInfo64()
|
||||
.ProtectKVOT(key, value, kTypeBlobIndex, "" /* timestamp */)
|
||||
.ProtectC(column_family_id));
|
||||
}
|
||||
return save.commit();
|
||||
}
|
||||
|
||||
@@ -1176,6 +1321,9 @@ Status WriteBatch::RollbackToSavePoint() {
|
||||
Clear();
|
||||
} else {
|
||||
rep_.resize(savepoint.size);
|
||||
if (prot_info_ != nullptr) {
|
||||
prot_info_->entries_.resize(savepoint.count);
|
||||
}
|
||||
WriteBatchInternal::SetCount(this, savepoint.count);
|
||||
content_flags_.store(savepoint.content_flags, std::memory_order_relaxed);
|
||||
}
|
||||
@@ -1195,12 +1343,12 @@ Status WriteBatch::PopSavePoint() {
|
||||
}
|
||||
|
||||
Status WriteBatch::AssignTimestamp(const Slice& ts) {
|
||||
TimestampAssigner ts_assigner(ts);
|
||||
TimestampAssigner ts_assigner(ts, prot_info_.get());
|
||||
return Iterate(&ts_assigner);
|
||||
}
|
||||
|
||||
Status WriteBatch::AssignTimestamps(const std::vector<Slice>& ts_list) {
|
||||
TimestampAssigner ts_assigner(ts_list);
|
||||
TimestampAssigner ts_assigner(ts_list, prot_info_.get());
|
||||
return Iterate(&ts_assigner);
|
||||
}
|
||||
|
||||
@@ -1217,6 +1365,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
DBImpl* db_;
|
||||
const bool concurrent_memtable_writes_;
|
||||
bool post_info_created_;
|
||||
const WriteBatch::ProtectionInfo* prot_info_;
|
||||
size_t prot_info_idx_;
|
||||
|
||||
bool* has_valid_writes_;
|
||||
// On some (!) platforms just default creating
|
||||
@@ -1279,6 +1429,16 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
(&duplicate_detector_)->IsDuplicateKeySeq(column_family_id, key, sequence_);
|
||||
}
|
||||
|
||||
const ProtectionInfoKVOTC64* NextProtectionInfo() {
|
||||
const ProtectionInfoKVOTC64* res = nullptr;
|
||||
if (prot_info_ != nullptr) {
|
||||
assert(prot_info_idx_ < prot_info_->entries_.size());
|
||||
res = &prot_info_->entries_[prot_info_idx_];
|
||||
++prot_info_idx_;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool WriteBeforePrepare() const override { return write_before_prepare_; }
|
||||
bool WriteAfterCommit() const override { return write_after_commit_; }
|
||||
@@ -1291,6 +1451,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
bool ignore_missing_column_families,
|
||||
uint64_t recovering_log_number, DB* db,
|
||||
bool concurrent_memtable_writes,
|
||||
const WriteBatch::ProtectionInfo* prot_info,
|
||||
bool* has_valid_writes = nullptr, bool seq_per_batch = false,
|
||||
bool batch_per_txn = true, bool hint_per_batch = false)
|
||||
: sequence_(_sequence),
|
||||
@@ -1303,6 +1464,8 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
db_(static_cast_with_check<DBImpl>(db)),
|
||||
concurrent_memtable_writes_(concurrent_memtable_writes),
|
||||
post_info_created_(false),
|
||||
prot_info_(prot_info),
|
||||
prot_info_idx_(0),
|
||||
has_valid_writes_(has_valid_writes),
|
||||
rebuilding_trx_(nullptr),
|
||||
rebuilding_trx_seq_(0),
|
||||
@@ -1360,6 +1523,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
void set_log_number_ref(uint64_t log) { log_number_ref_ = log; }
|
||||
void set_prot_info(const WriteBatch::ProtectionInfo* prot_info) {
|
||||
prot_info_ = prot_info;
|
||||
prot_info_idx_ = 0;
|
||||
}
|
||||
|
||||
SequenceNumber sequence() const { return sequence_; }
|
||||
|
||||
@@ -1415,9 +1582,11 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
Status PutCFImpl(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value, ValueType value_type) {
|
||||
const Slice& value, ValueType value_type,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info) {
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(write_after_commit_ && rebuilding_trx_ != nullptr)) {
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
return WriteBatchInternal::Put(rebuilding_trx_, column_family_id, key,
|
||||
value);
|
||||
// else insert the values to the memtable right away
|
||||
@@ -1429,6 +1598,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(!write_after_commit_);
|
||||
// The CF is probably flushed and hence no need for insert but we still
|
||||
// need to keep track of the keys for upcoming rollback/commit.
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::Put(rebuilding_trx_, column_family_id,
|
||||
key, value);
|
||||
if (ret_status.ok()) {
|
||||
@@ -1448,15 +1618,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(!seq_per_batch_ || !moptions->inplace_update_support);
|
||||
if (!moptions->inplace_update_support) {
|
||||
ret_status =
|
||||
mem->Add(sequence_, value_type, key, value,
|
||||
mem->Add(sequence_, value_type, key, value, kv_prot_info,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem),
|
||||
hint_per_batch_ ? &GetHintMap()[mem] : nullptr);
|
||||
} else if (moptions->inplace_callback == nullptr) {
|
||||
assert(!concurrent_memtable_writes_);
|
||||
ret_status = mem->Update(sequence_, key, value);
|
||||
ret_status = mem->Update(sequence_, key, value, kv_prot_info);
|
||||
} else {
|
||||
assert(!concurrent_memtable_writes_);
|
||||
ret_status = mem->UpdateCallback(sequence_, key, value);
|
||||
ret_status = mem->UpdateCallback(sequence_, key, value, kv_prot_info);
|
||||
if (ret_status.IsNotFound()) {
|
||||
// key not found in memtable. Do sst get, update, add
|
||||
SnapshotImpl read_from_snapshot;
|
||||
@@ -1484,7 +1654,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
} else {
|
||||
ret_status = Status::OK();
|
||||
}
|
||||
|
||||
if (ret_status.ok()) {
|
||||
UpdateStatus update_status;
|
||||
char* prev_buffer = const_cast<char*>(prev_value.c_str());
|
||||
@@ -1499,16 +1668,35 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
if (update_status == UpdateStatus::UPDATED_INPLACE) {
|
||||
assert(get_status.ok());
|
||||
// prev_value is updated in-place with final value.
|
||||
ret_status = mem->Add(sequence_, value_type, key,
|
||||
Slice(prev_buffer, prev_size));
|
||||
if (kv_prot_info != nullptr) {
|
||||
ProtectionInfoKVOTS64 updated_kv_prot_info(*kv_prot_info);
|
||||
updated_kv_prot_info.UpdateV(value,
|
||||
Slice(prev_buffer, prev_size));
|
||||
// prev_value is updated in-place with final value.
|
||||
ret_status = mem->Add(sequence_, value_type, key,
|
||||
Slice(prev_buffer, prev_size),
|
||||
&updated_kv_prot_info);
|
||||
} else {
|
||||
ret_status = mem->Add(sequence_, value_type, key,
|
||||
Slice(prev_buffer, prev_size),
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
if (ret_status.ok()) {
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
}
|
||||
} else if (update_status == UpdateStatus::UPDATED) {
|
||||
// merged_value contains the final value.
|
||||
ret_status =
|
||||
mem->Add(sequence_, value_type, key, Slice(merged_value));
|
||||
if (kv_prot_info != nullptr) {
|
||||
ProtectionInfoKVOTS64 updated_kv_prot_info(*kv_prot_info);
|
||||
updated_kv_prot_info.UpdateV(value, merged_value);
|
||||
// merged_value contains the final value.
|
||||
ret_status = mem->Add(sequence_, value_type, key,
|
||||
Slice(merged_value), &updated_kv_prot_info);
|
||||
} else {
|
||||
// merged_value contains the final value.
|
||||
ret_status =
|
||||
mem->Add(sequence_, value_type, key, Slice(merged_value),
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
if (ret_status.ok()) {
|
||||
RecordTick(moptions->statistics, NUMBER_KEYS_WRITTEN);
|
||||
}
|
||||
@@ -1531,6 +1719,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// away. So we only need to add to it when `ret_status.ok()`.
|
||||
if (UNLIKELY(ret_status.ok() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::Put(rebuilding_trx_, column_family_id,
|
||||
key, value);
|
||||
}
|
||||
@@ -1539,15 +1728,25 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
Status PutCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
return PutCFImpl(column_family_id, key, value, kTypeValue);
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
if (kv_prot_info != nullptr) {
|
||||
// Memtable needs seqno, doesn't need CF ID
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
return PutCFImpl(column_family_id, key, value, kTypeValue,
|
||||
&mem_kv_prot_info);
|
||||
}
|
||||
return PutCFImpl(column_family_id, key, value, kTypeValue,
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
|
||||
Status DeleteImpl(uint32_t /*column_family_id*/, const Slice& key,
|
||||
const Slice& value, ValueType delete_type) {
|
||||
const Slice& value, ValueType delete_type,
|
||||
const ProtectionInfoKVOTS64* kv_prot_info) {
|
||||
Status ret_status;
|
||||
MemTable* mem = cf_mems_->GetMemTable();
|
||||
ret_status =
|
||||
mem->Add(sequence_, delete_type, key, value,
|
||||
mem->Add(sequence_, delete_type, key, value, kv_prot_info,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem),
|
||||
hint_per_batch_ ? &GetHintMap()[mem] : nullptr);
|
||||
if (UNLIKELY(ret_status.IsTryAgain())) {
|
||||
@@ -1562,8 +1761,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
Status DeleteCF(uint32_t column_family_id, const Slice& key) override {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(write_after_commit_ && rebuilding_trx_ != nullptr)) {
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
return WriteBatchInternal::Delete(rebuilding_trx_, column_family_id, key);
|
||||
// else insert the values to the memtable right away
|
||||
}
|
||||
@@ -1574,6 +1775,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(!write_after_commit_);
|
||||
// The CF is probably flushed and hence no need for insert but we still
|
||||
// need to keep track of the keys for upcoming rollback/commit.
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status =
|
||||
WriteBatchInternal::Delete(rebuilding_trx_, column_family_id, key);
|
||||
if (ret_status.ok()) {
|
||||
@@ -1592,7 +1794,16 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
: 0;
|
||||
const ValueType delete_type =
|
||||
(0 == ts_sz) ? kTypeDeletion : kTypeDeletionWithTimestamp;
|
||||
ret_status = DeleteImpl(column_family_id, key, Slice(), delete_type);
|
||||
if (kv_prot_info != nullptr) {
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
mem_kv_prot_info.UpdateO(kTypeDeletion, delete_type);
|
||||
ret_status = DeleteImpl(column_family_id, key, Slice(), delete_type,
|
||||
&mem_kv_prot_info);
|
||||
} else {
|
||||
ret_status = DeleteImpl(column_family_id, key, Slice(), delete_type,
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
// optimize for non-recovery mode
|
||||
// If `ret_status` is `TryAgain` then the next (successful) try will add
|
||||
// the key to the rebuilding transaction object. If `ret_status` is
|
||||
@@ -1600,6 +1811,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// away. So we only need to add to it when `ret_status.ok()`.
|
||||
if (UNLIKELY(ret_status.ok() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status =
|
||||
WriteBatchInternal::Delete(rebuilding_trx_, column_family_id, key);
|
||||
}
|
||||
@@ -1607,8 +1819,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
|
||||
Status SingleDeleteCF(uint32_t column_family_id, const Slice& key) override {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(write_after_commit_ && rebuilding_trx_ != nullptr)) {
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
return WriteBatchInternal::SingleDelete(rebuilding_trx_, column_family_id,
|
||||
key);
|
||||
// else insert the values to the memtable right away
|
||||
@@ -1620,6 +1834,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(!write_after_commit_);
|
||||
// The CF is probably flushed and hence no need for insert but we still
|
||||
// need to keep track of the keys for upcoming rollback/commit.
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::SingleDelete(rebuilding_trx_,
|
||||
column_family_id, key);
|
||||
if (ret_status.ok()) {
|
||||
@@ -1632,8 +1847,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
assert(ret_status.ok());
|
||||
|
||||
ret_status =
|
||||
DeleteImpl(column_family_id, key, Slice(), kTypeSingleDeletion);
|
||||
if (kv_prot_info != nullptr) {
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
ret_status = DeleteImpl(column_family_id, key, Slice(),
|
||||
kTypeSingleDeletion, &mem_kv_prot_info);
|
||||
} else {
|
||||
ret_status = DeleteImpl(column_family_id, key, Slice(),
|
||||
kTypeSingleDeletion, nullptr /* kv_prot_info */);
|
||||
}
|
||||
// optimize for non-recovery mode
|
||||
// If `ret_status` is `TryAgain` then the next (successful) try will add
|
||||
// the key to the rebuilding transaction object. If `ret_status` is
|
||||
@@ -1641,6 +1863,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// away. So we only need to add to it when `ret_status.ok()`.
|
||||
if (UNLIKELY(ret_status.ok() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::SingleDelete(rebuilding_trx_,
|
||||
column_family_id, key);
|
||||
}
|
||||
@@ -1649,8 +1872,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
Status DeleteRangeCF(uint32_t column_family_id, const Slice& begin_key,
|
||||
const Slice& end_key) override {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(write_after_commit_ && rebuilding_trx_ != nullptr)) {
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
return WriteBatchInternal::DeleteRange(rebuilding_trx_, column_family_id,
|
||||
begin_key, end_key);
|
||||
// else insert the values to the memtable right away
|
||||
@@ -1662,6 +1887,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(!write_after_commit_);
|
||||
// The CF is probably flushed and hence no need for insert but we still
|
||||
// need to keep track of the keys for upcoming rollback/commit.
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::DeleteRange(
|
||||
rebuilding_trx_, column_family_id, begin_key, end_key);
|
||||
if (ret_status.ok()) {
|
||||
@@ -1704,8 +1930,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
}
|
||||
}
|
||||
|
||||
ret_status =
|
||||
DeleteImpl(column_family_id, begin_key, end_key, kTypeRangeDeletion);
|
||||
if (kv_prot_info != nullptr) {
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
ret_status = DeleteImpl(column_family_id, begin_key, end_key,
|
||||
kTypeRangeDeletion, &mem_kv_prot_info);
|
||||
} else {
|
||||
ret_status = DeleteImpl(column_family_id, begin_key, end_key,
|
||||
kTypeRangeDeletion, nullptr /* kv_prot_info */);
|
||||
}
|
||||
// optimize for non-recovery mode
|
||||
// If `ret_status` is `TryAgain` then the next (successful) try will add
|
||||
// the key to the rebuilding transaction object. If `ret_status` is
|
||||
@@ -1713,6 +1946,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// away. So we only need to add to it when `ret_status.ok()`.
|
||||
if (UNLIKELY(!ret_status.IsTryAgain() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::DeleteRange(
|
||||
rebuilding_trx_, column_family_id, begin_key, end_key);
|
||||
}
|
||||
@@ -1721,8 +1955,10 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
Status MergeCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(write_after_commit_ && rebuilding_trx_ != nullptr)) {
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
return WriteBatchInternal::Merge(rebuilding_trx_, column_family_id, key,
|
||||
value);
|
||||
// else insert the values to the memtable right away
|
||||
@@ -1734,6 +1970,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(!write_after_commit_);
|
||||
// The CF is probably flushed and hence no need for insert but we still
|
||||
// need to keep track of the keys for upcoming rollback/commit.
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::Merge(rebuilding_trx_,
|
||||
column_family_id, key, value);
|
||||
if (ret_status.ok()) {
|
||||
@@ -1801,7 +2038,6 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
assert(merge_operator);
|
||||
|
||||
std::string new_value;
|
||||
|
||||
Status merge_status = MergeHelper::TimedFullMerge(
|
||||
merge_operator, key, &get_value_slice, {value}, &new_value,
|
||||
moptions->info_log, moptions->statistics, Env::Default());
|
||||
@@ -1813,16 +2049,35 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
} else {
|
||||
// 3) Add value to memtable
|
||||
assert(!concurrent_memtable_writes_);
|
||||
ret_status = mem->Add(sequence_, kTypeValue, key, new_value);
|
||||
if (kv_prot_info != nullptr) {
|
||||
auto merged_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
merged_kv_prot_info.UpdateV(value, new_value);
|
||||
merged_kv_prot_info.UpdateO(kTypeMerge, kTypeValue);
|
||||
ret_status = mem->Add(sequence_, kTypeValue, key, new_value,
|
||||
&merged_kv_prot_info);
|
||||
} else {
|
||||
ret_status = mem->Add(sequence_, kTypeValue, key, new_value,
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!perform_merge) {
|
||||
assert(ret_status.ok());
|
||||
// Add merge operand to memtable
|
||||
ret_status =
|
||||
mem->Add(sequence_, kTypeMerge, key, value,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem));
|
||||
if (kv_prot_info != nullptr) {
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
ret_status =
|
||||
mem->Add(sequence_, kTypeMerge, key, value, &mem_kv_prot_info,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem));
|
||||
} else {
|
||||
ret_status = mem->Add(
|
||||
sequence_, kTypeMerge, key, value, nullptr /* kv_prot_info */,
|
||||
concurrent_memtable_writes_, get_post_process_info(mem));
|
||||
}
|
||||
}
|
||||
|
||||
if (UNLIKELY(ret_status.IsTryAgain())) {
|
||||
@@ -1840,6 +2095,7 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
// away. So we only need to add to it when `ret_status.ok()`.
|
||||
if (UNLIKELY(ret_status.ok() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
// TODO(ajkr): propagate `ProtectionInfoKVOTS64`.
|
||||
ret_status = WriteBatchInternal::Merge(rebuilding_trx_, column_family_id,
|
||||
key, value);
|
||||
}
|
||||
@@ -1848,8 +2104,18 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
|
||||
Status PutBlobIndexCF(uint32_t column_family_id, const Slice& key,
|
||||
const Slice& value) override {
|
||||
// Same as PutCF except for value type.
|
||||
return PutCFImpl(column_family_id, key, value, kTypeBlobIndex);
|
||||
const auto* kv_prot_info = NextProtectionInfo();
|
||||
if (kv_prot_info != nullptr) {
|
||||
// Memtable needs seqno, doesn't need CF ID
|
||||
auto mem_kv_prot_info =
|
||||
kv_prot_info->StripC(column_family_id).ProtectS(sequence_);
|
||||
// Same as PutCF except for value type.
|
||||
return PutCFImpl(column_family_id, key, value, kTypeBlobIndex,
|
||||
&mem_kv_prot_info);
|
||||
} else {
|
||||
return PutCFImpl(column_family_id, key, value, kTypeBlobIndex,
|
||||
nullptr /* kv_prot_info */);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckMemtableFull() {
|
||||
@@ -2055,8 +2321,8 @@ Status WriteBatchInternal::InsertInto(
|
||||
MemTableInserter inserter(
|
||||
sequence, memtables, flush_scheduler, trim_history_scheduler,
|
||||
ignore_missing_column_families, recovery_log_number, db,
|
||||
concurrent_memtable_writes, nullptr /*has_valid_writes*/, seq_per_batch,
|
||||
batch_per_txn);
|
||||
concurrent_memtable_writes, nullptr /* prot_info */,
|
||||
nullptr /*has_valid_writes*/, seq_per_batch, batch_per_txn);
|
||||
for (auto w : write_group) {
|
||||
if (w->CallbackFailed()) {
|
||||
continue;
|
||||
@@ -2069,6 +2335,7 @@ Status WriteBatchInternal::InsertInto(
|
||||
}
|
||||
SetSequence(w->batch, inserter.sequence());
|
||||
inserter.set_log_number_ref(w->log_ref);
|
||||
inserter.set_prot_info(w->batch->prot_info_.get());
|
||||
w->status = w->batch->Iterate(&inserter);
|
||||
if (!w->status.ok()) {
|
||||
return w->status;
|
||||
@@ -2090,13 +2357,15 @@ Status WriteBatchInternal::InsertInto(
|
||||
(void)batch_cnt;
|
||||
#endif
|
||||
assert(writer->ShouldWriteToMemtable());
|
||||
MemTableInserter inserter(
|
||||
sequence, memtables, flush_scheduler, trim_history_scheduler,
|
||||
ignore_missing_column_families, log_number, db,
|
||||
concurrent_memtable_writes, nullptr /*has_valid_writes*/, seq_per_batch,
|
||||
batch_per_txn, hint_per_batch);
|
||||
MemTableInserter inserter(sequence, memtables, flush_scheduler,
|
||||
trim_history_scheduler,
|
||||
ignore_missing_column_families, log_number, db,
|
||||
concurrent_memtable_writes, nullptr /* prot_info */,
|
||||
nullptr /*has_valid_writes*/, seq_per_batch,
|
||||
batch_per_txn, hint_per_batch);
|
||||
SetSequence(writer->batch, sequence);
|
||||
inserter.set_log_number_ref(writer->log_ref);
|
||||
inserter.set_prot_info(writer->batch->prot_info_.get());
|
||||
Status s = writer->batch->Iterate(&inserter);
|
||||
assert(!seq_per_batch || batch_cnt != 0);
|
||||
assert(!seq_per_batch || inserter.sequence() - sequence == batch_cnt);
|
||||
@@ -2116,8 +2385,8 @@ Status WriteBatchInternal::InsertInto(
|
||||
MemTableInserter inserter(Sequence(batch), memtables, flush_scheduler,
|
||||
trim_history_scheduler,
|
||||
ignore_missing_column_families, log_number, db,
|
||||
concurrent_memtable_writes, has_valid_writes,
|
||||
seq_per_batch, batch_per_txn);
|
||||
concurrent_memtable_writes, batch->prot_info_.get(),
|
||||
has_valid_writes, seq_per_batch, batch_per_txn);
|
||||
Status s = batch->Iterate(&inserter);
|
||||
if (next_seq != nullptr) {
|
||||
*next_seq = inserter.sequence();
|
||||
@@ -2130,6 +2399,7 @@ Status WriteBatchInternal::InsertInto(
|
||||
|
||||
Status WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) {
|
||||
assert(contents.size() >= WriteBatchInternal::kHeader);
|
||||
assert(b->prot_info_ == nullptr);
|
||||
b->rep_.assign(contents.data(), contents.size());
|
||||
b->content_flags_.store(ContentFlags::DEFERRED, std::memory_order_relaxed);
|
||||
return Status::OK();
|
||||
@@ -2137,6 +2407,8 @@ Status WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) {
|
||||
|
||||
Status WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src,
|
||||
const bool wal_only) {
|
||||
assert(dst->Count() == 0 ||
|
||||
(dst->prot_info_ == nullptr) == (src->prot_info_ == nullptr));
|
||||
size_t src_len;
|
||||
int src_count;
|
||||
uint32_t src_flags;
|
||||
@@ -2153,6 +2425,13 @@ Status WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src,
|
||||
src_flags = src->content_flags_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (dst->prot_info_ != nullptr) {
|
||||
std::copy(src->prot_info_->entries_.begin(),
|
||||
src->prot_info_->entries_.begin() + src_count,
|
||||
std::back_inserter(dst->prot_info_->entries_));
|
||||
} else if (src->prot_info_ != nullptr) {
|
||||
dst->prot_info_.reset(new WriteBatch::ProtectionInfo(*src->prot_info_));
|
||||
}
|
||||
SetCount(dst, Count(dst) + src_count);
|
||||
assert(src->rep_.size() >= WriteBatchInternal::kHeader);
|
||||
dst->rep_.append(src->rep_.data() + WriteBatchInternal::kHeader, src_len);
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
#include "db/flush_scheduler.h"
|
||||
#include "db/kv_checksum.h"
|
||||
#include "db/trim_history_scheduler.h"
|
||||
#include "db/write_thread.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -61,6 +63,14 @@ class ColumnFamilyMemTablesDefault : public ColumnFamilyMemTables {
|
||||
MemTable* mem_;
|
||||
};
|
||||
|
||||
struct WriteBatch::ProtectionInfo {
|
||||
// `WriteBatch` usually doesn't contain a huge number of keys so protecting
|
||||
// with a fixed, non-configurable eight bytes per key may work well enough.
|
||||
autovector<ProtectionInfoKVOTC64> entries_;
|
||||
|
||||
size_t GetBytesPerKey() const { return 8; }
|
||||
};
|
||||
|
||||
// WriteBatchInternal provides static methods for manipulating a
|
||||
// WriteBatch that we don't want in the public WriteBatch interface.
|
||||
class WriteBatchInternal {
|
||||
@@ -232,6 +242,9 @@ class LocalSavePoint {
|
||||
if (batch_->max_bytes_ && batch_->rep_.size() > batch_->max_bytes_) {
|
||||
batch_->rep_.resize(savepoint_.size);
|
||||
WriteBatchInternal::SetCount(batch_, savepoint_.count);
|
||||
if (batch_->prot_info_ != nullptr) {
|
||||
batch_->prot_info_->entries_.resize(savepoint_.count);
|
||||
}
|
||||
batch_->content_flags_.store(savepoint_.content_flags,
|
||||
std::memory_order_relaxed);
|
||||
return Status::MemoryLimit();
|
||||
|
||||
@@ -465,6 +465,11 @@ size_t WriteThread::EnterAsBatchGroupLeader(Writer* leader,
|
||||
break;
|
||||
}
|
||||
|
||||
if (w->protection_bytes_per_key != leader->protection_bytes_per_key) {
|
||||
// Do not mix writes with different levels of integrity protection.
|
||||
break;
|
||||
}
|
||||
|
||||
if (w->batch == nullptr) {
|
||||
// Do not include those writes with nullptr batch. Those are not writes,
|
||||
// those are something else. They want to be alone
|
||||
|
||||
@@ -119,6 +119,7 @@ class WriteThread {
|
||||
bool disable_wal;
|
||||
bool disable_memtable;
|
||||
size_t batch_cnt; // if non-zero, number of sub-batches in the write batch
|
||||
size_t protection_bytes_per_key;
|
||||
PreReleaseCallback* pre_release_callback;
|
||||
uint64_t log_used; // log number that this batch was inserted into
|
||||
uint64_t log_ref; // log number that memtable insert should reference
|
||||
@@ -142,6 +143,7 @@ class WriteThread {
|
||||
disable_wal(false),
|
||||
disable_memtable(false),
|
||||
batch_cnt(0),
|
||||
protection_bytes_per_key(0),
|
||||
pre_release_callback(nullptr),
|
||||
log_used(0),
|
||||
log_ref(0),
|
||||
@@ -163,6 +165,7 @@ class WriteThread {
|
||||
disable_wal(write_options.disableWAL),
|
||||
disable_memtable(_disable_memtable),
|
||||
batch_cnt(_batch_cnt),
|
||||
protection_bytes_per_key(_batch->GetProtectionBytesPerKey()),
|
||||
pre_release_callback(_pre_release_callback),
|
||||
log_used(0),
|
||||
log_ref(_log_ref),
|
||||
|
||||
@@ -31,7 +31,8 @@ class BatchedOpsStressTest : public StressTest {
|
||||
std::string keys[10] = {"9", "8", "7", "6", "5", "4", "3", "2", "1", "0"};
|
||||
std::string values[10] = {"9", "8", "7", "6", "5", "4", "3", "2", "1", "0"};
|
||||
Slice value_slices[10];
|
||||
WriteBatch batch;
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */, 0 /* ts_sz */,
|
||||
FLAGS_batch_protection_bytes_per_key);
|
||||
Status s;
|
||||
auto cfh = column_families_[rand_column_families[0]];
|
||||
std::string key_str = Key(rand_keys[0]);
|
||||
@@ -66,7 +67,8 @@ class BatchedOpsStressTest : public StressTest {
|
||||
std::unique_ptr<MutexLock>& /* lock */) override {
|
||||
std::string keys[10] = {"9", "7", "5", "3", "1", "8", "6", "4", "2", "0"};
|
||||
|
||||
WriteBatch batch;
|
||||
WriteBatch batch(0 /* reserved_bytes */, 0 /* max_bytes */, 0 /* ts_sz */,
|
||||
FLAGS_batch_protection_bytes_per_key);
|
||||
Status s;
|
||||
auto cfh = column_families_[rand_column_families[0]];
|
||||
std::string key_str = Key(rand_keys[0]);
|
||||
|
||||
@@ -206,6 +206,7 @@ DECLARE_string(bottommost_compression_type);
|
||||
DECLARE_int32(compression_max_dict_bytes);
|
||||
DECLARE_int32(compression_zstd_max_train_bytes);
|
||||
DECLARE_int32(compression_parallel_threads);
|
||||
DECLARE_uint64(compression_max_dict_buffer_bytes);
|
||||
DECLARE_string(checksum_type);
|
||||
DECLARE_string(hdfs);
|
||||
DECLARE_string(env_uri);
|
||||
@@ -232,6 +233,7 @@ DECLARE_int32(get_property_one_in);
|
||||
DECLARE_string(file_checksum_impl);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Options for StackableDB-based BlobDB
|
||||
DECLARE_bool(use_blob_db);
|
||||
DECLARE_uint64(blob_db_min_blob_size);
|
||||
DECLARE_uint64(blob_db_bytes_per_sync);
|
||||
@@ -239,6 +241,16 @@ DECLARE_uint64(blob_db_file_size);
|
||||
DECLARE_bool(blob_db_enable_gc);
|
||||
DECLARE_double(blob_db_gc_cutoff);
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Options for integrated BlobDB
|
||||
DECLARE_bool(allow_setting_blob_options_dynamically);
|
||||
DECLARE_bool(enable_blob_files);
|
||||
DECLARE_uint64(min_blob_size);
|
||||
DECLARE_uint64(blob_file_size);
|
||||
DECLARE_string(blob_compression_type);
|
||||
DECLARE_bool(enable_blob_garbage_collection);
|
||||
DECLARE_double(blob_garbage_collection_age_cutoff);
|
||||
|
||||
DECLARE_int32(approximate_size_one_in);
|
||||
DECLARE_bool(sync_fault_injection);
|
||||
|
||||
@@ -246,6 +258,7 @@ DECLARE_bool(best_efforts_recovery);
|
||||
DECLARE_bool(skip_verifydb);
|
||||
DECLARE_bool(enable_compaction_filter);
|
||||
DECLARE_bool(paranoid_file_checks);
|
||||
DECLARE_uint64(batch_protection_bytes_per_key);
|
||||
|
||||
const long KB = 1024;
|
||||
const int kRandomValueMaxFactor = 3;
|
||||
|
||||
@@ -325,33 +325,68 @@ DEFINE_bool(enable_write_thread_adaptive_yield, true,
|
||||
"Use a yielding spin loop for brief writer thread waits.");
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// BlobDB Options
|
||||
DEFINE_bool(use_blob_db, false, "Use BlobDB.");
|
||||
// Options for StackableDB-based BlobDB
|
||||
DEFINE_bool(use_blob_db, false, "[Stacked BlobDB] Use BlobDB.");
|
||||
|
||||
DEFINE_uint64(blob_db_min_blob_size,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().min_blob_size,
|
||||
"Smallest blob to store in a file. Blobs smaller than this "
|
||||
"will be inlined with the key in the LSM tree.");
|
||||
DEFINE_uint64(
|
||||
blob_db_min_blob_size,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().min_blob_size,
|
||||
"[Stacked BlobDB] Smallest blob to store in a file. Blobs "
|
||||
"smaller than this will be inlined with the key in the LSM tree.");
|
||||
|
||||
DEFINE_uint64(blob_db_bytes_per_sync,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().bytes_per_sync,
|
||||
"Sync blob files once per every N bytes written.");
|
||||
DEFINE_uint64(
|
||||
blob_db_bytes_per_sync,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().bytes_per_sync,
|
||||
"[Stacked BlobDB] Sync blob files once per every N bytes written.");
|
||||
|
||||
DEFINE_uint64(blob_db_file_size,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().blob_file_size,
|
||||
"Target size of each blob file.");
|
||||
"[Stacked BlobDB] Target size of each blob file.");
|
||||
|
||||
DEFINE_bool(
|
||||
blob_db_enable_gc,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().enable_garbage_collection,
|
||||
"Enable BlobDB garbage collection.");
|
||||
"[Stacked BlobDB] Enable BlobDB garbage collection.");
|
||||
|
||||
DEFINE_double(
|
||||
blob_db_gc_cutoff,
|
||||
ROCKSDB_NAMESPACE::blob_db::BlobDBOptions().garbage_collection_cutoff,
|
||||
"Cutoff ratio for BlobDB garbage collection.");
|
||||
"[Stacked BlobDB] Cutoff ratio for BlobDB garbage collection.");
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// Options for integrated BlobDB
|
||||
DEFINE_bool(allow_setting_blob_options_dynamically, false,
|
||||
"[Integrated BlobDB] Allow setting blob options dynamically.");
|
||||
|
||||
DEFINE_bool(
|
||||
enable_blob_files,
|
||||
ROCKSDB_NAMESPACE::AdvancedColumnFamilyOptions().enable_blob_files,
|
||||
"[Integrated BlobDB] Enable writing large values to separate blob files.");
|
||||
|
||||
DEFINE_uint64(min_blob_size,
|
||||
ROCKSDB_NAMESPACE::AdvancedColumnFamilyOptions().min_blob_size,
|
||||
"[Integrated BlobDB] The size of the smallest value to be stored "
|
||||
"separately in a blob file.");
|
||||
|
||||
DEFINE_uint64(blob_file_size,
|
||||
ROCKSDB_NAMESPACE::AdvancedColumnFamilyOptions().blob_file_size,
|
||||
"[Integrated BlobDB] The size limit for blob files.");
|
||||
|
||||
DEFINE_string(blob_compression_type, "none",
|
||||
"[Integrated BlobDB] The compression algorithm to use for large "
|
||||
"values stored in blob files.");
|
||||
|
||||
DEFINE_bool(enable_blob_garbage_collection,
|
||||
ROCKSDB_NAMESPACE::AdvancedColumnFamilyOptions()
|
||||
.enable_blob_garbage_collection,
|
||||
"[Integrated BlobDB] Enable blob garbage collection.");
|
||||
|
||||
DEFINE_double(blob_garbage_collection_age_cutoff,
|
||||
ROCKSDB_NAMESPACE::AdvancedColumnFamilyOptions()
|
||||
.blob_garbage_collection_age_cutoff,
|
||||
"[Integrated BlobDB] The cutoff in terms of blob file age for "
|
||||
"garbage collection.");
|
||||
|
||||
static const bool FLAGS_subcompactions_dummy __attribute__((__unused__)) =
|
||||
RegisterFlagValidator(&FLAGS_subcompactions, &ValidateUint32Range);
|
||||
|
||||
@@ -626,6 +661,10 @@ DEFINE_int32(compression_zstd_max_train_bytes, 0,
|
||||
DEFINE_int32(compression_parallel_threads, 1,
|
||||
"Number of threads for parallel compression.");
|
||||
|
||||
DEFINE_uint64(compression_max_dict_buffer_bytes, 0,
|
||||
"Buffering limit for SST file data to sample for dictionary "
|
||||
"compression.");
|
||||
|
||||
DEFINE_string(bottommost_compression_type, "disable",
|
||||
"Algorithm to use to compress bottommost level of the database. "
|
||||
"\"disable\" means disabling the feature");
|
||||
@@ -753,6 +792,11 @@ DEFINE_bool(paranoid_file_checks, true,
|
||||
"After writing every SST file, reopen it and read all the keys "
|
||||
"and validate checksums");
|
||||
|
||||
DEFINE_uint64(batch_protection_bytes_per_key, 0,
|
||||
"If nonzero, enables integrity protection in `WriteBatch` at the "
|
||||
"specified number of bytes per key. Currently the only supported "
|
||||
"nonzero value is eight.");
|
||||
|
||||
DEFINE_string(file_checksum_impl, "none",
|
||||
"Name of an implementation for file_checksum_gen_factory, or "
|
||||
"\"none\" for null.");
|
||||
|
||||
@@ -108,6 +108,22 @@ std::shared_ptr<Cache> StressTest::NewCache(size_t capacity) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> StressTest::GetBlobCompressionTags() {
|
||||
std::vector<std::string> compression_tags{"kNoCompression"};
|
||||
|
||||
if (Snappy_Supported()) {
|
||||
compression_tags.emplace_back("kSnappyCompression");
|
||||
}
|
||||
if (LZ4_Supported()) {
|
||||
compression_tags.emplace_back("kLZ4Compression");
|
||||
}
|
||||
if (ZSTD_Supported()) {
|
||||
compression_tags.emplace_back("kZSTD");
|
||||
}
|
||||
|
||||
return compression_tags;
|
||||
}
|
||||
|
||||
bool StressTest::BuildOptionsTable() {
|
||||
if (FLAGS_set_options_one_in <= 0) {
|
||||
return true;
|
||||
@@ -186,6 +202,21 @@ bool StressTest::BuildOptionsTable() {
|
||||
{"max_sequential_skip_in_iterations", {"4", "8", "12"}},
|
||||
};
|
||||
|
||||
if (FLAGS_allow_setting_blob_options_dynamically) {
|
||||
options_tbl.emplace("enable_blob_files",
|
||||
std::vector<std::string>{"false", "true"});
|
||||
options_tbl.emplace("min_blob_size",
|
||||
std::vector<std::string>{"0", "16", "256"});
|
||||
options_tbl.emplace("blob_file_size",
|
||||
std::vector<std::string>{"1M", "16M", "256M", "1G"});
|
||||
options_tbl.emplace("blob_compression_type", GetBlobCompressionTags());
|
||||
options_tbl.emplace("enable_blob_garbage_collection",
|
||||
std::vector<std::string>{"false", "true"});
|
||||
options_tbl.emplace(
|
||||
"blob_garbage_collection_age_cutoff",
|
||||
std::vector<std::string>{"0.0", "0.25", "0.5", "0.75", "1.0"});
|
||||
}
|
||||
|
||||
options_table_ = std::move(options_tbl);
|
||||
|
||||
for (const auto& iter : options_table_) {
|
||||
@@ -472,7 +503,7 @@ Status StressTest::NewTxn(WriteOptions& write_opts, Transaction** txn) {
|
||||
}
|
||||
static std::atomic<uint64_t> txn_id = {0};
|
||||
TransactionOptions txn_options;
|
||||
txn_options.lock_timeout = 60000; // 1min
|
||||
txn_options.lock_timeout = 600000; // 10 min
|
||||
txn_options.deadlock_detect = true;
|
||||
*txn = txn_db_->BeginTransaction(write_opts, txn_options);
|
||||
auto istr = std::to_string(txn_id.fetch_add(1));
|
||||
@@ -1869,7 +1900,7 @@ void StressTest::PrintEnv() const {
|
||||
fprintf(stdout, "TransactionDB : %s\n",
|
||||
FLAGS_use_txn ? "true" : "false");
|
||||
#ifndef ROCKSDB_LITE
|
||||
fprintf(stdout, "BlobDB : %s\n",
|
||||
fprintf(stdout, "Stacked BlobDB : %s\n",
|
||||
FLAGS_use_blob_db ? "true" : "false");
|
||||
#endif // !ROCKSDB_LITE
|
||||
fprintf(stdout, "Read only mode : %s\n",
|
||||
@@ -2052,6 +2083,8 @@ void StressTest::Open() {
|
||||
FLAGS_compression_zstd_max_train_bytes;
|
||||
options_.compression_opts.parallel_threads =
|
||||
FLAGS_compression_parallel_threads;
|
||||
options_.compression_opts.max_dict_buffer_bytes =
|
||||
FLAGS_compression_max_dict_buffer_bytes;
|
||||
options_.create_if_missing = true;
|
||||
options_.max_manifest_file_size = FLAGS_max_manifest_file_size;
|
||||
options_.inplace_update_support = FLAGS_in_place_update;
|
||||
@@ -2083,6 +2116,17 @@ void StressTest::Open() {
|
||||
options_.file_checksum_gen_factory =
|
||||
GetFileChecksumImpl(FLAGS_file_checksum_impl);
|
||||
options_.track_and_verify_wals_in_manifest = true;
|
||||
|
||||
// Integrated BlobDB
|
||||
options_.enable_blob_files = FLAGS_enable_blob_files;
|
||||
options_.min_blob_size = FLAGS_min_blob_size;
|
||||
options_.blob_file_size = FLAGS_blob_file_size;
|
||||
options_.blob_compression_type =
|
||||
StringToCompressionType(FLAGS_blob_compression_type.c_str());
|
||||
options_.enable_blob_garbage_collection =
|
||||
FLAGS_enable_blob_garbage_collection;
|
||||
options_.blob_garbage_collection_age_cutoff =
|
||||
FLAGS_blob_garbage_collection_age_cutoff;
|
||||
} else {
|
||||
#ifdef ROCKSDB_LITE
|
||||
fprintf(stderr, "--options_file not supported in lite mode\n");
|
||||
@@ -2172,6 +2216,30 @@ void StressTest::Open() {
|
||||
options_.best_efforts_recovery = FLAGS_best_efforts_recovery;
|
||||
options_.paranoid_file_checks = FLAGS_paranoid_file_checks;
|
||||
|
||||
if ((options_.enable_blob_files || options_.enable_blob_garbage_collection ||
|
||||
FLAGS_allow_setting_blob_options_dynamically) &&
|
||||
(FLAGS_use_merge || FLAGS_enable_compaction_filter ||
|
||||
FLAGS_backup_one_in > 0 || FLAGS_best_efforts_recovery)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Integrated BlobDB is currently incompatible with Merge, compaction "
|
||||
"filters, backup/restore, and best-effort recovery\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (options_.enable_blob_files) {
|
||||
fprintf(stdout,
|
||||
"Integrated BlobDB: blob files enabled, min blob size %" PRIu64
|
||||
", blob file size %" PRIu64 ", blob compression type %s\n",
|
||||
options_.min_blob_size, options_.blob_file_size,
|
||||
CompressionTypeToString(options_.blob_compression_type).c_str());
|
||||
}
|
||||
|
||||
if (options_.enable_blob_garbage_collection) {
|
||||
fprintf(stdout, "Integrated BlobDB: blob GC enabled, cutoff %f\n",
|
||||
options_.blob_garbage_collection_age_cutoff);
|
||||
}
|
||||
|
||||
fprintf(stdout, "DB path: [%s]\n", FLAGS_db.c_str());
|
||||
|
||||
Status s;
|
||||
@@ -2229,6 +2297,7 @@ void StressTest::Open() {
|
||||
options_.create_missing_column_families = true;
|
||||
if (!FLAGS_use_txn) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
// StackableDB-based BlobDB
|
||||
if (FLAGS_use_blob_db) {
|
||||
blob_db::BlobDBOptions blob_db_options;
|
||||
blob_db_options.min_blob_size = FLAGS_blob_db_min_blob_size;
|
||||
|
||||
Loaded 100 of 260 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user