mirror of
https://github.com/facebook/rocksdb.git
synced 2026-07-07 22:55:23 +08:00
Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d95050ca7 | |||
| 9857d45f37 | |||
| 5884cf5d5b | |||
| e32a64aa54 | |||
| 031368f67a | |||
| 5105db8d7b | |||
| ba7c46a3d8 | |||
| be40c99dda | |||
| 89dd231f6a | |||
| e931bbfec0 | |||
| 67f8189e68 | |||
| 0a91c691a9 | |||
| dff9219df6 | |||
| eeea27a048 | |||
| 2b64cddf99 | |||
| 79a21d67cb | |||
| 2eaad9e1b1 | |||
| 2a2d4d0f37 | |||
| a8aeefd0fd | |||
| 4d1ac19e3d | |||
| fbce7a3808 | |||
| 62afa968c2 | |||
| 75e4af14e0 | |||
| 239d17a19c | |||
| 04b3524ad0 | |||
| 99f5a800c3 | |||
| 29d12748b0 | |||
| 2021392e25 | |||
| a0e4421e81 | |||
| 1afbd1948c | |||
| 003e72b201 | |||
| 491779514e | |||
| b1ee191405 | |||
| 07c0fc002a | |||
| fd7d8dc56e | |||
| 40f2b65008 | |||
| 8ff6557e7f | |||
| 8e2749fd3a | |||
| 71239908cf | |||
| 80159f6e0b | |||
| 8a1488efbf | |||
| 3c2a448856 | |||
| 89cc06b3e7 | |||
| efe827baf0 | |||
| 98236fb10e | |||
| 7b2216c906 | |||
| 66e54c5984 | |||
| 93c6c18cf9 | |||
| 07030c6f4a | |||
| 11c4be2222 | |||
| d8bd9fc7b3 | |||
| db03172d08 | |||
| 8a06fe278f | |||
| ba2a3bf092 | |||
| 20c7d7c58a | |||
| b77569f18b | |||
| 1df8584896 | |||
| eee0af9af1 | |||
| e34b2e9f41 | |||
| 61932cdf1d | |||
| e102de7318 | |||
| ee4bd4780b | |||
| eb5a8c06dd | |||
| 70f2e0916a | |||
| ac2f90d6f9 | |||
| 29e8f6a698 | |||
| 3b9bfe8f14 | |||
| 7fec715db4 | |||
| e062a719cc | |||
| 9e1640403a | |||
| b937be3779 | |||
| 0b06af9146 | |||
| eb65d673fe | |||
| cc431ece37 | |||
| 51a8dc6d14 | |||
| dd6b7fc520 | |||
| 0baa5055f1 | |||
| 1a5fc4f577 | |||
| 7c19d43883 | |||
| 5c585e1908 | |||
| a65e905bbb | |||
| 7169ca9c80 | |||
| 8a97f35619 | |||
| 6cacb0d3a1 | |||
| 8c93b16f02 | |||
| 303d283420 | |||
| 4c336c6912 | |||
| 699411b2ca | |||
| 84a700819e | |||
| 869f0538dd | |||
| 9627e342c8 | |||
| 1c5f13f2a5 | |||
| 7582c5682b | |||
| 1861de455e | |||
| 5bd1258381 | |||
| 1aae41786a | |||
| 9aa1b1dc19 | |||
| e300ce211d | |||
| 60af964372 | |||
| bbbb5a280d | |||
| 76ef894f9f | |||
| bb69b4ce7f | |||
| cf9d8e45c0 | |||
| 0dc437d65c | |||
| 2400cd69e3 | |||
| ec346da98c | |||
| c442f6809f | |||
| 8b6b6aeb1a | |||
| c57f914482 | |||
| bcba372352 | |||
| 18aee7db7e | |||
| 16d103d35b | |||
| 202605143b | |||
| 9f1c84ca47 | |||
| c3911f1a72 | |||
| 5e794b0841 | |||
| 8b8a2e9f05 | |||
| 1e40696dd1 | |||
| 1ce105d0ea | |||
| cdc7ba3a32 | |||
| da42eceabc | |||
| 4c2aef04bd | |||
| 81543369e5 | |||
| 1f627210ca | |||
| c9c9709a1a | |||
| b6d8e36741 | |||
| fde0cd7ced | |||
| 06a92fcf5c | |||
| 746909ceda | |||
| 0b94468bba | |||
| 881e0dcc09 | |||
| c992eb118b | |||
| 1adbceb581 | |||
| a388c8cc6b | |||
| 30beecef8c | |||
| 6773901f76 | |||
| 7eb2824e3f | |||
| 394210f280 | |||
| 99a0305bb8 | |||
| ea347d80df | |||
| 2404f8b9ec | |||
| 248d10fb96 | |||
| 793e9b7f5b | |||
| 9a690a74e1 | |||
| 6c2c0635c9 | |||
| 0e2e67562f | |||
| f35f7f2704 | |||
| 6134ce6444 | |||
| a7a04b6898 | |||
| 25d54c799c | |||
| 6595267980 | |||
| 1b224324b5 | |||
| a16d1b2fd3 | |||
| b1cdb8cc86 | |||
| d8ec0a760a | |||
| 1c78e4b235 | |||
| eef27d0048 | |||
| 5227b315ec | |||
| 73dbe10bbf | |||
| 1eda625eab | |||
| fc9b416013 | |||
| b4cd51d847 | |||
| 00751e4292 | |||
| 0ea7db768e | |||
| ed90e2a450 | |||
| f4ade82ad2 | |||
| e8cb32ed67 | |||
| fa2a8cda7b | |||
| a8c89cc969 | |||
| ccbf468cb1 | |||
| 1a83f5a8ac | |||
| 05fba96927 | |||
| 850cc0dbed | |||
| 6528ecc800 | |||
| b99fe1ab74 | |||
| db87afbcb3 | |||
| 7b65666cf1 | |||
| c87c3a48af | |||
| 4a6840bd00 | |||
| 3dc823212d | |||
| 16bff5370d |
+177
-14
@@ -17,6 +17,13 @@ commands:
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install pyenv
|
||||
|
||||
install-cmake-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
name: Install cmake on macos
|
||||
command: |
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
|
||||
|
||||
increase-max-open-files-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
@@ -27,10 +34,14 @@ commands:
|
||||
sudo launchctl limit maxfiles 1048576
|
||||
|
||||
pre-steps:
|
||||
parameters:
|
||||
python-version:
|
||||
default: "3.5.9"
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- run: pyenv install --skip-existing 3.5.9
|
||||
- run: pyenv global 3.5.9
|
||||
- run: pyenv install --skip-existing <<parameters.python-version>>
|
||||
- run: pyenv global <<parameters.python-version>>
|
||||
- run:
|
||||
name: Setup Environment Variables
|
||||
command: |
|
||||
@@ -39,6 +50,11 @@ commands:
|
||||
echo "export SKIP_FORMAT_BUCK_CHECKS=1" >> $BASH_ENV
|
||||
echo "export PRINT_PARALLEL_OUTPUTS=1" >> $BASH_ENV
|
||||
|
||||
pre-steps-macos:
|
||||
steps:
|
||||
- pre-steps:
|
||||
python-version: "3.6.0"
|
||||
|
||||
post-steps:
|
||||
steps:
|
||||
- slack/status: *notify-on-master-failure
|
||||
@@ -63,7 +79,7 @@ commands:
|
||||
name: Install gflags
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
|
||||
|
||||
install-gflags-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
@@ -77,7 +93,7 @@ commands:
|
||||
name: Install gtest-parallel
|
||||
command: |
|
||||
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
|
||||
echo 'export PATH=$HOME/gtest-parallel:$PATH' >> $BASH_ENV
|
||||
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
@@ -89,15 +105,27 @@ executors:
|
||||
jobs:
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: 11.3.0
|
||||
xcode: 9.4.1
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- pre-steps
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-cmake:
|
||||
macos:
|
||||
xcode: 9.4.1
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-cmake-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n 1048576 && (mkdir build && cd build && cmake -DWITH_GFLAGS=0 .. && make V=1 -j32) | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
@@ -108,6 +136,26 @@ jobs:
|
||||
- run: make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-mem-env:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: MEM_ENV=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-encrypted-env:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: ENCRYPTED_ENV=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
@@ -115,7 +163,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 LIB_MODE=shared OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j32 all check_some | .circleci/cat_ignore_eagain
|
||||
- run: ASSERT_STATUS_CHECKED=1 TEST_UINT128_COMPAT=1 ROCKSDB_MODIFY_NPHASH=1 LIB_MODE=shared OPT="-DROCKSDB_NAMESPACE=alternative_rocksdb_ns" make V=1 -j32 all check_some | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-release:
|
||||
@@ -316,25 +364,102 @@ jobs:
|
||||
build-linux-java:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: 2xlarge
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run:
|
||||
name: "Build RocksDBJava"
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
make V=1 J=32 -j32 rocksdbjava jtest | .circleci/cat_ignore_eagain
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjava | .circleci/cat_ignore_eagain
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=8 -j8 jtest | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-java-static:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-java:
|
||||
macos:
|
||||
xcode: 9.4.1
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjava | .circleci/cat_ignore_eagain
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=8 -j8 jtest | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static:
|
||||
macos:
|
||||
xcode: 9.4.1
|
||||
resource_class: medium
|
||||
environment:
|
||||
JAVA_HOME: /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home
|
||||
steps:
|
||||
- increase-max-open-files-on-macos
|
||||
- install-pyenv-on-macos
|
||||
- install-gflags-on-macos
|
||||
- install-cmake-on-macos
|
||||
- pre-steps-macos
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> $BASH_ENV
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-examples:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: medium
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
@@ -344,6 +469,29 @@ jobs:
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4 | ../.circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-cmake-mingw:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y mingw-w64
|
||||
- run: sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
|
||||
- run:
|
||||
name: "Build cmake-mingw"
|
||||
command: |
|
||||
sudo apt-get install snapd && sudo snap install cmake --beta --classic
|
||||
export PATH=/snap/bin:$PATH
|
||||
sudo apt-get install -y openjdk-8-jdk
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
|
||||
export PATH=$JAVA_HOME/bin:$PATH
|
||||
echo "JAVA_HOME=${JAVA_HOME}"
|
||||
which java && java -version
|
||||
which javac && javac -version
|
||||
mkdir build && cd build && cmake -DJNI=1 -DWITH_GFLAGS=OFF .. -DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++ -DCMAKE_SYSTEM_NAME=Windows && make -j4 rocksdb rocksdbjni
|
||||
- post-steps
|
||||
|
||||
build-linux-non-shm:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
@@ -371,13 +519,19 @@ jobs:
|
||||
sed -i 's/[[:space:]]*$//; s/ / \.\//g; s/.*/.\/&/' /tmp/test_list
|
||||
cat /tmp/test_list
|
||||
export TEST_TMPDIR=/tmp/rocksdb_test_tmp
|
||||
/usr/bin/python ../gtest-parallel/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.
|
||||
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
|
||||
|
||||
workflows:
|
||||
build-linux:
|
||||
jobs:
|
||||
- build-linux
|
||||
build-linux-mem-env:
|
||||
jobs:
|
||||
- build-linux-mem-env
|
||||
build-linux-encrypted-env:
|
||||
jobs:
|
||||
- build-linux-encrypted-env
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
jobs:
|
||||
- build-linux-shared_lib-alt_namespace-status_checked
|
||||
@@ -434,6 +588,9 @@ workflows:
|
||||
build-java:
|
||||
jobs:
|
||||
- build-linux-java
|
||||
- build-linux-java-static
|
||||
- build-macos-java
|
||||
- build-macos-java-static
|
||||
build-examples:
|
||||
jobs:
|
||||
- build-examples
|
||||
@@ -457,3 +614,9 @@ workflows:
|
||||
build-macos:
|
||||
jobs:
|
||||
- build-macos
|
||||
build-macos-cmake:
|
||||
jobs:
|
||||
- build-macos-cmake
|
||||
build-cmake-mingw:
|
||||
jobs:
|
||||
- build-cmake-mingw
|
||||
|
||||
@@ -10,7 +10,7 @@ $process = Start-Process "${PWD}\vs_installer.exe" -ArgumentList $VS_INSTALL_ARG
|
||||
Remove-Item -Path vs_installer.exe -Force
|
||||
$exitCode = $process.ExitCode
|
||||
if (($exitCode -ne 0) -and ($exitCode -ne 3010)) {
|
||||
echo "VS 2017 installer exited with code $exitCode, which should be one of [0, 3010]."
|
||||
echo "VS 2015 installer exited with code $exitCode, which should be one of [0, 3010]."
|
||||
curl.exe --retry 3 -kL $COLLECT_DOWNLOAD_LINK --output Collect.exe
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
echo "Download of the VS Collect tool failed."
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: Download clang-format-diff.py
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/clang-format-diff.py
|
||||
args: https://raw.githubusercontent.com/llvm/llvm-project/master/clang/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
@@ -8,6 +8,7 @@ rocksdb.pc
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.o
|
||||
*.o.tmp
|
||||
*.so
|
||||
*.so.*
|
||||
*_test
|
||||
@@ -89,3 +90,6 @@ buckifier/__pycache__
|
||||
compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
|
||||
fuzz/proto/gen/
|
||||
fuzz/crash-*
|
||||
|
||||
@@ -105,6 +105,13 @@ matrix:
|
||||
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
|
||||
|
||||
+24
-4
@@ -571,6 +571,7 @@ set(SOURCES
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_builder.cc
|
||||
db/blob/blob_file_cache.cc
|
||||
db/blob/blob_file_garbage.cc
|
||||
db/blob/blob_file_meta.cc
|
||||
db/blob/blob_file_reader.cc
|
||||
@@ -686,6 +687,7 @@ set(SOURCES
|
||||
monitoring/thread_status_util_debug.cc
|
||||
options/cf_options.cc
|
||||
options/configurable.cc
|
||||
options/customizable.cc
|
||||
options/db_options.cc
|
||||
options/options.cc
|
||||
options/options_helper.cc
|
||||
@@ -809,8 +811,9 @@ set(SOURCES
|
||||
utilities/simulator_cache/sim_cache.cc
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector.cc
|
||||
utilities/trace/file_trace_reader_writer.cc
|
||||
utilities/transactions/lock/lock_tracker.cc
|
||||
utilities/transactions/lock/point_lock_tracker.cc
|
||||
utilities/transactions/lock/lock_manager.cc
|
||||
utilities/transactions/lock/point/point_lock_tracker.cc
|
||||
utilities/transactions/lock/point/point_lock_manager.cc
|
||||
utilities/transactions/optimistic_transaction_db_impl.cc
|
||||
utilities/transactions/optimistic_transaction.cc
|
||||
utilities/transactions/pessimistic_transaction.cc
|
||||
@@ -818,7 +821,6 @@ set(SOURCES
|
||||
utilities/transactions/snapshot_checker.cc
|
||||
utilities/transactions/transaction_base.cc
|
||||
utilities/transactions/transaction_db_mutex_impl.cc
|
||||
utilities/transactions/transaction_lock_mgr.cc
|
||||
utilities/transactions/transaction_util.cc
|
||||
utilities/transactions/write_prepared_txn.cc
|
||||
utilities/transactions/write_prepared_txn_db.cc
|
||||
@@ -829,6 +831,20 @@ set(SOURCES
|
||||
utilities/write_batch_with_index/write_batch_with_index_internal.cc
|
||||
$<TARGET_OBJECTS:build_version>)
|
||||
|
||||
list(APPEND SOURCE
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/concurrent_tree.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/keyrange.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/lock_request.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/locktree.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/manager.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/range_buffer.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/treenode.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/txnid_set.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/locktree/wfg.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/standalone_port.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/util/dbt.cc
|
||||
utilities/transactions/lock/range/range_tree/lib/util/memarena.cc)
|
||||
|
||||
if(HAVE_SSE42 AND NOT MSVC)
|
||||
set_source_files_properties(
|
||||
util/crc32c.cc
|
||||
@@ -1044,8 +1060,10 @@ if(WITH_TESTS)
|
||||
cache/lru_cache_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
db/blob/blob_file_cache_test.cc
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/blob_file_reader_test.cc
|
||||
db/blob/db_blob_basic_test.cc
|
||||
db/blob/db_blob_index_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
@@ -1139,6 +1157,7 @@ if(WITH_TESTS)
|
||||
monitoring/statistics_test.cc
|
||||
monitoring/stats_history_test.cc
|
||||
options/configurable_test.cc
|
||||
options/customizable_test.cc
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
@@ -1175,6 +1194,7 @@ if(WITH_TESTS)
|
||||
util/random_test.cc
|
||||
util/rate_limiter_test.cc
|
||||
util/repeatable_thread_test.cc
|
||||
util/ribbon_test.cc
|
||||
util/slice_test.cc
|
||||
util/slice_transform_test.cc
|
||||
util/timer_queue_test.cc
|
||||
@@ -1201,7 +1221,7 @@ if(WITH_TESTS)
|
||||
utilities/table_properties_collectors/compact_on_deletion_collector_test.cc
|
||||
utilities/transactions/optimistic_transaction_test.cc
|
||||
utilities/transactions/transaction_test.cc
|
||||
utilities/transactions/transaction_lock_mgr_test.cc
|
||||
utilities/transactions/lock/point/point_lock_manager_test.cc
|
||||
utilities/transactions/write_prepared_transaction_test.cc
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
|
||||
+63
-1
@@ -1,11 +1,71 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
## 6.16.4 (03/30/2021)
|
||||
### Bug Fixes
|
||||
* Fix build on ppc64 and musl build.
|
||||
|
||||
## 6.16.3 (02/05/2021)
|
||||
### 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.
|
||||
|
||||
## 6.16.2 (1/21/2021)
|
||||
### Bug Fixes
|
||||
* 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.
|
||||
|
||||
## 6.16.1 (1/20/2021)
|
||||
### 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.
|
||||
|
||||
## 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.
|
||||
* 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
|
||||
* Truncated WALs ending in incomplete records can no longer produce gaps in the recovered data when `WALRecoveryMode::kPointInTimeRecovery` is used. Gaps are still possible when WALs are truncated exactly on record boundaries; for complete protection, users should enable `track_and_verify_wals_in_manifest`.
|
||||
* Fix a bug where compressed blocks read by MultiGet are not inserted into the compressed block cache when use_direct_reads = true.
|
||||
* Fixed the issue of full scanning on obsolete files when there are too many outstanding compactions with ConcurrentTaskLimiter enabled.
|
||||
* 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.
|
||||
|
||||
### New Features
|
||||
* User defined timestamp feature supports `CompactRange` and `GetApproximateSizes`.
|
||||
* Support getting aggregated table properties (kAggregatedTableProperties and kAggregatedTablePropertiesAtLevel) with DB::GetMapProperty, for easier access to the data in a structured format.
|
||||
* Experimental option BlockBasedTableOptions::optimize_filters_for_memory now works with experimental Ribbon filter (as well as Bloom filter).
|
||||
|
||||
### Public API Change
|
||||
* Deprecated public but rarely-used FilterBitsBuilder::CalculateNumEntry, which is replaced with ApproximateNumEntries taking a size_t parameter and returning size_t.
|
||||
* 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.
|
||||
|
||||
## 6.15.0 (11/13/2020)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in the following combination of features: indexes with user keys (`format_version >= 3`), indexes are partitioned (`index_type == kTwoLevelIndexSearch`), and some index partitions are pinned in memory (`BlockBasedTableOptions::pin_l0_filter_and_index_blocks_in_cache`). The bug could cause keys to be truncated when read from the index leading to wrong read results or other unexpected behavior.
|
||||
* Fixed a bug when indexes are partitioned (`index_type == kTwoLevelIndexSearch`), some index partitions are pinned in memory (`BlockBasedTableOptions::pin_l0_filter_and_index_blocks_in_cache`), and partitions reads could be mixed between block cache and directly from the file (e.g., with `enable_index_compression == 1` and `mmap_read == 1`, partitions that were stored uncompressed due to poor compression ratio would be read directly from the file via mmap, while partitions that were stored compressed would be read from block cache). The bug could cause index partitions to be mistakenly considered empty during reads leading to wrong read results.
|
||||
* Since 6.12, memtable lookup should report unrecognized value_type as corruption (#7121).
|
||||
* Since 6.14, fix false positive flush/compaction `Status::Corruption` failure when `paranoid_file_checks == true` and range tombstones were written to the compaction output files.
|
||||
* Since 6.14, fix a bug that could cause a stalled write to crash with mixed of slowdown and no_slowdown writes (`WriteOptions.no_slowdown=true`).
|
||||
* Fixed a bug which causes hang in closing DB when refit level is set in opt build. It was because ContinueBackgroundWork() was called in assert statement which is a no op. It was introduced in 6.14.
|
||||
* Fixed a bug which causes Get() to return incorrect result when a key's merge operand is applied twice. This can occur if the thread performing Get() runs concurrently with a background flush thread and another thread writing to the MANIFEST file (PR6069).
|
||||
* Reverted a behavior change silently introduced in 6.14.2, in which the effects of the `ignore_unknown_options` flag (used in option parsing/loading functions) changed.
|
||||
* Reverted a behavior change silently introduced in 6.14, in which options parsing/loading functions began returning `NotFound` instead of `InvalidArgument` for option names not available in the present version.
|
||||
* Fixed MultiGet bugs it doesn't return valid data with user defined timestamp.
|
||||
* Fixed a potential bug caused by evaluating `TableBuilder::NeedCompact()` before `TableBuilder::Finish()` in compaction job. For example, the `NeedCompact()` method of `CompactOnDeletionCollector` returned by built-in `CompactOnDeletionCollectorFactory` requires `BlockBasedTable::Finish()` to return the correct result. The bug can cause a compaction-generated file not to be marked for future compaction based on deletion ratio.
|
||||
* Fixed a seek issue with prefix extractor and timestamp.
|
||||
* Fixed a bug of encoding and parsing BlockBasedTableOptions::read_amp_bytes_per_bit as a 64-bit integer.
|
||||
* Fixed a bug of a recovery corner case, details in PR7621.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
* When the `enabled` flag is set in `ColumnFamilyOptions::bottommost_compression_opts`, those compression options now take effect regardless of the value in `ColumnFamilyOptions::bottommost_compression`. Previously, those compression options only took effect when `ColumnFamilyOptions::bottommost_compression != kDisableCompressionOption`. Now, they additionally take effect when `ColumnFamilyOptions::bottommost_compression == kDisableCompressionOption` (such a setting causes bottommost compression type to fall back to `ColumnFamilyOptions::compression_per_level` if configured, and otherwise fall back to `ColumnFamilyOptions::compression`).
|
||||
|
||||
### New Features
|
||||
* An EXPERIMENTAL new Bloom alternative that saves about 30% space compared to Bloom filters, with about 3-4x construction time and similar query times is available using NewExperimentalRibbonFilterPolicy.
|
||||
|
||||
## 6.14 (10/09/2020)
|
||||
### Bug fixes
|
||||
@@ -27,6 +87,8 @@
|
||||
* Introduce options.check_flush_compaction_key_order with default value to be true. With this option, during flush and compaction, key order will be checked when writing to each SST file. If the order is violated, the flush or compaction will fail.
|
||||
* Added is_full_compaction to CompactionJobStats, so that the information is available through the EventListener interface.
|
||||
* Add more stats for MultiGet in Histogram to get number of data blocks, index blocks, filter blocks and sst files read from file system per level.
|
||||
* SST files have a new table property called db_host_id, which is set to the hostname by default. A new option in DBOptions, db_host_id, allows the property value to be overridden with a user specified string, or disable it completely by making the option string empty.
|
||||
* Methods to create customizable extensions -- such as TableFactory -- are exposed directly through the Customizable base class (from which these objects inherit). This change will allow these Customizable classes to be loaded and configured in a standard way (via CreateFromString). More information on how to write and use Customizable classes is in the customizable.h header file.
|
||||
|
||||
## 6.13 (09/12/2020)
|
||||
### Bug fixes
|
||||
|
||||
@@ -99,14 +99,14 @@ endif
|
||||
|
||||
ifneq ($(findstring rocksdbjava, $(MAKECMDGOALS)),)
|
||||
LIB_MODE=shared
|
||||
ifneq ($(findstring rocksdbjavastatic, $(MAKECMDGOALS)),)
|
||||
ifneq ($(findstring rocksdbjavastatic, $(MAKECMDGOALS)),)
|
||||
OBJ_DIR=jls
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticpublish)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
ifneq ($(DEBUG_LEVEL),2)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
ifeq ($(MAKECMDGOALS),rocksdbjavastaticpublish)
|
||||
DEBUG_LEVEL=0
|
||||
endif
|
||||
else
|
||||
OBJ_DIR=jl
|
||||
endif
|
||||
@@ -414,6 +414,10 @@ ifdef TEST_UINT128_COMPAT
|
||||
PLATFORM_CCFLAGS += -DTEST_UINT128_COMPAT=1
|
||||
PLATFORM_CXXFLAGS += -DTEST_UINT128_COMPAT=1
|
||||
endif
|
||||
ifdef ROCKSDB_MODIFY_NPHASH
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_MODIFY_NPHASH=1
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_MODIFY_NPHASH=1
|
||||
endif
|
||||
|
||||
# This (the first rule) must depend on "all".
|
||||
default: all
|
||||
@@ -506,6 +510,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)
|
||||
@@ -553,7 +563,7 @@ PARALLEL_TEST = \
|
||||
persistent_cache_test \
|
||||
table_test \
|
||||
transaction_test \
|
||||
transaction_lock_mgr_test \
|
||||
point_lock_manager_test \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
|
||||
@@ -577,6 +587,7 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
lru_cache_test \
|
||||
blob_file_addition_test \
|
||||
blob_file_builder_test \
|
||||
blob_file_cache_test \
|
||||
blob_file_garbage_test \
|
||||
blob_file_reader_test \
|
||||
bloom_test \
|
||||
@@ -584,11 +595,21 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
cleanable_test \
|
||||
checkpoint_test \
|
||||
coding_test \
|
||||
crc32c_test \
|
||||
dbformat_test \
|
||||
db_basic_test \
|
||||
db_blob_basic_test \
|
||||
db_blob_index_test \
|
||||
db_block_cache_test \
|
||||
db_flush_test \
|
||||
db_iterator_test \
|
||||
db_logical_block_size_cache_test \
|
||||
db_memtable_test \
|
||||
db_merge_operand_test \
|
||||
db_merge_operator_test \
|
||||
db_wal_test \
|
||||
db_with_timestamp_basic_test \
|
||||
db_with_timestamp_compaction_test \
|
||||
db_options_test \
|
||||
@@ -603,6 +624,7 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
env_logger_test \
|
||||
event_logger_test \
|
||||
error_handler_fs_test \
|
||||
external_sst_file_basic_test \
|
||||
auto_roll_logger_test \
|
||||
file_indexer_test \
|
||||
flush_job_test \
|
||||
@@ -618,16 +640,21 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
merger_test \
|
||||
mock_env_test \
|
||||
object_registry_test \
|
||||
optimistic_transaction_test \
|
||||
prefix_test \
|
||||
plain_table_db_test \
|
||||
repair_test \
|
||||
configurable_test \
|
||||
customizable_test \
|
||||
options_settable_test \
|
||||
options_test \
|
||||
point_lock_manager_test \
|
||||
random_test \
|
||||
range_del_aggregator_test \
|
||||
sst_file_reader_test \
|
||||
range_tombstone_fragmenter_test \
|
||||
repeatable_thread_test \
|
||||
ribbon_test \
|
||||
skiplist_test \
|
||||
slice_test \
|
||||
sst_dump_test \
|
||||
@@ -635,6 +662,7 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
stats_history_test \
|
||||
thread_local_test \
|
||||
trace_analyzer_test \
|
||||
transaction_test \
|
||||
env_timed_test \
|
||||
filelock_test \
|
||||
timer_queue_test \
|
||||
@@ -650,6 +678,8 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
version_edit_test \
|
||||
work_queue_test \
|
||||
write_controller_test \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
compaction_iterator_test \
|
||||
compaction_job_test \
|
||||
compaction_job_stats_test \
|
||||
@@ -668,6 +698,8 @@ ifdef ASSERT_STATUS_CHECKED
|
||||
import_column_family_test \
|
||||
memory_test \
|
||||
table_test \
|
||||
write_batch_test \
|
||||
write_batch_with_index_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS_PASSING_ASC += folly_synchronization_distributed_mutex_test
|
||||
@@ -686,6 +718,7 @@ endif
|
||||
# Not necessarily well thought out or up-to-date, but matches old list
|
||||
TESTS_PLATFORM_DEPENDENT := \
|
||||
db_basic_test \
|
||||
db_blob_basic_test \
|
||||
db_with_timestamp_basic_test \
|
||||
db_encryption_test \
|
||||
db_test2 \
|
||||
@@ -704,6 +737,7 @@ TESTS_PLATFORM_DEPENDENT := \
|
||||
io_posix_test \
|
||||
hash_test \
|
||||
random_test \
|
||||
ribbon_test \
|
||||
thread_local_test \
|
||||
work_queue_test \
|
||||
rate_limiter_test \
|
||||
@@ -1416,6 +1450,9 @@ hash_test: $(OBJ_DIR)/util/hash_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
random_test: $(OBJ_DIR)/util/random_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
ribbon_test: $(OBJ_DIR)/util/ribbon_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
option_change_migration_test: $(OBJ_DIR)/utilities/option_change_migration/option_change_migration_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1461,6 +1498,9 @@ slice_transform_test: $(OBJ_DIR)/util/slice_transform_test.o $(TEST_LIBRARY) $(L
|
||||
db_basic_test: $(OBJ_DIR)/db/db_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_blob_basic_test: $(OBJ_DIR)/db/blob/db_blob_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_with_timestamp_basic_test: $(OBJ_DIR)/db/db_with_timestamp_basic_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1784,6 +1824,9 @@ compact_files_test: $(OBJ_DIR)/db/compact_files_test.o $(TEST_LIBRARY) $(LIBRARY
|
||||
configurable_test: options/configurable_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
customizable_test: options/customizable_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
options_test: $(OBJ_DIR)/options/options_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1835,7 +1878,7 @@ write_callback_test: $(OBJ_DIR)/db/write_callback_test.o $(TEST_LIBRARY) $(LIBRA
|
||||
heap_test: $(OBJ_DIR)/util/heap_test.o $(GTEST)
|
||||
$(AM_LINK)
|
||||
|
||||
transaction_lock_mgr_test: utilities/transactions/transaction_lock_mgr_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
point_lock_manager_test: utilities/transactions/lock/point/point_lock_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
transaction_test: $(OBJ_DIR)/utilities/transactions/transaction_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
@@ -1913,6 +1956,9 @@ blob_file_addition_test: $(OBJ_DIR)/db/blob/blob_file_addition_test.o $(TEST_LIB
|
||||
blob_file_builder_test: $(OBJ_DIR)/db/blob/blob_file_builder_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_cache_test: $(OBJ_DIR)/db/blob/blob_file_cache_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_garbage_test: $(OBJ_DIR)/db/blob/blob_file_garbage_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -2050,8 +2096,8 @@ SNAPPY_DOWNLOAD_BASE ?= https://github.com/google/snappy/archive
|
||||
LZ4_VER ?= 1.9.2
|
||||
LZ4_SHA256 ?= 658ba6191fa44c92280d4aa2c271b0f4fbc0e34d249578dd05e50e76d0e5efcc
|
||||
LZ4_DOWNLOAD_BASE ?= https://github.com/lz4/lz4/archive
|
||||
ZSTD_VER ?= 1.4.4
|
||||
ZSTD_SHA256 ?= a364f5162c7d1a455cc915e8e3cf5f4bd8b75d09bc0f53965b0c9ca1383c52c8
|
||||
ZSTD_VER ?= 1.4.5
|
||||
ZSTD_SHA256 ?= 734d1f565c42f691f8420c8d06783ad818060fc390dee43ae0a89f86d0a4f8c2
|
||||
ZSTD_DOWNLOAD_BASE ?= https://github.com/facebook/zstd/archive
|
||||
CURL_SSL_OPTS ?= --tlsv1
|
||||
|
||||
@@ -2083,80 +2129,80 @@ ifeq ($(PLATFORM), OS_AIX)
|
||||
SNAPPY_MAKE_TARGET = libsnappy.la
|
||||
endif
|
||||
ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/openbsd
|
||||
JAVA_INCLUDE = -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/openbsd
|
||||
ROCKSDBJNILIB = librocksdbjni-openbsd$(ARCH).so
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-openbsd$(ARCH).jar
|
||||
ROCKSDB_JAR = rocksdbjni-$(ROCKSDB_JAVA_VERSION)-openbsd$(ARCH).jar
|
||||
endif
|
||||
|
||||
libz.a:
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
ifeq (,$(wildcard ./zlib-$(ZLIB_VER).tar.gz))
|
||||
zlib-$(ZLIB_VER).tar.gz:
|
||||
curl --fail --output zlib-$(ZLIB_VER).tar.gz --location ${ZLIB_DOWNLOAD_BASE}/zlib-$(ZLIB_VER).tar.gz
|
||||
endif
|
||||
ZLIB_SHA256_ACTUAL=`$(SHA256_CMD) zlib-$(ZLIB_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZLIB_SHA256)" != "$$ZLIB_SHA256_ACTUAL" ]; then \
|
||||
echo zlib-$(ZLIB_VER).tar.gz checksum mismatch, expected=\"$(ZLIB_SHA256)\" actual=\"$$ZLIB_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
libz.a: zlib-$(ZLIB_VER).tar.gz
|
||||
-rm -rf zlib-$(ZLIB_VER)
|
||||
tar xvzf zlib-$(ZLIB_VER).tar.gz
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${EXTRA_CFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' ./configure --static && $(MAKE)
|
||||
cd zlib-$(ZLIB_VER) && CFLAGS='-fPIC ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' ./configure --static && $(MAKE)
|
||||
cp zlib-$(ZLIB_VER)/libz.a .
|
||||
|
||||
libbz2.a:
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
ifeq (,$(wildcard ./bzip2-$(BZIP2_VER).tar.gz))
|
||||
bzip2-$(BZIP2_VER).tar.gz:
|
||||
curl --fail --output bzip2-$(BZIP2_VER).tar.gz --location ${CURL_SSL_OPTS} ${BZIP2_DOWNLOAD_BASE}/bzip2-$(BZIP2_VER).tar.gz
|
||||
endif
|
||||
BZIP2_SHA256_ACTUAL=`$(SHA256_CMD) bzip2-$(BZIP2_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(BZIP2_SHA256)" != "$$BZIP2_SHA256_ACTUAL" ]; then \
|
||||
echo bzip2-$(BZIP2_VER).tar.gz checksum mismatch, expected=\"$(BZIP2_SHA256)\" actual=\"$$BZIP2_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
libbz2.a: bzip2-$(BZIP2_VER).tar.gz
|
||||
-rm -rf bzip2-$(BZIP2_VER)
|
||||
tar xvzf bzip2-$(BZIP2_VER).tar.gz
|
||||
cd bzip2-$(BZIP2_VER) && $(MAKE) CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 ${EXTRA_CFLAGS}' AR='ar ${EXTRA_ARFLAGS}'
|
||||
cd bzip2-$(BZIP2_VER) && $(MAKE) CFLAGS='-fPIC -O2 -g -D_FILE_OFFSET_BITS=64 ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' AR='ar ${EXTRA_ARFLAGS}'
|
||||
cp bzip2-$(BZIP2_VER)/libbz2.a .
|
||||
|
||||
libsnappy.a:
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
ifeq (,$(wildcard ./snappy-$(SNAPPY_VER).tar.gz))
|
||||
snappy-$(SNAPPY_VER).tar.gz:
|
||||
curl --fail --output snappy-$(SNAPPY_VER).tar.gz --location ${CURL_SSL_OPTS} ${SNAPPY_DOWNLOAD_BASE}/$(SNAPPY_VER).tar.gz
|
||||
endif
|
||||
SNAPPY_SHA256_ACTUAL=`$(SHA256_CMD) snappy-$(SNAPPY_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(SNAPPY_SHA256)" != "$$SNAPPY_SHA256_ACTUAL" ]; then \
|
||||
echo snappy-$(SNAPPY_VER).tar.gz checksum mismatch, expected=\"$(SNAPPY_SHA256)\" actual=\"$$SNAPPY_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
libsnappy.a: snappy-$(SNAPPY_VER).tar.gz
|
||||
-rm -rf snappy-$(SNAPPY_VER)
|
||||
tar xvzf snappy-$(SNAPPY_VER).tar.gz
|
||||
mkdir snappy-$(SNAPPY_VER)/build
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='${EXTRA_CFLAGS}' CXXFLAGS='${EXTRA_CXXFLAGS}' LDFLAGS='${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cd snappy-$(SNAPPY_VER)/build && CFLAGS='${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' CXXFLAGS='${JAVA_STATIC_DEPS_CXXFLAGS} ${EXTRA_CXXFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON ${PLATFORM_CMAKE_FLAGS} .. && $(MAKE) ${SNAPPY_MAKE_TARGET}
|
||||
cp snappy-$(SNAPPY_VER)/build/libsnappy.a .
|
||||
|
||||
liblz4.a:
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
ifeq (,$(wildcard ./lz4-$(LZ4_VER).tar.gz))
|
||||
lz4-$(LZ4_VER).tar.gz:
|
||||
curl --fail --output lz4-$(LZ4_VER).tar.gz --location ${CURL_SSL_OPTS} ${LZ4_DOWNLOAD_BASE}/v$(LZ4_VER).tar.gz
|
||||
endif
|
||||
LZ4_SHA256_ACTUAL=`$(SHA256_CMD) lz4-$(LZ4_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(LZ4_SHA256)" != "$$LZ4_SHA256_ACTUAL" ]; then \
|
||||
echo lz4-$(LZ4_VER).tar.gz checksum mismatch, expected=\"$(LZ4_SHA256)\" actual=\"$$LZ4_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
liblz4.a: lz4-$(LZ4_VER).tar.gz
|
||||
-rm -rf lz4-$(LZ4_VER)
|
||||
tar xvzf lz4-$(LZ4_VER).tar.gz
|
||||
cd lz4-$(LZ4_VER)/lib && $(MAKE) CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' all
|
||||
cd lz4-$(LZ4_VER)/lib && $(MAKE) CFLAGS='-fPIC -O2 ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' all
|
||||
cp lz4-$(LZ4_VER)/lib/liblz4.a .
|
||||
|
||||
libzstd.a:
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
ifeq (,$(wildcard ./zstd-$(ZSTD_VER).tar.gz))
|
||||
zstd-$(ZSTD_VER).tar.gz:
|
||||
curl --fail --output zstd-$(ZSTD_VER).tar.gz --location ${CURL_SSL_OPTS} ${ZSTD_DOWNLOAD_BASE}/v$(ZSTD_VER).tar.gz
|
||||
endif
|
||||
ZSTD_SHA256_ACTUAL=`$(SHA256_CMD) zstd-$(ZSTD_VER).tar.gz | cut -d ' ' -f 1`; \
|
||||
if [ "$(ZSTD_SHA256)" != "$$ZSTD_SHA256_ACTUAL" ]; then \
|
||||
echo zstd-$(ZSTD_VER).tar.gz checksum mismatch, expected=\"$(ZSTD_SHA256)\" actual=\"$$ZSTD_SHA256_ACTUAL\"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
libzstd.a: zstd-$(ZSTD_VER).tar.gz
|
||||
-rm -rf zstd-$(ZSTD_VER)
|
||||
tar xvzf zstd-$(ZSTD_VER).tar.gz
|
||||
cd zstd-$(ZSTD_VER)/lib && DESTDIR=. PREFIX= $(MAKE) CFLAGS='-fPIC -O2 ${EXTRA_CFLAGS}' install
|
||||
cd zstd-$(ZSTD_VER)/lib && DESTDIR=. PREFIX= $(MAKE) CFLAGS='-fPIC -O2 ${JAVA_STATIC_DEPS_CCFLAGS} ${EXTRA_CFLAGS}' LDFLAGS='${JAVA_STATIC_DEPS_LDFLAGS} ${EXTRA_LDFLAGS}' libzstd.a
|
||||
cp zstd-$(ZSTD_VER)/lib/libzstd.a .
|
||||
|
||||
# A version of each $(LIB_OBJECTS) compiled with -fPIC and a fixed set of static compression libraries
|
||||
@@ -2165,14 +2211,23 @@ JAVA_COMPRESSIONS = libz.a libbz2.a libsnappy.a liblz4.a libzstd.a
|
||||
endif
|
||||
|
||||
JAVA_STATIC_FLAGS = -DZLIB -DBZIP2 -DSNAPPY -DLZ4 -DZSTD
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-$(ZLIB_VER) -I./bzip2-$(BZIP2_VER) -I./snappy-$(SNAPPY_VER) -I./lz4-$(LZ4_VER)/lib -I./zstd-$(ZSTD_VER)/lib/include
|
||||
ifneq ($(findstring rocksdbjavastatic, $(MAKECMDGOALS)),)
|
||||
JAVA_STATIC_INCLUDES = -I./zlib-$(ZLIB_VER) -I./bzip2-$(BZIP2_VER) -I./snappy-$(SNAPPY_VER) -I./snappy-$(SNAPPY_VER)/build -I./lz4-$(LZ4_VER)/lib -I./zstd-$(ZSTD_VER)/lib -I./zstd-$(ZSTD_VER)/lib/dictBuilder
|
||||
|
||||
ifneq ($(findstring rocksdbjavastatic, $(filter-out rocksdbjavastatic_deps, $(MAKECMDGOALS))),)
|
||||
CXXFLAGS += $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES)
|
||||
CFLAGS += $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES)
|
||||
CFLAGS += $(JAVA_STATIC_FLAGS) $(JAVA_STATIC_INCLUDES)
|
||||
endif
|
||||
rocksdbjavastatic: $(LIB_OBJECTS) $(JAVA_COMPRESSIONS)
|
||||
cd java;$(MAKE) javalib;
|
||||
rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
rocksdbjavastatic:
|
||||
ifeq ($(JAVA_HOME),)
|
||||
$(error JAVA_HOME is not set)
|
||||
endif
|
||||
$(MAKE) rocksdbjavastatic_deps
|
||||
$(MAKE) rocksdbjavastatic_libobjects
|
||||
$(MAKE) rocksdbjavastatic_javalib
|
||||
|
||||
rocksdbjavastatic_javalib:
|
||||
cd java;$(MAKE) javalib
|
||||
rm -f java/target/$(ROCKSDBJNILIB)
|
||||
$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC \
|
||||
-o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) \
|
||||
$(LIB_OBJECTS) $(COVERAGEFLAGS) \
|
||||
@@ -2189,6 +2244,10 @@ rocksdbjavastatic: $(LIB_OBJECTS) $(JAVA_COMPRESSIONS)
|
||||
openssl sha1 java/target/$(ROCKSDB_JAVADOCS_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_JAVADOCS_JAR).sha1
|
||||
openssl sha1 java/target/$(ROCKSDB_SOURCES_JAR) | sed 's/.*= \([0-9a-f]*\)/\1/' > java/target/$(ROCKSDB_SOURCES_JAR).sha1
|
||||
|
||||
rocksdbjavastatic_deps: $(JAVA_COMPRESSIONS)
|
||||
|
||||
rocksdbjavastatic_libobjects: $(LIB_OBJECTS)
|
||||
|
||||
rocksdbjavastaticrelease: rocksdbjavastatic
|
||||
cd java/crossbuild && (vagrant destroy -f || true) && vagrant up linux32 && vagrant halt linux32 && vagrant up linux64 && vagrant halt linux64 && vagrant up linux64-musl && vagrant halt linux64-musl
|
||||
cd java;jar -cf target/$(ROCKSDB_JAR_ALL) HISTORY*.md
|
||||
@@ -2204,7 +2263,7 @@ rocksdbjavastaticreleasedocker: rocksdbjavastatic rocksdbjavastaticdockerx86 roc
|
||||
|
||||
rocksdbjavastaticdockerx86:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_x86-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:centos6_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64:
|
||||
mkdir -p java/target
|
||||
@@ -2220,7 +2279,7 @@ rocksdbjavastaticdockerarm64v8:
|
||||
|
||||
rocksdbjavastaticdockerx86musl:
|
||||
mkdir -p java/target
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
docker run --rm --name rocksdb_linux_x86-musl-be --platform linux/386 --attach stdin --attach stdout --attach stderr --volume $(HOME)/.m2:/root/.m2:ro --volume `pwd`:/rocksdb-host:ro --volume /rocksdb-local-build --volume `pwd`/java/target:/rocksdb-java-target --env DEBUG_LEVEL=$(DEBUG_LEVEL) evolvedbinary/rocksjava:alpine3_x86-be /rocksdb-host/java/crossbuild/docker-build-linux-centos.sh
|
||||
|
||||
rocksdbjavastaticdockerx86_64musl:
|
||||
mkdir -p java/target
|
||||
@@ -2258,6 +2317,9 @@ jl/%.o: %.cc
|
||||
$(AM_V_CC)mkdir -p $(@D) && $(CXX) $(CXXFLAGS) -fPIC -c $< -o $@ $(COVERAGEFLAGS)
|
||||
|
||||
rocksdbjava: $(LIB_OBJECTS)
|
||||
ifeq ($(JAVA_HOME),)
|
||||
$(error JAVA_HOME is not set)
|
||||
endif
|
||||
$(AM_V_GEN)cd java;$(MAKE) javalib;
|
||||
$(AM_V_at)rm -f ./java/target/$(ROCKSDBJNILIB)
|
||||
$(AM_V_at)$(CXX) $(CXXFLAGS) -I./java/. $(JAVA_INCLUDE) -shared -fPIC -o ./java/target/$(ROCKSDBJNILIB) $(JNI_NATIVE_SOURCES) $(LIB_OBJECTS) $(JAVA_LDFLAGS) $(COVERAGEFLAGS)
|
||||
@@ -2388,6 +2450,8 @@ ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),format)
|
||||
ifneq ($(MAKECMDGOALS),jclean)
|
||||
ifneq ($(MAKECMDGOALS),jtest)
|
||||
ifneq ($(MAKECMDGOALS),rocksdbjavastatic)
|
||||
ifneq ($(MAKECMDGOALS),rocksdbjavastatic_deps)
|
||||
ifneq ($(MAKECMDGOALS),package)
|
||||
ifneq ($(MAKECMDGOALS),analyze)
|
||||
-include $(DEPFILES)
|
||||
@@ -2397,3 +2461,5 @@ endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://circleci.com/gh/facebook/rocksdb)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
[](http://140-211-168-68-openstack.osuosl.org:8080/job/rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
It is built on earlier work on [LevelDB](https://github.com/google/leveldb) by Sanjay Ghemawat (sanjay@google.com)
|
||||
|
||||
@@ -135,6 +135,7 @@ cpp_library(
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_builder.cc",
|
||||
"db/blob/blob_file_cache.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_file_reader.cc",
|
||||
@@ -254,6 +255,7 @@ cpp_library(
|
||||
"monitoring/thread_status_util_debug.cc",
|
||||
"options/cf_options.cc",
|
||||
"options/configurable.cc",
|
||||
"options/customizable.cc",
|
||||
"options/db_options.cc",
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
@@ -383,8 +385,21 @@ cpp_library(
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/transactions/lock/lock_tracker.cc",
|
||||
"utilities/transactions/lock/point_lock_tracker.cc",
|
||||
"utilities/transactions/lock/lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_tracker.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/concurrent_tree.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/keyrange.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/lock_request.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/locktree.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/manager.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/range_buffer.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/treenode.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/txnid_set.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/wfg.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/standalone_port.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/util/dbt.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/util/memarena.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
@@ -392,7 +407,6 @@ cpp_library(
|
||||
"utilities/transactions/snapshot_checker.cc",
|
||||
"utilities/transactions/transaction_base.cc",
|
||||
"utilities/transactions/transaction_db_mutex_impl.cc",
|
||||
"utilities/transactions/transaction_lock_mgr.cc",
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/transactions/write_prepared_txn.cc",
|
||||
"utilities/transactions/write_prepared_txn_db.cc",
|
||||
@@ -423,6 +437,7 @@ cpp_library(
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_builder.cc",
|
||||
"db/blob/blob_file_cache.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_file_reader.cc",
|
||||
@@ -542,6 +557,7 @@ cpp_library(
|
||||
"monitoring/thread_status_util_debug.cc",
|
||||
"options/cf_options.cc",
|
||||
"options/configurable.cc",
|
||||
"options/customizable.cc",
|
||||
"options/db_options.cc",
|
||||
"options/options.cc",
|
||||
"options/options_helper.cc",
|
||||
@@ -671,8 +687,21 @@ cpp_library(
|
||||
"utilities/simulator_cache/sim_cache.cc",
|
||||
"utilities/table_properties_collectors/compact_on_deletion_collector.cc",
|
||||
"utilities/trace/file_trace_reader_writer.cc",
|
||||
"utilities/transactions/lock/lock_tracker.cc",
|
||||
"utilities/transactions/lock/point_lock_tracker.cc",
|
||||
"utilities/transactions/lock/lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_manager.cc",
|
||||
"utilities/transactions/lock/point/point_lock_tracker.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/concurrent_tree.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/keyrange.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/lock_request.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/locktree.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/manager.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/range_buffer.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/treenode.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/txnid_set.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/locktree/wfg.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/standalone_port.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/util/dbt.cc",
|
||||
"utilities/transactions/lock/range/range_tree/lib/util/memarena.cc",
|
||||
"utilities/transactions/optimistic_transaction.cc",
|
||||
"utilities/transactions/optimistic_transaction_db_impl.cc",
|
||||
"utilities/transactions/pessimistic_transaction.cc",
|
||||
@@ -680,7 +709,6 @@ cpp_library(
|
||||
"utilities/transactions/snapshot_checker.cc",
|
||||
"utilities/transactions/transaction_base.cc",
|
||||
"utilities/transactions/transaction_db_mutex_impl.cc",
|
||||
"utilities/transactions/transaction_lock_mgr.cc",
|
||||
"utilities/transactions/transaction_util.cc",
|
||||
"utilities/transactions/write_prepared_txn.cc",
|
||||
"utilities/transactions/write_prepared_txn_db.cc",
|
||||
@@ -771,26 +799,24 @@ cpp_library(
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
if not is_opt_mode:
|
||||
cpp_binary(
|
||||
name = "c_test_bin",
|
||||
srcs = ["db/c_test.c"],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
)
|
||||
cpp_binary(
|
||||
name = "c_test_bin",
|
||||
srcs = ["db/c_test.c"],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
) if not is_opt_mode else None
|
||||
|
||||
if not is_opt_mode:
|
||||
custom_unittest(
|
||||
"c_test",
|
||||
command = [
|
||||
native.package_name() + "/buckifier/rocks_test_runner.sh",
|
||||
"$(location :{})".format("c_test_bin"),
|
||||
],
|
||||
type = "simple",
|
||||
)
|
||||
custom_unittest(
|
||||
name = "c_test",
|
||||
command = [
|
||||
native.package_name() + "/buckifier/rocks_test_runner.sh",
|
||||
"$(location :{})".format("c_test_bin"),
|
||||
],
|
||||
type = "simple",
|
||||
) if not is_opt_mode else None
|
||||
|
||||
cpp_library(
|
||||
name = "env_basic_test_lib",
|
||||
@@ -857,6 +883,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_file_cache_test",
|
||||
"db/blob/blob_file_cache_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_file_garbage_test",
|
||||
"db/blob/blob_file_garbage_test.cc",
|
||||
@@ -1081,6 +1114,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"customizable_test",
|
||||
"options/customizable_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"data_block_hash_index_test",
|
||||
"table/block_based/data_block_hash_index_test.cc",
|
||||
@@ -1095,6 +1135,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_blob_basic_test",
|
||||
"db/blob/db_blob_basic_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"db_blob_index_test",
|
||||
"db/blob/db_blob_index_test.cc",
|
||||
@@ -1711,6 +1758,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"point_lock_manager_test",
|
||||
"utilities/transactions/lock/point/point_lock_manager_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"prefetch_test",
|
||||
"file/prefetch_test.cc",
|
||||
@@ -1781,6 +1835,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"ribbon_test",
|
||||
"util/ribbon_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"sim_cache_test",
|
||||
"utilities/simulator_cache/sim_cache_test.cc",
|
||||
@@ -1900,13 +1961,6 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"transaction_lock_mgr_test",
|
||||
"utilities/transactions/transaction_lock_mgr_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"transaction_test",
|
||||
"utilities/transactions/transaction_test.cc",
|
||||
|
||||
@@ -44,7 +44,7 @@ Tango is using RocksDB as a graph storage to store all users' connection data an
|
||||
Turn is using RocksDB as a storage layer for their key/value store, serving at peak 2.4MM QPS out of different datacenters.
|
||||
Check out our RocksDB Protobuf merge operator at: https://github.com/vladb38/rocksdb_protobuf
|
||||
|
||||
## Santanader UK/Cloudera Profession Services
|
||||
## Santander UK/Cloudera Profession Services
|
||||
Check out their blog post: http://blog.cloudera.com/blog/2015/08/inside-santanders-near-real-time-data-ingest-architecture/
|
||||
|
||||
## Airbnb
|
||||
@@ -86,7 +86,7 @@ quasardb uses a heavily tuned RocksDB as its persistence layer.
|
||||
[Uber](http://eng.uber.com/cherami/) uses RocksDB as a durable and scalable task queue.
|
||||
|
||||
## 360 Pika
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been widely used in many company
|
||||
[360](http://www.360.cn/) [Pika](https://github.com/Qihoo360/pika) is a nosql compatible with redis. With the huge amount of data stored, redis may suffer for a capacity bottleneck, and pika was born for solving it. It has widely been used in many companies.
|
||||
|
||||
## LzLabs
|
||||
LzLabs is using RocksDB as a storage engine in their multi-database distributed framework to store application configuration and user data.
|
||||
@@ -104,8 +104,11 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
[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.
|
||||
|
||||
## Nebula Graph
|
||||
|
||||
[Nebula Graph](https://github.com/vesoft-inc/nebula) is a distributed, scalable, lightning-fast, open source graph database capable of hosting super large scale graphs with dozens of billions of vertices (nodes) and trillions of edges, with milliseconds of latency.
|
||||
|
||||
## YugabyteDB
|
||||
[YugabyteDB](https://www.yugabyte.com/) is an open source, high performance, distributed SQL database that uses RocksDB as its storage layer. For more information, please see https://github.com/yugabyte/yugabyte-db/.
|
||||
|
||||
## ArangoDB
|
||||
[ArangoDB](https://www.arangodb.com/) is a native multi-model database with flexible data models for documents, graphs, and key-values, for building high performance applications using a convenient SQL-like query language or JavaScript extensions. It uses RocksDB as its sotrage engine.
|
||||
|
||||
|
||||
@@ -135,11 +135,15 @@ def generate_targets(repo_path, deps_map):
|
||||
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,
|
||||
|
||||
@@ -79,26 +79,24 @@ class TARGETSBuilder(object):
|
||||
|
||||
def add_c_test(self):
|
||||
self.targets_file.write(b"""
|
||||
if not is_opt_mode:
|
||||
cpp_binary(
|
||||
name = "c_test_bin",
|
||||
srcs = ["db/c_test.c"],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
)
|
||||
cpp_binary(
|
||||
name = "c_test_bin",
|
||||
srcs = ["db/c_test.c"],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
) if not is_opt_mode else None
|
||||
|
||||
if not is_opt_mode:
|
||||
custom_unittest(
|
||||
"c_test",
|
||||
command = [
|
||||
native.package_name() + "/buckifier/rocks_test_runner.sh",
|
||||
"$(location :{})".format("c_test_bin"),
|
||||
],
|
||||
type = "simple",
|
||||
)
|
||||
custom_unittest(
|
||||
name = "c_test",
|
||||
command = [
|
||||
native.package_name() + "/buckifier/rocks_test_runner.sh",
|
||||
"$(location :{})".format("c_test_bin"),
|
||||
],
|
||||
type = "simple",
|
||||
) if not is_opt_mode else None
|
||||
""")
|
||||
|
||||
def register_test(self,
|
||||
|
||||
@@ -655,6 +655,28 @@ else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mmacosx-version-min=10.12"
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -mmacosx-version-min=10.12"
|
||||
PLATFORM_SHARED_LDFLAGS="$PLATFORM_SHARED_LDFLAGS -mmacosx-version-min=10.12"
|
||||
PLATFORM_CMAKE_FLAGS="-DCMAKE_OSX_DEPLOYMENT_TARGET=10.12"
|
||||
JAVA_STATIC_DEPS_COMMON_FLAGS="-mmacosx-version-min=10.12"
|
||||
JAVA_STATIC_DEPS_LDFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CCFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
JAVA_STATIC_DEPS_CXXFLAGS="$JAVA_STATIC_DEPS_COMMON_FLAGS"
|
||||
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
|
||||
|
||||
@@ -812,8 +834,12 @@ echo "CXX=$CXX" >> "$OUTPUT"
|
||||
echo "AR=$AR" >> "$OUTPUT"
|
||||
echo "PLATFORM=$PLATFORM" >> "$OUTPUT"
|
||||
echo "PLATFORM_LDFLAGS=$PLATFORM_LDFLAGS" >> "$OUTPUT"
|
||||
echo "PLATFORM_CMAKE_FLAGS=$PLATFORM_CMAKE_FLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_LDFLAGS=$JAVA_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_LDFLAGS=$JAVA_STATIC_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_DEPS_CCFLAGS=$JAVA_STATIC_DEPS_CCFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_DEPS_CXXFLAGS=$JAVA_STATIC_DEPS_CXXFLAGS" >> "$OUTPUT"
|
||||
echo "JAVA_STATIC_DEPS_LDFLAGS=$JAVA_STATIC_DEPS_LDFLAGS" >> "$OUTPUT"
|
||||
echo "JAVAC_ARGS=$JAVAC_ARGS" >> "$OUTPUT"
|
||||
echo "VALGRIND_VER=$VALGRIND_VER" >> "$OUTPUT"
|
||||
echo "PLATFORM_CCFLAGS=$PLATFORM_CCFLAGS" >> "$OUTPUT"
|
||||
@@ -847,3 +873,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
|
||||
|
||||
+10
-12
@@ -52,15 +52,15 @@ 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 http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo " curl --location https://tinyurl.com/y2kvokof -o ${REPO_ROOT}/clang-format-diff.py"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
echo "Then make sure clang-format is available and executable from \$PATH:"
|
||||
echo " clang-format --version"
|
||||
exit 128
|
||||
fi
|
||||
# Check argparse pre-req on interpreter, or it will fail
|
||||
@@ -75,17 +75,15 @@ else
|
||||
exit 129
|
||||
fi
|
||||
# Unfortunately, some machines have a Python2 clang-format-diff.py
|
||||
# installed but only a Python3 interpreter installed. Rather than trying
|
||||
# different Python versions that might be installed, we can try migrating
|
||||
# the code to Python3 if it looks like Python2
|
||||
# installed but only a Python3 interpreter installed. Unfortunately,
|
||||
# automatic 2to3 migration is insufficient, so suggest downloading latest.
|
||||
if grep -q "print '" "$CFD_PATH" && \
|
||||
${PYTHON:-python3} --version | grep -q 'ython 3'; then
|
||||
if [ ! -f "$REPO_ROOT/.py3/clang-format-diff.py" ]; then
|
||||
echo "Migrating $CFD_PATH to Python3 in a hidden file"
|
||||
mkdir -p "$REPO_ROOT/.py3"
|
||||
${PYTHON:-python3} -m lib2to3 -w -n -o "$REPO_ROOT/.py3" "$CFD_PATH" > /dev/null || exit 128
|
||||
fi
|
||||
CFD_PATH="$REPO_ROOT/.py3/clang-format-diff.py"
|
||||
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"
|
||||
exit 130
|
||||
fi
|
||||
CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
|
||||
# This had better work after all those checks
|
||||
|
||||
Vendored
+114
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Returns the cached value given a cache handle.
|
||||
template <typename T>
|
||||
T* GetFromCacheHandle(Cache* cache, Cache::Handle* handle) {
|
||||
assert(cache);
|
||||
assert(handle);
|
||||
|
||||
return static_cast<T*>(cache->Value(handle));
|
||||
}
|
||||
|
||||
// Simple generic deleter for Cache (to be used with Cache::Insert).
|
||||
template <typename T>
|
||||
void DeleteCacheEntry(const Slice& /* key */, void* value) {
|
||||
delete static_cast<T*>(value);
|
||||
}
|
||||
|
||||
// Turns a T* into a Slice so it can be used as a key with Cache.
|
||||
template <typename T>
|
||||
Slice GetSlice(const T* t) {
|
||||
return Slice(reinterpret_cast<const char*>(t), sizeof(T));
|
||||
}
|
||||
|
||||
// Generic resource management object for cache handles that releases the handle
|
||||
// when destroyed. Has unique ownership of the handle, so copying it is not
|
||||
// allowed, while moving it transfers ownership.
|
||||
template <typename T>
|
||||
class CacheHandleGuard {
|
||||
public:
|
||||
CacheHandleGuard() = default;
|
||||
|
||||
CacheHandleGuard(Cache* cache, Cache::Handle* handle)
|
||||
: cache_(cache),
|
||||
handle_(handle),
|
||||
value_(GetFromCacheHandle<T>(cache, handle)) {
|
||||
assert(cache_ && handle_ && value_);
|
||||
}
|
||||
|
||||
CacheHandleGuard(const CacheHandleGuard&) = delete;
|
||||
CacheHandleGuard& operator=(const CacheHandleGuard&) = delete;
|
||||
|
||||
CacheHandleGuard(CacheHandleGuard&& rhs) noexcept
|
||||
: cache_(rhs.cache_), handle_(rhs.handle_), value_(rhs.value_) {
|
||||
assert((!cache_ && !handle_ && !value_) || (cache_ && handle_ && value_));
|
||||
|
||||
rhs.ResetFields();
|
||||
}
|
||||
|
||||
CacheHandleGuard& operator=(CacheHandleGuard&& rhs) noexcept {
|
||||
if (this == &rhs) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
ReleaseHandle();
|
||||
|
||||
cache_ = rhs.cache_;
|
||||
handle_ = rhs.handle_;
|
||||
value_ = rhs.value_;
|
||||
|
||||
assert((!cache_ && !handle_ && !value_) || (cache_ && handle_ && value_));
|
||||
|
||||
rhs.ResetFields();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~CacheHandleGuard() { ReleaseHandle(); }
|
||||
|
||||
bool IsEmpty() const { return !handle_; }
|
||||
|
||||
Cache* GetCache() const { return cache_; }
|
||||
Cache::Handle* GetCacheHandle() const { return handle_; }
|
||||
T* GetValue() const { return value_; }
|
||||
|
||||
void Reset() {
|
||||
ReleaseHandle();
|
||||
ResetFields();
|
||||
}
|
||||
|
||||
private:
|
||||
void ReleaseHandle() {
|
||||
if (IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(cache_);
|
||||
cache_->Release(handle_);
|
||||
}
|
||||
|
||||
void ResetFields() {
|
||||
cache_ = nullptr;
|
||||
handle_ = nullptr;
|
||||
value_ = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Cache* cache_ = nullptr;
|
||||
Cache::Handle* handle_ = nullptr;
|
||||
T* value_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
-1
@@ -11,7 +11,6 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
Vendored
+2
-2
@@ -67,7 +67,7 @@ struct LRUHandle {
|
||||
IS_HIGH_PRI = (1 << 1),
|
||||
// Whether this entry is in high-pri pool.
|
||||
IN_HIGH_PRI_POOL = (1 << 2),
|
||||
// Wwhether this entry has had any lookups (hits).
|
||||
// Whether this entry has had any lookups (hits).
|
||||
HAS_HIT = (1 << 3),
|
||||
};
|
||||
|
||||
@@ -130,7 +130,7 @@ struct LRUHandle {
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
|
||||
// Caclculate the memory usage by metadata
|
||||
// Calculate the memory usage by metadata
|
||||
inline size_t CalcTotalCharge(
|
||||
CacheMetadataChargePolicy metadata_charge_policy) {
|
||||
size_t meta_charge = 0;
|
||||
|
||||
+23
-23
@@ -30,20 +30,19 @@ Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
return db_iter_->GetProperty(prop_name, prop);
|
||||
}
|
||||
|
||||
void ArenaWrappedDBIter::Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob,
|
||||
bool allow_refresh) {
|
||||
void ArenaWrappedDBIter::Init(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ = new (mem) DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
cf_options.user_comparator, nullptr, sequence,
|
||||
true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, allow_blob);
|
||||
db_iter_ =
|
||||
new (mem) DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
cf_options.user_comparator, /* iter */ nullptr, version,
|
||||
sequence, true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, expose_blob_index);
|
||||
sv_number_ = version_number;
|
||||
read_options_ = read_options;
|
||||
allow_refresh_ = allow_refresh;
|
||||
@@ -72,8 +71,9 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
Init(env, read_options_, *(cfd_->ioptions()), sv->mutable_cf_options,
|
||||
latest_seq, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, allow_blob_,
|
||||
sv->current, latest_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
cur_sv_number, read_callback_, db_impl_, cfd_, expose_blob_index_,
|
||||
allow_refresh_);
|
||||
|
||||
InternalIterator* internal_iter = db_impl_->NewInternalIterator(
|
||||
@@ -90,16 +90,16 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh) {
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, cf_options, mutable_cf_options, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
db_impl, cfd, allow_blob, allow_refresh);
|
||||
iter->Init(env, read_options, cf_options, mutable_cf_options, version,
|
||||
sequence, max_sequential_skip_in_iterations, version_number,
|
||||
read_callback, db_impl, cfd, expose_blob_index, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(db_impl, cfd, read_callback, allow_blob);
|
||||
iter->StoreRefreshInfo(db_impl, cfd, read_callback, expose_blob_index);
|
||||
}
|
||||
|
||||
return iter;
|
||||
|
||||
+11
-10
@@ -23,6 +23,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Arena;
|
||||
class Version;
|
||||
|
||||
// A wrapper iterator which wraps DB Iterator and the arena, with which the DB
|
||||
// iterator is supposed to be allocated. This class is used as an entry point of
|
||||
@@ -72,20 +73,20 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob, bool allow_refresh);
|
||||
bool expose_blob_index, bool allow_refresh);
|
||||
|
||||
// Store some parameters so we can refresh the iterator at a later point
|
||||
// with these same params
|
||||
void StoreRefreshInfo(DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
ReadCallback* read_callback, bool allow_blob) {
|
||||
ReadCallback* read_callback, bool expose_blob_index) {
|
||||
db_impl_ = db_impl;
|
||||
cfd_ = cfd;
|
||||
read_callback_ = read_callback;
|
||||
allow_blob_ = allow_blob;
|
||||
expose_blob_index_ = expose_blob_index;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -96,7 +97,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
DBImpl* db_impl_ = nullptr;
|
||||
ReadOptions read_options_;
|
||||
ReadCallback* read_callback_;
|
||||
bool allow_blob_ = false;
|
||||
bool expose_blob_index_ = false;
|
||||
bool allow_refresh_ = true;
|
||||
};
|
||||
|
||||
@@ -106,9 +107,9 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false,
|
||||
bool allow_refresh = true);
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false, bool allow_refresh = true);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "db/blob/blob_file_reader.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobFileCache::BlobFileCache(Cache* cache,
|
||||
const ImmutableCFOptions* immutable_cf_options,
|
||||
const FileOptions* file_options,
|
||||
uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist)
|
||||
: cache_(cache),
|
||||
mutex_(kNumberOfMutexStripes, GetSliceNPHash64),
|
||||
immutable_cf_options_(immutable_cf_options),
|
||||
file_options_(file_options),
|
||||
column_family_id_(column_family_id),
|
||||
blob_file_read_hist_(blob_file_read_hist) {
|
||||
assert(cache_);
|
||||
assert(immutable_cf_options_);
|
||||
assert(file_options_);
|
||||
}
|
||||
|
||||
Status BlobFileCache::GetBlobFileReader(
|
||||
uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader) {
|
||||
assert(blob_file_reader);
|
||||
assert(blob_file_reader->IsEmpty());
|
||||
|
||||
const Slice key = GetSlice(&blob_file_number);
|
||||
|
||||
assert(cache_);
|
||||
|
||||
Cache::Handle* handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
*blob_file_reader = CacheHandleGuard<BlobFileReader>(cache_, handle);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("BlobFileCache::GetBlobFileReader:DoubleCheck");
|
||||
|
||||
// Check again while holding mutex
|
||||
MutexLock lock(mutex_.get(key));
|
||||
|
||||
handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
*blob_file_reader = CacheHandleGuard<BlobFileReader>(cache_, handle);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
assert(immutable_cf_options_);
|
||||
Statistics* const statistics = immutable_cf_options_->statistics;
|
||||
|
||||
RecordTick(statistics, NO_FILE_OPENS);
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
{
|
||||
assert(file_options_);
|
||||
const Status s = BlobFileReader::Create(
|
||||
*immutable_cf_options_, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, &reader);
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
constexpr size_t charge = 1;
|
||||
|
||||
const Status s = cache_->Insert(key, reader.get(), charge,
|
||||
&DeleteCacheEntry<BlobFileReader>, &handle);
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
reader.release();
|
||||
|
||||
*blob_file_reader = CacheHandleGuard<BlobFileReader>(cache_, handle);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "cache/cache_helpers.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Cache;
|
||||
struct ImmutableCFOptions;
|
||||
struct FileOptions;
|
||||
class HistogramImpl;
|
||||
class Status;
|
||||
class BlobFileReader;
|
||||
class Slice;
|
||||
|
||||
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);
|
||||
|
||||
BlobFileCache(const BlobFileCache&) = delete;
|
||||
BlobFileCache& operator=(const BlobFileCache&) = delete;
|
||||
|
||||
Status GetBlobFileReader(uint64_t blob_file_number,
|
||||
CacheHandleGuard<BlobFileReader>* blob_file_reader);
|
||||
|
||||
private:
|
||||
Cache* cache_;
|
||||
// Note: mutex_ below is used to guard against multiple threads racing to open
|
||||
// the same file.
|
||||
Striped<port::Mutex, Slice> mutex_;
|
||||
const ImmutableCFOptions* immutable_cf_options_;
|
||||
const FileOptions* file_options_;
|
||||
uint32_t column_family_id_;
|
||||
HistogramImpl* blob_file_read_hist_;
|
||||
|
||||
static constexpr size_t kNumberOfMutexStripes = 1 << 7;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/read_write_util.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "options/cf_options.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
|
||||
// Creates a test blob file with a single blob in it.
|
||||
void WriteBlobFile(uint32_t column_family_id,
|
||||
const ImmutableCFOptions& immutable_cf_options,
|
||||
uint64_t blob_file_number) {
|
||||
assert(!immutable_cf_options.cf_paths.empty());
|
||||
|
||||
const std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options.cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
ASSERT_OK(NewWritableFile(immutable_cf_options.fs, blob_file_path, &file,
|
||||
FileOptions()));
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.env));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.env, statistics,
|
||||
blob_file_number, use_fsync);
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
|
||||
BlobLogHeader header(column_family_id, kNoCompression, has_ttl,
|
||||
expiration_range);
|
||||
|
||||
ASSERT_OK(blob_log_writer.WriteHeader(header));
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob[] = "blob";
|
||||
|
||||
std::string compressed_blob;
|
||||
Slice blob_to_write;
|
||||
|
||||
uint64_t key_offset = 0;
|
||||
uint64_t blob_offset = 0;
|
||||
|
||||
ASSERT_OK(blob_log_writer.AddRecord(key, blob, &key_offset, &blob_offset));
|
||||
|
||||
BlobLogFooter footer;
|
||||
footer.blob_count = 1;
|
||||
footer.expiration_range = expiration_range;
|
||||
|
||||
std::string checksum_method;
|
||||
std::string checksum_value;
|
||||
|
||||
ASSERT_OK(
|
||||
blob_log_writer.AppendFooter(footer, &checksum_method, &checksum_value));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class BlobFileCacheTest : public testing::Test {
|
||||
protected:
|
||||
BlobFileCacheTest() : mock_env_(Env::Default()) {}
|
||||
|
||||
MockEnv mock_env_;
|
||||
};
|
||||
|
||||
TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
Options options;
|
||||
options.env = &mock_env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileCacheTest_GetBlobFileReader"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
WriteBlobFile(column_family_id, immutable_cf_options, blob_file_number);
|
||||
|
||||
constexpr size_t capacity = 10;
|
||||
std::shared_ptr<Cache> backing_cache = NewLRUCache(capacity);
|
||||
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
|
||||
// First try: reader should be opened and put in cache
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &first));
|
||||
ASSERT_NE(first.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
|
||||
// Second try: reader should be served from cache
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &second));
|
||||
ASSERT_NE(second.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
|
||||
ASSERT_EQ(first.GetValue(), second.GetValue());
|
||||
}
|
||||
|
||||
TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
Options options;
|
||||
options.env = &mock_env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_,
|
||||
"BlobFileCacheTest_GetBlobFileReader_Race"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
WriteBlobFile(column_family_id, immutable_cf_options, blob_file_number);
|
||||
|
||||
constexpr size_t capacity = 10;
|
||||
std::shared_ptr<Cache> backing_cache = NewLRUCache(capacity);
|
||||
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
|
||||
CacheHandleGuard<BlobFileReader> first;
|
||||
CacheHandleGuard<BlobFileReader> second;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileCache::GetBlobFileReader:DoubleCheck", [&](void* /* arg */) {
|
||||
// Disabling sync points to prevent infinite recursion
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &second));
|
||||
ASSERT_NE(second.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(blob_file_cache.GetBlobFileReader(blob_file_number, &first));
|
||||
ASSERT_NE(first.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 0);
|
||||
|
||||
ASSERT_EQ(first.GetValue(), second.GetValue());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(BlobFileCacheTest, GetBlobFileReader_IOError) {
|
||||
Options options;
|
||||
options.env = &mock_env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_,
|
||||
"BlobFileCacheTest_GetBlobFileReader_IOError"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr size_t capacity = 10;
|
||||
std::shared_ptr<Cache> backing_cache = NewLRUCache(capacity);
|
||||
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
FileOptions file_options;
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
|
||||
// Note: there is no blob file with the below number
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
CacheHandleGuard<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(
|
||||
blob_file_cache.GetBlobFileReader(blob_file_number, &reader).IsIOError());
|
||||
ASSERT_EQ(reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 1);
|
||||
}
|
||||
|
||||
TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
Options options;
|
||||
options.env = &mock_env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
options.cf_paths.emplace_back(
|
||||
test::PerThreadDBPath(&mock_env_,
|
||||
"BlobFileCacheTest_GetBlobFileReader_CacheFull"),
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
ImmutableCFOptions immutable_cf_options(options);
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
WriteBlobFile(column_family_id, immutable_cf_options, blob_file_number);
|
||||
|
||||
constexpr size_t capacity = 0;
|
||||
constexpr int num_shard_bits = -1; // determined automatically
|
||||
constexpr bool strict_capacity_limit = true;
|
||||
std::shared_ptr<Cache> backing_cache =
|
||||
NewLRUCache(capacity, num_shard_bits, strict_capacity_limit);
|
||||
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist);
|
||||
|
||||
// Insert into cache should fail since it has zero capacity and
|
||||
// strict_capacity_limit is set
|
||||
CacheHandleGuard<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(blob_file_cache.GetBlobFileReader(blob_file_number, &reader)
|
||||
.IsIncomplete());
|
||||
ASSERT_EQ(reader.GetValue(), nullptr);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_OPENS), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(NO_FILE_ERRORS), 1);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/options.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/compression.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
@@ -95,10 +95,6 @@ Status BlobLogFooter::DecodeFrom(Slice src) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
uint64_t BlobLogRecord::CalculateAdjustmentForRecordHeader(uint64_t key_size) {
|
||||
return key_size + kHeaderSize;
|
||||
}
|
||||
|
||||
void BlobLogRecord::EncodeHeaderTo(std::string* dst) {
|
||||
assert(dst != nullptr);
|
||||
dst->clear();
|
||||
|
||||
@@ -107,7 +107,9 @@ struct BlobLogRecord {
|
||||
// Note that the offset field of BlobIndex actually points to the blob value
|
||||
// as opposed to the start of the blob record. The following method can
|
||||
// be used to calculate the adjustment needed to read the blob record header.
|
||||
static uint64_t CalculateAdjustmentForRecordHeader(uint64_t key_size);
|
||||
static uint64_t CalculateAdjustmentForRecordHeader(uint64_t key_size) {
|
||||
return key_size + kHeaderSize;
|
||||
}
|
||||
|
||||
uint64_t key_size = 0;
|
||||
uint64_t value_size = 0;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include "db/blob/blob_log_sequential_reader.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "util/stop_watch.h"
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBBlobBasicTest : public DBTestBase {
|
||||
protected:
|
||||
DBBlobBasicTest()
|
||||
: DBTestBase("/db_blob_basic_test", /* env_do_fsync */ false) {}
|
||||
};
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetBlob) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
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);
|
||||
|
||||
// Try again with no I/O allowed. The table and the necessary blocks should
|
||||
// already be in their respective caches; however, the blob itself can only be
|
||||
// read from the blob file, so the read should return Incomplete.
|
||||
ReadOptions read_options;
|
||||
read_options.read_tier = kBlockCacheTier;
|
||||
|
||||
PinnableSlice result;
|
||||
ASSERT_TRUE(db_->Get(read_options, db_->DefaultColumnFamily(), key, &result)
|
||||
.IsIncomplete());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetBlobs) {
|
||||
constexpr size_t min_blob_size = 6;
|
||||
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = min_blob_size;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// Put then retrieve three key-values. The first value is below the size limit
|
||||
// and is thus stored inline; the other two are stored separately as blobs.
|
||||
constexpr size_t num_keys = 3;
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "short";
|
||||
static_assert(sizeof(first_value) - 1 < min_blob_size,
|
||||
"first_value too long to be inlined");
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "long_value";
|
||||
static_assert(sizeof(second_value) - 1 >= min_blob_size,
|
||||
"second_value too short to be stored as blob");
|
||||
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "other_long_value";
|
||||
static_assert(sizeof(third_value) - 1 >= min_blob_size,
|
||||
"third_value too short to be stored as blob");
|
||||
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_options;
|
||||
|
||||
std::array<Slice, num_keys> keys{{first_key, second_key, third_key}};
|
||||
|
||||
{
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(values[1], second_value);
|
||||
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(values[2], third_value);
|
||||
}
|
||||
|
||||
// Try again with no I/O allowed. The table and the necessary blocks should
|
||||
// already be in their respective caches. The first (inlined) value should be
|
||||
// successfully read; however, the two blob values could only be read from the
|
||||
// blob file, so for those the read should return Incomplete.
|
||||
read_options.read_tier = kBlockCacheTier;
|
||||
|
||||
{
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(read_options, db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], first_value);
|
||||
|
||||
ASSERT_TRUE(statuses[1].IsIncomplete());
|
||||
|
||||
ASSERT_TRUE(statuses[2].IsIncomplete());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetBlob_CorruptIndex) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
|
||||
// Fake a corrupt blob index.
|
||||
const std::string blob_index("foobar");
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
PinnableSlice result;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetBlob_InlinedTTLIndex) {
|
||||
constexpr uint64_t min_blob_size = 10;
|
||||
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = min_blob_size;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob[] = "short";
|
||||
static_assert(sizeof(short) - 1 < min_blob_size,
|
||||
"Blob too long to be inlined");
|
||||
|
||||
// Fake an inlined TTL blob index.
|
||||
std::string blob_index;
|
||||
|
||||
constexpr uint64_t expiration = 1234567890;
|
||||
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, expiration, blob);
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
PinnableSlice result;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetBlob_IndexWithInvalidFileNumber) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
|
||||
// Fake a blob index referencing a non-existent blob file.
|
||||
std::string blob_index;
|
||||
|
||||
constexpr uint64_t blob_file_number = 1000;
|
||||
constexpr uint64_t offset = 1234;
|
||||
constexpr uint64_t size = 5678;
|
||||
|
||||
BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size,
|
||||
kNoCompression);
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
PinnableSlice result;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result)
|
||||
.IsCorruption());
|
||||
}
|
||||
|
||||
class DBBlobBasicIOErrorTest : public DBBlobBasicTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
protected:
|
||||
DBBlobBasicIOErrorTest() : sync_point_(GetParam()) {
|
||||
fault_injection_env_.reset(new FaultInjectionTestEnv(env_));
|
||||
}
|
||||
~DBBlobBasicIOErrorTest() { Close(); }
|
||||
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBlobBasicTest, DBBlobBasicIOErrorTest,
|
||||
::testing::ValuesIn(std::vector<std::string>{
|
||||
"BlobFileReader::OpenFile:NewRandomAccessFile",
|
||||
"BlobFileReader::GetBlob:ReadFromFile"}));
|
||||
|
||||
TEST_P(DBBlobBasicIOErrorTest, GetBlob_IOError) {
|
||||
Options options;
|
||||
options.env = fault_injection_env_.get();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char key[] = "key";
|
||||
constexpr char blob_value[] = "blob_value";
|
||||
|
||||
ASSERT_OK(Put(key, blob_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_->SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
PinnableSlice result;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), db_->DefaultColumnFamily(), key, &result)
|
||||
.IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_P(DBBlobBasicIOErrorTest, MultiGetBlobs_IOError) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_injection_env_.get();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr size_t num_keys = 2;
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::array<Slice, num_keys> keys{{first_key, second_key}};
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_->SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_TRUE(statuses[0].IsIOError());
|
||||
ASSERT_TRUE(statuses[1].IsIOError());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -73,6 +73,9 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
if (s.IsNotFound()) {
|
||||
return "NOT_FOUND";
|
||||
}
|
||||
if (s.IsCorruption()) {
|
||||
return "CORRUPTION";
|
||||
}
|
||||
if (s.IsNotSupported()) {
|
||||
return "NOT_SUPPORTED";
|
||||
}
|
||||
@@ -95,11 +98,12 @@ class DBBlobIndexTest : public DBTestBase {
|
||||
ArenaWrappedDBIter* GetBlobIterator() {
|
||||
return dbfull()->NewIteratorImpl(
|
||||
ReadOptions(), cfd(), dbfull()->GetLatestSequenceNumber(),
|
||||
nullptr /*read_callback*/, true /*allow_blob*/);
|
||||
nullptr /*read_callback*/, true /*expose_blob_index*/);
|
||||
}
|
||||
|
||||
Options GetTestOptions() {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.num_levels = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -153,8 +157,13 @@ TEST_F(DBBlobIndexTest, Write) {
|
||||
}
|
||||
}
|
||||
|
||||
// Get should be able to return blob index if is_blob_index is provided,
|
||||
// otherwise return Status::NotSupported status.
|
||||
// Note: the following test case pertains to the StackableDB-based BlobDB
|
||||
// implementation. Get should be able to return blob index if is_blob_index is
|
||||
// provided, otherwise it should return Status::NotSupported (when reading from
|
||||
// memtable) or Status::Corruption (when reading from SST). Reading from SST
|
||||
// returns Corruption because we can't differentiate between the application
|
||||
// accidentally opening the base DB of a stacked BlobDB and actual corruption
|
||||
// when using the integrated BlobDB.
|
||||
TEST_F(DBBlobIndexTest, Get) {
|
||||
for (auto tier : kAllTiers) {
|
||||
DestroyAndReopen(GetTestOptions());
|
||||
@@ -171,15 +180,22 @@ TEST_F(DBBlobIndexTest, Get) {
|
||||
ASSERT_EQ("value", GetImpl("key", &is_blob_index));
|
||||
ASSERT_FALSE(is_blob_index);
|
||||
// Verify blob index
|
||||
ASSERT_TRUE(Get("blob_key", &value).IsNotSupported());
|
||||
ASSERT_EQ("NOT_SUPPORTED", GetImpl("blob_key"));
|
||||
if (tier <= kImmutableMemtables) {
|
||||
ASSERT_TRUE(Get("blob_key", &value).IsNotSupported());
|
||||
ASSERT_EQ("NOT_SUPPORTED", GetImpl("blob_key"));
|
||||
} else {
|
||||
ASSERT_TRUE(Get("blob_key", &value).IsCorruption());
|
||||
ASSERT_EQ("CORRUPTION", GetImpl("blob_key"));
|
||||
}
|
||||
ASSERT_EQ("blob_index", GetImpl("blob_key", &is_blob_index));
|
||||
ASSERT_TRUE(is_blob_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Get should NOT return Status::NotSupported if blob index is updated with
|
||||
// a normal value.
|
||||
// Note: the following test case pertains to the StackableDB-based BlobDB
|
||||
// implementation. Get should NOT return Status::NotSupported/Status::Corruption
|
||||
// if blob index is updated with a normal value. See the test case above for
|
||||
// more details.
|
||||
TEST_F(DBBlobIndexTest, Updated) {
|
||||
for (auto tier : kAllTiers) {
|
||||
DestroyAndReopen(GetTestOptions());
|
||||
@@ -206,7 +222,11 @@ TEST_F(DBBlobIndexTest, Updated) {
|
||||
ASSERT_EQ("blob_index", GetBlobIndex("key" + ToString(i), snapshot));
|
||||
}
|
||||
ASSERT_EQ("new_value", Get("key1"));
|
||||
ASSERT_EQ("NOT_SUPPORTED", GetImpl("key2"));
|
||||
if (tier <= kImmutableMemtables) {
|
||||
ASSERT_EQ("NOT_SUPPORTED", GetImpl("key2"));
|
||||
} else {
|
||||
ASSERT_EQ("CORRUPTION", GetImpl("key2"));
|
||||
}
|
||||
ASSERT_EQ("NOT_FOUND", Get("key3"));
|
||||
ASSERT_EQ("NOT_FOUND", Get("key4"));
|
||||
ASSERT_EQ("a,b,c", GetImpl("key5"));
|
||||
@@ -218,8 +238,11 @@ TEST_F(DBBlobIndexTest, Updated) {
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator should get blob value if allow_blob flag is set,
|
||||
// otherwise return Status::NotSupported status.
|
||||
// Note: the following test case pertains to the StackableDB-based BlobDB
|
||||
// implementation. When a blob iterator is used, it should set the
|
||||
// expose_blob_index flag for the underlying DBIter, and retrieve/return the
|
||||
// corresponding blob value. If a regular DBIter is created (i.e.
|
||||
// expose_blob_index is not set), it should return Status::Corruption.
|
||||
TEST_F(DBBlobIndexTest, Iterate) {
|
||||
const std::vector<std::vector<ValueType>> data = {
|
||||
/*00*/ {kTypeValue},
|
||||
@@ -282,6 +305,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
std::function<void(Iterator*)> extra_check = nullptr) {
|
||||
// Seek
|
||||
auto* iterator = create_iterator();
|
||||
ASSERT_OK(iterator->status());
|
||||
ASSERT_OK(iterator->Refresh());
|
||||
iterator->Seek(get_key(index));
|
||||
check_iterator(iterator, expected_status, forward_value);
|
||||
@@ -295,6 +319,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
ASSERT_OK(iterator->Refresh());
|
||||
iterator->Seek(get_key(index - 1));
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_OK(iterator->status());
|
||||
iterator->Next();
|
||||
check_iterator(iterator, expected_status, forward_value);
|
||||
if (extra_check) {
|
||||
@@ -304,6 +329,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
|
||||
// SeekForPrev
|
||||
iterator = create_iterator();
|
||||
ASSERT_OK(iterator->status());
|
||||
ASSERT_OK(iterator->Refresh());
|
||||
iterator->SeekForPrev(get_key(index));
|
||||
check_iterator(iterator, expected_status, backward_value);
|
||||
@@ -316,6 +342,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
iterator = create_iterator();
|
||||
iterator->Seek(get_key(index + 1));
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_OK(iterator->status());
|
||||
iterator->Prev();
|
||||
check_iterator(iterator, expected_status, backward_value);
|
||||
if (extra_check) {
|
||||
@@ -353,7 +380,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
ASSERT_OK(Write(&batch));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
FAIL();
|
||||
};
|
||||
}
|
||||
snapshots.push_back(dbfull()->GetSnapshot());
|
||||
@@ -364,8 +391,8 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
MoveDataTo(tier);
|
||||
|
||||
// Normal iterator
|
||||
verify(1, Status::kNotSupported, "", "", create_normal_iterator);
|
||||
verify(3, Status::kNotSupported, "", "", create_normal_iterator);
|
||||
verify(1, Status::kCorruption, "", "", create_normal_iterator);
|
||||
verify(3, Status::kCorruption, "", "", create_normal_iterator);
|
||||
verify(5, Status::kOk, get_value(5, 0), get_value(5, 0),
|
||||
create_normal_iterator);
|
||||
verify(7, Status::kOk, get_value(8, 0), get_value(6, 0),
|
||||
|
||||
+24
-16
@@ -69,8 +69,8 @@ TableBuilder* NewTableBuilder(
|
||||
}
|
||||
|
||||
Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions, Env* env, FileSystem* fs,
|
||||
const ImmutableCFOptions& ioptions,
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const FileOptions& file_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
@@ -91,7 +91,7 @@ Status BuildTable(
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint,
|
||||
const uint64_t file_creation_time, const std::string& db_id,
|
||||
const std::string& db_session_id) {
|
||||
const std::string& db_session_id, const std::string* full_history_ts_low) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -120,15 +120,14 @@ Status BuildTable(
|
||||
EventHelpers::NotifyTableFileCreationStarted(
|
||||
ioptions.listeners, dbname, column_family_name, fname, job_id, reason);
|
||||
#endif // !ROCKSDB_LITE
|
||||
Env* env = db_options.env;
|
||||
assert(env);
|
||||
FileSystem* fs = db_options.fs.get();
|
||||
assert(fs);
|
||||
TableProperties tp;
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
TableBuilder* builder;
|
||||
std::unique_ptr<WritableFileWriter> file_writer;
|
||||
// Currently we only enable dictionary compression during compaction to the
|
||||
// bottommost level.
|
||||
CompressionOptions compression_opts_for_flush(compression_opts);
|
||||
compression_opts_for_flush.max_dict_bytes = 0;
|
||||
compression_opts_for_flush.zstd_max_train_bytes = 0;
|
||||
{
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
#ifndef NDEBUG
|
||||
@@ -160,7 +159,7 @@ Status BuildTable(
|
||||
ioptions, mutable_cf_options, internal_comparator,
|
||||
int_tbl_prop_collector_factories, column_family_id,
|
||||
column_family_name, file_writer.get(), compression,
|
||||
sample_for_compression, compression_opts_for_flush, level,
|
||||
sample_for_compression, compression_opts, level,
|
||||
false /* skip_filters */, creation_time, oldest_key_time,
|
||||
0 /*target_file_size*/, file_creation_time, db_id, db_session_id);
|
||||
}
|
||||
@@ -185,7 +184,11 @@ Status BuildTable(
|
||||
&snapshots, earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
ShouldReportDetailedTime(env, ioptions.statistics),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
blob_file_builder.get(), ioptions.allow_data_in_errors);
|
||||
blob_file_builder.get(), ioptions.allow_data_in_errors,
|
||||
/*compaction=*/nullptr,
|
||||
/*compaction_filter=*/nullptr, /*shutting_down=*/nullptr,
|
||||
/*preserve_deletes_seqnum=*/0, /*manual_compaction_paused=*/nullptr,
|
||||
db_options.info_log, full_history_ts_low);
|
||||
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
@@ -212,6 +215,7 @@ Status BuildTable(
|
||||
} else if (!c_iter.status().ok()) {
|
||||
s = c_iter.status();
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
auto range_del_it = range_del_agg->NewIterator();
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
@@ -222,10 +226,6 @@ Status BuildTable(
|
||||
meta->UpdateBoundariesForRange(kv.first, tombstone.SerializeEndKey(),
|
||||
tombstone.seq_, internal_comparator);
|
||||
}
|
||||
|
||||
if (blob_file_builder) {
|
||||
s = blob_file_builder->Finish();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT("BuildTable:BeforeFinishBuildTable");
|
||||
@@ -273,6 +273,14 @@ Status BuildTable(
|
||||
s = *io_status;
|
||||
}
|
||||
|
||||
if (blob_file_builder) {
|
||||
if (s.ok()) {
|
||||
s = blob_file_builder->Finish();
|
||||
}
|
||||
|
||||
blob_file_builder.reset();
|
||||
}
|
||||
|
||||
// TODO Also check the IO status when create the Iterator.
|
||||
|
||||
if (s.ok() && !empty) {
|
||||
@@ -318,6 +326,8 @@ Status BuildTable(
|
||||
}
|
||||
|
||||
if (!s.ok() || meta->fd.GetFileSize() == 0) {
|
||||
TEST_SYNC_POINT("BuildTable:BeforeDeleteFile");
|
||||
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
|
||||
Status ignored = fs->DeleteFile(fname, IOOptions(), dbg);
|
||||
@@ -330,8 +340,6 @@ Status BuildTable(
|
||||
ignored = fs->DeleteFile(blob_file_path, IOOptions(), dbg);
|
||||
ignored.PermitUncheckedError();
|
||||
}
|
||||
|
||||
blob_file_additions->clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -65,8 +65,8 @@ TableBuilder* NewTableBuilder(
|
||||
// @param column_family_name Name of the column family that is also identified
|
||||
// by column_family_id, or empty string if unknown.
|
||||
extern Status BuildTable(
|
||||
const std::string& dbname, VersionSet* versions, Env* env, FileSystem* fs,
|
||||
const ImmutableCFOptions& options,
|
||||
const std::string& dbname, VersionSet* versions,
|
||||
const ImmutableDBOptions& db_options, const ImmutableCFOptions& options,
|
||||
const MutableCFOptions& mutable_cf_options, const FileOptions& file_options,
|
||||
TableCache* table_cache, InternalIterator* iter,
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
@@ -89,6 +89,7 @@ extern Status BuildTable(
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const uint64_t file_creation_time = 0, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
const std::string& db_session_id = "",
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -60,7 +60,6 @@ using ROCKSDB_NAMESPACE::ColumnFamilyDescriptor;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyHandle;
|
||||
using ROCKSDB_NAMESPACE::ColumnFamilyOptions;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilter;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilterContext;
|
||||
using ROCKSDB_NAMESPACE::CompactionFilterFactory;
|
||||
using ROCKSDB_NAMESPACE::CompactionOptionsFIFO;
|
||||
using ROCKSDB_NAMESPACE::CompactRangeOptions;
|
||||
@@ -82,7 +81,6 @@ using ROCKSDB_NAMESPACE::LiveFileMetaData;
|
||||
using ROCKSDB_NAMESPACE::Logger;
|
||||
using ROCKSDB_NAMESPACE::MemoryUtil;
|
||||
using ROCKSDB_NAMESPACE::MergeOperator;
|
||||
using ROCKSDB_NAMESPACE::MergeOperators;
|
||||
using ROCKSDB_NAMESPACE::NewBloomFilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::NewGenericRateLimiter;
|
||||
using ROCKSDB_NAMESPACE::NewLRUCache;
|
||||
@@ -115,10 +113,8 @@ using ROCKSDB_NAMESPACE::WriteBatch;
|
||||
using ROCKSDB_NAMESPACE::WriteBatchWithIndex;
|
||||
using ROCKSDB_NAMESPACE::WriteOptions;
|
||||
|
||||
using std::shared_ptr;
|
||||
using std::vector;
|
||||
using std::unordered_set;
|
||||
using std::map;
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -181,6 +177,9 @@ struct rocksdb_transaction_options_t {
|
||||
struct rocksdb_transaction_t {
|
||||
Transaction* rep;
|
||||
};
|
||||
struct rocksdb_backupable_db_options_t {
|
||||
BackupableDBOptions rep;
|
||||
};
|
||||
struct rocksdb_checkpoint_t {
|
||||
Checkpoint* rep;
|
||||
};
|
||||
@@ -549,6 +548,18 @@ rocksdb_backup_engine_t* rocksdb_backup_engine_open(
|
||||
return result;
|
||||
}
|
||||
|
||||
rocksdb_backup_engine_t* rocksdb_backup_engine_open_opts(
|
||||
const rocksdb_backupable_db_options_t* options, rocksdb_env_t* env,
|
||||
char** errptr) {
|
||||
BackupEngine* be;
|
||||
if (SaveError(errptr, BackupEngine::Open(options->rep, env->rep, &be))) {
|
||||
return nullptr;
|
||||
}
|
||||
rocksdb_backup_engine_t* result = new rocksdb_backup_engine_t;
|
||||
result->rep = be;
|
||||
return result;
|
||||
}
|
||||
|
||||
void rocksdb_backup_engine_create_new_backup(rocksdb_backup_engine_t* be,
|
||||
rocksdb_t* db,
|
||||
char** errptr) {
|
||||
@@ -645,6 +656,128 @@ void rocksdb_backup_engine_close(rocksdb_backup_engine_t* be) {
|
||||
delete be;
|
||||
}
|
||||
|
||||
rocksdb_backupable_db_options_t* rocksdb_backupable_db_options_create(
|
||||
const char* backup_dir) {
|
||||
return new rocksdb_backupable_db_options_t{
|
||||
BackupableDBOptions(std::string(backup_dir))};
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_backup_dir(
|
||||
rocksdb_backupable_db_options_t* options, const char* backup_dir) {
|
||||
options->rep.backup_dir = std::string(backup_dir);
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_env(
|
||||
rocksdb_backupable_db_options_t* options, rocksdb_env_t* env) {
|
||||
options->rep.backup_env = (env ? env->rep : nullptr);
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_share_table_files(
|
||||
rocksdb_backupable_db_options_t* options, unsigned char val) {
|
||||
options->rep.share_table_files = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_backupable_db_options_get_share_table_files(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.share_table_files;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_sync(
|
||||
rocksdb_backupable_db_options_t* options, unsigned char val) {
|
||||
options->rep.sync = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_backupable_db_options_get_sync(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.sync;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_destroy_old_data(
|
||||
rocksdb_backupable_db_options_t* options, unsigned char val) {
|
||||
options->rep.destroy_old_data = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_backupable_db_options_get_destroy_old_data(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.destroy_old_data;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_backup_log_files(
|
||||
rocksdb_backupable_db_options_t* options, unsigned char val) {
|
||||
options->rep.backup_log_files = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_backupable_db_options_get_backup_log_files(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.backup_log_files;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_backup_rate_limit(
|
||||
rocksdb_backupable_db_options_t* options, uint64_t limit) {
|
||||
options->rep.backup_rate_limit = limit;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_backupable_db_options_get_backup_rate_limit(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.backup_rate_limit;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_restore_rate_limit(
|
||||
rocksdb_backupable_db_options_t* options, uint64_t limit) {
|
||||
options->rep.restore_rate_limit = limit;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_backupable_db_options_get_restore_rate_limit(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.restore_rate_limit;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_max_background_operations(
|
||||
rocksdb_backupable_db_options_t* options, int val) {
|
||||
options->rep.max_background_operations = val;
|
||||
}
|
||||
|
||||
int rocksdb_backupable_db_options_get_max_background_operations(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.max_background_operations;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_callback_trigger_interval_size(
|
||||
rocksdb_backupable_db_options_t* options, uint64_t size) {
|
||||
options->rep.callback_trigger_interval_size = size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_backupable_db_options_get_callback_trigger_interval_size(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.callback_trigger_interval_size;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_max_valid_backups_to_open(
|
||||
rocksdb_backupable_db_options_t* options, int val) {
|
||||
options->rep.max_valid_backups_to_open = val;
|
||||
}
|
||||
|
||||
int rocksdb_backupable_db_options_get_max_valid_backups_to_open(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return options->rep.max_valid_backups_to_open;
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_set_share_files_with_checksum_naming(
|
||||
rocksdb_backupable_db_options_t* options, int val) {
|
||||
options->rep.share_files_with_checksum_naming =
|
||||
static_cast<BackupableDBOptions::ShareFilesNaming>(val);
|
||||
}
|
||||
|
||||
int rocksdb_backupable_db_options_get_share_files_with_checksum_naming(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
return static_cast<int>(options->rep.share_files_with_checksum_naming);
|
||||
}
|
||||
|
||||
void rocksdb_backupable_db_options_destroy(
|
||||
rocksdb_backupable_db_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
rocksdb_checkpoint_t* rocksdb_checkpoint_object_create(rocksdb_t* db,
|
||||
char** errptr) {
|
||||
Checkpoint* checkpoint;
|
||||
@@ -3985,20 +4118,36 @@ void rocksdb_env_set_background_threads(rocksdb_env_t* env, int n) {
|
||||
env->rep->SetBackgroundThreads(n);
|
||||
}
|
||||
|
||||
int rocksdb_env_get_background_threads(rocksdb_env_t* env) {
|
||||
return env->rep->GetBackgroundThreads();
|
||||
}
|
||||
|
||||
void rocksdb_env_set_bottom_priority_background_threads(rocksdb_env_t* env,
|
||||
int n) {
|
||||
env->rep->SetBackgroundThreads(n, Env::BOTTOM);
|
||||
}
|
||||
|
||||
int rocksdb_env_get_bottom_priority_background_threads(rocksdb_env_t* env) {
|
||||
return env->rep->GetBackgroundThreads(Env::BOTTOM);
|
||||
}
|
||||
|
||||
void rocksdb_env_set_high_priority_background_threads(rocksdb_env_t* env, int n) {
|
||||
env->rep->SetBackgroundThreads(n, Env::HIGH);
|
||||
}
|
||||
|
||||
int rocksdb_env_get_high_priority_background_threads(rocksdb_env_t* env) {
|
||||
return env->rep->GetBackgroundThreads(Env::HIGH);
|
||||
}
|
||||
|
||||
void rocksdb_env_set_low_priority_background_threads(rocksdb_env_t* env,
|
||||
int n) {
|
||||
env->rep->SetBackgroundThreads(n, Env::LOW);
|
||||
}
|
||||
|
||||
int rocksdb_env_get_low_priority_background_threads(rocksdb_env_t* env) {
|
||||
return env->rep->GetBackgroundThreads(Env::LOW);
|
||||
}
|
||||
|
||||
void rocksdb_env_join_all_threads(rocksdb_env_t* env) {
|
||||
env->rep->WaitForJoin();
|
||||
}
|
||||
@@ -4226,32 +4375,62 @@ void rocksdb_universal_compaction_options_set_size_ratio(
|
||||
uco->rep->size_ratio = ratio;
|
||||
}
|
||||
|
||||
int rocksdb_universal_compaction_options_get_size_ratio(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
return uco->rep->size_ratio;
|
||||
}
|
||||
|
||||
void rocksdb_universal_compaction_options_set_min_merge_width(
|
||||
rocksdb_universal_compaction_options_t* uco, int w) {
|
||||
uco->rep->min_merge_width = w;
|
||||
}
|
||||
|
||||
int rocksdb_universal_compaction_options_get_min_merge_width(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
return uco->rep->min_merge_width;
|
||||
}
|
||||
|
||||
void rocksdb_universal_compaction_options_set_max_merge_width(
|
||||
rocksdb_universal_compaction_options_t* uco, int w) {
|
||||
uco->rep->max_merge_width = w;
|
||||
}
|
||||
|
||||
int rocksdb_universal_compaction_options_get_max_merge_width(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
return uco->rep->max_merge_width;
|
||||
}
|
||||
|
||||
void rocksdb_universal_compaction_options_set_max_size_amplification_percent(
|
||||
rocksdb_universal_compaction_options_t* uco, int p) {
|
||||
uco->rep->max_size_amplification_percent = p;
|
||||
}
|
||||
|
||||
int rocksdb_universal_compaction_options_get_max_size_amplification_percent(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
return uco->rep->max_size_amplification_percent;
|
||||
}
|
||||
|
||||
void rocksdb_universal_compaction_options_set_compression_size_percent(
|
||||
rocksdb_universal_compaction_options_t* uco, int p) {
|
||||
uco->rep->compression_size_percent = p;
|
||||
}
|
||||
|
||||
int rocksdb_universal_compaction_options_get_compression_size_percent(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
return uco->rep->compression_size_percent;
|
||||
}
|
||||
|
||||
void rocksdb_universal_compaction_options_set_stop_style(
|
||||
rocksdb_universal_compaction_options_t* uco, int style) {
|
||||
uco->rep->stop_style =
|
||||
static_cast<ROCKSDB_NAMESPACE::CompactionStopStyle>(style);
|
||||
}
|
||||
|
||||
int rocksdb_universal_compaction_options_get_stop_style(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
return static_cast<int>(uco->rep->stop_style);
|
||||
}
|
||||
|
||||
void rocksdb_universal_compaction_options_destroy(
|
||||
rocksdb_universal_compaction_options_t* uco) {
|
||||
delete uco->rep;
|
||||
@@ -4269,6 +4448,11 @@ void rocksdb_fifo_compaction_options_set_max_table_files_size(
|
||||
fifo_opts->rep.max_table_files_size = size;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_fifo_compaction_options_get_max_table_files_size(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts) {
|
||||
return fifo_opts->rep.max_table_files_size;
|
||||
}
|
||||
|
||||
void rocksdb_fifo_compaction_options_destroy(
|
||||
rocksdb_fifo_compaction_options_t* fifo_opts) {
|
||||
delete fifo_opts;
|
||||
|
||||
+118
@@ -2355,6 +2355,124 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cache_destroy(co);
|
||||
}
|
||||
|
||||
StartPhase("env");
|
||||
{
|
||||
rocksdb_env_t* e;
|
||||
e = rocksdb_create_default_env();
|
||||
|
||||
rocksdb_env_set_background_threads(e, 10);
|
||||
CheckCondition(10 == rocksdb_env_get_background_threads(e));
|
||||
|
||||
rocksdb_env_set_high_priority_background_threads(e, 20);
|
||||
CheckCondition(20 == rocksdb_env_get_high_priority_background_threads(e));
|
||||
|
||||
rocksdb_env_set_low_priority_background_threads(e, 30);
|
||||
CheckCondition(30 == rocksdb_env_get_low_priority_background_threads(e));
|
||||
|
||||
rocksdb_env_set_bottom_priority_background_threads(e, 40);
|
||||
CheckCondition(40 == rocksdb_env_get_bottom_priority_background_threads(e));
|
||||
|
||||
rocksdb_env_destroy(e);
|
||||
}
|
||||
|
||||
StartPhase("universal_compaction_options");
|
||||
{
|
||||
rocksdb_universal_compaction_options_t* uco;
|
||||
uco = rocksdb_universal_compaction_options_create();
|
||||
|
||||
rocksdb_universal_compaction_options_set_size_ratio(uco, 5);
|
||||
CheckCondition(5 ==
|
||||
rocksdb_universal_compaction_options_get_size_ratio(uco));
|
||||
|
||||
rocksdb_universal_compaction_options_set_min_merge_width(uco, 15);
|
||||
CheckCondition(
|
||||
15 == rocksdb_universal_compaction_options_get_min_merge_width(uco));
|
||||
|
||||
rocksdb_universal_compaction_options_set_max_merge_width(uco, 25);
|
||||
CheckCondition(
|
||||
25 == rocksdb_universal_compaction_options_get_max_merge_width(uco));
|
||||
|
||||
rocksdb_universal_compaction_options_set_max_size_amplification_percent(uco,
|
||||
35);
|
||||
CheckCondition(
|
||||
35 ==
|
||||
rocksdb_universal_compaction_options_get_max_size_amplification_percent(
|
||||
uco));
|
||||
|
||||
rocksdb_universal_compaction_options_set_compression_size_percent(uco, 45);
|
||||
CheckCondition(
|
||||
45 ==
|
||||
rocksdb_universal_compaction_options_get_compression_size_percent(uco));
|
||||
|
||||
rocksdb_universal_compaction_options_set_stop_style(uco, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_universal_compaction_options_get_stop_style(uco));
|
||||
|
||||
rocksdb_universal_compaction_options_destroy(uco);
|
||||
}
|
||||
|
||||
StartPhase("fifo_compaction_options");
|
||||
{
|
||||
rocksdb_fifo_compaction_options_t* fco;
|
||||
fco = rocksdb_fifo_compaction_options_create();
|
||||
|
||||
rocksdb_fifo_compaction_options_set_max_table_files_size(fco, 100000);
|
||||
CheckCondition(
|
||||
100000 ==
|
||||
rocksdb_fifo_compaction_options_get_max_table_files_size(fco));
|
||||
|
||||
rocksdb_fifo_compaction_options_destroy(fco);
|
||||
}
|
||||
|
||||
StartPhase("backupable_db_option");
|
||||
{
|
||||
rocksdb_backupable_db_options_t* bdo;
|
||||
bdo = rocksdb_backupable_db_options_create("path");
|
||||
|
||||
rocksdb_backupable_db_options_set_share_table_files(bdo, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_backupable_db_options_get_share_table_files(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_sync(bdo, 1);
|
||||
CheckCondition(1 == rocksdb_backupable_db_options_get_sync(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_destroy_old_data(bdo, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_backupable_db_options_get_destroy_old_data(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_backup_log_files(bdo, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_backupable_db_options_get_backup_log_files(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_backup_rate_limit(bdo, 123);
|
||||
CheckCondition(123 ==
|
||||
rocksdb_backupable_db_options_get_backup_rate_limit(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_restore_rate_limit(bdo, 37);
|
||||
CheckCondition(37 ==
|
||||
rocksdb_backupable_db_options_get_restore_rate_limit(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_max_background_operations(bdo, 20);
|
||||
CheckCondition(
|
||||
20 == rocksdb_backupable_db_options_get_max_background_operations(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_callback_trigger_interval_size(bdo, 9000);
|
||||
CheckCondition(
|
||||
9000 ==
|
||||
rocksdb_backupable_db_options_get_callback_trigger_interval_size(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_max_valid_backups_to_open(bdo, 40);
|
||||
CheckCondition(
|
||||
40 == rocksdb_backupable_db_options_get_max_valid_backups_to_open(bdo));
|
||||
|
||||
rocksdb_backupable_db_options_set_share_files_with_checksum_naming(bdo, 2);
|
||||
CheckCondition(
|
||||
2 == rocksdb_backupable_db_options_get_share_files_with_checksum_naming(
|
||||
bdo));
|
||||
|
||||
rocksdb_backupable_db_options_destroy(bdo);
|
||||
}
|
||||
|
||||
StartPhase("iterate_upper_bound");
|
||||
{
|
||||
// Create new empty database
|
||||
|
||||
+28
-15
@@ -16,6 +16,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_cache.h"
|
||||
#include "db/compaction/compaction_picker.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
@@ -448,9 +449,7 @@ void SuperVersion::Cleanup() {
|
||||
to_delete.push_back(m);
|
||||
}
|
||||
current->Unref();
|
||||
if (cfd->Unref()) {
|
||||
delete cfd;
|
||||
}
|
||||
cfd->UnrefAndTryDelete(this);
|
||||
}
|
||||
|
||||
void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
@@ -559,6 +558,10 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
new InternalStats(ioptions_.num_levels, db_options.env, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, file_options, _table_cache,
|
||||
block_cache_tracer, io_tracer));
|
||||
blob_file_cache_.reset(
|
||||
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
|
||||
internal_stats_->GetBlobFileReadHist()));
|
||||
|
||||
if (ioptions_.compaction_style == kCompactionStyleLevel) {
|
||||
compaction_picker_.reset(
|
||||
new LevelCompactionPicker(ioptions_, &internal_comparator_));
|
||||
@@ -655,7 +658,7 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::UnrefAndTryDelete() {
|
||||
bool ColumnFamilyData::UnrefAndTryDelete(SuperVersion* sv_under_cleanup) {
|
||||
int old_refs = refs_.fetch_sub(1);
|
||||
assert(old_refs > 0);
|
||||
|
||||
@@ -665,7 +668,11 @@ bool ColumnFamilyData::UnrefAndTryDelete() {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (old_refs == 2 && super_version_ != nullptr) {
|
||||
// If called under SuperVersion::Cleanup, we should not re-enter Cleanup on
|
||||
// the same SuperVersion. (But while installing a new SuperVersion, this
|
||||
// cfd could be referenced only by two SuperVersions.)
|
||||
if (old_refs == 2 && super_version_ != nullptr &&
|
||||
super_version_ != sv_under_cleanup) {
|
||||
// Only the super_version_ holds me
|
||||
SuperVersion* sv = super_version_;
|
||||
super_version_ = nullptr;
|
||||
@@ -703,9 +710,7 @@ uint64_t ColumnFamilyData::OldestLogToKeep() {
|
||||
auto current_log = GetLogNumber();
|
||||
|
||||
if (allow_2pc_) {
|
||||
autovector<MemTable*> empty_list;
|
||||
auto imm_prep_log =
|
||||
imm()->PrecomputeMinLogContainingPrepSection(empty_list);
|
||||
auto imm_prep_log = imm()->PrecomputeMinLogContainingPrepSection();
|
||||
auto mem_prep_log = mem()->GetMinLogContainingPrepSection();
|
||||
|
||||
if (imm_prep_log > 0 && imm_prep_log < current_log) {
|
||||
@@ -1083,7 +1088,7 @@ bool ColumnFamilyData::RangeOverlapWithCompaction(
|
||||
|
||||
Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
const autovector<Range>& ranges, SuperVersion* super_version,
|
||||
bool* overlap) {
|
||||
bool allow_data_in_errors, bool* overlap) {
|
||||
assert(overlap != nullptr);
|
||||
*overlap = false;
|
||||
// Create an InternalIterator over all unflushed memtables
|
||||
@@ -1116,13 +1121,12 @@ Status ColumnFamilyData::RangesOverlapWithMemtables(
|
||||
memtable_iter->Seek(range_start.Encode());
|
||||
status = memtable_iter->status();
|
||||
ParsedInternalKey seek_result;
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ParseInternalKey(memtable_iter->key(), &seek_result) !=
|
||||
Status::OK()) {
|
||||
status = Status::Corruption("DB have corrupted keys");
|
||||
}
|
||||
|
||||
if (status.ok() && memtable_iter->Valid()) {
|
||||
status = ParseInternalKey(memtable_iter->key(), &seek_result,
|
||||
allow_data_in_errors);
|
||||
}
|
||||
|
||||
if (status.ok()) {
|
||||
if (memtable_iter->Valid() &&
|
||||
ucmp->Compare(seek_result.user_key, ranges[i].limit) <= 0) {
|
||||
@@ -1335,6 +1339,15 @@ Status ColumnFamilyData::ValidateOptions(
|
||||
"Block-Based Table format. ");
|
||||
}
|
||||
}
|
||||
|
||||
if (cf_options.enable_blob_garbage_collection &&
|
||||
(cf_options.blob_garbage_collection_age_cutoff < 0.0 ||
|
||||
cf_options.blob_garbage_collection_age_cutoff > 1.0)) {
|
||||
return Status::InvalidArgument(
|
||||
"The age cutoff for blob garbage collection should be in the range "
|
||||
"[0.0, 1.0].");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+29
-13
@@ -44,6 +44,7 @@ class LogBuffer;
|
||||
class InstrumentedMutex;
|
||||
class InstrumentedMutexLock;
|
||||
struct SuperVersionContext;
|
||||
class BlobFileCache;
|
||||
|
||||
extern const double kIncSlowdownRatio;
|
||||
// This file contains a list of data structures for managing column family
|
||||
@@ -278,21 +279,11 @@ class ColumnFamilyData {
|
||||
// holding a DB mutex, or as the leader in a write batch group).
|
||||
void Ref() { refs_.fetch_add(1); }
|
||||
|
||||
// Unref decreases the reference count, but does not handle deletion
|
||||
// when the count goes to 0. If this method returns true then the
|
||||
// caller should delete the instance immediately, or later, by calling
|
||||
// FreeDeadColumnFamilies(). Unref() can only be called while holding
|
||||
// a DB mutex, or during single-threaded recovery.
|
||||
bool Unref() {
|
||||
int old_refs = refs_.fetch_sub(1);
|
||||
assert(old_refs > 0);
|
||||
return old_refs == 1;
|
||||
}
|
||||
|
||||
// UnrefAndTryDelete() decreases the reference count and do free if needed,
|
||||
// return true if this is freed else false, UnrefAndTryDelete() can only
|
||||
// be called while holding a DB mutex, or during single-threaded recovery.
|
||||
bool UnrefAndTryDelete();
|
||||
// sv_under_cleanup is only provided when called from SuperVersion::Cleanup.
|
||||
bool UnrefAndTryDelete(SuperVersion* sv_under_cleanup = nullptr);
|
||||
|
||||
// SetDropped() can only be called under following conditions:
|
||||
// 1) Holding a DB mutex,
|
||||
@@ -359,6 +350,11 @@ class ColumnFamilyData {
|
||||
|
||||
MemTableList* imm() { return &imm_; }
|
||||
MemTable* mem() { return mem_; }
|
||||
|
||||
bool IsEmpty() {
|
||||
return mem()->GetFirstSequenceNumber() == 0 && imm()->NumNotFlushed() == 0;
|
||||
}
|
||||
|
||||
Version* current() { return current_; }
|
||||
Version* dummy_versions() { return dummy_versions_; }
|
||||
void SetCurrent(Version* _current);
|
||||
@@ -381,6 +377,7 @@ class ColumnFamilyData {
|
||||
SequenceNumber earliest_seq);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
|
||||
|
||||
// See documentation in compaction_picker.h
|
||||
// REQUIRES: DB mutex held
|
||||
@@ -404,7 +401,8 @@ class ColumnFamilyData {
|
||||
//
|
||||
// Thread-safe
|
||||
Status RangesOverlapWithMemtables(const autovector<Range>& ranges,
|
||||
SuperVersion* super_version, bool* overlap);
|
||||
SuperVersion* super_version,
|
||||
bool allow_data_in_errors, bool* overlap);
|
||||
|
||||
// A flag to tell a manual compaction is to compact all levels together
|
||||
// instead of a specific level.
|
||||
@@ -506,6 +504,21 @@ class ColumnFamilyData {
|
||||
|
||||
FSDirectory* GetDataDir(size_t path_id) const;
|
||||
|
||||
// full_history_ts_low_ can only increase.
|
||||
void SetFullHistoryTsLow(std::string ts_low) {
|
||||
assert(!ts_low.empty());
|
||||
const Comparator* ucmp = user_comparator();
|
||||
assert(ucmp);
|
||||
if (full_history_ts_low_.empty() ||
|
||||
ucmp->CompareTimestamp(ts_low, full_history_ts_low_) > 0) {
|
||||
full_history_ts_low_ = std::move(ts_low);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& GetFullHistoryTsLow() const {
|
||||
return full_history_ts_low_;
|
||||
}
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
|
||||
private:
|
||||
@@ -543,6 +556,7 @@ class ColumnFamilyData {
|
||||
const bool is_delete_range_supported_;
|
||||
|
||||
std::unique_ptr<TableCache> table_cache_;
|
||||
std::unique_ptr<BlobFileCache> blob_file_cache_;
|
||||
|
||||
std::unique_ptr<InternalStats> internal_stats_;
|
||||
|
||||
@@ -601,6 +615,8 @@ class ColumnFamilyData {
|
||||
std::vector<std::shared_ptr<FSDirectory>> data_dirs_;
|
||||
|
||||
bool db_paths_registered_;
|
||||
|
||||
std::string full_history_ts_low_;
|
||||
};
|
||||
|
||||
// ColumnFamilySet has interesting thread-safety requirements
|
||||
|
||||
@@ -35,10 +35,10 @@ namespace ROCKSDB_NAMESPACE {
|
||||
static const int kValueSize = 1000;
|
||||
|
||||
// counts how many operations were performed
|
||||
class EnvCounter : public EnvWrapper {
|
||||
class EnvCounter : public SpecialEnv {
|
||||
public:
|
||||
explicit EnvCounter(Env* base)
|
||||
: EnvWrapper(base), num_new_writable_file_(0) {}
|
||||
: SpecialEnv(base), num_new_writable_file_(0) {}
|
||||
int GetNumberOfNewWritableFileCalls() {
|
||||
return num_new_writable_file_;
|
||||
}
|
||||
@@ -68,6 +68,7 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
#endif // !ROCKSDB_LITE
|
||||
EXPECT_NE(nullptr, base_env);
|
||||
env_ = new EnvCounter(base_env);
|
||||
env_->skip_fsync_ = true;
|
||||
dbname_ = test::PerThreadDBPath("column_family_test");
|
||||
db_options_.create_if_missing = true;
|
||||
db_options_.fail_if_options_file_error = true;
|
||||
@@ -87,7 +88,6 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
#endif // ROCKSDB_LITE
|
||||
column_families.push_back(cfdescriptor);
|
||||
}
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
Destroy(column_families);
|
||||
delete env_;
|
||||
@@ -3271,7 +3271,7 @@ TEST_P(ColumnFamilyTest, DISABLED_LogTruncationTest) {
|
||||
FileType type;
|
||||
if (!(ParseFileName(filenames[i], &number, &type))) continue;
|
||||
|
||||
if (type != kLogFile) continue;
|
||||
if (type != kWalFile) continue;
|
||||
|
||||
logfs.push_back(filenames[i]);
|
||||
}
|
||||
@@ -3391,6 +3391,30 @@ TEST_P(ColumnFamilyTest, MultipleCFPathsTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ColumnFamilyTest, ValidateBlobGCCutoff) {
|
||||
DBOptions db_options;
|
||||
|
||||
ColumnFamilyOptions cf_options;
|
||||
cf_options.enable_blob_garbage_collection = true;
|
||||
|
||||
cf_options.blob_garbage_collection_age_cutoff = -0.5;
|
||||
ASSERT_TRUE(ColumnFamilyData::ValidateOptions(db_options, cf_options)
|
||||
.IsInvalidArgument());
|
||||
|
||||
cf_options.blob_garbage_collection_age_cutoff = 0.0;
|
||||
ASSERT_OK(ColumnFamilyData::ValidateOptions(db_options, cf_options));
|
||||
|
||||
cf_options.blob_garbage_collection_age_cutoff = 0.5;
|
||||
ASSERT_OK(ColumnFamilyData::ValidateOptions(db_options, cf_options));
|
||||
|
||||
cf_options.blob_garbage_collection_age_cutoff = 1.0;
|
||||
ASSERT_OK(ColumnFamilyData::ValidateOptions(db_options, cf_options));
|
||||
|
||||
cf_options.blob_garbage_collection_age_cutoff = 1.5;
|
||||
ASSERT_TRUE(ColumnFamilyData::ValidateOptions(db_options, cf_options)
|
||||
.IsInvalidArgument());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
@@ -248,12 +248,6 @@ Compaction::Compaction(VersionStorageInfo* vstorage,
|
||||
if (max_subcompactions_ == 0) {
|
||||
max_subcompactions_ = _mutable_db_options.max_subcompactions;
|
||||
}
|
||||
if (!bottommost_level_) {
|
||||
// Currently we only enable dictionary compression during compaction to the
|
||||
// bottommost level.
|
||||
output_compression_opts_.max_dict_bytes = 0;
|
||||
output_compression_opts_.zstd_max_train_bytes = 0;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 1; i < inputs_.size(); ++i) {
|
||||
@@ -383,7 +377,13 @@ bool Compaction::KeyNotExistsBeyondOutputLevel(
|
||||
auto* f = files[level_ptrs->at(lvl)];
|
||||
if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) {
|
||||
// We've advanced far enough
|
||||
if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) {
|
||||
// In the presence of user-defined timestamp, we may need to handle
|
||||
// the case in which f->smallest.user_key() (including ts) has the
|
||||
// same user key, but the ts part is smaller. If so,
|
||||
// Compare(user_key, f->smallest.user_key()) returns -1.
|
||||
// That's why we need CompareWithoutTimestamp().
|
||||
if (user_cmp->CompareWithoutTimestamp(user_key,
|
||||
f->smallest.user_key()) >= 0) {
|
||||
// Key falls in this file's range, so it may
|
||||
// exist beyond output level
|
||||
return false;
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
#include "db/blob/blob_file_builder.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "port/likely.h"
|
||||
#include "rocksdb/listener.h"
|
||||
@@ -44,16 +46,17 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::shared_ptr<Logger> info_log)
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low)
|
||||
: CompactionIterator(
|
||||
input, cmp, merge_helper, last_sequence, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, env,
|
||||
report_detailed_time, expect_valid_internal_key, range_del_agg,
|
||||
blob_file_builder, allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new CompactionProxy(compaction) : nullptr),
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum,
|
||||
manual_compaction_paused, info_log) {}
|
||||
manual_compaction_paused, info_log, full_history_ts_low) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -68,7 +71,8 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::shared_ptr<Logger> info_log)
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low)
|
||||
: input_(input),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
@@ -85,12 +89,17 @@ CompactionIterator::CompactionIterator(
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
timestamp_size_(cmp_ ? cmp_->timestamp_size() : 0),
|
||||
full_history_ts_low_(full_history_ts_low),
|
||||
current_user_key_sequence_(0),
|
||||
current_user_key_snapshot_(0),
|
||||
merge_out_iter_(merge_helper_),
|
||||
blob_garbage_collection_cutoff_file_number_(
|
||||
ComputeBlobGarbageCollectionCutoffFileNumber(compaction_.get())),
|
||||
current_key_committed_(false),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors) {
|
||||
cmp_with_history_ts_low_(0) {
|
||||
assert(compaction_filter_ == nullptr || compaction_ != nullptr);
|
||||
assert(snapshots_ != nullptr);
|
||||
bottommost_level_ = compaction_ == nullptr
|
||||
@@ -117,6 +126,8 @@ CompactionIterator::CompactionIterator(
|
||||
for (size_t i = 1; i < snapshots_->size(); ++i) {
|
||||
assert(snapshots_->at(i - 1) < snapshots_->at(i));
|
||||
}
|
||||
assert(timestamp_size_ == 0 || !full_history_ts_low_ ||
|
||||
timestamp_size_ == full_history_ts_low_->size());
|
||||
#endif
|
||||
input_->SetPinnedItersMgr(&pinned_iters_mgr_);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:AfterInit", compaction_.get());
|
||||
@@ -151,13 +162,13 @@ void CompactionIterator::Next() {
|
||||
if (merge_out_iter_.Valid()) {
|
||||
key_ = merge_out_iter_.key();
|
||||
value_ = merge_out_iter_.value();
|
||||
Status s = ParseInternalKey(key_, &ikey_);
|
||||
Status s = ParseInternalKey(key_, &ikey_, allow_data_in_errors_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to be valid.
|
||||
assert(s.ok());
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_FATAL(info_log_, "Invalid key (%s) in compaction",
|
||||
key_.ToString(true).c_str());
|
||||
ROCKS_LOG_FATAL(info_log_, "Invalid key in compaction. %s",
|
||||
s.getState());
|
||||
}
|
||||
|
||||
// Keep current_key_ in sync.
|
||||
@@ -270,22 +281,14 @@ void CompactionIterator::NextFromInput() {
|
||||
value_ = input_->value();
|
||||
iter_stats_.num_input_records++;
|
||||
|
||||
Status pikStatus = ParseInternalKey(key_, &ikey_);
|
||||
if (!pikStatus.ok()) {
|
||||
Status pik_status = ParseInternalKey(key_, &ikey_, allow_data_in_errors_);
|
||||
if (!pik_status.ok()) {
|
||||
iter_stats_.num_input_corrupt_records++;
|
||||
|
||||
// If `expect_valid_internal_key_` is false, return the corrupted key
|
||||
// and let the caller decide what to do with it.
|
||||
// TODO(noetzli): We should have a more elegant solution for this.
|
||||
if (expect_valid_internal_key_) {
|
||||
std::string msg("Corrupted internal key not expected.");
|
||||
if (allow_data_in_errors_) {
|
||||
msg.append(" Corrupt key: " + ikey_.user_key.ToString(/*hex=*/true) +
|
||||
". ");
|
||||
msg.append("key type: " + std::to_string(ikey_.type) + ".");
|
||||
msg.append("seq: " + std::to_string(ikey_.sequence) + ".");
|
||||
}
|
||||
status_ = Status::Corruption(msg.c_str());
|
||||
status_ = pik_status;
|
||||
return;
|
||||
}
|
||||
key_ = current_key_.SetInternalKey(key_);
|
||||
@@ -298,7 +301,8 @@ void CompactionIterator::NextFromInput() {
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator:ProcessKV", &ikey_);
|
||||
|
||||
// Update input statistics
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion) {
|
||||
if (ikey_.type == kTypeDeletion || ikey_.type == kTypeSingleDeletion ||
|
||||
ikey_.type == kTypeDeletionWithTimestamp) {
|
||||
iter_stats_.num_input_deletion_records++;
|
||||
}
|
||||
iter_stats_.total_input_raw_key_bytes += key_.size();
|
||||
@@ -311,19 +315,56 @@ void CompactionIterator::NextFromInput() {
|
||||
// merge_helper_->compaction_filter_skip_until_.
|
||||
Slice skip_until;
|
||||
|
||||
int cmp_user_key_without_ts = 0;
|
||||
int cmp_ts = 0;
|
||||
if (has_current_user_key_) {
|
||||
cmp_user_key_without_ts =
|
||||
timestamp_size_
|
||||
? cmp_->CompareWithoutTimestamp(ikey_.user_key, current_user_key_)
|
||||
: cmp_->Compare(ikey_.user_key, current_user_key_);
|
||||
// if timestamp_size_ > 0, then curr_ts_ has been initialized by a
|
||||
// previous key.
|
||||
cmp_ts = timestamp_size_ ? cmp_->CompareTimestamp(
|
||||
ExtractTimestampFromUserKey(
|
||||
ikey_.user_key, timestamp_size_),
|
||||
curr_ts_)
|
||||
: 0;
|
||||
}
|
||||
|
||||
// Check whether the user key changed. After this if statement current_key_
|
||||
// is a copy of the current input key (maybe converted to a delete by the
|
||||
// compaction filter). ikey_.user_key is pointing to the copy.
|
||||
if (!has_current_user_key_ ||
|
||||
!cmp_->Equal(ikey_.user_key, current_user_key_)) {
|
||||
if (!has_current_user_key_ || cmp_user_key_without_ts != 0 || cmp_ts != 0) {
|
||||
// First occurrence of this user key
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
||||
|
||||
// If timestamp_size_ > 0, then copy from ikey_ to curr_ts_ for the use
|
||||
// in next iteration to compare with the timestamp of next key.
|
||||
UpdateTimestampAndCompareWithFullHistoryLow();
|
||||
|
||||
// If
|
||||
// (1) !has_current_user_key_, OR
|
||||
// (2) timestamp is disabled, OR
|
||||
// (3) all history will be preserved, OR
|
||||
// (4) user key (excluding timestamp) is different from previous key, OR
|
||||
// (5) timestamp is NO older than *full_history_ts_low_
|
||||
// then current_user_key_ must be treated as a different user key.
|
||||
// This means, if a user key (excluding ts) is the same as the previous
|
||||
// user key, and its ts is older than *full_history_ts_low_, then we
|
||||
// consider this key for GC, e.g. it may be dropped if certain conditions
|
||||
// match.
|
||||
if (!has_current_user_key_ || !timestamp_size_ || !full_history_ts_low_ ||
|
||||
0 != cmp_user_key_without_ts || cmp_with_history_ts_low_ >= 0) {
|
||||
// Initialize for future comparison for rule (A) and etc.
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
has_current_user_key_ = true;
|
||||
}
|
||||
current_user_key_ = ikey_.user_key;
|
||||
has_current_user_key_ = true;
|
||||
|
||||
has_outputted_key_ = false;
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
|
||||
current_key_committed_ = KeyCommitted(ikey_.sequence);
|
||||
|
||||
// Apply the compaction filter to the first committed version of the user
|
||||
@@ -439,7 +480,8 @@ void CompactionIterator::NextFromInput() {
|
||||
// Check whether the next key exists, is not corrupt, and is the same key
|
||||
// as the single delete.
|
||||
if (input_->Valid() &&
|
||||
ParseInternalKey(input_->key(), &next_ikey) == Status::OK() &&
|
||||
ParseInternalKey(input_->key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok() &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
@@ -543,9 +585,12 @@ void CompactionIterator::NextFromInput() {
|
||||
last_sequence, current_user_key_sequence_);
|
||||
}
|
||||
|
||||
++iter_stats_.num_record_drop_hidden; // (A)
|
||||
++iter_stats_.num_record_drop_hidden; // rule (A)
|
||||
input_->Next();
|
||||
} else if (compaction_ != nullptr && ikey_.type == kTypeDeletion &&
|
||||
} else if (compaction_ != nullptr &&
|
||||
(ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeDeletionWithTimestamp &&
|
||||
cmp_with_history_ts_low_ < 0)) &&
|
||||
IN_EARLIEST_SNAPSHOT(ikey_.sequence) &&
|
||||
ikeyNotNeededForIncrementalSnapshot() &&
|
||||
compaction_->KeyNotExistsBeyondOutputLevel(ikey_.user_key,
|
||||
@@ -569,13 +614,19 @@ void CompactionIterator::NextFromInput() {
|
||||
// given that:
|
||||
// (1) The deletion is earlier than earliest_write_conflict_snapshot, and
|
||||
// (2) No value exist earlier than the deletion.
|
||||
//
|
||||
// Note also that a deletion marker of type kTypeDeletionWithTimestamp
|
||||
// will be treated as a different user key unless the timestamp is older
|
||||
// than *full_history_ts_low_.
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
if (!bottommost_level_) {
|
||||
++iter_stats_.num_optimized_del_drop_obsolete;
|
||||
}
|
||||
input_->Next();
|
||||
} else if ((ikey_.type == kTypeDeletion) && bottommost_level_ &&
|
||||
ikeyNotNeededForIncrementalSnapshot()) {
|
||||
} else if ((ikey_.type == kTypeDeletion ||
|
||||
(ikey_.type == kTypeDeletionWithTimestamp &&
|
||||
cmp_with_history_ts_low_ < 0)) &&
|
||||
bottommost_level_ && ikeyNotNeededForIncrementalSnapshot()) {
|
||||
// Handle the case where we have a delete key at the bottom most level
|
||||
// We can skip outputting the key iff there are no subsequent puts for this
|
||||
// key
|
||||
@@ -583,12 +634,18 @@ void CompactionIterator::NextFromInput() {
|
||||
ikey_.user_key, &level_ptrs_));
|
||||
ParsedInternalKey next_ikey;
|
||||
input_->Next();
|
||||
// Skip over all versions of this key that happen to occur in the same snapshot
|
||||
// range as the delete
|
||||
// Skip over all versions of this key that happen to occur in the same
|
||||
// snapshot range as the delete.
|
||||
//
|
||||
// Note that a deletion marker of type kTypeDeletionWithTimestamp will be
|
||||
// considered to have a different user key unless the timestamp is older
|
||||
// than *full_history_ts_low_.
|
||||
while (!IsPausingManualCompaction() && !IsShuttingDown() &&
|
||||
input_->Valid() &&
|
||||
(ParseInternalKey(input_->key(), &next_ikey) == Status::OK()) &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key) &&
|
||||
(ParseInternalKey(input_->key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok()) &&
|
||||
0 == cmp_->CompareWithoutTimestamp(ikey_.user_key,
|
||||
next_ikey.user_key) &&
|
||||
(prev_snapshot == 0 ||
|
||||
DEFINITELY_NOT_IN_SNAPSHOT(next_ikey.sequence, prev_snapshot))) {
|
||||
input_->Next();
|
||||
@@ -596,8 +653,10 @@ void CompactionIterator::NextFromInput() {
|
||||
// If you find you still need to output a row with this key, we need to output the
|
||||
// delete too
|
||||
if (input_->Valid() &&
|
||||
(ParseInternalKey(input_->key(), &next_ikey) == Status::OK()) &&
|
||||
cmp_->Equal(ikey_.user_key, next_ikey.user_key)) {
|
||||
(ParseInternalKey(input_->key(), &next_ikey, allow_data_in_errors_)
|
||||
.ok()) &&
|
||||
0 == cmp_->CompareWithoutTimestamp(ikey_.user_key,
|
||||
next_ikey.user_key)) {
|
||||
valid_ = true;
|
||||
at_next_ = true;
|
||||
}
|
||||
@@ -613,8 +672,9 @@ void CompactionIterator::NextFromInput() {
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
Status s = merge_helper_->MergeUntil(input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_);
|
||||
Status s =
|
||||
merge_helper_->MergeUntil(input_, range_del_agg_, prev_snapshot,
|
||||
bottommost_level_, allow_data_in_errors_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
if (!s.ok() && !s.IsMergeInProgress()) {
|
||||
@@ -625,13 +685,13 @@ void CompactionIterator::NextFromInput() {
|
||||
// These will be correctly set below.
|
||||
key_ = merge_out_iter_.key();
|
||||
value_ = merge_out_iter_.value();
|
||||
pikStatus = ParseInternalKey(key_, &ikey_);
|
||||
pik_status = ParseInternalKey(key_, &ikey_, allow_data_in_errors_);
|
||||
// MergeUntil stops when it encounters a corrupt key and does not
|
||||
// include them in the result, so we expect the keys here to valid.
|
||||
assert(pikStatus.ok());
|
||||
if (!pikStatus.ok()) {
|
||||
ROCKS_LOG_FATAL(info_log_, "Invalid key (%s) in compaction",
|
||||
key_.ToString(true).c_str());
|
||||
assert(pik_status.ok());
|
||||
if (!pik_status.ok()) {
|
||||
ROCKS_LOG_FATAL(info_log_, "Invalid key in compaction. %s",
|
||||
pik_status.getState());
|
||||
}
|
||||
// Keep current_key_ in sync.
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
@@ -677,40 +737,136 @@ void CompactionIterator::NextFromInput() {
|
||||
}
|
||||
}
|
||||
|
||||
bool CompactionIterator::ExtractLargeValueIfNeededImpl() {
|
||||
if (!blob_file_builder_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
blob_index_.clear();
|
||||
const Status s = blob_file_builder_->Add(user_key(), value_, &blob_index_);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blob_index_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value_ = blob_index_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompactionIterator::ExtractLargeValueIfNeeded() {
|
||||
assert(ikey_.type == kTypeValue);
|
||||
|
||||
if (!ExtractLargeValueIfNeededImpl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ikey_.type = kTypeBlobIndex;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
|
||||
void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
assert(ikey_.type == kTypeBlobIndex);
|
||||
|
||||
if (!compaction_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// GC for integrated BlobDB
|
||||
if (compaction_->enable_blob_garbage_collection()) {
|
||||
BlobIndex blob_index;
|
||||
|
||||
{
|
||||
const Status s = blob_index.DecodeFrom(value_);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_index.IsInlined() || blob_index.HasTTL()) {
|
||||
status_ = Status::Corruption("Unexpected TTL/inlined blob index");
|
||||
valid_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (blob_index.file_number() >=
|
||||
blob_garbage_collection_cutoff_file_number_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Version* const version = compaction_->input_version();
|
||||
assert(version);
|
||||
|
||||
{
|
||||
const Status s =
|
||||
version->GetBlob(ReadOptions(), user_key(), blob_index, &blob_value_);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
value_ = blob_value_;
|
||||
|
||||
if (ExtractLargeValueIfNeededImpl()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ikey_.type = kTypeValue;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// GC for stacked BlobDB
|
||||
if (compaction_filter_) {
|
||||
const auto blob_decision = compaction_filter_->PrepareBlobOutput(
|
||||
user_key(), value_, &compaction_filter_value_);
|
||||
|
||||
if (blob_decision == CompactionFilter::BlobDecision::kCorruption) {
|
||||
status_ =
|
||||
Status::Corruption("Corrupted blob reference encountered during GC");
|
||||
valid_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (blob_decision == CompactionFilter::BlobDecision::kIOError) {
|
||||
status_ = Status::IOError("Could not relocate blob during GC");
|
||||
valid_ = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (blob_decision == CompactionFilter::BlobDecision::kChangeValue) {
|
||||
value_ = compaction_filter_value_;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompactionIterator::PrepareOutput() {
|
||||
if (valid_) {
|
||||
if (ikey_.type == kTypeValue) {
|
||||
if (blob_file_builder_) {
|
||||
blob_index_.clear();
|
||||
const Status s =
|
||||
blob_file_builder_->Add(user_key(), value_, &blob_index_);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
} else if (!blob_index_.empty()) {
|
||||
value_ = blob_index_;
|
||||
ikey_.type = kTypeBlobIndex;
|
||||
current_key_.UpdateInternalKey(ikey_.sequence, ikey_.type);
|
||||
}
|
||||
}
|
||||
ExtractLargeValueIfNeeded();
|
||||
} else if (ikey_.type == kTypeBlobIndex) {
|
||||
if (compaction_filter_) {
|
||||
const auto blob_decision = compaction_filter_->PrepareBlobOutput(
|
||||
user_key(), value_, &compaction_filter_value_);
|
||||
|
||||
if (blob_decision == CompactionFilter::BlobDecision::kCorruption) {
|
||||
status_ = Status::Corruption(
|
||||
"Corrupted blob reference encountered during GC");
|
||||
valid_ = false;
|
||||
} else if (blob_decision == CompactionFilter::BlobDecision::kIOError) {
|
||||
status_ = Status::IOError("Could not relocate blob during GC");
|
||||
valid_ = false;
|
||||
} else if (blob_decision ==
|
||||
CompactionFilter::BlobDecision::kChangeValue) {
|
||||
value_ = compaction_filter_value_;
|
||||
}
|
||||
}
|
||||
GarbageCollectBlobIfNeeded();
|
||||
}
|
||||
|
||||
// Zeroing out the sequence number leads to better compression.
|
||||
@@ -735,7 +891,18 @@ void CompactionIterator::PrepareOutput() {
|
||||
ikey_.type);
|
||||
}
|
||||
ikey_.sequence = 0;
|
||||
current_key_.UpdateInternalKey(0, ikey_.type);
|
||||
if (!timestamp_size_) {
|
||||
current_key_.UpdateInternalKey(0, ikey_.type);
|
||||
} else if (full_history_ts_low_ && cmp_with_history_ts_low_ < 0) {
|
||||
// We can also zero out timestamp for better compression.
|
||||
// For the same user key (excluding timestamp), the timestamp-based
|
||||
// history can be collapsed to save some space if the timestamp is
|
||||
// older than *full_history_ts_low_.
|
||||
const std::string kTsMin(timestamp_size_, static_cast<char>(0));
|
||||
const Slice ts_slice = kTsMin;
|
||||
ikey_.SetTimestamp(ts_slice);
|
||||
current_key_.UpdateInternalKey(0, ikey_.type, &ts_slice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -827,4 +994,30 @@ bool CompactionIterator::IsInEarliestSnapshot(SequenceNumber sequence) {
|
||||
return in_snapshot == SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
uint64_t CompactionIterator::ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
const CompactionProxy* compaction) {
|
||||
if (!compaction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!compaction->enable_blob_garbage_collection()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Version* const version = compaction->input_version();
|
||||
assert(version);
|
||||
|
||||
const VersionStorageInfo* const storage_info = version->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
|
||||
auto it = blob_files.begin();
|
||||
std::advance(
|
||||
it, compaction->blob_garbage_collection_age_cutoff() * blob_files.size());
|
||||
|
||||
return it != blob_files.end() ? it->first
|
||||
: std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
@@ -29,33 +30,76 @@ class CompactionIterator {
|
||||
// CompactionIterator uses. Tests can override it.
|
||||
class CompactionProxy {
|
||||
public:
|
||||
explicit CompactionProxy(const Compaction* compaction)
|
||||
: compaction_(compaction) {}
|
||||
|
||||
virtual ~CompactionProxy() = default;
|
||||
virtual int level(size_t /*compaction_input_level*/ = 0) const {
|
||||
return compaction_->level();
|
||||
}
|
||||
|
||||
virtual int level() const = 0;
|
||||
|
||||
virtual bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const {
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const = 0;
|
||||
|
||||
virtual bool bottommost_level() const = 0;
|
||||
|
||||
virtual int number_levels() const = 0;
|
||||
|
||||
virtual Slice GetLargestUserKey() const = 0;
|
||||
|
||||
virtual bool allow_ingest_behind() const = 0;
|
||||
|
||||
virtual bool preserve_deletes() const = 0;
|
||||
|
||||
virtual bool enable_blob_garbage_collection() const = 0;
|
||||
|
||||
virtual double blob_garbage_collection_age_cutoff() const = 0;
|
||||
|
||||
virtual Version* input_version() const = 0;
|
||||
};
|
||||
|
||||
class RealCompaction : public CompactionProxy {
|
||||
public:
|
||||
explicit RealCompaction(const Compaction* compaction)
|
||||
: compaction_(compaction) {
|
||||
assert(compaction_);
|
||||
assert(compaction_->immutable_cf_options());
|
||||
assert(compaction_->mutable_cf_options());
|
||||
}
|
||||
|
||||
int level() const override { return compaction_->level(); }
|
||||
|
||||
bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& user_key, std::vector<size_t>* level_ptrs) const override {
|
||||
return compaction_->KeyNotExistsBeyondOutputLevel(user_key, level_ptrs);
|
||||
}
|
||||
virtual bool bottommost_level() const {
|
||||
|
||||
bool bottommost_level() const override {
|
||||
return compaction_->bottommost_level();
|
||||
}
|
||||
virtual int number_levels() const { return compaction_->number_levels(); }
|
||||
virtual Slice GetLargestUserKey() const {
|
||||
|
||||
int number_levels() const override { return compaction_->number_levels(); }
|
||||
|
||||
Slice GetLargestUserKey() const override {
|
||||
return compaction_->GetLargestUserKey();
|
||||
}
|
||||
virtual bool allow_ingest_behind() const {
|
||||
|
||||
bool allow_ingest_behind() const override {
|
||||
return compaction_->immutable_cf_options()->allow_ingest_behind;
|
||||
}
|
||||
virtual bool preserve_deletes() const {
|
||||
|
||||
bool preserve_deletes() const override {
|
||||
return compaction_->immutable_cf_options()->preserve_deletes;
|
||||
}
|
||||
|
||||
protected:
|
||||
CompactionProxy() = default;
|
||||
bool enable_blob_garbage_collection() const override {
|
||||
return compaction_->mutable_cf_options()->enable_blob_garbage_collection;
|
||||
}
|
||||
|
||||
double blob_garbage_collection_age_cutoff() const override {
|
||||
return compaction_->mutable_cf_options()
|
||||
->blob_garbage_collection_age_cutoff;
|
||||
}
|
||||
|
||||
Version* input_version() const override {
|
||||
return compaction_->input_version();
|
||||
}
|
||||
|
||||
private:
|
||||
const Compaction* compaction_;
|
||||
@@ -75,7 +119,8 @@ class CompactionIterator {
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr);
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
@@ -92,7 +137,8 @@ class CompactionIterator {
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr);
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -121,11 +167,30 @@ class CompactionIterator {
|
||||
// Processes the input stream to find the next output
|
||||
void NextFromInput();
|
||||
|
||||
// Do last preparations before presenting the output to the callee. At this
|
||||
// point this only zeroes out the sequence number if possible for better
|
||||
// compression.
|
||||
// Do final preparations before presenting the output to the callee.
|
||||
void PrepareOutput();
|
||||
|
||||
// Passes the output value to the blob file builder (if any), and replaces it
|
||||
// with the corresponding blob reference if it has been actually written to a
|
||||
// blob file (i.e. if it passed the value size check). Returns true if the
|
||||
// value got extracted to a blob file, false otherwise.
|
||||
bool ExtractLargeValueIfNeededImpl();
|
||||
|
||||
// Extracts large values as described above, and updates the internal key's
|
||||
// type to kTypeBlobIndex if the value got extracted. Should only be called
|
||||
// for regular values (kTypeValue).
|
||||
void ExtractLargeValueIfNeeded();
|
||||
|
||||
// Relocates valid blobs residing in the oldest blob files if garbage
|
||||
// collection is enabled. Relocated blobs are written to new blob files or
|
||||
// inlined in the LSM tree depending on the current settings (i.e.
|
||||
// enable_blob_files and min_blob_size). Should only be called for blob
|
||||
// references (kTypeBlobIndex).
|
||||
//
|
||||
// Note: the stacked BlobDB implementation's compaction filter based GC
|
||||
// algorithm is also called from here.
|
||||
void GarbageCollectBlobIfNeeded();
|
||||
|
||||
// Invoke compaction filter if needed.
|
||||
// Return true on success, false on failures (e.g.: kIOError).
|
||||
bool InvokeFilterIfNeeded(bool* need_skip, Slice* skip_until);
|
||||
@@ -152,6 +217,23 @@ class CompactionIterator {
|
||||
|
||||
bool IsInEarliestSnapshot(SequenceNumber sequence);
|
||||
|
||||
// Extract user-defined timestamp from user key if possible and compare it
|
||||
// with *full_history_ts_low_ if applicable.
|
||||
inline void UpdateTimestampAndCompareWithFullHistoryLow() {
|
||||
if (!timestamp_size_) {
|
||||
return;
|
||||
}
|
||||
Slice ts = ExtractTimestampFromUserKey(ikey_.user_key, timestamp_size_);
|
||||
curr_ts_.assign(ts.data(), ts.size());
|
||||
if (full_history_ts_low_) {
|
||||
cmp_with_history_ts_low_ =
|
||||
cmp_->CompareTimestamp(ts, *full_history_ts_low_);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
const CompactionProxy* compaction);
|
||||
|
||||
InternalIterator* input_;
|
||||
const Comparator* cmp_;
|
||||
MergeHelper* merge_helper_;
|
||||
@@ -181,6 +263,20 @@ class CompactionIterator {
|
||||
SequenceNumber earliest_snapshot_;
|
||||
SequenceNumber latest_snapshot_;
|
||||
|
||||
std::shared_ptr<Logger> info_log_;
|
||||
|
||||
bool allow_data_in_errors_;
|
||||
|
||||
// Comes from comparator.
|
||||
const size_t timestamp_size_;
|
||||
|
||||
// Lower bound timestamp to retain full history in terms of user-defined
|
||||
// timestamp. If a key's timestamp is older than full_history_ts_low_, then
|
||||
// the key *may* be eligible for garbage collection (GC). The skipping logic
|
||||
// is in `NextFromInput()` and `PrepareOutput()`.
|
||||
// If nullptr, NO GC will be performed and all history will be preserved.
|
||||
const std::string* const full_history_ts_low_;
|
||||
|
||||
// State
|
||||
//
|
||||
// Points to a copy of the current compaction iterator output (current_key_)
|
||||
@@ -199,11 +295,13 @@ class CompactionIterator {
|
||||
// Stores whether ikey_.user_key is valid. If set to false, the user key is
|
||||
// not compared against the current key in the underlying iterator.
|
||||
bool has_current_user_key_ = false;
|
||||
bool at_next_ = false; // If false, the iterator
|
||||
// Holds a copy of the current compaction iterator output (or current key in
|
||||
// the underlying iterator during NextFromInput()).
|
||||
// If false, the iterator holds a copy of the current compaction iterator
|
||||
// output (or current key in the underlying iterator during NextFromInput()).
|
||||
bool at_next_ = false;
|
||||
|
||||
IterKey current_key_;
|
||||
Slice current_user_key_;
|
||||
std::string curr_ts_;
|
||||
SequenceNumber current_user_key_sequence_;
|
||||
SequenceNumber current_user_key_snapshot_;
|
||||
|
||||
@@ -218,7 +316,11 @@ class CompactionIterator {
|
||||
// PinnedIteratorsManager used to pin input_ Iterator blocks while reading
|
||||
// merge operands and then releasing them after consuming them.
|
||||
PinnedIteratorsManager pinned_iters_mgr_;
|
||||
|
||||
uint64_t blob_garbage_collection_cutoff_file_number_;
|
||||
|
||||
std::string blob_index_;
|
||||
PinnableSlice blob_value_;
|
||||
std::string compaction_filter_value_;
|
||||
InternalKey compaction_filter_skip_until_;
|
||||
// "level_ptrs" holds indices that remember which file of an associated
|
||||
@@ -233,9 +335,9 @@ class CompactionIterator {
|
||||
// Used to avoid purging uncommitted values. The application can specify
|
||||
// uncommitted values by providing a SnapshotChecker object.
|
||||
bool current_key_committed_;
|
||||
std::shared_ptr<Logger> info_log_;
|
||||
|
||||
bool allow_data_in_errors_;
|
||||
// Saved result of ucmp->CompareTimestamp(current_ts_, *full_history_ts_low_)
|
||||
int cmp_with_history_ts_low_;
|
||||
|
||||
bool IsShuttingDown() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
|
||||
@@ -156,23 +156,32 @@ class LoggingForwardVectorIterator : public InternalIterator {
|
||||
|
||||
class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
public:
|
||||
FakeCompaction() = default;
|
||||
int level() const override { return 0; }
|
||||
|
||||
int level(size_t /*compaction_input_level*/) const override { return 0; }
|
||||
bool KeyNotExistsBeyondOutputLevel(
|
||||
const Slice& /*user_key*/,
|
||||
std::vector<size_t>* /*level_ptrs*/) const override {
|
||||
return is_bottommost_level || key_not_exists_beyond_output_level;
|
||||
}
|
||||
|
||||
bool bottommost_level() const override { return is_bottommost_level; }
|
||||
|
||||
int number_levels() const override { return 1; }
|
||||
|
||||
Slice GetLargestUserKey() const override {
|
||||
return "\xff\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
}
|
||||
|
||||
bool allow_ingest_behind() const override { return is_allow_ingest_behind; }
|
||||
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool enable_blob_garbage_collection() const override { return false; }
|
||||
|
||||
double blob_garbage_collection_age_cutoff() const override { return 0.0; }
|
||||
|
||||
Version* input_version() const override { return nullptr; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
@@ -216,6 +225,9 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
CompactionIteratorTest()
|
||||
: cmp_(BytewiseComparator()), icmp_(cmp_), snapshots_({}) {}
|
||||
|
||||
explicit CompactionIteratorTest(const Comparator* ucmp)
|
||||
: cmp_(ucmp), icmp_(cmp_), snapshots_({}) {}
|
||||
|
||||
void InitIterators(
|
||||
const std::vector<std::string>& ks, const std::vector<std::string>& vs,
|
||||
const std::vector<std::string>& range_del_ks,
|
||||
@@ -224,7 +236,9 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
SequenceNumber last_committed_sequence = kMaxSequenceNumber,
|
||||
MergeOperator* merge_op = nullptr, CompactionFilter* filter = nullptr,
|
||||
bool bottommost_level = false,
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
|
||||
bool key_not_exists_beyond_output_level = false,
|
||||
const std::string* full_history_ts_low = nullptr) {
|
||||
std::unique_ptr<InternalIterator> unfragmented_range_del_iter(
|
||||
new test::VectorIterator(range_del_ks, range_del_vs));
|
||||
auto tombstone_list = std::make_shared<FragmentedRangeTombstoneList>(
|
||||
@@ -236,10 +250,12 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
range_del_agg_->AddTombstones(std::move(range_del_iter));
|
||||
|
||||
std::unique_ptr<CompactionIterator::CompactionProxy> compaction;
|
||||
if (filter || bottommost_level) {
|
||||
if (filter || bottommost_level || key_not_exists_beyond_output_level) {
|
||||
compaction_proxy_ = new FakeCompaction();
|
||||
compaction_proxy_->is_bottommost_level = bottommost_level;
|
||||
compaction_proxy_->is_allow_ingest_behind = AllowIngestBehind();
|
||||
compaction_proxy_->key_not_exists_beyond_output_level =
|
||||
key_not_exists_beyond_output_level;
|
||||
compaction.reset(compaction_proxy_);
|
||||
}
|
||||
bool use_snapshot_checker = UseSnapshotChecker() || GetParam();
|
||||
@@ -252,6 +268,11 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
0 /*latest_snapshot*/, snapshot_checker_.get(),
|
||||
0 /*level*/, nullptr /*statistics*/, &shutting_down_));
|
||||
|
||||
if (c_iter_) {
|
||||
// Since iter_ is still used in ~CompactionIterator(), we call
|
||||
// ~CompactionIterator() first.
|
||||
c_iter_.reset();
|
||||
}
|
||||
iter_.reset(new LoggingForwardVectorIterator(ks, vs));
|
||||
iter_->SeekToFirst();
|
||||
c_iter_.reset(new CompactionIterator(
|
||||
@@ -259,8 +280,10 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
earliest_write_conflict_snapshot, snapshot_checker_.get(),
|
||||
Env::Default(), false /* report_detailed_time */, false,
|
||||
range_del_agg_.get(), nullptr /* blob_file_builder */,
|
||||
false /*allow_data_in_errors*/, std::move(compaction), filter,
|
||||
&shutting_down_));
|
||||
true /*allow_data_in_errors*/, std::move(compaction), filter,
|
||||
&shutting_down_, /*preserve_deletes_seqnum=*/0,
|
||||
/*manual_compaction_paused=*/nullptr, /*info_log=*/nullptr,
|
||||
full_history_ts_low));
|
||||
}
|
||||
|
||||
void AddSnapshot(SequenceNumber snapshot,
|
||||
@@ -282,10 +305,13 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
MergeOperator* merge_operator = nullptr,
|
||||
CompactionFilter* compaction_filter = nullptr,
|
||||
bool bottommost_level = false,
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber) {
|
||||
SequenceNumber earliest_write_conflict_snapshot = kMaxSequenceNumber,
|
||||
bool key_not_exists_beyond_output_level = false,
|
||||
const std::string* full_history_ts_low = nullptr) {
|
||||
InitIterators(input_keys, input_values, {}, {}, kMaxSequenceNumber,
|
||||
last_committed_seq, merge_operator, compaction_filter,
|
||||
bottommost_level, earliest_write_conflict_snapshot);
|
||||
bottommost_level, earliest_write_conflict_snapshot,
|
||||
key_not_exists_beyond_output_level, full_history_ts_low);
|
||||
c_iter_->SeekToFirst();
|
||||
for (size_t i = 0; i < expected_keys.size(); i++) {
|
||||
std::string info = "i = " + ToString(i);
|
||||
@@ -299,6 +325,11 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
ASSERT_FALSE(c_iter_->Valid());
|
||||
}
|
||||
|
||||
void ClearSnapshots() {
|
||||
snapshots_.clear();
|
||||
snapshot_map_.clear();
|
||||
}
|
||||
|
||||
const Comparator* cmp_;
|
||||
const InternalKeyComparator icmp_;
|
||||
std::vector<SequenceNumber> snapshots_;
|
||||
@@ -1033,6 +1064,188 @@ INSTANTIATE_TEST_CASE_P(CompactionIteratorWithAllowIngestBehindTestInstance,
|
||||
CompactionIteratorWithAllowIngestBehindTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
class CompactionIteratorTsGcTest : public CompactionIteratorTest {
|
||||
public:
|
||||
CompactionIteratorTsGcTest()
|
||||
: CompactionIteratorTest(test::ComparatorWithU64Ts()) {}
|
||||
};
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, NoKeyEligibleForGC) {
|
||||
constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(/*ts=*/103, user_key[0], /*seq=*/4, kTypeValue),
|
||||
test::KeyStr(/*ts=*/102, user_key[0], /*seq=*/3,
|
||||
kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/104, user_key[1], /*seq=*/5, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"a3", "", "b2"};
|
||||
std::string full_history_ts_low;
|
||||
// All keys' timestamps are newer than or equal to 102, thus none of them
|
||||
// will be eligible for GC.
|
||||
PutFixed64(&full_history_ts_low, 102);
|
||||
const std::vector<std::string>& expected_keys = input_keys;
|
||||
const std::vector<std::string>& expected_values = input_values;
|
||||
const std::vector<std::pair<bool, bool>> params = {
|
||||
{false, false}, {false, true}, {true, true}};
|
||||
for (const std::pair<bool, bool>& param : params) {
|
||||
const bool bottommost_level = param.first;
|
||||
const bool key_not_exists_beyond_output_level = param.second;
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
bottommost_level,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
key_not_exists_beyond_output_level, &full_history_ts_low);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, AllKeysOlderThanThreshold) {
|
||||
constexpr char user_key[][2] = {{'a', '\0'}, {'b', '\0'}};
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(/*ts=*/103, user_key[0], /*seq=*/4,
|
||||
kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/102, user_key[0], /*seq=*/3, kTypeValue),
|
||||
test::KeyStr(/*ts=*/101, user_key[0], /*seq=*/2, kTypeValue),
|
||||
test::KeyStr(/*ts=*/104, user_key[1], /*seq=*/5, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"", "a2", "a1", "b5"};
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, std::numeric_limits<uint64_t>::max());
|
||||
{
|
||||
// With a snapshot at seq 3, both the deletion marker and the key at 3 must
|
||||
// be preserved.
|
||||
AddSnapshot(3);
|
||||
const std::vector<std::string> expected_keys = {
|
||||
input_keys[0], input_keys[1], input_keys[3]};
|
||||
const std::vector<std::string> expected_values = {"", "a2", "b5"};
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
|
||||
ClearSnapshots();
|
||||
}
|
||||
{
|
||||
// No snapshot, the deletion marker should be preserved because the user
|
||||
// key may appear beyond output level.
|
||||
const std::vector<std::string> expected_keys = {input_keys[0],
|
||||
input_keys[3]};
|
||||
const std::vector<std::string> expected_values = {"", "b5"};
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
|
||||
}
|
||||
{
|
||||
// No snapshot, the deletion marker can be dropped because the user key
|
||||
// does not appear in higher levels.
|
||||
const std::vector<std::string> expected_keys = {input_keys[3]};
|
||||
const std::vector<std::string> expected_values = {"b5"};
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, NewHidesOldSameSnapshot) {
|
||||
constexpr char user_key[] = "a";
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
|
||||
test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeValue),
|
||||
test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"", "a2", "a1", "a0"};
|
||||
{
|
||||
std::string full_history_ts_low;
|
||||
// Keys whose timestamps larger than or equal to 102 will be preserved.
|
||||
PutFixed64(&full_history_ts_low, 102);
|
||||
const std::vector<std::string> expected_keys = {input_keys[0],
|
||||
input_keys[1]};
|
||||
const std::vector<std::string> expected_values = {"", "a2"};
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, DropTombstones) {
|
||||
constexpr char user_key[] = "a";
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
|
||||
test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"", "a2", "", "a0"};
|
||||
const std::vector<std::string> expected_keys = {input_keys[0], input_keys[1]};
|
||||
const std::vector<std::string> expected_values = {"", "a2"};
|
||||
|
||||
// Take a snapshot at seq 2.
|
||||
AddSnapshot(2);
|
||||
|
||||
{
|
||||
// Non-bottommost level, but key does not exist beyond output level.
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, 102);
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_sequence=*/kMaxSequenceNumber,
|
||||
/*merge_op=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/false,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
|
||||
}
|
||||
{
|
||||
// Bottommost level
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, 102);
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/true,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/false, &full_history_ts_low);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CompactionIteratorTsGcTest, RewriteTs) {
|
||||
constexpr char user_key[] = "a";
|
||||
const std::vector<std::string> input_keys = {
|
||||
test::KeyStr(/*ts=*/103, user_key, /*seq=*/4, kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/102, user_key, /*seq=*/3, kTypeValue),
|
||||
test::KeyStr(/*ts=*/101, user_key, /*seq=*/2, kTypeDeletionWithTimestamp),
|
||||
test::KeyStr(/*ts=*/100, user_key, /*seq=*/1, kTypeValue)};
|
||||
const std::vector<std::string> input_values = {"", "a2", "", "a0"};
|
||||
const std::vector<std::string> expected_keys = {
|
||||
input_keys[0], input_keys[1], input_keys[2],
|
||||
test::KeyStr(/*ts=*/0, user_key, /*seq=*/0, kTypeValue)};
|
||||
const std::vector<std::string> expected_values = {"", "a2", "", "a0"};
|
||||
|
||||
AddSnapshot(1);
|
||||
AddSnapshot(2);
|
||||
|
||||
{
|
||||
// Bottommost level and need to rewrite both ts and seq.
|
||||
std::string full_history_ts_low;
|
||||
PutFixed64(&full_history_ts_low, 102);
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
/*bottommost_level=*/true,
|
||||
/*earliest_write_conflict_snapshot=*/kMaxSequenceNumber,
|
||||
/*key_not_exists_beyond_output_level=*/true, &full_history_ts_low);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(CompactionIteratorTsGcTestInstance,
|
||||
CompactionIteratorTsGcTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+155
-64
@@ -20,6 +20,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
#include "db/blob/blob_file_builder.h"
|
||||
#include "db/builder.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
@@ -138,6 +140,7 @@ struct CompactionJob::SubcompactionState {
|
||||
|
||||
// State kept for output being generated
|
||||
std::vector<Output> outputs;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
std::unique_ptr<WritableFileWriter> outfile;
|
||||
std::unique_ptr<TableBuilder> builder;
|
||||
|
||||
@@ -231,21 +234,13 @@ struct CompactionJob::CompactionState {
|
||||
std::vector<CompactionJob::SubcompactionState> sub_compact_states;
|
||||
Status status;
|
||||
|
||||
uint64_t total_bytes;
|
||||
uint64_t num_output_records;
|
||||
size_t num_output_files = 0;
|
||||
uint64_t total_bytes = 0;
|
||||
size_t num_blob_output_files = 0;
|
||||
uint64_t total_blob_bytes = 0;
|
||||
uint64_t num_output_records = 0;
|
||||
|
||||
explicit CompactionState(Compaction* c)
|
||||
: compaction(c),
|
||||
total_bytes(0),
|
||||
num_output_records(0) {}
|
||||
|
||||
size_t NumOutputFiles() {
|
||||
size_t total = 0;
|
||||
for (auto& s : sub_compact_states) {
|
||||
total += s.outputs.size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
explicit CompactionState(Compaction* c) : compaction(c) {}
|
||||
|
||||
Slice SmallestUserKey() {
|
||||
for (const auto& sub_compact_state : sub_compact_states) {
|
||||
@@ -272,11 +267,29 @@ struct CompactionJob::CompactionState {
|
||||
};
|
||||
|
||||
void CompactionJob::AggregateStatistics() {
|
||||
assert(compact_);
|
||||
|
||||
for (SubcompactionState& sc : compact_->sub_compact_states) {
|
||||
auto& outputs = sc.outputs;
|
||||
|
||||
if (!outputs.empty() && !outputs.back().meta.fd.file_size) {
|
||||
// An error occurred, so ignore the last output.
|
||||
outputs.pop_back();
|
||||
}
|
||||
|
||||
compact_->num_output_files += outputs.size();
|
||||
compact_->total_bytes += sc.total_bytes;
|
||||
|
||||
const auto& blobs = sc.blob_file_additions;
|
||||
|
||||
compact_->num_blob_output_files += blobs.size();
|
||||
|
||||
for (const auto& blob : blobs) {
|
||||
compact_->total_blob_bytes += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
compact_->num_output_records += sc.num_output_records;
|
||||
}
|
||||
for (SubcompactionState& sc : compact_->sub_compact_states) {
|
||||
|
||||
compaction_job_stats_->Add(sc.compaction_job_stats);
|
||||
}
|
||||
}
|
||||
@@ -286,7 +299,8 @@ CompactionJob::CompactionJob(
|
||||
const FileOptions& file_options, VersionSet* versions,
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory, Statistics* stats,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory,
|
||||
FSDirectory* blob_output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
@@ -295,7 +309,7 @@ CompactionJob::CompactionJob(
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused, const std::string& db_id,
|
||||
const std::string& db_session_id)
|
||||
const std::string& db_session_id, std::string full_history_ts_low)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
@@ -317,6 +331,7 @@ CompactionJob::CompactionJob(
|
||||
log_buffer_(log_buffer),
|
||||
db_directory_(db_directory),
|
||||
output_directory_(output_directory),
|
||||
blob_output_directory_(blob_output_directory),
|
||||
stats_(stats),
|
||||
db_mutex_(db_mutex),
|
||||
db_error_handler_(db_error_handler),
|
||||
@@ -329,7 +344,8 @@ CompactionJob::CompactionJob(
|
||||
paranoid_file_checks_(paranoid_file_checks),
|
||||
measure_io_stats_(measure_io_stats),
|
||||
write_hint_(Env::WLTH_NOT_SET),
|
||||
thread_pri_(thread_pri) {
|
||||
thread_pri_(thread_pri),
|
||||
full_history_ts_low_(std::move(full_history_ts_low)) {
|
||||
assert(compaction_job_stats_ != nullptr);
|
||||
assert(log_buffer_ != nullptr);
|
||||
const auto* cfd = compact_->compaction->column_family_data();
|
||||
@@ -604,18 +620,34 @@ Status CompactionJob::Run() {
|
||||
// Check if any thread encountered an error during execution
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
bool wrote_new_blob_files = false;
|
||||
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
if (!state.status.ok()) {
|
||||
status = state.status;
|
||||
io_s = state.io_status;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.blob_file_additions.empty()) {
|
||||
wrote_new_blob_files = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (io_status_.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
if (status.ok() && output_directory_) {
|
||||
io_s = output_directory_->Fsync(IOOptions(), nullptr);
|
||||
if (status.ok()) {
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
|
||||
if (output_directory_) {
|
||||
io_s = output_directory_->Fsync(IOOptions(), dbg);
|
||||
}
|
||||
|
||||
if (io_s.ok() && wrote_new_blob_files && blob_output_directory_ &&
|
||||
blob_output_directory_ != output_directory_) {
|
||||
io_s = blob_output_directory_->Fsync(IOOptions(), dbg);
|
||||
}
|
||||
}
|
||||
if (io_status_.ok()) {
|
||||
io_status_ = io_s;
|
||||
@@ -721,6 +753,7 @@ Status CompactionJob::Run() {
|
||||
// Finish up all book-keeping to unify the subcompaction results
|
||||
AggregateStatistics();
|
||||
UpdateCompactionStats();
|
||||
|
||||
RecordCompactionIOStats();
|
||||
LogFlush(db_options_.info_log);
|
||||
TEST_SYNC_POINT("CompactionJob::Run():End");
|
||||
@@ -730,11 +763,16 @@ Status CompactionJob::Run() {
|
||||
}
|
||||
|
||||
Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
assert(compact_);
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_INSTALL);
|
||||
db_mutex_->AssertHeld();
|
||||
Status status = compact_->status;
|
||||
|
||||
ColumnFamilyData* cfd = compact_->compaction->column_family_data();
|
||||
assert(cfd);
|
||||
|
||||
cfd->internal_stats()->AddCompactionStats(
|
||||
compact_->compaction->output_level(), thread_pri_, compaction_stats_);
|
||||
|
||||
@@ -744,6 +782,7 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
if (!versions_->io_status().ok()) {
|
||||
io_status_ = versions_->io_status();
|
||||
}
|
||||
|
||||
VersionStorageInfo::LevelSummaryStorage tmp;
|
||||
auto vstorage = cfd->current()->storage_info();
|
||||
const auto& stats = compaction_stats_;
|
||||
@@ -768,6 +807,8 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
stats.bytes_written / static_cast<double>(stats.micros);
|
||||
}
|
||||
|
||||
const std::string& column_family_name = cfd->GetName();
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer_,
|
||||
"[%s] compacted to: %s, MB/sec: %.1f rd, %.1f wr, level %d, "
|
||||
@@ -775,8 +816,9 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
"MB in(%.1f, %.1f) out(%.1f), read-write-amplify(%.1f) "
|
||||
"write-amplify(%.1f) %s, records in: %" PRIu64
|
||||
", records dropped: %" PRIu64 " output_compression: %s\n",
|
||||
cfd->GetName().c_str(), vstorage->LevelSummary(&tmp), bytes_read_per_sec,
|
||||
bytes_written_per_sec, compact_->compaction->output_level(),
|
||||
column_family_name.c_str(), vstorage->LevelSummary(&tmp),
|
||||
bytes_read_per_sec, bytes_written_per_sec,
|
||||
compact_->compaction->output_level(),
|
||||
stats.num_input_files_in_non_output_levels,
|
||||
stats.num_input_files_in_output_level, stats.num_output_files,
|
||||
stats.bytes_read_non_output_levels / 1048576.0,
|
||||
@@ -787,6 +829,15 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
CompressionTypeToString(compact_->compaction->output_compression())
|
||||
.c_str());
|
||||
|
||||
const auto& blob_files = vstorage->GetBlobFiles();
|
||||
if (!blob_files.empty()) {
|
||||
ROCKS_LOG_BUFFER(log_buffer_,
|
||||
"[%s] Blob file summary: head=%" PRIu64 ", tail=%" PRIu64
|
||||
"\n",
|
||||
column_family_name.c_str(), blob_files.begin()->first,
|
||||
blob_files.rbegin()->first);
|
||||
}
|
||||
|
||||
UpdateCompactionJobStats(stats);
|
||||
|
||||
auto stream = event_logger_->LogToBuffer(log_buffer_);
|
||||
@@ -795,11 +846,18 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
<< "compaction_time_micros" << stats.micros
|
||||
<< "compaction_time_cpu_micros" << stats.cpu_micros << "output_level"
|
||||
<< compact_->compaction->output_level() << "num_output_files"
|
||||
<< compact_->NumOutputFiles() << "total_output_size"
|
||||
<< compact_->total_bytes << "num_input_records"
|
||||
<< stats.num_input_records << "num_output_records"
|
||||
<< compact_->num_output_records << "num_subcompactions"
|
||||
<< compact_->sub_compact_states.size() << "output_compression"
|
||||
<< compact_->num_output_files << "total_output_size"
|
||||
<< compact_->total_bytes;
|
||||
|
||||
if (compact_->num_blob_output_files > 0) {
|
||||
stream << "num_blob_output_files" << compact_->num_blob_output_files
|
||||
<< "total_blob_output_size" << compact_->total_blob_bytes;
|
||||
}
|
||||
|
||||
stream << "num_input_records" << stats.num_input_records
|
||||
<< "num_output_records" << compact_->num_output_records
|
||||
<< "num_subcompactions" << compact_->sub_compact_states.size()
|
||||
<< "output_compression"
|
||||
<< CompressionTypeToString(compact_->compaction->output_compression());
|
||||
|
||||
stream << "num_single_delete_mismatches"
|
||||
@@ -823,12 +881,18 @@ Status CompactionJob::Install(const MutableCFOptions& mutable_cf_options) {
|
||||
}
|
||||
stream.EndArray();
|
||||
|
||||
if (!blob_files.empty()) {
|
||||
stream << "blob_file_head" << blob_files.begin()->first;
|
||||
stream << "blob_file_tail" << blob_files.rbegin()->first;
|
||||
}
|
||||
|
||||
CleanupCompaction();
|
||||
return status;
|
||||
}
|
||||
|
||||
void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
assert(sub_compact != nullptr);
|
||||
assert(sub_compact);
|
||||
assert(sub_compact->compaction);
|
||||
|
||||
uint64_t prev_cpu_micros = env_->NowCPUNanos() / 1000;
|
||||
|
||||
@@ -899,6 +963,22 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
snapshot_checker_, compact_->compaction->level(),
|
||||
db_options_.statistics.get());
|
||||
|
||||
const MutableCFOptions* mutable_cf_options =
|
||||
sub_compact->compaction->mutable_cf_options();
|
||||
assert(mutable_cf_options);
|
||||
|
||||
std::vector<std::string> blob_file_paths;
|
||||
|
||||
std::unique_ptr<BlobFileBuilder> blob_file_builder(
|
||||
mutable_cf_options->enable_blob_files
|
||||
? new BlobFileBuilder(
|
||||
versions_, env_, fs_.get(),
|
||||
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)
|
||||
: nullptr);
|
||||
|
||||
TEST_SYNC_POINT("CompactionJob::Run():Inprogress");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
@@ -916,15 +996,17 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
}
|
||||
|
||||
Status status;
|
||||
const std::string* const full_history_ts_low =
|
||||
full_history_ts_low_.empty() ? nullptr : &full_history_ts_low_;
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, &range_del_agg,
|
||||
/* blob_file_builder */ nullptr, db_options_.allow_data_in_errors,
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
preserve_deletes_seqnum_, manual_compaction_paused_,
|
||||
db_options_.info_log));
|
||||
preserve_deletes_seqnum_, manual_compaction_paused_, db_options_.info_log,
|
||||
full_history_ts_low));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -1093,6 +1175,14 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
RecordDroppedKeys(range_del_out_stats, &sub_compact->compaction_job_stats);
|
||||
}
|
||||
|
||||
if (blob_file_builder) {
|
||||
if (status.ok()) {
|
||||
status = blob_file_builder->Finish();
|
||||
}
|
||||
|
||||
blob_file_builder.reset();
|
||||
}
|
||||
|
||||
sub_compact->compaction_job_stats.cpu_micros =
|
||||
env_->NowCPUNanos() / 1000 - prev_cpu_micros;
|
||||
|
||||
@@ -1352,7 +1442,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
ExtractInternalKeyFooter(meta->smallest.Encode()) !=
|
||||
PackSequenceAndType(0, kTypeRangeDeletion));
|
||||
}
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
}
|
||||
const uint64_t current_entries = sub_compact->builder->NumEntries();
|
||||
if (s.ok()) {
|
||||
@@ -1367,6 +1456,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
const uint64_t current_bytes = sub_compact->builder->FileSize();
|
||||
if (s.ok()) {
|
||||
meta->fd.file_size = current_bytes;
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
}
|
||||
sub_compact->current_output()->finished = true;
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
@@ -1465,9 +1555,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
"CompactionJob::FinishCompactionOutputFile:"
|
||||
"MaxAllowedSpaceReached");
|
||||
InstrumentedMutexLock l(db_mutex_);
|
||||
// Should handle return error?
|
||||
db_error_handler_->SetBGError(s, BackgroundErrorReason::kCompaction)
|
||||
.PermitUncheckedError();
|
||||
db_error_handler_->SetBGError(s, BackgroundErrorReason::kCompaction);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1479,9 +1567,13 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
|
||||
Status CompactionJob::InstallCompactionResults(
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
assert(compact_);
|
||||
|
||||
db_mutex_->AssertHeld();
|
||||
|
||||
auto* compaction = compact_->compaction;
|
||||
assert(compaction);
|
||||
|
||||
// paranoia: verify that the files that we started with
|
||||
// still exist in the current version and in the same original level.
|
||||
// This ensures that a concurrent compaction did not erroneously
|
||||
@@ -1497,23 +1589,32 @@ Status CompactionJob::InstallCompactionResults(
|
||||
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
ROCKS_LOG_INFO(
|
||||
db_options_.info_log, "[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary), compact_->total_bytes);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Compacted %s => %" PRIu64 " bytes",
|
||||
compaction->column_family_data()->GetName().c_str(), job_id_,
|
||||
compaction->InputLevelSummary(&inputs_summary),
|
||||
compact_->total_bytes + compact_->total_blob_bytes);
|
||||
}
|
||||
|
||||
VersionEdit* const edit = compaction->edit();
|
||||
assert(edit);
|
||||
|
||||
// Add compaction inputs
|
||||
compaction->AddInputDeletions(compact_->compaction->edit());
|
||||
compaction->AddInputDeletions(edit);
|
||||
|
||||
for (const auto& sub_compact : compact_->sub_compact_states) {
|
||||
for (const auto& out : sub_compact.outputs) {
|
||||
compaction->edit()->AddFile(compaction->output_level(), out.meta);
|
||||
edit->AddFile(compaction->output_level(), out.meta);
|
||||
}
|
||||
|
||||
for (const auto& blob : sub_compact.blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
}
|
||||
}
|
||||
|
||||
return versions_->LogAndApply(compaction->column_family_data(),
|
||||
mutable_cf_options, compaction->edit(),
|
||||
db_mutex_, db_directory_);
|
||||
mutable_cf_options, edit, db_mutex_,
|
||||
db_directory_);
|
||||
}
|
||||
|
||||
void CompactionJob::RecordCompactionIOStats() {
|
||||
@@ -1689,6 +1790,8 @@ void CopyPrefix(const Slice& src, size_t prefix_length, std::string* dst) {
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
void CompactionJob::UpdateCompactionStats() {
|
||||
assert(compact_);
|
||||
|
||||
Compaction* compaction = compact_->compaction;
|
||||
compaction_stats_.num_input_files_in_non_output_levels = 0;
|
||||
compaction_stats_.num_input_files_in_output_level = 0;
|
||||
@@ -1706,27 +1809,15 @@ void CompactionJob::UpdateCompactionStats() {
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t num_output_records = 0;
|
||||
compaction_stats_.num_output_files =
|
||||
static_cast<int>(compact_->num_output_files) +
|
||||
static_cast<int>(compact_->num_blob_output_files);
|
||||
compaction_stats_.bytes_written =
|
||||
compact_->total_bytes + compact_->total_blob_bytes;
|
||||
|
||||
for (const auto& sub_compact : compact_->sub_compact_states) {
|
||||
size_t num_output_files = sub_compact.outputs.size();
|
||||
if (sub_compact.builder != nullptr) {
|
||||
// An error occurred so ignore the last output.
|
||||
assert(num_output_files > 0);
|
||||
--num_output_files;
|
||||
}
|
||||
compaction_stats_.num_output_files += static_cast<int>(num_output_files);
|
||||
|
||||
num_output_records += sub_compact.num_output_records;
|
||||
|
||||
for (const auto& out : sub_compact.outputs) {
|
||||
compaction_stats_.bytes_written += out.meta.fd.file_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (compaction_stats_.num_input_records > num_output_records) {
|
||||
if (compaction_stats_.num_input_records > compact_->num_output_records) {
|
||||
compaction_stats_.num_dropped_records =
|
||||
compaction_stats_.num_input_records - num_output_records;
|
||||
compaction_stats_.num_input_records - compact_->num_output_records;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1765,7 +1856,7 @@ void CompactionJob::UpdateCompactionJobStats(
|
||||
compaction_job_stats_->num_output_records = compact_->num_output_records;
|
||||
compaction_job_stats_->num_output_files = stats.num_output_files;
|
||||
|
||||
if (compact_->NumOutputFiles() > 0U) {
|
||||
if (stats.num_output_files > 0) {
|
||||
CopyPrefix(compact_->SmallestUserKey(),
|
||||
CompactionJobStats::kMaxPrefixLength,
|
||||
&compaction_job_stats_->smallest_output_key_prefix);
|
||||
|
||||
@@ -68,8 +68,8 @@ class CompactionJob {
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum, LogBuffer* log_buffer,
|
||||
FSDirectory* db_directory, FSDirectory* output_directory,
|
||||
Statistics* stats, InstrumentedMutex* db_mutex,
|
||||
ErrorHandler* db_error_handler,
|
||||
FSDirectory* blob_output_directory, Statistics* stats,
|
||||
InstrumentedMutex* db_mutex, ErrorHandler* db_error_handler,
|
||||
std::vector<SequenceNumber> existing_snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker,
|
||||
@@ -78,7 +78,8 @@ class CompactionJob {
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "");
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "");
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -169,6 +170,7 @@ class CompactionJob {
|
||||
LogBuffer* log_buffer_;
|
||||
FSDirectory* db_directory_;
|
||||
FSDirectory* output_directory_;
|
||||
FSDirectory* blob_output_directory_;
|
||||
Statistics* stats_;
|
||||
InstrumentedMutex* db_mutex_;
|
||||
ErrorHandler* db_error_handler_;
|
||||
@@ -200,6 +202,7 @@ class CompactionJob {
|
||||
Env::WriteLifeTimeHint write_hint_;
|
||||
Env::Priority thread_pri_;
|
||||
IOStatus io_status_;
|
||||
std::string full_history_ts_low_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "logging/logging.h"
|
||||
#include "memtable/hash_linklist_rep.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "monitoring/thread_status_util.h"
|
||||
|
||||
@@ -67,13 +67,14 @@ void VerifyInitializationOfCompactionJobStats(
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(icanadi) Make it simpler once we mock out VersionSet
|
||||
class CompactionJobTest : public testing::Test {
|
||||
public:
|
||||
CompactionJobTest()
|
||||
class CompactionJobTestBase : public testing::Test {
|
||||
protected:
|
||||
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_)),
|
||||
dbname_(test::PerThreadDBPath("compaction_job_test")),
|
||||
dbname_(std::move(dbname)),
|
||||
ucmp_(ucmp),
|
||||
db_options_(),
|
||||
mutable_cf_options_(cf_options_),
|
||||
mutable_db_options_(),
|
||||
@@ -86,12 +87,17 @@ class CompactionJobTest : public testing::Test {
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
error_handler_(nullptr, db_options_, &mutex_) {
|
||||
error_handler_(nullptr, db_options_, &mutex_),
|
||||
encode_u64_ts_(std::move(encode_u64_ts)) {}
|
||||
|
||||
void SetUp() override {
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
db_options_.env = env_;
|
||||
db_options_.fs = fs_;
|
||||
db_options_.db_paths.emplace_back(dbname_,
|
||||
std::numeric_limits<uint64_t>::max());
|
||||
cf_options_.comparator = ucmp_;
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
}
|
||||
|
||||
std::string GenerateFileName(uint64_t file_number) {
|
||||
@@ -102,9 +108,10 @@ class CompactionJobTest : public testing::Test {
|
||||
return TableFileName(db_paths, meta.fd.GetNumber(), meta.fd.GetPathId());
|
||||
}
|
||||
|
||||
static std::string KeyStr(const std::string& user_key,
|
||||
const SequenceNumber seq_num, const ValueType t) {
|
||||
return InternalKey(user_key, seq_num, t).Encode().ToString();
|
||||
std::string KeyStr(const std::string& user_key, const SequenceNumber seq_num,
|
||||
const ValueType t, uint64_t ts = 0) {
|
||||
std::string user_key_with_ts = user_key + encode_u64_ts_(ts);
|
||||
return InternalKey(user_key_with_ts, seq_num, t).Encode().ToString();
|
||||
}
|
||||
|
||||
static std::string BlobStr(uint64_t blob_file_number, uint64_t offset,
|
||||
@@ -144,7 +151,8 @@ class CompactionJobTest : public testing::Test {
|
||||
std::string skey;
|
||||
std::string value;
|
||||
std::tie(skey, value) = kv;
|
||||
const Status pikStatus = ParseInternalKey(skey, &key);
|
||||
const Status pik_status =
|
||||
ParseInternalKey(skey, &key, true /* log_err_key */);
|
||||
|
||||
smallest_seqno = std::min(smallest_seqno, key.sequence);
|
||||
largest_seqno = std::max(largest_seqno, key.sequence);
|
||||
@@ -162,7 +170,7 @@ class CompactionJobTest : public testing::Test {
|
||||
|
||||
first_key = false;
|
||||
|
||||
if (pikStatus.ok() && key.type == kTypeBlobIndex) {
|
||||
if (pik_status.ok() && key.type == kTypeBlobIndex) {
|
||||
BlobIndex blob_index;
|
||||
const Status s = blob_index.DecodeFrom(value);
|
||||
if (!s.ok()) {
|
||||
@@ -207,9 +215,9 @@ class CompactionJobTest : public testing::Test {
|
||||
// returns expected result after compaction
|
||||
mock::KVVector CreateTwoFiles(bool gen_corrupted_keys) {
|
||||
stl_wrappers::KVMap expected_results;
|
||||
const int kKeysPerFile = 10000;
|
||||
const int kCorruptKeysPerFile = 200;
|
||||
const int kMatchingKeys = kKeysPerFile / 2;
|
||||
constexpr int kKeysPerFile = 10000;
|
||||
constexpr int kCorruptKeysPerFile = 200;
|
||||
constexpr int kMatchingKeys = kKeysPerFile / 2;
|
||||
SequenceNumber sequence_number = 0;
|
||||
|
||||
auto corrupt_id = [&](int id) {
|
||||
@@ -238,7 +246,7 @@ class CompactionJobTest : public testing::Test {
|
||||
{bottommost_internal_key.Encode().ToString(), value});
|
||||
}
|
||||
}
|
||||
mock::SortKVVector(&contents);
|
||||
mock::SortKVVector(&contents, ucmp_);
|
||||
|
||||
AddMockFile(contents);
|
||||
}
|
||||
@@ -254,7 +262,7 @@ class CompactionJobTest : public testing::Test {
|
||||
}
|
||||
|
||||
void NewDB() {
|
||||
DestroyDB(dbname_, Options());
|
||||
EXPECT_OK(DestroyDB(dbname_, Options()));
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
@@ -264,12 +272,6 @@ class CompactionJobTest : public testing::Test {
|
||||
SetIdentityFile(env_, dbname_);
|
||||
|
||||
VersionEdit new_db;
|
||||
if (db_options_.write_dbid_to_manifest) {
|
||||
DBImpl* impl = new DBImpl(DBOptions(), dbname_);
|
||||
std::string db_id;
|
||||
impl->GetDbIdentityFromIdentityFile(&db_id);
|
||||
new_db.SetDBId(db_id);
|
||||
}
|
||||
new_db.SetLogNumber(0);
|
||||
new_db.SetNextFile(2);
|
||||
new_db.SetLastSequence(0);
|
||||
@@ -293,13 +295,12 @@ class CompactionJobTest : public testing::Test {
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
cf_options_.table_factory = mock_table_factory_;
|
||||
cf_options_.merge_operator = merge_op_;
|
||||
cf_options_.compaction_filter = compaction_filter_.get();
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.emplace_back(kDefaultColumnFamilyName, cf_options_);
|
||||
|
||||
EXPECT_OK(versions_->Recover(column_families, false));
|
||||
ASSERT_OK(versions_->Recover(column_families, false));
|
||||
cfd_ = versions_->GetColumnFamilySet()->GetDefault();
|
||||
}
|
||||
|
||||
@@ -337,19 +338,22 @@ class CompactionJobTest : public testing::Test {
|
||||
EventLogger event_logger(db_options_.info_log.get());
|
||||
// TODO(yiwu) add a mock snapshot checker and add test for it.
|
||||
SnapshotChecker* snapshot_checker = nullptr;
|
||||
ASSERT_TRUE(full_history_ts_low_.empty() ||
|
||||
ucmp_->timestamp_size() == full_history_ts_low_.size());
|
||||
CompactionJob compaction_job(
|
||||
0, &compaction, db_options_, env_options_, versions_.get(),
|
||||
&shutting_down_, preserve_deletes_seqnum_, &log_buffer, nullptr,
|
||||
nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
nullptr, nullptr, nullptr, &mutex_, &error_handler_, snapshots,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER, nullptr /* IOTracer */);
|
||||
Env::Priority::USER, nullptr /* IOTracer */,
|
||||
/*manual_compaction_paused=*/nullptr, /*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low_);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
compaction_job.Prepare();
|
||||
mutex_.Unlock();
|
||||
Status s;
|
||||
s = compaction_job.Run();
|
||||
Status s = compaction_job.Run();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(compaction_job.io_status());
|
||||
mutex_.Lock();
|
||||
@@ -379,6 +383,7 @@ class CompactionJobTest : public testing::Test {
|
||||
Env* env_;
|
||||
std::shared_ptr<FileSystem> fs_;
|
||||
std::string dbname_;
|
||||
const Comparator* const ucmp_;
|
||||
EnvOptions env_options_;
|
||||
ImmutableDBOptions db_options_;
|
||||
ColumnFamilyOptions cf_options_;
|
||||
@@ -397,6 +402,17 @@ class CompactionJobTest : public testing::Test {
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_;
|
||||
std::shared_ptr<MergeOperator> merge_op_;
|
||||
ErrorHandler error_handler_;
|
||||
std::string full_history_ts_low_;
|
||||
const std::function<std::string(uint64_t)> encode_u64_ts_;
|
||||
};
|
||||
|
||||
// TODO(icanadi) Make it simpler once we mock out VersionSet
|
||||
class CompactionJobTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTest()
|
||||
: CompactionJobTestBase(test::PerThreadDBPath("compaction_job_test"),
|
||||
BytewiseComparator(),
|
||||
[](uint64_t /*ts*/) { return ""; }) {}
|
||||
};
|
||||
|
||||
TEST_F(CompactionJobTest, Simple) {
|
||||
@@ -1077,6 +1093,118 @@ TEST_F(CompactionJobTest, OldestBlobFileNumber) {
|
||||
/* expected_oldest_blob_file_number */ 19);
|
||||
}
|
||||
|
||||
class CompactionJobTimestampTest : public CompactionJobTestBase {
|
||||
public:
|
||||
CompactionJobTimestampTest()
|
||||
: CompactionJobTestBase(test::PerThreadDBPath("compaction_job_ts_test"),
|
||||
test::ComparatorWithU64Ts(), test::EncodeInt) {}
|
||||
};
|
||||
|
||||
TEST_F(CompactionJobTimestampTest, GCDisabled) {
|
||||
NewDB();
|
||||
|
||||
auto file1 =
|
||||
mock::MakeMockFile({{KeyStr("a", 10, ValueType::kTypeValue, 100), "a10"},
|
||||
{KeyStr("a", 9, ValueType::kTypeValue, 99), "a9"},
|
||||
{KeyStr("b", 8, ValueType::kTypeValue, 98), "b8"}});
|
||||
AddMockFile(file1);
|
||||
|
||||
auto file2 = mock::MakeMockFile(
|
||||
{{KeyStr("b", 7, ValueType::kTypeDeletionWithTimestamp, 97), ""},
|
||||
{KeyStr("c", 6, ValueType::kTypeDeletionWithTimestamp, 96), ""},
|
||||
{KeyStr("c", 5, ValueType::kTypeValue, 95), "c5"}});
|
||||
AddMockFile(file2);
|
||||
|
||||
SetLastSequence(10);
|
||||
|
||||
auto expected_results = mock::MakeMockFile(
|
||||
{{KeyStr("a", 10, ValueType::kTypeValue, 100), "a10"},
|
||||
{KeyStr("a", 9, ValueType::kTypeValue, 99), "a9"},
|
||||
{KeyStr("b", 8, ValueType::kTypeValue, 98), "b8"},
|
||||
{KeyStr("b", 7, ValueType::kTypeDeletionWithTimestamp, 97), ""},
|
||||
{KeyStr("c", 6, ValueType::kTypeDeletionWithTimestamp, 96), ""},
|
||||
{KeyStr("c", 5, ValueType::kTypeValue, 95), "c5"}});
|
||||
const auto& files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTimestampTest, NoKeyExpired) {
|
||||
NewDB();
|
||||
|
||||
auto file1 =
|
||||
mock::MakeMockFile({{KeyStr("a", 6, ValueType::kTypeValue, 100), "a6"},
|
||||
{KeyStr("b", 7, ValueType::kTypeValue, 101), "b7"},
|
||||
{KeyStr("c", 5, ValueType::kTypeValue, 99), "c5"}});
|
||||
AddMockFile(file1);
|
||||
|
||||
auto file2 =
|
||||
mock::MakeMockFile({{KeyStr("a", 4, ValueType::kTypeValue, 98), "a4"},
|
||||
{KeyStr("c", 3, ValueType::kTypeValue, 97), "c3"}});
|
||||
AddMockFile(file2);
|
||||
|
||||
SetLastSequence(101);
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 6, ValueType::kTypeValue, 100), "a6"},
|
||||
{KeyStr("a", 4, ValueType::kTypeValue, 98), "a4"},
|
||||
{KeyStr("b", 7, ValueType::kTypeValue, 101), "b7"},
|
||||
{KeyStr("c", 5, ValueType::kTypeValue, 99), "c5"},
|
||||
{KeyStr("c", 3, ValueType::kTypeValue, 97), "c3"}});
|
||||
const auto& files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
|
||||
full_history_ts_low_ = encode_u64_ts_(0);
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTimestampTest, AllKeysExpired) {
|
||||
NewDB();
|
||||
|
||||
auto file1 = mock::MakeMockFile(
|
||||
{{KeyStr("a", 5, ValueType::kTypeDeletionWithTimestamp, 100), ""},
|
||||
{KeyStr("b", 6, ValueType::kTypeValue, 99), "b6"}});
|
||||
AddMockFile(file1);
|
||||
|
||||
auto file2 = mock::MakeMockFile(
|
||||
{{KeyStr("a", 4, ValueType::kTypeValue, 98), "a4"},
|
||||
{KeyStr("b", 3, ValueType::kTypeDeletionWithTimestamp, 97), ""},
|
||||
{KeyStr("b", 2, ValueType::kTypeValue, 96), "b2"}});
|
||||
AddMockFile(file2);
|
||||
|
||||
SetLastSequence(6);
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("b", 0, ValueType::kTypeValue, 0), "b6"}});
|
||||
const auto& files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
|
||||
full_history_ts_low_ = encode_u64_ts_(std::numeric_limits<uint64_t>::max());
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
TEST_F(CompactionJobTimestampTest, SomeKeysExpired) {
|
||||
NewDB();
|
||||
|
||||
auto file1 =
|
||||
mock::MakeMockFile({{KeyStr("a", 5, ValueType::kTypeValue, 50), "a5"},
|
||||
{KeyStr("b", 6, ValueType::kTypeValue, 49), "b6"}});
|
||||
AddMockFile(file1);
|
||||
|
||||
auto file2 = mock::MakeMockFile(
|
||||
{{KeyStr("a", 3, ValueType::kTypeValue, 48), "a3"},
|
||||
{KeyStr("a", 2, ValueType::kTypeValue, 46), "a2"},
|
||||
{KeyStr("b", 4, ValueType::kTypeDeletionWithTimestamp, 47), ""}});
|
||||
AddMockFile(file2);
|
||||
|
||||
SetLastSequence(6);
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 5, ValueType::kTypeValue, 50), "a5"},
|
||||
{KeyStr("b", 6, ValueType::kTypeValue, 49), "b6"}});
|
||||
const auto& files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
|
||||
full_history_ts_low_ = encode_u64_ts_(49);
|
||||
RunCompaction({files}, expected_results);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -139,11 +139,9 @@ CompressionOptions GetCompressionOptions(const MutableCFOptions& cf_options,
|
||||
if (!enable_compression) {
|
||||
return cf_options.compression_opts;
|
||||
}
|
||||
// If bottommost_compression is set and we are compacting to the
|
||||
// bottommost level then we should use the specified compression options
|
||||
// for the bottmomost_compression.
|
||||
if (cf_options.bottommost_compression != kDisableCompressionOption &&
|
||||
level >= (vstorage->num_non_empty_levels() - 1) &&
|
||||
// If bottommost_compression_opts is enabled and we are compacting to the
|
||||
// bottommost level then we should use the specified compression options.
|
||||
if (level >= (vstorage->num_non_empty_levels() - 1) &&
|
||||
cf_options.bottommost_compression_opts.enabled) {
|
||||
return cf_options.bottommost_compression_opts;
|
||||
}
|
||||
@@ -1045,6 +1043,8 @@ void CompactionPicker::RegisterCompaction(Compaction* c) {
|
||||
level0_compactions_in_progress_.insert(c);
|
||||
}
|
||||
compactions_in_progress_.insert(c);
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::RegisterCompaction:Registered",
|
||||
c);
|
||||
}
|
||||
|
||||
void CompactionPicker::UnregisterCompaction(Compaction* c) {
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
|
||||
#include "logging/logging.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "util/string_util.h"
|
||||
#include "utilities/merge_operators.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace {
|
||||
|
||||
+102
-23
@@ -9,7 +9,6 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
@@ -22,6 +21,7 @@
|
||||
#include "db/version_set.h"
|
||||
#include "env/composite_env_wrapper.h"
|
||||
#include "file/filename.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
@@ -33,16 +33,18 @@
|
||||
#include "table/mock_table.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/random.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
static const int kValueSize = 1000;
|
||||
static constexpr int kValueSize = 1000;
|
||||
|
||||
class CorruptionTest : public testing::Test {
|
||||
public:
|
||||
test::ErrorEnv env_;
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
test::ErrorEnv* env_;
|
||||
std::string dbname_;
|
||||
std::shared_ptr<Cache> tiny_cache_;
|
||||
Options options_;
|
||||
@@ -53,9 +55,19 @@ class CorruptionTest : public testing::Test {
|
||||
// set it to 0), test SequenceNumberRecovery will fail, likely because of a
|
||||
// bug in recovery code. Keep it 4 for now to make the test passes.
|
||||
tiny_cache_ = NewLRUCache(100, 4);
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
const char* test_env_uri = getenv("TEST_ENV_URI");
|
||||
if (test_env_uri) {
|
||||
Status s = Env::LoadEnv(test_env_uri, &base_env, &env_guard_);
|
||||
EXPECT_OK(s);
|
||||
EXPECT_NE(Env::Default(), base_env);
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
env_ = new test::ErrorEnv(base_env);
|
||||
options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options_.env = &env_;
|
||||
dbname_ = test::PerThreadDBPath("corruption_test");
|
||||
options_.env = env_;
|
||||
dbname_ = test::PerThreadDBPath(env_, "corruption_test");
|
||||
Status s = DestroyDB(dbname_, options_);
|
||||
EXPECT_OK(s);
|
||||
|
||||
@@ -69,9 +81,19 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
|
||||
~CorruptionTest() override {
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->LoadDependency({});
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
DestroyDB(dbname_, Options());
|
||||
if (getenv("KEEP_DB")) {
|
||||
fprintf(stdout, "db is still at %s\n", dbname_.c_str());
|
||||
} else {
|
||||
Options opts;
|
||||
opts.env = env_->target();
|
||||
EXPECT_OK(DestroyDB(dbname_, opts));
|
||||
}
|
||||
delete env_;
|
||||
}
|
||||
|
||||
void CloseDb() {
|
||||
@@ -86,7 +108,7 @@ class CorruptionTest : public testing::Test {
|
||||
if (opt.env == Options().env) {
|
||||
// If env is not overridden, replace it with ErrorEnv.
|
||||
// Otherwise, the test already uses a non-default Env.
|
||||
opt.env = &env_;
|
||||
opt.env = env_;
|
||||
}
|
||||
opt.arena_block_size = 4096;
|
||||
BlockBasedTableOptions table_options;
|
||||
@@ -169,7 +191,7 @@ class CorruptionTest : public testing::Test {
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
std::vector<std::string> filenames;
|
||||
ASSERT_OK(env_.GetChildren(dbname_, &filenames));
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &filenames));
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
std::string fname;
|
||||
@@ -184,7 +206,7 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty()) << filetype;
|
||||
|
||||
test::CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
ASSERT_OK(test::CorruptFile(env_, fname, offset, bytes_to_corrupt));
|
||||
}
|
||||
|
||||
// corrupts exactly one file at level `level`. if no file found at level,
|
||||
@@ -194,7 +216,8 @@ class CorruptionTest : public testing::Test {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
for (const auto& m : metadata) {
|
||||
if (m.level == level) {
|
||||
test::CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
ASSERT_OK(test::CorruptFile(env_, dbname_ + "/" + m.name, offset,
|
||||
bytes_to_corrupt));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -249,8 +272,8 @@ TEST_F(CorruptionTest, Recovery) {
|
||||
// is not available for WAL though.
|
||||
CloseDb();
|
||||
#endif
|
||||
Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record
|
||||
Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block
|
||||
Corrupt(kWalFile, 19, 1); // WriteBatch tag for first record
|
||||
Corrupt(kWalFile, log::kBlockSize + 1000, 1); // Somewhere in second block
|
||||
ASSERT_TRUE(!TryReopen().ok());
|
||||
options_.paranoid_checks = false;
|
||||
Reopen(&options_);
|
||||
@@ -260,14 +283,14 @@ TEST_F(CorruptionTest, Recovery) {
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, RecoverWriteError) {
|
||||
env_.writable_file_error_ = true;
|
||||
env_->writable_file_error_ = true;
|
||||
Status s = TryReopen();
|
||||
ASSERT_TRUE(!s.ok());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
|
||||
// Do enough writing to force minor compaction
|
||||
env_.writable_file_error_ = true;
|
||||
env_->writable_file_error_ = true;
|
||||
const int num =
|
||||
static_cast<int>(3 + (Options().write_buffer_size / kValueSize));
|
||||
std::string value_storage;
|
||||
@@ -283,8 +306,8 @@ TEST_F(CorruptionTest, NewFileErrorDuringWrite) {
|
||||
ASSERT_TRUE(!failed || !s.ok());
|
||||
}
|
||||
ASSERT_TRUE(!s.ok());
|
||||
ASSERT_GE(env_.num_writable_file_errors_, 1);
|
||||
env_.writable_file_error_ = false;
|
||||
ASSERT_GE(env_->num_writable_file_errors_, 1);
|
||||
env_->writable_file_error_ = false;
|
||||
Reopen();
|
||||
}
|
||||
|
||||
@@ -302,7 +325,7 @@ TEST_F(CorruptionTest, TableFile) {
|
||||
|
||||
TEST_F(CorruptionTest, VerifyChecksumReadahead) {
|
||||
Options options;
|
||||
SpecialEnv senv(Env::Default());
|
||||
SpecialEnv senv(env_->target());
|
||||
options.env = &senv;
|
||||
// Disable block cache as we are going to check checksum for
|
||||
// the same file twice and measure number of reads.
|
||||
@@ -424,6 +447,7 @@ TEST_F(CorruptionTest, CorruptedDescriptor) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputError) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
Reopen(&options);
|
||||
Build(10);
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
@@ -444,6 +468,7 @@ TEST_F(CorruptionTest, CompactionInputError) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionInputErrorParanoid) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.paranoid_checks = true;
|
||||
options.write_buffer_size = 131072;
|
||||
options.max_write_buffer_number = 2;
|
||||
@@ -529,13 +554,15 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
test::CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
ASSERT_OK(test::CorruptFile(env_, filename,
|
||||
static_cast<int>(range_del_handle.offset()), 1));
|
||||
ASSERT_TRUE(TryReopen().IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
for (int iter = 0; iter < 2; ++iter) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.paranoid_checks = true;
|
||||
options.create_if_missing = true;
|
||||
Reopen(&options);
|
||||
@@ -544,7 +571,7 @@ TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
std::vector<LiveFileMetaData> metadata;
|
||||
dbi->GetLiveFilesMetaData(&metadata);
|
||||
ASSERT_GT(metadata.size(), size_t(0));
|
||||
ASSERT_GT(metadata.size(), 0);
|
||||
std::string filename = dbname_ + metadata[0].name;
|
||||
|
||||
delete db_;
|
||||
@@ -552,15 +579,15 @@ TEST_F(CorruptionTest, FileSystemStateCorrupted) {
|
||||
|
||||
if (iter == 0) { // corrupt file size
|
||||
std::unique_ptr<WritableFile> file;
|
||||
env_.NewWritableFile(filename, &file, EnvOptions());
|
||||
env_->NewWritableFile(filename, &file, EnvOptions());
|
||||
ASSERT_OK(file->Append(Slice("corrupted sst")));
|
||||
file.reset();
|
||||
Status x = TryReopen(&options);
|
||||
ASSERT_TRUE(x.IsCorruption());
|
||||
} else { // delete the file
|
||||
ASSERT_OK(env_.DeleteFile(filename));
|
||||
ASSERT_OK(env_->DeleteFile(filename));
|
||||
Status x = TryReopen(&options);
|
||||
ASSERT_TRUE(x.IsPathNotFound());
|
||||
ASSERT_TRUE(x.IsCorruption());
|
||||
}
|
||||
|
||||
ASSERT_OK(DestroyDB(dbname_, options_));
|
||||
@@ -574,6 +601,7 @@ static const auto& corruption_modes = {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -601,6 +629,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnFlush) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
@@ -630,6 +659,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksOnCompact) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -662,6 +692,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeFirst) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -697,6 +728,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRange) {
|
||||
|
||||
TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
options.paranoid_file_checks = true;
|
||||
options.create_if_missing = true;
|
||||
@@ -729,6 +761,7 @@ TEST_F(CorruptionTest, ParanoidFileChecksWithDeleteRangeLast) {
|
||||
|
||||
TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.allow_data_in_errors = true;
|
||||
auto mode = mock::MockTableFactory::kCorruptKey;
|
||||
@@ -754,6 +787,7 @@ TEST_F(CorruptionTest, LogCorruptionErrorsInCompactionIterator) {
|
||||
|
||||
TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
options.check_flush_compaction_key_order = false;
|
||||
@@ -777,6 +811,7 @@ TEST_F(CorruptionTest, CompactionKeyOrderCheck) {
|
||||
|
||||
TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.paranoid_file_checks = false;
|
||||
options.create_if_missing = true;
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "true"}}));
|
||||
@@ -805,7 +840,6 @@ TEST_F(CorruptionTest, FlushKeyOrderCheck) {
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, DisableKeyOrderCheck) {
|
||||
Options options;
|
||||
ASSERT_OK(db_->SetOptions({{"check_flush_compaction_key_order", "false"}}));
|
||||
DBImpl* dbi = static_cast_with_check<DBImpl>(db_);
|
||||
|
||||
@@ -824,10 +858,55 @@ TEST_F(CorruptionTest, DisableKeyOrderCheck) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
CloseDb();
|
||||
Options options;
|
||||
options.env = env_;
|
||||
ASSERT_OK(DestroyDB(dbname_, options));
|
||||
options.create_if_missing = true;
|
||||
options.file_checksum_gen_factory =
|
||||
ROCKSDB_NAMESPACE::GetFileChecksumGenCrc32cFactory();
|
||||
Reopen(&options);
|
||||
|
||||
Build(10, 5);
|
||||
|
||||
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
|
||||
CloseDb();
|
||||
|
||||
// Corrupt the first byte of each table file, this must be data block.
|
||||
Corrupt(kTableFile, 0, 1);
|
||||
|
||||
ASSERT_OK(TryReopen(&options));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
int count{0};
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::VerifySstFileChecksum:mismatch", [&](void* arg) {
|
||||
auto* s = reinterpret_cast<Status*>(arg);
|
||||
assert(s);
|
||||
++count;
|
||||
ASSERT_NOK(*s);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
ASSERT_TRUE(db_->VerifyFileChecksums(ReadOptions()).IsCorruption());
|
||||
ASSERT_EQ(1, count);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,15 @@ class CuckooTableDBTest : public testing::Test {
|
||||
ASSERT_OK(DB::Open(opts, dbname_, &db_));
|
||||
}
|
||||
|
||||
void DestroyAndReopen(Options* options) {
|
||||
assert(options);
|
||||
ASSERT_OK(db_->Close());
|
||||
delete db_;
|
||||
db_ = nullptr;
|
||||
ASSERT_OK(DestroyDB(dbname_, *options));
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
Status Put(const Slice& k, const Slice& v) {
|
||||
return db_->Put(WriteOptions(), k, v);
|
||||
}
|
||||
@@ -205,7 +214,7 @@ static std::string Uint64Key(uint64_t i) {
|
||||
TEST_F(CuckooTableDBTest, Uint64Comparator) {
|
||||
Options options = CurrentOptions();
|
||||
options.comparator = test::Uint64Comparator();
|
||||
Reopen(&options);
|
||||
DestroyAndReopen(&options);
|
||||
|
||||
ASSERT_OK(Put(Uint64Key(1), "v1"));
|
||||
ASSERT_OK(Put(Uint64Key(2), "v2"));
|
||||
|
||||
+291
-11
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/flush_block_policy.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
@@ -37,7 +38,10 @@ TEST_F(DBBasicTest, OpenWhenOpen) {
|
||||
options.env = env_;
|
||||
DB* db2 = nullptr;
|
||||
Status s = DB::Open(options, dbname_, &db2);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_NOK(s) << [db2]() {
|
||||
delete db2;
|
||||
return "db2 open: ok";
|
||||
}();
|
||||
ASSERT_EQ(Status::Code::kIOError, s.code());
|
||||
ASSERT_EQ(Status::SubCode::kNone, s.subcode());
|
||||
ASSERT_TRUE(strstr(s.getState(), "lock ") != nullptr);
|
||||
@@ -401,16 +405,18 @@ TEST_F(DBBasicTest, GetSnapshot) {
|
||||
|
||||
TEST_F(DBBasicTest, CheckLock) {
|
||||
do {
|
||||
DB* localdb;
|
||||
DB* localdb = nullptr;
|
||||
Options options = CurrentOptions();
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
// second open should fail
|
||||
Status s = DB::Open(options, dbname_, &localdb);
|
||||
ASSERT_NOK(s);
|
||||
ASSERT_NOK(s) << [localdb]() {
|
||||
delete localdb;
|
||||
return "localdb open: ok";
|
||||
}();
|
||||
#ifdef OS_LINUX
|
||||
ASSERT_TRUE(s.ToString().find("lock hold by current process") !=
|
||||
std::string::npos);
|
||||
ASSERT_TRUE(s.ToString().find("lock ") != std::string::npos);
|
||||
#endif // OS_LINUX
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
@@ -1875,6 +1881,7 @@ TEST_F(DBBasicTest, MultiGetStats) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1;
|
||||
@@ -1884,7 +1891,7 @@ TEST_F(DBBasicTest, MultiGetStats) {
|
||||
table_options.no_block_cache = true;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
int total_keys = 2000;
|
||||
@@ -2168,7 +2175,7 @@ TEST_F(DBBasicTest, MultiGetIOBufferOverrun) {
|
||||
table_options.block_size = 16 * 1024;
|
||||
ASSERT_TRUE(table_options.block_size >
|
||||
BlockBasedTable::kMultiGetReadStackBufSize);
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
std::string zero_str(128, '\0');
|
||||
@@ -2278,6 +2285,43 @@ class TableFileListener : public EventListener {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_F(DBBasicTest, LastSstFileNotInManifest) {
|
||||
// If the last sst file is not tracked in MANIFEST,
|
||||
// or the VersionEdit for the last sst file is not synced,
|
||||
// on recovery, the last sst file should be deleted,
|
||||
// and new sst files shouldn't reuse its file number.
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
Close();
|
||||
|
||||
// Manually add a sst file.
|
||||
constexpr uint64_t kSstFileNumber = 100;
|
||||
const std::string kSstFile = MakeTableFileName(dbname_, kSstFileNumber);
|
||||
ASSERT_OK(WriteStringToFile(env_, /* data = */ "bad sst file content",
|
||||
/* fname = */ kSstFile,
|
||||
/* should_sync = */ true));
|
||||
ASSERT_OK(env_->FileExists(kSstFile));
|
||||
|
||||
TableFileListener* listener = new TableFileListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
Reopen(options);
|
||||
// kSstFile should already be deleted.
|
||||
ASSERT_TRUE(env_->FileExists(kSstFile).IsNotFound());
|
||||
|
||||
ASSERT_OK(Put("k", "v"));
|
||||
ASSERT_OK(Flush());
|
||||
// New sst file should have file number > kSstFileNumber.
|
||||
std::vector<std::string>& files =
|
||||
listener->GetFiles(kDefaultColumnFamilyName);
|
||||
ASSERT_EQ(files.size(), 1);
|
||||
const std::string fname = files[0].erase(0, (dbname_ + "/").size());
|
||||
uint64_t number = 0;
|
||||
FileType type = kTableFile;
|
||||
ASSERT_TRUE(ParseFileName(fname, &number, &type));
|
||||
ASSERT_EQ(type, kTableFile);
|
||||
ASSERT_GT(number, kSstFileNumber);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -2405,7 +2449,7 @@ TEST_F(DBBasicTest, RecoverWithNoManifest) {
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &files));
|
||||
for (const auto& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile;
|
||||
FileType type = kWalFile;
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
ASSERT_OK(env_->DeleteFile(dbname_ + "/" + file));
|
||||
}
|
||||
@@ -2461,6 +2505,42 @@ TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, DisableTrackWal) {
|
||||
// If WAL tracking was enabled, and then disabled during reopen,
|
||||
// the previously tracked WALs should be removed from MANIFEST.
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
// extremely small write buffer size,
|
||||
// so that new WALs are created more frequently.
|
||||
options.write_buffer_size = 100;
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
ASSERT_OK(Put("foo" + std::to_string(i), "value" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
// Some WALs are tracked.
|
||||
ASSERT_FALSE(dbfull()->TEST_GetVersionSet()->GetWalSet().GetWals().empty());
|
||||
Close();
|
||||
|
||||
// Disable WAL tracking.
|
||||
options.track_and_verify_wals_in_manifest = false;
|
||||
options.create_if_missing = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
// Previously tracked WALs are cleared.
|
||||
ASSERT_TRUE(dbfull()->TEST_GetVersionSet()->GetWalSet().GetWals().empty());
|
||||
Close();
|
||||
|
||||
// Re-enable WAL tracking again.
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
options.create_if_missing = false;
|
||||
ASSERT_OK(TryReopen(options));
|
||||
ASSERT_TRUE(dbfull()->TEST_GetVersionSet()->GetWalSet().GetWals().empty());
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBBasicTest, ManifestChecksumMismatch) {
|
||||
@@ -2490,6 +2570,64 @@ TEST_F(DBBasicTest, ManifestChecksumMismatch) {
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
class DBBasicTestTrackWal : public DBTestBase,
|
||||
public testing::WithParamInterface<bool> {
|
||||
public:
|
||||
DBBasicTestTrackWal()
|
||||
: DBTestBase("/db_basic_test_track_wal", /*env_do_fsync=*/false) {}
|
||||
|
||||
int CountWalFiles() {
|
||||
VectorLogPtr log_files;
|
||||
dbfull()->GetSortedWalFiles(log_files);
|
||||
return static_cast<int>(log_files.size());
|
||||
};
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestTrackWal, DoNotTrackObsoleteWal) {
|
||||
// If a WAL becomes obsolete after flushing, but is not deleted from disk yet,
|
||||
// then if SyncWAL is called afterwards, the obsolete WAL should not be
|
||||
// tracked in MANIFEST.
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
options.atomic_flush = GetParam();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"cf"}, options);
|
||||
ASSERT_EQ(handles_.size(), 2); // default, cf
|
||||
// Do not delete WALs.
|
||||
ASSERT_OK(db_->DisableFileDeletions());
|
||||
constexpr int n = 10;
|
||||
std::vector<std::unique_ptr<LogFile>> wals(n);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
// Generate a new WAL for each key-value.
|
||||
const int cf = i % 2;
|
||||
ASSERT_OK(db_->GetCurrentWalFile(&wals[i]));
|
||||
ASSERT_OK(Put(cf, "k" + std::to_string(i), "v" + std::to_string(i)));
|
||||
ASSERT_OK(Flush({0, 1}));
|
||||
}
|
||||
ASSERT_EQ(CountWalFiles(), n);
|
||||
// Since all WALs are obsolete, no WAL should be tracked in MANIFEST.
|
||||
ASSERT_OK(db_->SyncWAL());
|
||||
|
||||
// Manually delete all WALs.
|
||||
Close();
|
||||
for (const auto& wal : wals) {
|
||||
ASSERT_OK(env_->DeleteFile(LogFileName(dbname_, wal->LogNumber())));
|
||||
}
|
||||
|
||||
// If SyncWAL tracks the obsolete WALs in MANIFEST,
|
||||
// reopen will fail because the WALs are missing from disk.
|
||||
ASSERT_OK(TryReopenWithColumnFamilies({"default", "cf"}, options));
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBasicTestTrackWal, DBBasicTestTrackWal,
|
||||
testing::Bool());
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
class DBBasicTestMultiGet : public DBTestBase {
|
||||
public:
|
||||
DBBasicTestMultiGet(std::string test_dir, int num_cfs, bool compressed_cache,
|
||||
@@ -2549,12 +2687,13 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
table_options.block_cache_compressed = compressed_cache_;
|
||||
table_options.flush_block_policy_factory.reset(
|
||||
new MyFlushBlockPolicyFactory());
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
if (!compression_enabled_) {
|
||||
options.compression = kNoCompression;
|
||||
} else {
|
||||
options.compression_opts.parallel_threads = compression_parallel_threads;
|
||||
}
|
||||
options_ = options;
|
||||
Reopen(options);
|
||||
|
||||
if (num_cfs > 1) {
|
||||
@@ -2624,6 +2763,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
bool compression_enabled() { return compression_enabled_; }
|
||||
bool has_compressed_cache() { return compressed_cache_ != nullptr; }
|
||||
bool has_uncompressed_cache() { return uncompressed_cache_ != nullptr; }
|
||||
Options get_options() { return options_; }
|
||||
|
||||
static void SetUpTestCase() {}
|
||||
static void TearDownTestCase() {}
|
||||
@@ -2709,6 +2849,7 @@ class DBBasicTestMultiGet : public DBTestBase {
|
||||
|
||||
std::shared_ptr<MyBlockCache> compressed_cache_;
|
||||
std::shared_ptr<MyBlockCache> uncompressed_cache_;
|
||||
Options options_;
|
||||
bool compression_enabled_;
|
||||
std::vector<std::string> values_;
|
||||
std::vector<std::string> uncompressable_values_;
|
||||
@@ -2851,6 +2992,123 @@ TEST_P(DBBasicTestWithParallelIO, MultiGet) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBBasicTestWithParallelIO, MultiGetDirectIO) {
|
||||
class FakeDirectIOEnv : public EnvWrapper {
|
||||
class FakeDirectIOSequentialFile;
|
||||
class FakeDirectIORandomAccessFile;
|
||||
|
||||
public:
|
||||
FakeDirectIOEnv(Env* env) : EnvWrapper(env) {}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& fname,
|
||||
std::unique_ptr<RandomAccessFile>* result,
|
||||
const EnvOptions& options) override {
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
assert(options.use_direct_reads);
|
||||
EnvOptions opts = options;
|
||||
opts.use_direct_reads = false;
|
||||
Status s = target()->NewRandomAccessFile(fname, &file, opts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
result->reset(new FakeDirectIORandomAccessFile(std::move(file)));
|
||||
return s;
|
||||
}
|
||||
|
||||
private:
|
||||
class FakeDirectIOSequentialFile : public SequentialFileWrapper {
|
||||
public:
|
||||
FakeDirectIOSequentialFile(std::unique_ptr<SequentialFile>&& file)
|
||||
: SequentialFileWrapper(file.get()), file_(std::move(file)) {}
|
||||
~FakeDirectIOSequentialFile() {}
|
||||
|
||||
bool use_direct_io() const override { return true; }
|
||||
size_t GetRequiredBufferAlignment() const override { return 1; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<SequentialFile> file_;
|
||||
};
|
||||
|
||||
class FakeDirectIORandomAccessFile : public RandomAccessFileWrapper {
|
||||
public:
|
||||
FakeDirectIORandomAccessFile(std::unique_ptr<RandomAccessFile>&& file)
|
||||
: RandomAccessFileWrapper(file.get()), file_(std::move(file)) {}
|
||||
~FakeDirectIORandomAccessFile() {}
|
||||
|
||||
bool use_direct_io() const override { return true; }
|
||||
size_t GetRequiredBufferAlignment() const override { return 1; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<RandomAccessFile> file_;
|
||||
};
|
||||
};
|
||||
|
||||
std::unique_ptr<FakeDirectIOEnv> env(new FakeDirectIOEnv(env_));
|
||||
Options opts = get_options();
|
||||
opts.env = env.get();
|
||||
opts.use_direct_reads = true;
|
||||
Reopen(opts);
|
||||
|
||||
std::vector<std::string> key_data(10);
|
||||
std::vector<Slice> keys;
|
||||
// We cannot resize a PinnableSlice vector, so just set initial size to
|
||||
// largest we think we will need
|
||||
std::vector<PinnableSlice> values(10);
|
||||
std::vector<Status> statuses;
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = fill_cache();
|
||||
|
||||
// Warm up the cache first
|
||||
key_data.emplace_back(Key(0));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
key_data.emplace_back(Key(50));
|
||||
keys.emplace_back(Slice(key_data.back()));
|
||||
statuses.resize(keys.size());
|
||||
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(0, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(50, values[1].ToString()));
|
||||
|
||||
int random_reads = env_->random_read_counter_.Read();
|
||||
key_data[0] = Key(1);
|
||||
key_data[1] = Key(51);
|
||||
keys[0] = Slice(key_data[0]);
|
||||
keys[1] = Slice(key_data[1]);
|
||||
values[0].Reset();
|
||||
values[1].Reset();
|
||||
if (uncompressed_cache_) {
|
||||
uncompressed_cache_->SetCapacity(0);
|
||||
uncompressed_cache_->SetCapacity(1048576);
|
||||
}
|
||||
dbfull()->MultiGet(ro, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data(), true);
|
||||
ASSERT_TRUE(CheckValue(1, values[0].ToString()));
|
||||
ASSERT_TRUE(CheckValue(51, values[1].ToString()));
|
||||
|
||||
bool read_from_cache = false;
|
||||
if (fill_cache()) {
|
||||
if (has_uncompressed_cache()) {
|
||||
read_from_cache = true;
|
||||
} else if (has_compressed_cache() && compression_enabled()) {
|
||||
read_from_cache = true;
|
||||
}
|
||||
}
|
||||
|
||||
int expected_reads = random_reads;
|
||||
if (!compression_enabled() || !has_compressed_cache()) {
|
||||
expected_reads += 2;
|
||||
} else {
|
||||
expected_reads += (read_from_cache ? 0 : 2);
|
||||
}
|
||||
if (env_->random_read_counter_.Read() != expected_reads) {
|
||||
ASSERT_EQ(env_->random_read_counter_.Read(), expected_reads);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBBasicTestWithParallelIO, MultiGetWithChecksumMismatch) {
|
||||
std::vector<std::string> key_data(10);
|
||||
std::vector<Slice> keys;
|
||||
@@ -2976,7 +3234,7 @@ class DeadlineFS : public FileSystemWrapper {
|
||||
// or to simply delay but return success anyway. The latter mimics the
|
||||
// behavior of PosixFileSystem, which does not enforce any timeout
|
||||
explicit DeadlineFS(SpecialEnv* env, bool error_on_delay)
|
||||
: FileSystemWrapper(FileSystem::Default()),
|
||||
: FileSystemWrapper(env->GetFileSystem()),
|
||||
deadline_(std::chrono::microseconds::zero()),
|
||||
io_timeout_(std::chrono::microseconds::zero()),
|
||||
env_(env),
|
||||
@@ -3151,7 +3409,7 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = cache;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.env = env.get();
|
||||
SetTimeElapseOnlySleepOnReopen(&options);
|
||||
ReopenWithColumnFamilies(GetCFNames(), options);
|
||||
@@ -3303,6 +3561,28 @@ TEST_F(DBBasicTest, ManifestWriteFailure) {
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, VerifyFileChecksums) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("a", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->VerifyFileChecksums(ReadOptions()).IsInvalidArgument());
|
||||
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
Reopen(options);
|
||||
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
|
||||
|
||||
// Write an L0 with checksum computed.
|
||||
ASSERT_OK(Put("b", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->VerifyFileChecksums(ReadOptions()));
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
// A test class for intercepting random reads and injecting artificial
|
||||
// delays. Used for testing the deadline/timeout feature
|
||||
class DBBasicTestDeadline
|
||||
|
||||
@@ -677,7 +677,7 @@ TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
// Create a new SST file. This will further trigger a compaction
|
||||
// and generate another file.
|
||||
ASSERT_OK(Flush(1));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(3, /* Totally 3 files created up to now */
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
|
||||
@@ -692,7 +692,7 @@ TEST_F(DBBlockCacheTest, ParanoidFileChecks) {
|
||||
ASSERT_OK(Put(1, "1_key4", "val4"));
|
||||
ASSERT_OK(Put(1, "9_key4", "val4"));
|
||||
ASSERT_OK(Flush(1));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(3, /* Totally 3 files created up to now */
|
||||
TestGetTickerCount(options, BLOCK_CACHE_ADD));
|
||||
}
|
||||
@@ -837,8 +837,9 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
Random rnd(301);
|
||||
for (auto compression_type : compression_types) {
|
||||
Options options = CurrentOptions();
|
||||
options.compression = compression_type;
|
||||
options.compression_opts.max_dict_bytes = 4096;
|
||||
options.bottommost_compression = compression_type;
|
||||
options.bottommost_compression_opts.max_dict_bytes = 4096;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
options.create_if_missing = true;
|
||||
options.num_levels = 2;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
@@ -859,7 +860,7 @@ TEST_F(DBBlockCacheTest, CacheCompressionDict) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
ASSERT_EQ(kNumFiles, NumTableFilesAtLevel(1));
|
||||
|
||||
@@ -991,6 +992,9 @@ TEST_P(DBBlockCachePinningTest, TwoLevelDB) {
|
||||
++expected_index_misses;
|
||||
}
|
||||
}
|
||||
if (unpartitioned_pinning_ == PinningTier::kNone) {
|
||||
++expected_compression_dict_misses;
|
||||
}
|
||||
ASSERT_EQ(expected_filter_misses,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(expected_index_misses,
|
||||
|
||||
@@ -514,24 +514,24 @@ INSTANTIATE_TEST_CASE_P(
|
||||
::testing::Values(
|
||||
std::make_tuple(BFP::kDeprecatedBlock, false,
|
||||
test::kDefaultFormatVersion),
|
||||
std::make_tuple(BFP::kAuto, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(BFP::kAuto, false, test::kDefaultFormatVersion)));
|
||||
std::make_tuple(BFP::kAutoBloom, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(BFP::kAutoBloom, false, test::kDefaultFormatVersion)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(BFP::kDeprecatedBlock, false,
|
||||
test::kDefaultFormatVersion),
|
||||
std::make_tuple(BFP::kAuto, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(BFP::kAuto, false, test::kDefaultFormatVersion)));
|
||||
std::make_tuple(BFP::kAutoBloom, true, test::kDefaultFormatVersion),
|
||||
std::make_tuple(BFP::kAutoBloom, false, test::kDefaultFormatVersion)));
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatLatest, DBBloomFilterTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(BFP::kDeprecatedBlock, false,
|
||||
test::kLatestFormatVersion),
|
||||
std::make_tuple(BFP::kAuto, true, test::kLatestFormatVersion),
|
||||
std::make_tuple(BFP::kAuto, false, test::kLatestFormatVersion)));
|
||||
std::make_tuple(BFP::kAutoBloom, true, test::kLatestFormatVersion),
|
||||
std::make_tuple(BFP::kAutoBloom, false, test::kLatestFormatVersion)));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
@@ -1730,6 +1730,7 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterUpperBound) {
|
||||
int using_full_builder = bfp_impl != BFP::kDeprecatedBlock;
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = CurrentOptions().env;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(4));
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
@@ -1860,6 +1861,7 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterMultipleSST) {
|
||||
for (auto bfp_impl : BFP::kAllFixedImpls) {
|
||||
int using_full_builder = bfp_impl != BFP::kDeprecatedBlock;
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
@@ -2052,6 +2054,7 @@ TEST_F(DBBloomFilterTest, DynamicBloomFilterNewColumnFamily) {
|
||||
TEST_F(DBBloomFilterTest, DynamicBloomFilterOptions) {
|
||||
for (auto bfp_impl : BFP::kAllFixedImpls) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
@@ -314,7 +314,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey));
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey, true /* log_err_key */));
|
||||
total++;
|
||||
if (ikey.sequence != 0) {
|
||||
count++;
|
||||
@@ -405,7 +405,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilter) {
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey));
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey, true /* log_err_key */));
|
||||
ASSERT_NE(ikey.sequence, (unsigned)0);
|
||||
count++;
|
||||
iter->Next();
|
||||
@@ -624,7 +624,7 @@ TEST_F(DBTestCompactionFilter, CompactionFilterContextManual) {
|
||||
ASSERT_OK(iter->status());
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey));
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey, true /* log_err_key */));
|
||||
total++;
|
||||
if (ikey.sequence != 0) {
|
||||
count++;
|
||||
|
||||
+433
-3
@@ -7,6 +7,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
@@ -28,6 +31,31 @@ class DBCompactionTest : public DBTestBase {
|
||||
public:
|
||||
DBCompactionTest()
|
||||
: DBTestBase("/db_compaction_test", /*env_do_fsync=*/true) {}
|
||||
|
||||
std::vector<uint64_t> GetBlobFileNumbers() {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
|
||||
std::vector<uint64_t> result;
|
||||
result.reserve(blob_files.size());
|
||||
|
||||
for (const auto& blob_file : blob_files) {
|
||||
result.emplace_back(blob_file.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class DBCompactionTestWithParam
|
||||
@@ -3270,7 +3298,7 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
table_options.block_cache = NewLRUCache(64 << 20); // 64MB
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -5023,7 +5051,7 @@ TEST_F(DBCompactionTest, ManualCompactionFailsInReadOnlyMode) {
|
||||
// is in read-only mode. Verify it now at least returns, despite failing.
|
||||
const int kNumL0Files = 4;
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options opts = CurrentOptions();
|
||||
opts.disable_auto_compactions = true;
|
||||
opts.env = mock_env.get();
|
||||
@@ -5250,7 +5278,7 @@ TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 400; // small block size
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
@@ -5845,6 +5873,408 @@ TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
|
||||
ASSERT_EQ("0,5", FilesPerLevel(0));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlob) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
constexpr char second_value[] = "second_value";
|
||||
constexpr char third_value[] = "third_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
ASSERT_OK(Put(second_key, first_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(first_key, second_value));
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(first_key, third_value));
|
||||
ASSERT_OK(Put(second_key, third_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
options.enable_blob_files = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
ASSERT_EQ(Get(first_key), third_value);
|
||||
ASSERT_EQ(Get(second_key), third_value);
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& l1_files = storage_info->LevelFiles(1);
|
||||
ASSERT_EQ(l1_files.size(), 1);
|
||||
|
||||
const FileMetaData* const table_file = l1_files[0];
|
||||
assert(table_file);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
ASSERT_EQ(blob_files.size(), 1);
|
||||
|
||||
const auto& blob_file = blob_files.begin()->second;
|
||||
assert(blob_file);
|
||||
|
||||
ASSERT_EQ(table_file->smallest.user_key(), first_key);
|
||||
ASSERT_EQ(table_file->largest.user_key(), second_key);
|
||||
ASSERT_EQ(table_file->oldest_blob_file_number,
|
||||
blob_file->GetBlobFileNumber());
|
||||
|
||||
ASSERT_EQ(blob_file->GetTotalBlobCount(), 2);
|
||||
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
|
||||
const uint64_t expected_bytes =
|
||||
table_file->fd.GetFileSize() + blob_file->GetTotalBlobBytes();
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written, expected_bytes);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 2);
|
||||
}
|
||||
|
||||
class DBCompactionTestBlobError
|
||||
: public DBCompactionTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBCompactionTestBlobError()
|
||||
: fault_injection_env_(env_), sync_point_(GetParam()) {}
|
||||
~DBCompactionTestBlobError() { Close(); }
|
||||
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobError, DBCompactionTestBlobError,
|
||||
::testing::ValuesIn(std::vector<std::string>{
|
||||
"BlobFileBuilder::WriteBlobToFile:AddRecord",
|
||||
"BlobFileBuilder::WriteBlobToFile:AppendFooter"}));
|
||||
|
||||
TEST_P(DBCompactionTestBlobError, CompactionError) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
constexpr char second_value[] = "second_value";
|
||||
constexpr char third_value[] = "third_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
ASSERT_OK(Put(second_key, first_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(first_key, second_value));
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put(first_key, third_value));
|
||||
ASSERT_OK(Put(second_key, third_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
options.enable_blob_files = true;
|
||||
options.env = &fault_injection_env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), begin, end).IsIOError());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& l1_files = storage_info->LevelFiles(1);
|
||||
ASSERT_TRUE(l1_files.empty());
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
ASSERT_TRUE(blob_files.empty());
|
||||
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_GE(compaction_stats.size(), 2);
|
||||
|
||||
if (sync_point_ == "BlobFileBuilder::WriteBlobToFile:AddRecord") {
|
||||
ASSERT_EQ(compaction_stats[1].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 0);
|
||||
} else {
|
||||
// SST file writing succeeded; blob file writing failed (during Finish)
|
||||
ASSERT_GT(compaction_stats[1].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[1].num_output_files, 1);
|
||||
}
|
||||
}
|
||||
|
||||
class DBCompactionTestBlobGC
|
||||
: public DBCompactionTest,
|
||||
public testing::WithParamInterface<std::tuple<double, bool>> {
|
||||
public:
|
||||
DBCompactionTestBlobGC()
|
||||
: blob_gc_age_cutoff_(std::get<0>(GetParam())),
|
||||
updated_enable_blob_files_(std::get<1>(GetParam())) {}
|
||||
|
||||
double blob_gc_age_cutoff_;
|
||||
bool updated_enable_blob_files_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobGC, DBCompactionTestBlobGC,
|
||||
::testing::Combine(::testing::Values(0.0, 0.5, 1.0),
|
||||
::testing::Bool()));
|
||||
|
||||
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGC) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.blob_file_size = 32; // one blob per file
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = blob_gc_age_cutoff_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "third_value";
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
constexpr char fourth_value[] = "fourth_value";
|
||||
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
ASSERT_OK(Put(fourth_key, fourth_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
const std::vector<uint64_t> original_blob_files = GetBlobFileNumbers();
|
||||
|
||||
ASSERT_EQ(original_blob_files.size(), 4);
|
||||
|
||||
const size_t cutoff_index = static_cast<size_t>(
|
||||
options.blob_garbage_collection_age_cutoff * original_blob_files.size());
|
||||
|
||||
// Note: turning off enable_blob_files before the compaction results in
|
||||
// garbage collected values getting inlined.
|
||||
size_t expected_number_of_files = original_blob_files.size();
|
||||
|
||||
if (!updated_enable_blob_files_) {
|
||||
ASSERT_OK(db_->SetOptions({{"enable_blob_files", "false"}}));
|
||||
|
||||
expected_number_of_files -= cutoff_index;
|
||||
}
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
ASSERT_EQ(Get(first_key), first_value);
|
||||
ASSERT_EQ(Get(second_key), second_value);
|
||||
ASSERT_EQ(Get(third_key), third_value);
|
||||
ASSERT_EQ(Get(fourth_key), fourth_value);
|
||||
|
||||
const std::vector<uint64_t> new_blob_files = GetBlobFileNumbers();
|
||||
|
||||
ASSERT_EQ(new_blob_files.size(), expected_number_of_files);
|
||||
|
||||
// Original blob files below the cutoff should be gone, original blob files at
|
||||
// or above the cutoff should be still there
|
||||
for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
|
||||
ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlobGCError_CorruptIndex) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 1.0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "third_value";
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
constexpr char corrupt_blob_index[] = "foobar";
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(WriteBatchInternal::PutBlobIndex(&batch, 0, fourth_key,
|
||||
corrupt_blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlobGCError_InlinedTTLIndex) {
|
||||
constexpr uint64_t min_blob_size = 10;
|
||||
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = min_blob_size;
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 1.0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "third_value";
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
constexpr char blob[] = "short";
|
||||
static_assert(sizeof(short) - 1 < min_blob_size,
|
||||
"Blob too long to be inlined");
|
||||
|
||||
// Fake an inlined TTL blob index.
|
||||
std::string blob_index;
|
||||
|
||||
constexpr uint64_t expiration = 1234567890;
|
||||
|
||||
BlobIndex::EncodeInlinedTTL(&blob_index, expiration, blob);
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(
|
||||
WriteBatchInternal::PutBlobIndex(&batch, 0, fourth_key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactionWithBlobGCError_IndexWithInvalidFileNumber) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.enable_blob_files = true;
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 1.0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "third_value";
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
|
||||
// Fake a blob index referencing a non-existent blob file.
|
||||
std::string blob_index;
|
||||
|
||||
constexpr uint64_t blob_file_number = 1000;
|
||||
constexpr uint64_t offset = 1234;
|
||||
constexpr uint64_t size = 5678;
|
||||
|
||||
BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size,
|
||||
kNoCompression);
|
||||
|
||||
WriteBatch batch;
|
||||
ASSERT_OK(
|
||||
WriteBatchInternal::PutBlobIndex(&batch, 0, fourth_key, blob_index));
|
||||
ASSERT_OK(db_->Write(WriteOptions(), &batch));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_TRUE(
|
||||
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -141,6 +141,7 @@ TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
||||
options.max_background_compactions = 2;
|
||||
options.num_levels = 5;
|
||||
options.max_compaction_bytes = 0; // Force not expanding in compactions
|
||||
options.db_host_id = ""; // Setting this messes up the file size calculation
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
@@ -18,6 +18,13 @@ class DBEncryptionTest : public DBTestBase {
|
||||
public:
|
||||
DBEncryptionTest()
|
||||
: DBTestBase("/db_encryption_test", /*env_do_fsync=*/true) {}
|
||||
Env* GetTargetEnv() {
|
||||
if (encrypted_env_ != nullptr) {
|
||||
return (static_cast<EnvWrapper*>(encrypted_env_))->target();
|
||||
} else {
|
||||
return env_;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -34,20 +41,20 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
auto status = env_->GetChildren(dbname_, &fileNames);
|
||||
ASSERT_OK(status);
|
||||
|
||||
auto defaultEnv = Env::Default();
|
||||
Env* target = GetTargetEnv();
|
||||
int hits = 0;
|
||||
for (auto it = fileNames.begin() ; it != fileNames.end(); ++it) {
|
||||
if ((*it == "..") || (*it == ".")) {
|
||||
if ((*it == "..") || (*it == ".") || (*it == "LOCK")) {
|
||||
continue;
|
||||
}
|
||||
auto filePath = dbname_ + "/" + *it;
|
||||
std::unique_ptr<SequentialFile> seqFile;
|
||||
auto envOptions = EnvOptions(CurrentOptions());
|
||||
status = defaultEnv->NewSequentialFile(filePath, &seqFile, envOptions);
|
||||
status = target->NewSequentialFile(filePath, &seqFile, envOptions);
|
||||
ASSERT_OK(status);
|
||||
|
||||
uint64_t fileSize;
|
||||
status = defaultEnv->GetFileSize(filePath, &fileSize);
|
||||
status = target->GetFileSize(filePath, &fileSize);
|
||||
ASSERT_OK(status);
|
||||
|
||||
std::string scratch;
|
||||
@@ -85,7 +92,7 @@ TEST_F(DBEncryptionTest, CheckEncrypted) {
|
||||
}
|
||||
|
||||
TEST_F(DBEncryptionTest, ReadEmptyFile) {
|
||||
auto defaultEnv = Env::Default();
|
||||
auto defaultEnv = GetTargetEnv();
|
||||
|
||||
// create empty file for reading it back in later
|
||||
auto envOptions = EnvOptions(CurrentOptions());
|
||||
|
||||
+178
-10
@@ -14,6 +14,7 @@
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/mutexlock.h"
|
||||
@@ -450,9 +451,11 @@ 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;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
@@ -470,9 +473,7 @@ TEST_F(DBFlushTest, FlushWithBlob) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_EQ(Get("key1"), short_value);
|
||||
|
||||
// TODO: enable once Get support is implemented for blobs
|
||||
// ASSERT_EQ(Get("key2"), long_value);
|
||||
ASSERT_EQ(Get("key2"), long_value);
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
@@ -527,10 +528,12 @@ TEST_F(DBFlushTest, FlushWithBlob) {
|
||||
class DBFlushTestBlobError : public DBFlushTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBFlushTestBlobError() : fault_injection_env_(env_) {}
|
||||
DBFlushTestBlobError()
|
||||
: fault_injection_env_(env_), sync_point_(GetParam()) {}
|
||||
~DBFlushTestBlobError() { Close(); }
|
||||
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBFlushTestBlobError, DBFlushTestBlobError,
|
||||
@@ -548,11 +551,12 @@ TEST_P(DBFlushTestBlobError, FlushError) {
|
||||
|
||||
ASSERT_OK(Put("key", "blob"));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(GetParam(), [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false, Status::IOError());
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable", [this](void* /* arg */) {
|
||||
"BuildTable:BeforeDeleteFile", [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(true);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -601,14 +605,112 @@ TEST_P(DBFlushTestBlobError, FlushError) {
|
||||
|
||||
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
|
||||
ASSERT_FALSE(compaction_stats.empty());
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 0);
|
||||
|
||||
if (sync_point_ == "BlobFileBuilder::WriteBlobToFile:AddRecord") {
|
||||
ASSERT_EQ(compaction_stats[0].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 0);
|
||||
} else {
|
||||
// SST file writing succeeded; blob file writing failed (during Finish)
|
||||
ASSERT_GT(compaction_stats[0].bytes_written, 0);
|
||||
ASSERT_EQ(compaction_stats[0].num_output_files, 1);
|
||||
}
|
||||
|
||||
const uint64_t* const cf_stats_value = internal_stats->TEST_GetCFStatsValue();
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED], 0);
|
||||
ASSERT_EQ(cf_stats_value[InternalStats::BYTES_FLUSHED],
|
||||
compaction_stats[0].bytes_written);
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_P(DBAtomicFlushTest, ManualFlushUnder2PC) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.allow_2pc = true;
|
||||
options.atomic_flush = GetParam();
|
||||
// 64MB so that memtable flush won't be trigger by the small writes.
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
|
||||
// Destroy the DB to recreate as a TransactionDB.
|
||||
Close();
|
||||
Destroy(options, true);
|
||||
|
||||
// Create a TransactionDB.
|
||||
TransactionDB* txn_db = nullptr;
|
||||
TransactionDBOptions txn_db_opts;
|
||||
txn_db_opts.write_policy = TxnDBWritePolicy::WRITE_COMMITTED;
|
||||
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, &txn_db));
|
||||
ASSERT_NE(txn_db, nullptr);
|
||||
db_ = txn_db;
|
||||
|
||||
// Create two more columns other than default CF.
|
||||
std::vector<std::string> cfs = {"puppy", "kitty"};
|
||||
CreateColumnFamilies(cfs, options);
|
||||
ASSERT_EQ(handles_.size(), 2);
|
||||
ASSERT_EQ(handles_[0]->GetName(), cfs[0]);
|
||||
ASSERT_EQ(handles_[1]->GetName(), cfs[1]);
|
||||
const size_t kNumCfToFlush = options.atomic_flush ? 2 : 1;
|
||||
|
||||
WriteOptions wopts;
|
||||
TransactionOptions txn_opts;
|
||||
// txn1 only prepare, but does not commit.
|
||||
// The WAL containing the prepared but uncommitted data must be kept.
|
||||
Transaction* txn1 = txn_db->BeginTransaction(wopts, txn_opts, nullptr);
|
||||
// txn2 not only prepare, but also commit.
|
||||
Transaction* txn2 = txn_db->BeginTransaction(wopts, txn_opts, nullptr);
|
||||
ASSERT_NE(txn1, nullptr);
|
||||
ASSERT_NE(txn2, nullptr);
|
||||
for (size_t i = 0; i < kNumCfToFlush; i++) {
|
||||
ASSERT_OK(txn1->Put(handles_[i], "k1", "v1"));
|
||||
ASSERT_OK(txn2->Put(handles_[i], "k2", "v2"));
|
||||
}
|
||||
// A txn must be named before prepare.
|
||||
ASSERT_OK(txn1->SetName("txn1"));
|
||||
ASSERT_OK(txn2->SetName("txn2"));
|
||||
// Prepare writes to WAL, but not to memtable. (WriteCommitted)
|
||||
ASSERT_OK(txn1->Prepare());
|
||||
ASSERT_OK(txn2->Prepare());
|
||||
// Commit writes to memtable.
|
||||
ASSERT_OK(txn2->Commit());
|
||||
delete txn1;
|
||||
delete txn2;
|
||||
|
||||
// There are still data in memtable not flushed.
|
||||
// But since data is small enough to reside in the active memtable,
|
||||
// there are no immutable memtable.
|
||||
for (size_t i = 0; i < kNumCfToFlush; i++) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_FALSE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
|
||||
// Atomic flush memtables,
|
||||
// the min log with prepared data should be written to MANIFEST.
|
||||
std::vector<ColumnFamilyHandle*> cfs_to_flush(kNumCfToFlush);
|
||||
for (size_t i = 0; i < kNumCfToFlush; i++) {
|
||||
cfs_to_flush[i] = handles_[i];
|
||||
}
|
||||
ASSERT_OK(txn_db->Flush(FlushOptions(), cfs_to_flush));
|
||||
|
||||
// There are no remaining data in memtable after flush.
|
||||
for (size_t i = 0; i < kNumCfToFlush; i++) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
ASSERT_EQ(cfh->cfd()->GetFlushReason(), FlushReason::kManualFlush);
|
||||
}
|
||||
|
||||
// The recovered min log number with prepared data should be non-zero.
|
||||
// In 2pc mode, MinLogNumberToKeep returns the
|
||||
// VersionSet::min_log_number_to_keep_2pc recovered from MANIFEST, if it's 0,
|
||||
// it means atomic flush didn't write the min_log_number_to_keep to MANIFEST.
|
||||
cfs.push_back(kDefaultColumnFamilyName);
|
||||
ASSERT_OK(TryReopenWithColumnFamilies(cfs, options));
|
||||
DBImpl* db_impl = reinterpret_cast<DBImpl*>(db_);
|
||||
ASSERT_TRUE(db_impl->allow_2pc());
|
||||
ASSERT_NE(db_impl->MinLogNumberToKeep(), 0);
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -623,18 +725,84 @@ TEST_P(DBAtomicFlushTest, ManualAtomicFlush) {
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
ASSERT_OK(Put(static_cast<int>(i) /*cf*/, "key", "value", wopts));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_FALSE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
|
||||
std::vector<int> cf_ids;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
cf_ids.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_ids));
|
||||
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(cfh->cfd()->GetFlushReason(), FlushReason::kManualFlush);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, PrecomputeMinLogNumberToKeepNon2PC) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.atomic_flush = GetParam();
|
||||
options.write_buffer_size = (static_cast<size_t>(64) << 20);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
const size_t num_cfs = handles_.size();
|
||||
ASSERT_EQ(num_cfs, 2);
|
||||
WriteOptions wopts;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
ASSERT_OK(Put(static_cast<int>(i) /*cf*/, "key", "value", wopts));
|
||||
}
|
||||
|
||||
{
|
||||
// Flush the default CF only.
|
||||
std::vector<int> cf_ids{0};
|
||||
ASSERT_OK(Flush(cf_ids));
|
||||
|
||||
autovector<ColumnFamilyData*> flushed_cfds;
|
||||
autovector<autovector<VersionEdit*>> flush_edits;
|
||||
auto flushed_cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[0]);
|
||||
flushed_cfds.push_back(flushed_cfh->cfd());
|
||||
flush_edits.push_back({});
|
||||
auto unflushed_cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[1]);
|
||||
|
||||
ASSERT_EQ(PrecomputeMinLogNumberToKeepNon2PC(dbfull()->TEST_GetVersionSet(),
|
||||
flushed_cfds, flush_edits),
|
||||
unflushed_cfh->cfd()->GetLogNumber());
|
||||
}
|
||||
|
||||
{
|
||||
// Flush all CFs.
|
||||
std::vector<int> cf_ids;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
cf_ids.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
ASSERT_OK(Flush(cf_ids));
|
||||
uint64_t log_num_after_flush = dbfull()->TEST_GetCurrentLogNumber();
|
||||
|
||||
uint64_t min_log_number_to_keep = port::kMaxUint64;
|
||||
autovector<ColumnFamilyData*> flushed_cfds;
|
||||
autovector<autovector<VersionEdit*>> flush_edits;
|
||||
for (size_t i = 0; i != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
flushed_cfds.push_back(cfh->cfd());
|
||||
flush_edits.push_back({});
|
||||
min_log_number_to_keep =
|
||||
std::min(min_log_number_to_keep, cfh->cfd()->GetLogNumber());
|
||||
}
|
||||
ASSERT_EQ(min_log_number_to_keep, log_num_after_flush);
|
||||
ASSERT_EQ(PrecomputeMinLogNumberToKeepNon2PC(dbfull()->TEST_GetVersionSet(),
|
||||
flushed_cfds, flush_edits),
|
||||
min_log_number_to_keep);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBAtomicFlushTest, AtomicFlushTriggeredByMemTableFull) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
+145
-40
@@ -18,6 +18,7 @@
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -637,7 +638,7 @@ Status DBImpl::CloseHelper() {
|
||||
|
||||
if (immutable_db_options_.info_log && own_info_log_) {
|
||||
Status s = immutable_db_options_.info_log->Close();
|
||||
if (!s.ok() && ret.ok()) {
|
||||
if (!s.ok() && !s.IsNotSupported() && ret.ok()) {
|
||||
ret = s;
|
||||
}
|
||||
}
|
||||
@@ -976,6 +977,7 @@ Status DBImpl::SetOptions(
|
||||
MutableCFOptions new_options;
|
||||
Status s;
|
||||
Status persist_options_status;
|
||||
persist_options_status.PermitUncheckedError(); // Allow uninitialized access
|
||||
SuperVersionContext sv_context(/* create_superversion */ true);
|
||||
{
|
||||
auto db_options = GetDBOptions();
|
||||
@@ -1281,7 +1283,11 @@ Status DBImpl::SyncWAL() {
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:1");
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
MarkLogsSynced(current_log_number, need_log_dir_sync, status);
|
||||
if (status.ok()) {
|
||||
status = MarkLogsSynced(current_log_number, need_log_dir_sync);
|
||||
} else {
|
||||
MarkLogsNotSynced(current_log_number);
|
||||
}
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncWAL:BeforeMarkLogsSynced:2");
|
||||
|
||||
@@ -1307,27 +1313,54 @@ Status DBImpl::UnlockWAL() {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir,
|
||||
const Status& status) {
|
||||
Status DBImpl::MarkLogsSynced(uint64_t up_to, bool synced_dir) {
|
||||
mutex_.AssertHeld();
|
||||
if (synced_dir && logfile_number_ == up_to && status.ok()) {
|
||||
if (synced_dir && logfile_number_ == up_to) {
|
||||
log_dir_synced_ = true;
|
||||
}
|
||||
VersionEdit synced_wals;
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;) {
|
||||
auto& log = *it;
|
||||
assert(log.getting_synced);
|
||||
if (status.ok() && logs_.size() > 1) {
|
||||
logs_to_free_.push_back(log.ReleaseWriter());
|
||||
auto& wal = *it;
|
||||
assert(wal.getting_synced);
|
||||
if (logs_.size() > 1) {
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest &&
|
||||
wal.writer->file()->GetFileSize() > 0) {
|
||||
synced_wals.AddWal(wal.number,
|
||||
WalMetadata(wal.writer->file()->GetFileSize()));
|
||||
}
|
||||
logs_to_free_.push_back(wal.ReleaseWriter());
|
||||
// To modify logs_ both mutex_ and log_write_mutex_ must be held
|
||||
InstrumentedMutexLock l(&log_write_mutex_);
|
||||
it = logs_.erase(it);
|
||||
} else {
|
||||
log.getting_synced = false;
|
||||
wal.getting_synced = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
assert(!status.ok() || logs_.empty() || logs_[0].number > up_to ||
|
||||
assert(logs_.empty() || logs_[0].number > up_to ||
|
||||
(logs_.size() == 1 && !logs_[0].getting_synced));
|
||||
|
||||
Status s;
|
||||
if (synced_wals.IsWalAddition()) {
|
||||
// not empty, write to MANIFEST.
|
||||
s = versions_->LogAndApplyToDefaultColumnFamily(&synced_wals, &mutex_);
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
}
|
||||
log_sync_cv_.SignalAll();
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::MarkLogsNotSynced(uint64_t up_to) {
|
||||
mutex_.AssertHeld();
|
||||
for (auto it = logs_.begin(); it != logs_.end() && it->number <= up_to;
|
||||
++it) {
|
||||
auto& wal = *it;
|
||||
assert(wal.getting_synced);
|
||||
wal.getting_synced = false;
|
||||
}
|
||||
log_sync_cv_.SignalAll();
|
||||
}
|
||||
|
||||
@@ -2172,7 +2205,7 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
for (; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
s = MultiGetImpl(read_options, cf_iter->start, cf_iter->num_keys,
|
||||
&sorted_keys, cf_iter->super_version, consistent_seqnum,
|
||||
read_callback, nullptr);
|
||||
read_callback);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
@@ -2344,7 +2377,7 @@ void DBImpl::MultiGetWithCallback(
|
||||
|
||||
Status s = MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum,
|
||||
read_callback, nullptr);
|
||||
read_callback);
|
||||
assert(s.ok() || s.IsTimedOut() || s.IsAborted());
|
||||
ReturnAndCleanupSuperVersion(multiget_cf_data[0].cfd,
|
||||
multiget_cf_data[0].super_version);
|
||||
@@ -2363,7 +2396,7 @@ Status DBImpl::MultiGetImpl(
|
||||
const ReadOptions& read_options, size_t start_key, size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* super_version, SequenceNumber snapshot,
|
||||
ReadCallback* callback, bool* is_blob_index) {
|
||||
ReadCallback* callback) {
|
||||
PERF_CPU_TIMER_GUARD(get_cpu_nanos, env_);
|
||||
StopWatch sw(env_, stats_, DB_MULTIGET);
|
||||
|
||||
@@ -2402,11 +2435,9 @@ Status DBImpl::MultiGetImpl(
|
||||
(read_options.read_tier == kPersistedTier &&
|
||||
has_unpersisted_data_.load(std::memory_order_relaxed));
|
||||
if (!skip_memtable) {
|
||||
super_version->mem->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
super_version->mem->MultiGet(read_options, &range, callback);
|
||||
if (!range.empty()) {
|
||||
super_version->imm->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
super_version->imm->MultiGet(read_options, &range, callback);
|
||||
}
|
||||
if (!range.empty()) {
|
||||
lookup_current = true;
|
||||
@@ -2416,8 +2447,7 @@ Status DBImpl::MultiGetImpl(
|
||||
}
|
||||
if (lookup_current) {
|
||||
PERF_TIMER_GUARD(get_from_output_files_time);
|
||||
super_version->current->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
super_version->current->MultiGet(read_options, &range, callback);
|
||||
}
|
||||
curr_value_size = range.GetValueSize();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
@@ -2771,7 +2801,7 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
/* allow_unprepared_value */ true);
|
||||
result = NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations, read_callback,
|
||||
this, cfd);
|
||||
#endif
|
||||
@@ -2792,7 +2822,7 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob,
|
||||
bool expose_blob_index,
|
||||
bool allow_refresh) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
|
||||
@@ -2857,9 +2887,9 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
// likely that any iterator pointer is close to the iterator it points to so
|
||||
// that they are likely to be in the same cache line and/or page.
|
||||
ArenaWrappedDBIter* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, snapshot,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback, this, cfd, allow_blob,
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, sv->current,
|
||||
snapshot, sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback, this, cfd, expose_blob_index,
|
||||
read_options.snapshot != nullptr ? false : allow_refresh);
|
||||
|
||||
InternalIterator* internal_iter = NewInternalIterator(
|
||||
@@ -2897,7 +2927,7 @@ Status DBImpl::NewIterators(
|
||||
/* allow_unprepared_value */ true);
|
||||
iterators->push_back(NewDBIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
cfd->user_comparator(), iter, kMaxSequenceNumber,
|
||||
cfd->user_comparator(), iter, sv->current, kMaxSequenceNumber,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
read_callback, this, cfd));
|
||||
}
|
||||
@@ -3368,6 +3398,10 @@ Status DBImpl::GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
return Status::InvalidArgument("Invalid options");
|
||||
}
|
||||
|
||||
const Comparator* const ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
|
||||
Version* v;
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
@@ -3375,9 +3409,23 @@ Status DBImpl::GetApproximateSizes(const SizeApproximationOptions& options,
|
||||
v = sv->current;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
Slice start = range[i].start;
|
||||
Slice limit = range[i].limit;
|
||||
|
||||
// Add timestamp if needed
|
||||
std::string start_with_ts, limit_with_ts;
|
||||
if (ts_sz > 0) {
|
||||
// Maximum timestamp means including all key with any timestamp
|
||||
AppendKeyWithMaxTimestamp(&start_with_ts, start, ts_sz);
|
||||
// Append a maximum timestamp as the range limit is exclusive:
|
||||
// [start, limit)
|
||||
AppendKeyWithMaxTimestamp(&limit_with_ts, limit, ts_sz);
|
||||
start = start_with_ts;
|
||||
limit = limit_with_ts;
|
||||
}
|
||||
// Convert user_key into a corresponding internal key.
|
||||
InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k1(start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
InternalKey k2(limit, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
sizes[i] = 0;
|
||||
if (options.include_files) {
|
||||
sizes[i] += versions_->ApproximateSize(
|
||||
@@ -3429,14 +3477,14 @@ Status DBImpl::DeleteFile(std::string name) {
|
||||
FileType type;
|
||||
WalFileType log_type;
|
||||
if (!ParseFileName(name, &number, &type, &log_type) ||
|
||||
(type != kTableFile && type != kLogFile)) {
|
||||
(type != kTableFile && type != kWalFile)) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log, "DeleteFile %s failed.\n",
|
||||
name.c_str());
|
||||
return Status::InvalidArgument("Invalid file name");
|
||||
}
|
||||
|
||||
Status status;
|
||||
if (type == kLogFile) {
|
||||
if (type == kWalFile) {
|
||||
// Only allow deleting archived log files
|
||||
if (log_type != kArchivedLogFile) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
@@ -3861,7 +3909,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
std::string path_to_delete = dbname + "/" + fname;
|
||||
if (type == kMetaDatabase) {
|
||||
del = DestroyDB(path_to_delete, options);
|
||||
} else if (type == kTableFile || type == kLogFile) {
|
||||
} else if (type == kTableFile || type == kWalFile) {
|
||||
del = DeleteDBFile(&soptions, path_to_delete, dbname,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
} else {
|
||||
@@ -3915,7 +3963,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
if (env->GetChildren(archivedir, &archiveFiles).ok()) {
|
||||
// Delete archival files.
|
||||
for (const auto& file : archiveFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
if (ParseFileName(file, &number, &type) && type == kWalFile) {
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, archivedir + "/" + file, archivedir,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path);
|
||||
@@ -3931,7 +3979,7 @@ Status DestroyDB(const std::string& dbname, const Options& options,
|
||||
// Delete log files in the WAL dir
|
||||
if (wal_dir_exists) {
|
||||
for (const auto& file : walDirFiles) {
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
if (ParseFileName(file, &number, &type) && type == kWalFile) {
|
||||
Status del =
|
||||
DeleteDBFile(&soptions, LogFileName(soptions.wal_dir, number),
|
||||
soptions.wal_dir, /*force_bg=*/false,
|
||||
@@ -4556,8 +4604,7 @@ Status DBImpl::IngestExternalFiles(
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
const IOStatus& io_s = versions_->io_status();
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kManifestWrite)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
|
||||
// Resume writes to the DB
|
||||
@@ -4719,14 +4766,36 @@ Status DBImpl::CreateColumnFamilyWithImport(
|
||||
temp_s.ToString().c_str());
|
||||
}
|
||||
// Always returns Status::OK()
|
||||
assert(DestroyColumnFamilyHandle(*handle).ok());
|
||||
temp_s = DestroyColumnFamilyHandle(*handle);
|
||||
assert(temp_s.ok());
|
||||
*handle = nullptr;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyFileChecksums(const ReadOptions& read_options) {
|
||||
return VerifyChecksumInternal(read_options, /*use_file_checksum=*/true);
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyChecksum(const ReadOptions& read_options) {
|
||||
return VerifyChecksumInternal(read_options, /*use_file_checksum=*/false);
|
||||
}
|
||||
|
||||
Status DBImpl::VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
bool use_file_checksum) {
|
||||
Status s;
|
||||
|
||||
if (use_file_checksum) {
|
||||
FileChecksumGenFactory* const file_checksum_gen_factory =
|
||||
immutable_db_options_.file_checksum_gen_factory.get();
|
||||
if (!file_checksum_gen_factory) {
|
||||
s = Status::InvalidArgument(
|
||||
"Cannot verify file checksum if options.file_checksum_gen_factory is "
|
||||
"null");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ColumnFamilyData*> cfd_list;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -4741,11 +4810,12 @@ Status DBImpl::VerifyChecksum(const ReadOptions& read_options) {
|
||||
for (auto cfd : cfd_list) {
|
||||
sv_list.push_back(cfd->GetReferencedSuperVersion(this));
|
||||
}
|
||||
|
||||
for (auto& sv : sv_list) {
|
||||
VersionStorageInfo* vstorage = sv->current->storage_info();
|
||||
ColumnFamilyData* cfd = sv->current->cfd();
|
||||
Options opts;
|
||||
{
|
||||
if (!use_file_checksum) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
opts = Options(BuildDBOptions(immutable_db_options_, mutable_db_options_),
|
||||
cfd->GetLatestCFOptions());
|
||||
@@ -4753,11 +4823,18 @@ Status DBImpl::VerifyChecksum(const ReadOptions& read_options) {
|
||||
for (int i = 0; i < vstorage->num_non_empty_levels() && s.ok(); i++) {
|
||||
for (size_t j = 0; j < vstorage->LevelFilesBrief(i).num_files && s.ok();
|
||||
j++) {
|
||||
const auto& fd = vstorage->LevelFilesBrief(i).files[j].fd;
|
||||
const auto& fd_with_krange = vstorage->LevelFilesBrief(i).files[j];
|
||||
const auto& fd = fd_with_krange.fd;
|
||||
const FileMetaData* fmeta = fd_with_krange.file_metadata;
|
||||
assert(fmeta);
|
||||
std::string fname = TableFileName(cfd->ioptions()->cf_paths,
|
||||
fd.GetNumber(), fd.GetPathId());
|
||||
s = ROCKSDB_NAMESPACE::VerifySstFileChecksum(opts, file_options_,
|
||||
read_options, fname);
|
||||
if (use_file_checksum) {
|
||||
s = VerifySstFileChecksum(*fmeta, fname, read_options);
|
||||
} else {
|
||||
s = ROCKSDB_NAMESPACE::VerifySstFileChecksum(opts, file_options_,
|
||||
read_options, fname);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
@@ -4788,6 +4865,34 @@ Status DBImpl::VerifyChecksum(const ReadOptions& read_options) {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::VerifySstFileChecksum(const FileMetaData& fmeta,
|
||||
const std::string& fname,
|
||||
const ReadOptions& read_options) {
|
||||
Status s;
|
||||
if (fmeta.file_checksum == kUnknownFileChecksum) {
|
||||
return s;
|
||||
}
|
||||
std::string file_checksum;
|
||||
std::string func_name;
|
||||
s = ROCKSDB_NAMESPACE::GenerateOneFileChecksum(
|
||||
fs_.get(), fname, immutable_db_options_.file_checksum_gen_factory.get(),
|
||||
fmeta.file_checksum_func_name, &file_checksum, &func_name,
|
||||
read_options.readahead_size, immutable_db_options_.allow_mmap_reads,
|
||||
io_tracer_);
|
||||
if (s.ok()) {
|
||||
assert(fmeta.file_checksum_func_name == func_name);
|
||||
if (file_checksum != fmeta.file_checksum) {
|
||||
std::ostringstream oss;
|
||||
oss << fname << " file checksum mismatch, ";
|
||||
oss << "expecting " << Slice(fmeta.file_checksum).ToString(/*hex=*/true);
|
||||
oss << ", but actual " << Slice(file_checksum).ToString(/*hex=*/true);
|
||||
s = Status::Corruption(oss.str());
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::VerifySstFileChecksum:mismatch", &s);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBImpl::NotifyOnExternalFileIngested(
|
||||
ColumnFamilyData* cfd, const ExternalSstFileIngestionJob& ingestion_job) {
|
||||
if (immutable_db_options_.listeners.empty()) {
|
||||
|
||||
+74
-10
@@ -431,8 +431,28 @@ class DBImpl : public DB {
|
||||
const ExportImportFilesMetaData& metadata,
|
||||
ColumnFamilyHandle** handle) override;
|
||||
|
||||
using DB::VerifyFileChecksums;
|
||||
Status VerifyFileChecksums(const ReadOptions& read_options) override;
|
||||
|
||||
using DB::VerifyChecksum;
|
||||
virtual Status VerifyChecksum(const ReadOptions& /*read_options*/) override;
|
||||
// Verify the checksums of files in db. Currently only tables are checked.
|
||||
//
|
||||
// read_options: controls file I/O behavior, e.g. read ahead size while
|
||||
// reading all the live table files.
|
||||
//
|
||||
// use_file_checksum: if false, verify the block checksums of all live table
|
||||
// in db. Otherwise, obtain the file checksums and compare
|
||||
// with the MANIFEST. Currently, file checksums are
|
||||
// recomputed by reading all table files.
|
||||
//
|
||||
// Returns: OK if there is no file whose file or block checksum mismatches.
|
||||
Status VerifyChecksumInternal(const ReadOptions& read_options,
|
||||
bool use_file_checksum);
|
||||
|
||||
Status VerifySstFileChecksum(const FileMetaData& fmeta,
|
||||
const std::string& fpath,
|
||||
const ReadOptions& read_options);
|
||||
|
||||
using DB::StartTrace;
|
||||
virtual Status StartTrace(
|
||||
@@ -502,7 +522,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
ReadCallback* read_callback,
|
||||
bool allow_blob = false,
|
||||
bool expose_blob_index = false,
|
||||
bool allow_refresh = true);
|
||||
|
||||
virtual SequenceNumber GetLastPublishedSequence() const {
|
||||
@@ -902,7 +922,7 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family = nullptr,
|
||||
bool disallow_trivial_move = false);
|
||||
|
||||
void TEST_SwitchWAL();
|
||||
Status TEST_SwitchWAL();
|
||||
|
||||
bool TEST_UnableToReleaseOldestLog() { return unable_to_release_oldest_log_; }
|
||||
|
||||
@@ -997,6 +1017,12 @@ class DBImpl : public DB {
|
||||
|
||||
VersionSet* TEST_GetVersionSet() const { return versions_.get(); }
|
||||
|
||||
uint64_t TEST_GetCurrentLogNumber() const {
|
||||
InstrumentedMutexLock l(mutex());
|
||||
assert(!logs_.empty());
|
||||
return logs_.back().number;
|
||||
}
|
||||
|
||||
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
|
||||
return files_grabbed_for_purge_;
|
||||
}
|
||||
@@ -1084,6 +1110,15 @@ class DBImpl : public DB {
|
||||
// If need_enter_write_thread = false, the method will enter write thread.
|
||||
Status WriteOptionsFile(bool need_mutex_lock, bool need_enter_write_thread);
|
||||
|
||||
Status CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
Status GetApproximateSizesInternal(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes);
|
||||
|
||||
// The following two functions can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
// 2. db_mutex is NOT held
|
||||
@@ -1195,14 +1230,22 @@ class DBImpl : public DB {
|
||||
|
||||
virtual bool OwnTablesAndLogs() const { return true; }
|
||||
|
||||
// Set DB identity file, and write DB ID to manifest if necessary.
|
||||
Status SetDBId();
|
||||
|
||||
// REQUIRES: db mutex held when calling this function, but the db mutex can
|
||||
// be released and re-acquired. Db mutex will be held when the function
|
||||
// returns.
|
||||
// After best-efforts recovery, there may be SST files in db/cf paths that are
|
||||
// not referenced in the MANIFEST. We delete these SST files. In the
|
||||
// After recovery, there may be SST files in db/cf paths that are
|
||||
// not referenced in the MANIFEST (e.g.
|
||||
// 1. It's best effort recovery;
|
||||
// 2. The VersionEdits referencing the SST files are appended to
|
||||
// MANIFEST, DB crashes when syncing the MANIFEST, the VersionEdits are
|
||||
// still not synced to MANIFEST during recovery.)
|
||||
// We delete these SST files. In the
|
||||
// meantime, we find out the largest file number present in the paths, and
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
Status FinishBestEffortsRecovery();
|
||||
Status DeleteUnreferencedSstFiles();
|
||||
|
||||
// SetDbSessionId() should be called in the constuctor DBImpl()
|
||||
// to ensure that db_session_id_ gets updated every time the DB is opened
|
||||
@@ -1682,7 +1725,9 @@ class DBImpl : public DB {
|
||||
std::unique_ptr<TaskLimiterToken>* token, LogBuffer* log_buffer);
|
||||
|
||||
// helper function to call after some of the logs_ were synced
|
||||
void MarkLogsSynced(uint64_t up_to, bool synced_dir, const Status& status);
|
||||
Status MarkLogsSynced(uint64_t up_to, bool synced_dir);
|
||||
// WALs with log number up to up_to are not synced successfully.
|
||||
void MarkLogsNotSynced(uint64_t up_to);
|
||||
|
||||
SnapshotImpl* GetSnapshotImpl(bool is_write_conflict_boundary,
|
||||
bool lock = true);
|
||||
@@ -1821,8 +1866,7 @@ class DBImpl : public DB {
|
||||
Status MultiGetImpl(
|
||||
const ReadOptions& read_options, size_t start_key, size_t num_keys,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys,
|
||||
SuperVersion* sv, SequenceNumber snap_seqnum, ReadCallback* callback,
|
||||
bool* is_blob_index);
|
||||
SuperVersion* sv, SequenceNumber snap_seqnum, ReadCallback* callback);
|
||||
|
||||
Status DisableFileDeletionsWithLock();
|
||||
|
||||
@@ -2184,11 +2228,27 @@ extern CompressionType GetCompressionFlush(
|
||||
// `memtables_to_flush`) will be flushed and thus will not depend on any WAL
|
||||
// file.
|
||||
// The function is only applicable to 2pc mode.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep(
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
autovector<VersionEdit*> edit_list,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
// For atomic flush.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker);
|
||||
|
||||
// In non-2PC mode, WALs with log number < the returned number can be
|
||||
// deleted after the cfd_to_flush column family is flushed successfully.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list);
|
||||
// For atomic flush.
|
||||
extern uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists);
|
||||
|
||||
// `cfd_to_flush` is the column family whose memtable will be flushed and thus
|
||||
// will not depend on any WAL file. nullptr means no memtable is being flushed.
|
||||
@@ -2196,6 +2256,10 @@ extern uint64_t PrecomputeMinLogNumberToKeep(
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const ColumnFamilyData* cfd_to_flush,
|
||||
const autovector<MemTable*>& memtables_to_flush);
|
||||
// For atomic flush.
|
||||
extern uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush);
|
||||
|
||||
// Fix user-supplied options to be reasonable
|
||||
template <class T, class V>
|
||||
|
||||
@@ -123,15 +123,17 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
// "number < current_log_number".
|
||||
MarkLogsSynced(current_log_number - 1, true, io_s);
|
||||
if (io_s.ok()) {
|
||||
io_s = status_to_io_status(MarkLogsSynced(current_log_number - 1, true));
|
||||
} else {
|
||||
MarkLogsNotSynced(current_log_number - 1);
|
||||
}
|
||||
if (!io_s.ok()) {
|
||||
if (total_log_size_ > 0) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Failed");
|
||||
return io_s;
|
||||
@@ -155,14 +157,14 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
nullptr /* memtable_id */, file_options_for_compaction_, versions_.get(),
|
||||
&mutex_, &shutting_down_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, job_context, log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(cfd, 0U),
|
||||
port::kMaxUint64 /* memtable_id */, file_options_for_compaction_,
|
||||
versions_.get(), &mutex_, &shutting_down_, snapshot_seqs,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, job_context,
|
||||
log_buffer, directories_.GetDbDir(), GetDataDir(cfd, 0U),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
io_tracer_, db_id_, db_session_id_);
|
||||
io_tracer_, db_id_, db_session_id_, cfd->GetFullHistoryTsLow());
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
@@ -188,8 +190,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
s = io_s;
|
||||
if (!io_s.ok() && !io_s.IsShutdownInProgress() &&
|
||||
!io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::SyncClosedLogs:Skip");
|
||||
@@ -249,24 +250,23 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
// be pessimistic and try write to a new MANIFEST.
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
if (!versions_->io_status().ok()) {
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kManifestWrite)
|
||||
.PermitUncheckedError();
|
||||
if (total_log_size_ > 0) {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
} else {
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
}
|
||||
} else if (total_log_size_ > 0) {
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
} else {
|
||||
// If we got here, then we decided not to care about the i_os status (either
|
||||
@@ -291,9 +291,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::FlushMemTableToOutputFile:MaxAllowedSpaceReached",
|
||||
&new_bg_error);
|
||||
// Should handle this error?
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
@@ -309,30 +307,22 @@ Status DBImpl::FlushMemTablesToOutputFiles(
|
||||
return AtomicFlushMemTablesToOutputFiles(
|
||||
bg_flush_args, made_progress, job_context, log_buffer, thread_pri);
|
||||
}
|
||||
assert(bg_flush_args.size() == 1);
|
||||
std::vector<SequenceNumber> snapshot_seqs;
|
||||
SequenceNumber earliest_write_conflict_snapshot;
|
||||
SnapshotChecker* snapshot_checker;
|
||||
GetSnapshotContext(job_context, &snapshot_seqs,
|
||||
&earliest_write_conflict_snapshot, &snapshot_checker);
|
||||
Status status;
|
||||
for (auto& arg : bg_flush_args) {
|
||||
ColumnFamilyData* cfd = arg.cfd_;
|
||||
MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context = arg.superversion_context_;
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options, made_progress, job_context,
|
||||
superversion_context, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, log_buffer, thread_pri);
|
||||
if (!s.ok()) {
|
||||
status = s;
|
||||
if (!s.IsShutdownInProgress() && !s.IsColumnFamilyDropped()) {
|
||||
// At this point, DB is not shutting down, nor is cfd dropped.
|
||||
// Something is wrong, thus we break out of the loop.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
const auto& bg_flush_arg = bg_flush_args[0];
|
||||
ColumnFamilyData* cfd = bg_flush_arg.cfd_;
|
||||
MutableCFOptions mutable_cf_options = *cfd->GetLatestMutableCFOptions();
|
||||
SuperVersionContext* superversion_context =
|
||||
bg_flush_arg.superversion_context_;
|
||||
Status s = FlushMemTableToOutputFile(
|
||||
cfd, mutable_cf_options, made_progress, job_context, superversion_context,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
log_buffer, thread_pri);
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -395,7 +385,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
all_mutable_cf_options.emplace_back(*cfd->GetLatestMutableCFOptions());
|
||||
const MutableCFOptions& mutable_cf_options = all_mutable_cf_options.back();
|
||||
const uint64_t* max_memtable_id = &(bg_flush_args[i].max_memtable_id_);
|
||||
uint64_t max_memtable_id = bg_flush_args[i].max_memtable_id_;
|
||||
jobs.emplace_back(new FlushJob(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
max_memtable_id, file_options_for_compaction_, versions_.get(), &mutex_,
|
||||
@@ -404,7 +394,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri, io_tracer_, db_id_, db_session_id_));
|
||||
thread_pri, io_tracer_, db_id_, db_session_id_,
|
||||
cfd->GetFullHistoryTsLow()));
|
||||
jobs.back()->PickMemTable();
|
||||
}
|
||||
|
||||
@@ -581,7 +572,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
|
||||
s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &mutex_, tmp_file_meta,
|
||||
versions_.get(), &logs_with_prep_tracker_, &mutex_, tmp_file_meta,
|
||||
&job_context->memtables_to_free, directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
|
||||
@@ -641,9 +632,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
error_handler_.GetBGError().ok()) {
|
||||
Status new_bg_error =
|
||||
Status::SpaceLimit("Max allowed space was reached");
|
||||
// Should Handle this error?
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(new_bg_error,
|
||||
BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -660,24 +650,23 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// be pessimistic and try write to a new MANIFEST.
|
||||
// TODO: distinguish between MANIFEST write and CURRENT renaming
|
||||
if (!versions_->io_status().ok()) {
|
||||
// Should Handle this error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kManifestWrite)
|
||||
.PermitUncheckedError();
|
||||
if (total_log_size_ > 0) {
|
||||
// If the WAL is empty, we use different error reason
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
} else {
|
||||
error_handler_.SetBGError(io_s,
|
||||
BackgroundErrorReason::kManifestWriteNoWAL);
|
||||
}
|
||||
} else if (total_log_size_ > 0) {
|
||||
// Should Handle this error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
// If the WAL is empty, we use different error reason
|
||||
// Should Handle this error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlushNoWAL);
|
||||
}
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
// Should Handle this error?
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(new_bg_error, BackgroundErrorReason::kFlush);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -777,7 +766,41 @@ void DBImpl::NotifyOnFlushCompleted(
|
||||
|
||||
Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
const Slice* begin_without_ts,
|
||||
const Slice* end_without_ts) {
|
||||
const Comparator* const ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
if (ts_sz == 0) {
|
||||
return CompactRangeInternal(options, column_family, begin_without_ts,
|
||||
end_without_ts);
|
||||
}
|
||||
|
||||
std::string begin_str;
|
||||
std::string end_str;
|
||||
|
||||
// CompactRange compact all keys: [begin, end] inclusively. Add maximum
|
||||
// timestamp to include all `begin` keys, and add minimal timestamp to include
|
||||
// all `end` keys.
|
||||
if (begin_without_ts != nullptr) {
|
||||
AppendKeyWithMaxTimestamp(&begin_str, *begin_without_ts, ts_sz);
|
||||
}
|
||||
if (end_without_ts != nullptr) {
|
||||
AppendKeyWithMinTimestamp(&end_str, *end_without_ts, ts_sz);
|
||||
}
|
||||
Slice begin(begin_str);
|
||||
Slice end(end_str);
|
||||
|
||||
Slice* begin_with_ts = begin_without_ts ? &begin : nullptr;
|
||||
Slice* end_with_ts = end_without_ts ? &end : nullptr;
|
||||
|
||||
return CompactRangeInternal(options, column_family, begin_with_ts,
|
||||
end_with_ts);
|
||||
}
|
||||
|
||||
Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
|
||||
@@ -792,7 +815,9 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
// changes to RangesOverlapWithMemtables.
|
||||
Range range(*begin, *end);
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
cfd->RangesOverlapWithMemtables({range}, super_version, &flush_needed);
|
||||
cfd->RangesOverlapWithMemtables({range}, super_version,
|
||||
immutable_db_options_.allow_data_in_errors,
|
||||
&flush_needed);
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
|
||||
@@ -950,7 +975,8 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
s = ReFitLevel(cfd, final_output_level, options.target_level);
|
||||
TEST_SYNC_POINT("DBImpl::CompactRange:PostRefitLevel");
|
||||
// ContinueBackgroundWork always return Status::OK().
|
||||
assert(ContinueBackgroundWork().ok());
|
||||
Status temp_s = ContinueBackgroundWork();
|
||||
assert(temp_s.ok());
|
||||
}
|
||||
EnableManualCompaction();
|
||||
}
|
||||
@@ -1146,13 +1172,15 @@ Status DBImpl::CompactFilesImpl(
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
file_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()), stats_, &mutex_,
|
||||
&error_handler_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()),
|
||||
GetDataDir(c->column_family_data(), 0), stats_, &mutex_, &error_handler_,
|
||||
snapshot_seqs, earliest_write_conflict_snapshot, snapshot_checker,
|
||||
table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
&manual_compaction_paused_, db_id_, db_session_id_);
|
||||
&manual_compaction_paused_, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow());
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -1216,11 +1244,9 @@ Status DBImpl::CompactFilesImpl(
|
||||
job_context->job_id, status.ToString().c_str());
|
||||
IOStatus io_s = compaction_job.io_status();
|
||||
if (!io_s.ok()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kCompaction)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kCompaction);
|
||||
} else {
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1689,8 +1715,9 @@ void DBImpl::GenerateFlushRequest(const autovector<ColumnFamilyData*>& cfds,
|
||||
Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
const FlushOptions& flush_options,
|
||||
FlushReason flush_reason, bool writes_stopped) {
|
||||
// This method should not be called if atomic_flush is true.
|
||||
assert(!immutable_db_options_.atomic_flush);
|
||||
Status s;
|
||||
uint64_t flush_memtable_id = 0;
|
||||
if (!flush_options.allow_write_stall) {
|
||||
bool flush_needed = true;
|
||||
s = WaitUntilFlushWouldNotStallWrites(cfd, &flush_needed);
|
||||
@@ -1700,7 +1727,8 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
}
|
||||
|
||||
FlushRequest flush_req;
|
||||
autovector<FlushRequest> flush_reqs;
|
||||
autovector<uint64_t> memtable_ids_to_wait;
|
||||
{
|
||||
WriteContext context;
|
||||
InstrumentedMutexLock guard_lock(&mutex_);
|
||||
@@ -1715,18 +1743,24 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
}
|
||||
WaitForPendingWrites();
|
||||
|
||||
if (!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load()) {
|
||||
if (flush_reason != FlushReason::kErrorRecoveryRetryFlush) {
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
} else {
|
||||
assert(cfd->imm()->NumNotFlushed() > 0);
|
||||
}
|
||||
if (flush_reason != FlushReason::kErrorRecoveryRetryFlush &&
|
||||
(!cfd->mem()->IsEmpty() || !cached_recoverable_state_empty_.load())) {
|
||||
// Note that, when flush reason is kErrorRecoveryRetryFlush, during the
|
||||
// auto retry resume, we want to avoid creating new small memtables.
|
||||
// Therefore, SwitchMemtable will not be called. Also, since ResumeImpl
|
||||
// will iterate through all the CFs and call FlushMemtable during auto
|
||||
// retry resume, it is possible that in some CFs,
|
||||
// cfd->imm()->NumNotFlushed() = 0. In this case, so no flush request will
|
||||
// be created and scheduled, status::OK() will be returned.
|
||||
s = SwitchMemtable(cfd, &context);
|
||||
}
|
||||
const uint64_t flush_memtable_id = port::kMaxUint64;
|
||||
if (s.ok()) {
|
||||
if (cfd->imm()->NumNotFlushed() != 0 || !cfd->mem()->IsEmpty() ||
|
||||
!cached_recoverable_state_empty_.load()) {
|
||||
flush_memtable_id = cfd->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd, flush_memtable_id);
|
||||
FlushRequest req{{cfd, flush_memtable_id}};
|
||||
flush_reqs.emplace_back(std::move(req));
|
||||
memtable_ids_to_wait.emplace_back(cfd->imm()->GetLatestMemTableID());
|
||||
}
|
||||
if (immutable_db_options_.persist_stats_to_disk &&
|
||||
flush_reason != FlushReason::kErrorRecoveryRetryFlush) {
|
||||
@@ -1752,15 +1786,19 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
"to avoid holding old logs",
|
||||
cfd->GetName().c_str());
|
||||
s = SwitchMemtable(cfd_stats, &context);
|
||||
flush_memtable_id = cfd_stats->imm()->GetLatestMemTableID();
|
||||
flush_req.emplace_back(cfd_stats, flush_memtable_id);
|
||||
FlushRequest req{{cfd_stats, flush_memtable_id}};
|
||||
flush_reqs.emplace_back(std::move(req));
|
||||
memtable_ids_to_wait.emplace_back(
|
||||
cfd->imm()->GetLatestMemTableID());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok() && !flush_req.empty()) {
|
||||
for (auto& elem : flush_req) {
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
|
||||
if (s.ok() && !flush_reqs.empty()) {
|
||||
for (const auto& req : flush_reqs) {
|
||||
assert(req.size() == 1);
|
||||
ColumnFamilyData* loop_cfd = req[0].first;
|
||||
loop_cfd->imm()->FlushRequested();
|
||||
}
|
||||
// If the caller wants to wait for this flush to complete, it indicates
|
||||
@@ -1768,12 +1806,15 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
// other threads which may drop the column family concurrently.
|
||||
// Therefore, we increase the cfd's ref count.
|
||||
if (flush_options.wait) {
|
||||
for (auto& elem : flush_req) {
|
||||
ColumnFamilyData* loop_cfd = elem.first;
|
||||
for (const auto& req : flush_reqs) {
|
||||
assert(req.size() == 1);
|
||||
ColumnFamilyData* loop_cfd = req[0].first;
|
||||
loop_cfd->Ref();
|
||||
}
|
||||
}
|
||||
SchedulePendingFlush(flush_req, flush_reason);
|
||||
for (const auto& req : flush_reqs) {
|
||||
SchedulePendingFlush(req, flush_reason);
|
||||
}
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
|
||||
@@ -1789,9 +1830,11 @@ Status DBImpl::FlushMemTable(ColumnFamilyData* cfd,
|
||||
if (s.ok() && flush_options.wait) {
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
autovector<const uint64_t*> flush_memtable_ids;
|
||||
for (auto& iter : flush_req) {
|
||||
cfds.push_back(iter.first);
|
||||
flush_memtable_ids.push_back(&(iter.second));
|
||||
assert(flush_reqs.size() == memtable_ids_to_wait.size());
|
||||
for (size_t i = 0; i < flush_reqs.size(); ++i) {
|
||||
assert(flush_reqs[i].size() == 1);
|
||||
cfds.push_back(flush_reqs[i][0].first);
|
||||
flush_memtable_ids.push_back(&(memtable_ids_to_wait[i]));
|
||||
}
|
||||
s = WaitForFlushMemTables(
|
||||
cfds, flush_memtable_ids,
|
||||
@@ -2216,6 +2259,17 @@ DBImpl::FlushRequest DBImpl::PopFirstFromFlushQueue() {
|
||||
assert(!flush_queue_.empty());
|
||||
FlushRequest flush_req = flush_queue_.front();
|
||||
flush_queue_.pop_front();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
assert(flush_req.size() == 1);
|
||||
}
|
||||
for (const auto& elem : flush_req) {
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
ColumnFamilyData* cfd = elem.first;
|
||||
assert(cfd);
|
||||
assert(cfd->queued_for_flush());
|
||||
cfd->set_queued_for_flush(false);
|
||||
}
|
||||
}
|
||||
// TODO: need to unset flush reason?
|
||||
return flush_req;
|
||||
}
|
||||
@@ -2248,19 +2302,36 @@ ColumnFamilyData* DBImpl::PickCompactionFromQueue(
|
||||
|
||||
void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req,
|
||||
FlushReason flush_reason) {
|
||||
mutex_.AssertHeld();
|
||||
if (flush_req.empty()) {
|
||||
return;
|
||||
}
|
||||
for (auto& iter : flush_req) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
cfd->Ref();
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
// For the non-atomic flush case, we never schedule multiple column
|
||||
// families in the same flush request.
|
||||
assert(flush_req.size() == 1);
|
||||
ColumnFamilyData* cfd = flush_req[0].first;
|
||||
assert(cfd);
|
||||
if (!cfd->queued_for_flush() && cfd->imm()->IsFlushPending()) {
|
||||
cfd->Ref();
|
||||
cfd->set_queued_for_flush(true);
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
}
|
||||
} else {
|
||||
for (auto& iter : flush_req) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
cfd->Ref();
|
||||
cfd->SetFlushReason(flush_reason);
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
}
|
||||
++unscheduled_flushes_;
|
||||
flush_queue_.push_back(flush_req);
|
||||
}
|
||||
|
||||
void DBImpl::SchedulePendingCompaction(ColumnFamilyData* cfd) {
|
||||
mutex_.AssertHeld();
|
||||
if (!cfd->queued_for_compaction() && cfd->NeedsCompaction()) {
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
@@ -2555,7 +2626,8 @@ void DBImpl::BackgroundCallCompaction(PrepickedCompaction* prepicked_compaction,
|
||||
// failure). Thus, we force full scan in FindObsoleteFiles()
|
||||
FindObsoleteFiles(&job_context, !s.ok() && !s.IsShutdownInProgress() &&
|
||||
!s.IsManualCompactionPaused() &&
|
||||
!s.IsColumnFamilyDropped());
|
||||
!s.IsColumnFamilyDropped() &&
|
||||
!s.IsBusy());
|
||||
TEST_SYNC_POINT("DBImpl::BackgroundCallCompaction:FoundObsoleteFiles");
|
||||
|
||||
// delete unnecessary files if any, this is done outside the mutex
|
||||
@@ -2953,14 +3025,15 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
job_context->job_id, c.get(), immutable_db_options_,
|
||||
file_options_for_compaction_, versions_.get(), &shutting_down_,
|
||||
preserve_deletes_seqnum_.load(), log_buffer, directories_.GetDbDir(),
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()), stats_,
|
||||
&mutex_, &error_handler_, snapshot_seqs,
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks,
|
||||
GetDataDir(c->column_family_data(), c->output_path_id()),
|
||||
GetDataDir(c->column_family_data(), 0), stats_, &mutex_,
|
||||
&error_handler_, snapshot_seqs, earliest_write_conflict_snapshot,
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr, db_id_,
|
||||
db_session_id_);
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow());
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
@@ -3025,10 +3098,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
auto err_reason = versions_->io_status().ok()
|
||||
? BackgroundErrorReason::kCompaction
|
||||
: BackgroundErrorReason::kManifestWrite;
|
||||
error_handler_.SetBGError(io_s, err_reason).PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, err_reason);
|
||||
} else {
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kCompaction);
|
||||
}
|
||||
if (c != nullptr && !is_manual && !error_handler_.IsBGWorkStopped()) {
|
||||
// Put this cfd back in the compaction queue so we can retry after some
|
||||
|
||||
@@ -22,12 +22,13 @@ uint64_t DBImpl::TEST_GetLevel0TotalSize() {
|
||||
return default_cf_handle_->cfd()->current()->storage_info()->NumLevelBytes(0);
|
||||
}
|
||||
|
||||
void DBImpl::TEST_SwitchWAL() {
|
||||
Status DBImpl::TEST_SwitchWAL() {
|
||||
WriteContext write_context;
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
void* writer = TEST_BeginWrite();
|
||||
SwitchWAL(&write_context);
|
||||
auto s = SwitchWAL(&write_context);
|
||||
TEST_EndWrite(writer);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool DBImpl::TEST_WALBufferIsEmpty(bool lock) {
|
||||
@@ -170,7 +171,7 @@ Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
while ((bg_bottom_compaction_scheduled_ || bg_compaction_scheduled_ ||
|
||||
bg_flush_scheduled_ ||
|
||||
(wait_unscheduled && unscheduled_compactions_)) &&
|
||||
(error_handler_.GetBGError() == Status::OK())) {
|
||||
(error_handler_.GetBGError().ok())) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
return error_handler_.GetBGError();
|
||||
|
||||
+164
-18
@@ -6,16 +6,17 @@
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
#include "db/db_impl/db_impl.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/event_helpers.h"
|
||||
#include "db/memtable_list.h"
|
||||
#include "file/file_util.h"
|
||||
#include "file/filename.h"
|
||||
#include "file/sst_file_manager_impl.h"
|
||||
#include "port/port.h"
|
||||
#include "util/autovector.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -319,7 +320,7 @@ void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
const_cast<std::string*>(&fname));
|
||||
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kBlobFile || type == kLogFile) {
|
||||
if (type == kTableFile || type == kBlobFile || type == kWalFile) {
|
||||
file_deletion_status =
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
@@ -466,7 +467,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
|
||||
bool keep = true;
|
||||
switch (type) {
|
||||
case kLogFile:
|
||||
case kWalFile:
|
||||
keep = ((number >= state.log_number) ||
|
||||
(number == state.prev_log_number) ||
|
||||
(log_recycle_files_set.find(number) !=
|
||||
@@ -546,7 +547,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
(type == kWalFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
fname = dir_to_sync +
|
||||
((!dir_to_sync.empty() && dir_to_sync.back() == '/') ||
|
||||
(!to_delete.empty() && to_delete.front() == '/')
|
||||
@@ -556,7 +557,7 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (type == kLogFile && (immutable_db_options_.wal_ttl_seconds > 0 ||
|
||||
if (type == kWalFile && (immutable_db_options_.wal_ttl_seconds > 0 ||
|
||||
immutable_db_options_.wal_size_limit_mb > 0)) {
|
||||
wal_manager_.ArchiveWALFile(fname, number);
|
||||
continue;
|
||||
@@ -658,13 +659,15 @@ uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
|
||||
// we must look through the memtables for two phase transactions
|
||||
// that have been committed but not yet flushed
|
||||
std::unordered_set<MemTable*> memtables_to_flush_set(
|
||||
memtables_to_flush.begin(), memtables_to_flush.end());
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped() || loop_cfd == cfd_to_flush) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto log = loop_cfd->imm()->PrecomputeMinLogContainingPrepSection(
|
||||
memtables_to_flush);
|
||||
&memtables_to_flush_set);
|
||||
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
@@ -680,16 +683,41 @@ uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
return min_log;
|
||||
}
|
||||
|
||||
uint64_t PrecomputeMinLogNumberToKeep(
|
||||
uint64_t FindMinPrepLogReferencedByMemTable(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
std::unordered_set<ColumnFamilyData*> cfds_to_flush_set(cfds_to_flush.begin(),
|
||||
cfds_to_flush.end());
|
||||
std::unordered_set<MemTable*> memtables_to_flush_set;
|
||||
for (const autovector<MemTable*>* memtables : memtables_to_flush) {
|
||||
memtables_to_flush_set.insert(memtables->begin(), memtables->end());
|
||||
}
|
||||
for (auto loop_cfd : *vset->GetColumnFamilySet()) {
|
||||
if (loop_cfd->IsDropped() || cfds_to_flush_set.count(loop_cfd)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto log = loop_cfd->imm()->PrecomputeMinLogContainingPrepSection(
|
||||
&memtables_to_flush_set);
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
|
||||
log = loop_cfd->mem()->GetMinLogContainingPrepSection();
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
autovector<VersionEdit*> edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
const autovector<VersionEdit*>& edit_list) {
|
||||
assert(vset != nullptr);
|
||||
assert(prep_tracker != nullptr);
|
||||
// Calculate updated min_log_number_to_keep
|
||||
// Since the function should only be called in 2pc mode, log number in
|
||||
// the version edit should be sufficient.
|
||||
|
||||
// Precompute the min log number containing unflushed data for the column
|
||||
// family being flushed (`cfd_to_flush`).
|
||||
@@ -713,6 +741,58 @@ uint64_t PrecomputeMinLogNumberToKeep(
|
||||
min_log_number_to_keep =
|
||||
std::min(cf_min_log_number_to_keep, min_log_number_to_keep);
|
||||
}
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
uint64_t PrecomputeMinLogNumberToKeepNon2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists) {
|
||||
assert(vset != nullptr);
|
||||
assert(!cfds_to_flush.empty());
|
||||
assert(cfds_to_flush.size() == edit_lists.size());
|
||||
|
||||
uint64_t min_log_number_to_keep = port::kMaxUint64;
|
||||
for (const auto& edit_list : edit_lists) {
|
||||
uint64_t log = 0;
|
||||
for (const auto& e : edit_list) {
|
||||
if (e->HasLogNumber()) {
|
||||
log = std::max(log, e->GetLogNumber());
|
||||
}
|
||||
}
|
||||
if (log != 0) {
|
||||
min_log_number_to_keep = std::min(min_log_number_to_keep, log);
|
||||
}
|
||||
}
|
||||
if (min_log_number_to_keep == port::kMaxUint64) {
|
||||
min_log_number_to_keep = cfds_to_flush[0]->GetLogNumber();
|
||||
for (size_t i = 1; i < cfds_to_flush.size(); i++) {
|
||||
min_log_number_to_keep =
|
||||
std::min(min_log_number_to_keep, cfds_to_flush[i]->GetLogNumber());
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<const ColumnFamilyData*> flushed_cfds(
|
||||
cfds_to_flush.begin(), cfds_to_flush.end());
|
||||
min_log_number_to_keep =
|
||||
std::min(min_log_number_to_keep,
|
||||
vset->PreComputeMinLogNumberWithUnflushedData(flushed_cfds));
|
||||
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const ColumnFamilyData& cfd_to_flush,
|
||||
const autovector<VersionEdit*>& edit_list,
|
||||
const autovector<MemTable*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
assert(vset != nullptr);
|
||||
assert(prep_tracker != nullptr);
|
||||
// Calculate updated min_log_number_to_keep
|
||||
// Since the function should only be called in 2pc mode, log number in
|
||||
// the version edit should be sufficient.
|
||||
|
||||
uint64_t min_log_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfd_to_flush, edit_list);
|
||||
|
||||
// if are 2pc we must consider logs containing prepared
|
||||
// sections of outstanding transactions.
|
||||
@@ -741,7 +821,75 @@ uint64_t PrecomputeMinLogNumberToKeep(
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
Status DBImpl::FinishBestEffortsRecovery() {
|
||||
uint64_t PrecomputeMinLogNumberToKeep2PC(
|
||||
VersionSet* vset, const autovector<ColumnFamilyData*>& cfds_to_flush,
|
||||
const autovector<autovector<VersionEdit*>>& edit_lists,
|
||||
const autovector<const autovector<MemTable*>*>& memtables_to_flush,
|
||||
LogsWithPrepTracker* prep_tracker) {
|
||||
assert(vset != nullptr);
|
||||
assert(prep_tracker != nullptr);
|
||||
assert(cfds_to_flush.size() == edit_lists.size());
|
||||
assert(cfds_to_flush.size() == memtables_to_flush.size());
|
||||
|
||||
uint64_t min_log_number_to_keep =
|
||||
PrecomputeMinLogNumberToKeepNon2PC(vset, cfds_to_flush, edit_lists);
|
||||
|
||||
uint64_t min_log_in_prep_heap =
|
||||
prep_tracker->FindMinLogContainingOutstandingPrep();
|
||||
|
||||
if (min_log_in_prep_heap != 0 &&
|
||||
min_log_in_prep_heap < min_log_number_to_keep) {
|
||||
min_log_number_to_keep = min_log_in_prep_heap;
|
||||
}
|
||||
|
||||
uint64_t min_log_refed_by_mem = FindMinPrepLogReferencedByMemTable(
|
||||
vset, cfds_to_flush, memtables_to_flush);
|
||||
|
||||
if (min_log_refed_by_mem != 0 &&
|
||||
min_log_refed_by_mem < min_log_number_to_keep) {
|
||||
min_log_number_to_keep = min_log_refed_by_mem;
|
||||
}
|
||||
|
||||
return min_log_number_to_keep;
|
||||
}
|
||||
|
||||
Status DBImpl::SetDBId() {
|
||||
Status s;
|
||||
// Happens when immutable_db_options_.write_dbid_to_manifest is set to true
|
||||
// the very first time.
|
||||
if (db_id_.empty()) {
|
||||
// Check for the IDENTITY file and create it if not there.
|
||||
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
|
||||
// Typically Identity file is created in NewDB() and for some reason if
|
||||
// it is no longer available then at this point DB ID is not in Identity
|
||||
// file or Manifest.
|
||||
if (s.IsNotFound()) {
|
||||
s = SetIdentityFile(env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
s = GetDbIdentityFromIdentityFile(&db_id_);
|
||||
if (immutable_db_options_.write_dbid_to_manifest && s.ok()) {
|
||||
VersionEdit edit;
|
||||
edit.SetDBId(db_id_);
|
||||
Options options;
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
versions_->db_id_ = db_id_;
|
||||
s = versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options, &edit, &mutex_, nullptr,
|
||||
/* new_descriptor_log */ false);
|
||||
}
|
||||
} else {
|
||||
s = SetIdentityFile(env_, dbname_, db_id_);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::DeleteUnreferencedSstFiles() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
@@ -797,8 +945,6 @@ Status DBImpl::FinishBestEffortsRecovery() {
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
ColumnFamilyData* default_cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
assert(default_cfd);
|
||||
// Even if new_descriptor_log is false, we will still switch to a new
|
||||
// MANIFEST and update CURRENT file, since this is in recovery.
|
||||
s = versions_->LogAndApply(
|
||||
default_cfd, *default_cfd->GetLatestMutableCFOptions(), &edit, &mutex_,
|
||||
directories_.GetDbDir(), /*new_descriptor_log*/ false);
|
||||
|
||||
+114
-103
@@ -401,7 +401,7 @@ Status DBImpl::Recover(
|
||||
}
|
||||
for (const std::string& file : files_in_dbname) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile; // initialize
|
||||
FileType type = kWalFile; // initialize
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
// Found MANIFEST (descriptor log), thus best-efforts recovery does
|
||||
// not have to treat the db as empty.
|
||||
@@ -479,42 +479,14 @@ Status DBImpl::Recover(
|
||||
// TryRecover may delete previous column_family_set_.
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
s = FinishBestEffortsRecovery();
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
// Happens when immutable_db_options_.write_dbid_to_manifest is set to true
|
||||
// the very first time.
|
||||
if (db_id_.empty()) {
|
||||
// Check for the IDENTITY file and create it if not there.
|
||||
s = fs_->FileExists(IdentityFileName(dbname_), IOOptions(), nullptr);
|
||||
// Typically Identity file is created in NewDB() and for some reason if
|
||||
// it is no longer available then at this point DB ID is not in Identity
|
||||
// file or Manifest.
|
||||
if (s.IsNotFound()) {
|
||||
s = SetIdentityFile(env_, dbname_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
} else if (!s.ok()) {
|
||||
assert(s.IsIOError());
|
||||
return s;
|
||||
}
|
||||
s = GetDbIdentityFromIdentityFile(&db_id_);
|
||||
if (immutable_db_options_.write_dbid_to_manifest && s.ok()) {
|
||||
VersionEdit edit;
|
||||
edit.SetDBId(db_id_);
|
||||
Options options;
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
versions_->db_id_ = db_id_;
|
||||
s = versions_->LogAndApply(versions_->GetColumnFamilySet()->GetDefault(),
|
||||
mutable_cf_options, &edit, &mutex_, nullptr,
|
||||
false);
|
||||
}
|
||||
} else {
|
||||
s = SetIdentityFile(env_, dbname_, db_id_);
|
||||
s = SetDBId();
|
||||
if (s.ok() && !read_only) {
|
||||
s = DeleteUnreferencedSstFiles();
|
||||
}
|
||||
|
||||
if (immutable_db_options_.paranoid_checks && s.ok()) {
|
||||
@@ -536,7 +508,7 @@ Status DBImpl::Recover(
|
||||
|
||||
std::vector<std::string> files_in_wal_dir;
|
||||
if (s.ok()) {
|
||||
// Initial max_total_in_memory_state_ before recovery logs. Log recovery
|
||||
// Initial max_total_in_memory_state_ before recovery wals. Log recovery
|
||||
// may check this value to decide whether to flush.
|
||||
max_total_in_memory_state_ = 0;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -571,32 +543,53 @@ Status DBImpl::Recover(
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> logs;
|
||||
std::unordered_map<uint64_t, std::string> wal_files;
|
||||
for (const auto& file : files_in_wal_dir) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(file, &number, &type) && type == kLogFile) {
|
||||
if (ParseFileName(file, &number, &type) && type == kWalFile) {
|
||||
if (is_new_db) {
|
||||
return Status::Corruption(
|
||||
"While creating a new Db, wal_dir contains "
|
||||
"existing log file: ",
|
||||
file);
|
||||
} else {
|
||||
logs.push_back(number);
|
||||
wal_files[number] =
|
||||
LogFileName(immutable_db_options_.wal_dir, number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logs.size() > 0) {
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
// Verify WALs in MANIFEST.
|
||||
s = versions_->GetWalSet().CheckWals(env_, wal_files);
|
||||
} // else since best effort recovery does not recover from WALs, no need
|
||||
// to check WALs.
|
||||
} else if (!versions_->GetWalSet().GetWals().empty()) {
|
||||
// Tracking is disabled, clear previously tracked WALs from MANIFEST,
|
||||
// otherwise, in the future, if WAL tracking is enabled again,
|
||||
// since the WALs deleted when WAL tracking is disabled are not persisted
|
||||
// into MANIFEST, WAL check may fail.
|
||||
VersionEdit edit;
|
||||
WalNumber max_wal_number =
|
||||
versions_->GetWalSet().GetWals().rbegin()->first;
|
||||
edit.DeleteWalsBefore(max_wal_number + 1);
|
||||
s = versions_->LogAndApplyToDefaultColumnFamily(&edit, &mutex_);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (!wal_files.empty()) {
|
||||
if (error_if_wal_file_exists) {
|
||||
return Status::Corruption(
|
||||
"The db was opened in readonly mode with error_if_wal_file_exists"
|
||||
"flag but a WAL file already exists");
|
||||
} else if (error_if_data_exists_in_wals) {
|
||||
for (auto& log : logs) {
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, log);
|
||||
for (auto& wal_file : wal_files) {
|
||||
uint64_t bytes;
|
||||
s = env_->GetFileSize(fname, &bytes);
|
||||
s = env_->GetFileSize(wal_file.second, &bytes);
|
||||
if (s.ok()) {
|
||||
if (bytes > 0) {
|
||||
return Status::Corruption(
|
||||
@@ -608,13 +601,19 @@ Status DBImpl::Recover(
|
||||
}
|
||||
}
|
||||
|
||||
if (!logs.empty()) {
|
||||
// Recover in the order in which the logs were generated
|
||||
std::sort(logs.begin(), logs.end());
|
||||
bool corrupted_log_found = false;
|
||||
s = RecoverLogFiles(logs, &next_sequence, read_only,
|
||||
&corrupted_log_found);
|
||||
if (corrupted_log_found && recovered_seq != nullptr) {
|
||||
if (!wal_files.empty()) {
|
||||
// Recover in the order in which the wals were generated
|
||||
std::vector<uint64_t> wals;
|
||||
wals.reserve(wal_files.size());
|
||||
for (const auto& wal_file : wal_files) {
|
||||
wals.push_back(wal_file.first);
|
||||
}
|
||||
std::sort(wals.begin(), wals.end());
|
||||
|
||||
bool corrupted_wal_found = false;
|
||||
s = RecoverLogFiles(wals, &next_sequence, read_only,
|
||||
&corrupted_wal_found);
|
||||
if (corrupted_wal_found && recovered_seq != nullptr) {
|
||||
*recovered_seq = next_sequence;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
@@ -767,10 +766,10 @@ Status DBImpl::InitPersistStatsColumnFamily() {
|
||||
return s;
|
||||
}
|
||||
|
||||
// REQUIRES: log_numbers are sorted in ascending order
|
||||
Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// REQUIRES: wal_numbers are sorted in ascending order
|
||||
Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
SequenceNumber* next_sequence, bool read_only,
|
||||
bool* corrupted_log_found) {
|
||||
bool* corrupted_wal_found) {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Env* env;
|
||||
Logger* info_log;
|
||||
@@ -800,10 +799,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
auto stream = event_logger_.Log();
|
||||
stream << "job" << job_id << "event"
|
||||
<< "recovery_started";
|
||||
stream << "log_files";
|
||||
stream << "wal_files";
|
||||
stream.StartArray();
|
||||
for (auto log_number : log_numbers) {
|
||||
stream << log_number;
|
||||
for (auto wal_number : wal_numbers) {
|
||||
stream << wal_number;
|
||||
}
|
||||
stream.EndArray();
|
||||
}
|
||||
@@ -826,25 +825,25 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
bool stop_replay_by_wal_filter = false;
|
||||
bool stop_replay_for_corruption = false;
|
||||
bool flushed = false;
|
||||
uint64_t corrupted_log_number = kMaxSequenceNumber;
|
||||
uint64_t min_log_number = MinLogNumberToKeep();
|
||||
for (auto log_number : log_numbers) {
|
||||
if (log_number < min_log_number) {
|
||||
uint64_t corrupted_wal_number = kMaxSequenceNumber;
|
||||
uint64_t min_wal_number = MinLogNumberToKeep();
|
||||
for (auto wal_number : wal_numbers) {
|
||||
if (wal_number < min_wal_number) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Skipping log #%" PRIu64
|
||||
" since it is older than min log to keep #%" PRIu64,
|
||||
log_number, min_log_number);
|
||||
wal_number, min_wal_number);
|
||||
continue;
|
||||
}
|
||||
// The previous incarnation may not have written any MANIFEST
|
||||
// records after allocating this log number. So we manually
|
||||
// update the file number allocation counter in VersionSet.
|
||||
versions_->MarkFileNumberUsed(log_number);
|
||||
versions_->MarkFileNumberUsed(wal_number);
|
||||
// Open the log file
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, log_number);
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, wal_number);
|
||||
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Recovering log #%" PRIu64 " mode %d", log_number,
|
||||
"Recovering log #%" PRIu64 " mode %d", wal_number,
|
||||
static_cast<int>(immutable_db_options_.wal_recovery_mode));
|
||||
auto logFileDropped = [this, &fname]() {
|
||||
uint64_t bytes;
|
||||
@@ -897,7 +896,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// to be skipped instead of propagating bad information (like overly
|
||||
// large sequence numbers).
|
||||
log::Reader reader(immutable_db_options_.info_log, std::move(file_reader),
|
||||
&reporter, true /*checksum*/, log_number);
|
||||
&reporter, true /*checksum*/, wal_number);
|
||||
|
||||
// Determine if we should tolerate incomplete records at the tail end of the
|
||||
// Read all the records and add to a memtable
|
||||
@@ -945,7 +944,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
|
||||
WalFilter::WalProcessingOption wal_processing_option =
|
||||
immutable_db_options_.wal_filter->LogRecordFound(
|
||||
log_number, fname, batch, &new_batch, &batch_changed);
|
||||
wal_number, fname, batch, &new_batch, &batch_changed);
|
||||
|
||||
switch (wal_processing_option) {
|
||||
case WalFilter::WalProcessingOption::kContinueProcessing:
|
||||
@@ -997,7 +996,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
" mode %d log filter %s returned "
|
||||
"more records (%d) than original (%d) which is not allowed. "
|
||||
"Aborting recovery.",
|
||||
log_number,
|
||||
wal_number,
|
||||
static_cast<int>(immutable_db_options_.wal_recovery_mode),
|
||||
immutable_db_options_.wal_filter->Name(), new_count,
|
||||
original_count);
|
||||
@@ -1024,7 +1023,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
bool has_valid_writes = false;
|
||||
status = WriteBatchInternal::InsertInto(
|
||||
&batch, column_family_memtables_.get(), &flush_scheduler_,
|
||||
&trim_history_scheduler_, true, log_number, this,
|
||||
&trim_history_scheduler_, true, wal_number, this,
|
||||
false /* concurrent_memtable_writes */, next_sequence,
|
||||
&has_valid_writes, seq_per_batch_, batch_per_txn_);
|
||||
MaybeIgnoreError(&status);
|
||||
@@ -1044,7 +1043,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
cfd->UnrefAndTryDelete();
|
||||
// If this asserts, it means that InsertInto failed in
|
||||
// filtering updates to already-flushed column families
|
||||
assert(cfd->GetLogNumber() <= log_number);
|
||||
assert(cfd->GetLogNumber() <= wal_number);
|
||||
auto iter = version_edits.find(cfd->GetID());
|
||||
assert(iter != version_edits.end());
|
||||
VersionEdit* edit = &iter->second;
|
||||
@@ -1081,21 +1080,21 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
" seq #%" PRIu64
|
||||
". %s. This likely mean loss of synced WAL, "
|
||||
"thus recovery fails.",
|
||||
log_number, *next_sequence,
|
||||
wal_number, *next_sequence,
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
// We should ignore the error but not continue replaying
|
||||
status = Status::OK();
|
||||
stop_replay_for_corruption = true;
|
||||
corrupted_log_number = log_number;
|
||||
if (corrupted_log_found != nullptr) {
|
||||
*corrupted_log_found = true;
|
||||
corrupted_wal_number = wal_number;
|
||||
if (corrupted_wal_found != nullptr) {
|
||||
*corrupted_wal_found = true;
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Point in time recovered to log #%" PRIu64
|
||||
" seq #%" PRIu64,
|
||||
log_number, *next_sequence);
|
||||
wal_number, *next_sequence);
|
||||
} else {
|
||||
assert(immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords ||
|
||||
@@ -1121,7 +1120,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// corruption. This could during PIT recovery when the WAL is corrupted and
|
||||
// some (but not all) CFs are flushed
|
||||
// Exclude the PIT case where no log is dropped after the corruption point.
|
||||
// This is to cover the case for empty logs after corrupted log, in which we
|
||||
// This is to cover the case for empty wals after corrupted log, in which we
|
||||
// don't reset stop_replay_for_corruption.
|
||||
if (stop_replay_for_corruption == true &&
|
||||
(immutable_db_options_.wal_recovery_mode ==
|
||||
@@ -1129,7 +1128,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords)) {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->GetLogNumber() > corrupted_log_number) {
|
||||
if (cfd->GetLogNumber() > corrupted_wal_number) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"Column family inconsistency: SST file contains data"
|
||||
" beyond the point of corruption.");
|
||||
@@ -1144,16 +1143,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
if (!read_only) {
|
||||
// no need to refcount since client still doesn't have access
|
||||
// to the DB and can not drop column families while we iterate
|
||||
auto max_log_number = log_numbers.back();
|
||||
const WalNumber max_wal_number = wal_numbers.back();
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
auto iter = version_edits.find(cfd->GetID());
|
||||
assert(iter != version_edits.end());
|
||||
VersionEdit* edit = &iter->second;
|
||||
|
||||
if (cfd->GetLogNumber() > max_log_number) {
|
||||
if (cfd->GetLogNumber() > max_wal_number) {
|
||||
// Column family cfd has already flushed the data
|
||||
// from all logs. Memtable has to be empty because
|
||||
// we filter the updates based on log_number
|
||||
// from all wals. Memtable has to be empty because
|
||||
// we filter the updates based on wal_number
|
||||
// (in WriteBatch::InsertInto)
|
||||
assert(cfd->mem()->GetFirstSequenceNumber() == 0);
|
||||
assert(edit->NumEntries() == 0);
|
||||
@@ -1185,13 +1184,13 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// Update the log number info in the version edit corresponding to this
|
||||
// column family. Note that the version edits will be written to MANIFEST
|
||||
// together later.
|
||||
// writing log_number in the manifest means that any log file
|
||||
// with number strongly less than (log_number + 1) is already
|
||||
// writing wal_number in the manifest means that any log file
|
||||
// with number strongly less than (wal_number + 1) is already
|
||||
// recovered and should be ignored on next reincarnation.
|
||||
// Since we already recovered max_log_number, we want all logs
|
||||
// with numbers `<= max_log_number` (includes this one) to be ignored
|
||||
// Since we already recovered max_wal_number, we want all wals
|
||||
// with numbers `<= max_wal_number` (includes this one) to be ignored
|
||||
if (flushed || cfd->mem()->GetFirstSequenceNumber() == 0) {
|
||||
edit->SetLogNumber(max_log_number + 1);
|
||||
edit->SetLogNumber(max_wal_number + 1);
|
||||
}
|
||||
}
|
||||
if (status.ok()) {
|
||||
@@ -1199,7 +1198,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
// not actually used. that is because VersionSet assumes
|
||||
// VersionSet::next_file_number_ always to be strictly greater than any
|
||||
// log number
|
||||
versions_->MarkFileNumberUsed(max_log_number + 1);
|
||||
versions_->MarkFileNumberUsed(max_wal_number + 1);
|
||||
|
||||
autovector<ColumnFamilyData*> cfds;
|
||||
autovector<const MutableCFOptions*> cf_opts;
|
||||
@@ -1211,6 +1210,14 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
assert(iter != version_edits.end());
|
||||
edit_lists.push_back({&iter->second});
|
||||
}
|
||||
|
||||
std::unique_ptr<VersionEdit> wal_deletion;
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest) {
|
||||
wal_deletion.reset(new VersionEdit);
|
||||
wal_deletion->DeleteWalsBefore(max_wal_number + 1);
|
||||
edit_lists.back().push_back(wal_deletion.get());
|
||||
}
|
||||
|
||||
// write MANIFEST with update
|
||||
status = versions_->LogAndApply(cfds, cf_opts, edit_lists, &mutex_,
|
||||
directories_.GetDbDir(),
|
||||
@@ -1219,7 +1226,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
}
|
||||
|
||||
if (status.ok() && data_seen && !flushed) {
|
||||
status = RestoreAliveLogFiles(log_numbers);
|
||||
status = RestoreAliveLogFiles(wal_numbers);
|
||||
}
|
||||
|
||||
event_logger_.Log() << "job" << job_id << "event"
|
||||
@@ -1228,8 +1235,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
return status;
|
||||
}
|
||||
|
||||
Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers) {
|
||||
if (log_numbers.empty()) {
|
||||
Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& wal_numbers) {
|
||||
if (wal_numbers.empty()) {
|
||||
return Status::OK();
|
||||
}
|
||||
Status s;
|
||||
@@ -1242,20 +1249,20 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers) {
|
||||
// FindObsoleteFiles()
|
||||
total_log_size_ = 0;
|
||||
log_empty_ = false;
|
||||
for (auto log_number : log_numbers) {
|
||||
LogFileNumberSize log(log_number);
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, log_number);
|
||||
// This gets the appear size of the logs, not including preallocated space.
|
||||
for (auto wal_number : wal_numbers) {
|
||||
LogFileNumberSize log(wal_number);
|
||||
std::string fname = LogFileName(immutable_db_options_.wal_dir, wal_number);
|
||||
// This gets the appear size of the wals, not including preallocated space.
|
||||
s = env_->GetFileSize(fname, &log.size);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
total_log_size_ += log.size;
|
||||
alive_log_files_.push_back(log);
|
||||
// We preallocate space for logs, but then after a crash and restart, those
|
||||
// We preallocate space for wals, but then after a crash and restart, those
|
||||
// preallocated space are not needed anymore. It is likely only the last
|
||||
// log has such preallocated space, so we only truncate for the last log.
|
||||
if (log_number == log_numbers.back()) {
|
||||
if (wal_number == wal_numbers.back()) {
|
||||
std::unique_ptr<FSWritableFile> last_log;
|
||||
Status truncate_status = fs_->ReopenWritableFile(
|
||||
fname,
|
||||
@@ -1272,7 +1279,7 @@ Status DBImpl::RestoreAliveLogFiles(const std::vector<uint64_t>& log_numbers) {
|
||||
// Not a critical error if fail to truncate.
|
||||
if (!truncate_status.ok()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Failed to truncate log #%" PRIu64 ": %s", log_number,
|
||||
"Failed to truncate log #%" PRIu64 ": %s", wal_number,
|
||||
truncate_status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
@@ -1340,7 +1347,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
|
||||
IOStatus io_s;
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), env_, fs_.get(), *cfd->ioptions(),
|
||||
dbname_, versions_.get(), immutable_db_options_, *cfd->ioptions(),
|
||||
mutable_cf_options, file_options_for_compaction_, cfd->table_cache(),
|
||||
iter.get(), std::move(range_del_iters), &meta, &blob_file_additions,
|
||||
cfd->internal_comparator(), cfd->int_tbl_prop_collector_factories(),
|
||||
@@ -1361,6 +1368,9 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
cfd->GetName().c_str(), meta.fd.GetNumber(),
|
||||
meta.fd.GetFileSize(), s.ToString().c_str());
|
||||
mutex_.Lock();
|
||||
|
||||
io_s.PermitUncheckedError(); // TODO(AR) is this correct, or should we
|
||||
// return io_s if not ok()?
|
||||
}
|
||||
}
|
||||
ReleaseFileNumberFromPendingOutputs(pending_outputs_inserted_elem);
|
||||
@@ -1368,7 +1378,6 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
// Note that if file_size is zero, the file has been deleted and
|
||||
// should not be added to the manifest.
|
||||
const bool has_output = meta.fd.GetFileSize() > 0;
|
||||
assert(has_output || blob_file_additions.empty());
|
||||
|
||||
constexpr int level = 0;
|
||||
|
||||
@@ -1388,15 +1397,16 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
|
||||
if (has_output) {
|
||||
stats.bytes_written = meta.fd.GetFileSize();
|
||||
|
||||
const auto& blobs = edit->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files = static_cast<int>(blobs.size()) + 1;
|
||||
stats.num_output_files = 1;
|
||||
}
|
||||
|
||||
const auto& blobs = edit->GetBlobFileAdditions();
|
||||
for (const auto& blob : blobs) {
|
||||
stats.bytes_written += blob.GetTotalBlobBytes();
|
||||
}
|
||||
|
||||
stats.num_output_files += static_cast<int>(blobs.size());
|
||||
|
||||
cfd->internal_stats()->AddCompactionStats(level, Env::Priority::USER, stats);
|
||||
cfd->internal_stats()->AddCFStats(InternalStats::BYTES_FLUSHED,
|
||||
stats.bytes_written);
|
||||
@@ -1556,6 +1566,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
InstrumentedMutexLock wl(&impl->log_write_mutex_);
|
||||
impl->logfile_number_ = new_log_number;
|
||||
assert(new_log != nullptr);
|
||||
assert(impl->logs_.empty());
|
||||
impl->logs_.emplace_back(new_log_number, new_log);
|
||||
}
|
||||
|
||||
@@ -1610,7 +1621,7 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
// the trick we use in kPointInTimeRecovery which assumes the first seq in
|
||||
// the log right after the corrupted log is one larger than the last seq
|
||||
// we read from the logs. To let this trick keep working, we add a dummy
|
||||
// we read from the wals. To let this trick keep working, we add a dummy
|
||||
// entry with the expected sequence to the first log right after recovery.
|
||||
// In non-WritePrepared case also the new log after recovery could be
|
||||
// empty, and thus missing the consecutive seq hint to distinguish
|
||||
|
||||
@@ -83,7 +83,7 @@ Iterator* DBImplReadOnly::NewIterator(const ReadOptions& read_options,
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
read_seq,
|
||||
super_version->current, read_seq,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
@@ -115,7 +115,8 @@ Status DBImplReadOnly::NewIterators(
|
||||
auto* cfd = static_cast_with_check<ColumnFamilyHandleImpl>(cfh)->cfd();
|
||||
auto* sv = cfd->GetSuperVersion()->Ref();
|
||||
auto* db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options, read_seq,
|
||||
env_, read_options, *cfd->ioptions(), sv->mutable_cf_options,
|
||||
sv->current, read_seq,
|
||||
sv->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
sv->version_number, read_callback);
|
||||
auto* internal_iter = NewInternalIterator(
|
||||
|
||||
@@ -112,7 +112,7 @@ Status DBImplSecondary::FindNewLogNumbers(std::vector<uint64_t>* logs) {
|
||||
for (size_t i = 0; i < filenames.size(); i++) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == kLogFile &&
|
||||
if (ParseFileName(filenames[i], &number, &type) && type == kWalFile &&
|
||||
number >= log_number_min) {
|
||||
logs->push_back(number);
|
||||
}
|
||||
@@ -421,7 +421,7 @@ ArenaWrappedDBIter* DBImplSecondary::NewIteratorImpl(
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
auto db_iter = NewArenaWrappedDbIterator(
|
||||
env_, read_options, *cfd->ioptions(), super_version->mutable_cf_options,
|
||||
snapshot,
|
||||
super_version->current, snapshot,
|
||||
super_version->mutable_cf_options.max_sequential_skip_in_iterations,
|
||||
super_version->version_number, read_callback);
|
||||
auto internal_iter = NewInternalIterator(
|
||||
|
||||
+104
-35
@@ -426,7 +426,11 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (need_log_sync) {
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, status);
|
||||
if (status.ok()) {
|
||||
status = MarkLogsSynced(logfile_number_, need_log_dir_sync);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
}
|
||||
mutex_.Unlock();
|
||||
// Requesting sync with two_write_queues_ is expected to be very rare. We
|
||||
// hence provide a simple implementation that is not necessarily efficient.
|
||||
@@ -551,7 +555,11 @@ Status DBImpl::PipelinedWriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (need_log_sync) {
|
||||
mutex_.Lock();
|
||||
MarkLogsSynced(logfile_number_, need_log_dir_sync, w.status);
|
||||
if (w.status.ok()) {
|
||||
w.status = MarkLogsSynced(logfile_number_, need_log_dir_sync);
|
||||
} else {
|
||||
MarkLogsNotSynced(logfile_number_);
|
||||
}
|
||||
mutex_.Unlock();
|
||||
}
|
||||
|
||||
@@ -658,7 +666,6 @@ Status DBImpl::WriteImplWALOnly(
|
||||
const uint64_t log_ref, uint64_t* seq_used, const size_t sub_batch_cnt,
|
||||
PreReleaseCallback* pre_release_callback, const AssignOrder assign_order,
|
||||
const PublishLastSeq publish_last_seq, const bool disable_memtable) {
|
||||
Status status;
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, sub_batch_cnt, pre_release_callback);
|
||||
@@ -680,6 +687,8 @@ Status DBImpl::WriteImplWALOnly(
|
||||
assert(w.state == WriteThread::STATE_GROUP_LEADER);
|
||||
|
||||
if (publish_last_seq == kDoPublishLastSeq) {
|
||||
Status status;
|
||||
|
||||
// Currently we only use kDoPublishLastSeq in unordered_write
|
||||
assert(immutable_db_options_.unordered_write);
|
||||
WriteContext write_context;
|
||||
@@ -756,6 +765,7 @@ Status DBImpl::WriteImplWALOnly(
|
||||
}
|
||||
seq_inc = total_batch_cnt;
|
||||
}
|
||||
Status status;
|
||||
IOStatus io_s;
|
||||
if (!write_options.disableWAL) {
|
||||
io_s = ConcurrentWriteToWAL(write_group, log_used, &last_sequence, seq_inc);
|
||||
@@ -842,8 +852,7 @@ void DBImpl::WriteStatusCheckOnLocked(const Status& status) {
|
||||
if (immutable_db_options_.paranoid_checks && !status.ok() &&
|
||||
!status.IsBusy() && !status.IsIncomplete()) {
|
||||
// Maybe change the return status to void?
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kWriteCallback)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kWriteCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,8 +864,7 @@ void DBImpl::WriteStatusCheck(const Status& status) {
|
||||
!status.IsBusy() && !status.IsIncomplete()) {
|
||||
mutex_.Lock();
|
||||
// Maybe change the return status to void?
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kWriteCallback)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(status, BackgroundErrorReason::kWriteCallback);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -869,8 +877,7 @@ void DBImpl::IOStatusCheck(const IOStatus& io_status) {
|
||||
io_status.IsIOFenced()) {
|
||||
mutex_.Lock();
|
||||
// Maybe change the return status to void?
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_status, BackgroundErrorReason::kWriteCallback);
|
||||
mutex_.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -1327,10 +1334,17 @@ Status DBImpl::SwitchWAL(WriteContext* write_context) {
|
||||
}
|
||||
for (auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferManager);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
return status;
|
||||
@@ -1406,10 +1420,17 @@ Status DBImpl::HandleWriteBufferFull(WriteContext* write_context) {
|
||||
}
|
||||
for (const auto cfd : cfds) {
|
||||
cfd->imm()->FlushRequested();
|
||||
if (!immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
}
|
||||
}
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
return status;
|
||||
@@ -1633,10 +1654,16 @@ Status DBImpl::ScheduleFlushes(WriteContext* context) {
|
||||
if (status.ok()) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
AssignAtomicFlushSeq(cfds);
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
} else {
|
||||
for (auto* cfd : cfds) {
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest({cfd}, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
}
|
||||
}
|
||||
FlushRequest flush_req;
|
||||
GenerateFlushRequest(cfds, &flush_req);
|
||||
SchedulePendingFlush(flush_req, FlushReason::kWriteBufferFull);
|
||||
MaybeScheduleFlushOrCompaction();
|
||||
}
|
||||
return status;
|
||||
@@ -1782,32 +1809,74 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
}
|
||||
// We may have lost data from the WritableFileBuffer in-memory buffer for
|
||||
// the current log, so treat it as a fatal error and set bg_error
|
||||
// Should handle return error?
|
||||
if (!io_s.ok()) {
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kMemTable);
|
||||
} else {
|
||||
// Should handle return error?
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kMemTable)
|
||||
.PermitUncheckedError();
|
||||
error_handler_.SetBGError(s, BackgroundErrorReason::kMemTable);
|
||||
}
|
||||
// Read back bg_error in order to get the right severity
|
||||
s = error_handler_.GetBGError();
|
||||
return s;
|
||||
}
|
||||
|
||||
for (auto loop_cfd : *versions_->GetColumnFamilySet()) {
|
||||
// all this is just optimization to delete logs that
|
||||
// are no longer needed -- if CF is empty, that means it
|
||||
// doesn't need that particular log to stay alive, so we just
|
||||
// advance the log number. no need to persist this in the manifest
|
||||
if (loop_cfd->mem()->GetFirstSequenceNumber() == 0 &&
|
||||
loop_cfd->imm()->NumNotFlushed() == 0) {
|
||||
if (creating_new_log) {
|
||||
loop_cfd->SetLogNumber(logfile_number_);
|
||||
bool empty_cf_updated = false;
|
||||
if (immutable_db_options_.track_and_verify_wals_in_manifest &&
|
||||
!immutable_db_options_.allow_2pc && creating_new_log) {
|
||||
// In non-2pc mode, WALs become obsolete if they do not contain unflushed
|
||||
// data. Updating the empty CF's log number might cause some WALs to become
|
||||
// obsolete. So we should track the WAL obsoletion event before actually
|
||||
// updating the empty CF's log number.
|
||||
uint64_t min_wal_number_to_keep =
|
||||
versions_->PreComputeMinLogNumberWithUnflushedData(logfile_number_);
|
||||
if (min_wal_number_to_keep >
|
||||
versions_->GetWalSet().GetMinWalNumberToKeep()) {
|
||||
// Get a snapshot of the empty column families.
|
||||
// LogAndApply may release and reacquire db
|
||||
// mutex, during that period, column family may become empty (e.g. its
|
||||
// flush succeeds), then it affects the computed min_log_number_to_keep,
|
||||
// so we take a snapshot for consistency of column family data
|
||||
// status. If a column family becomes non-empty afterwards, its active log
|
||||
// should still be the created new log, so the min_log_number_to_keep is
|
||||
// not affected.
|
||||
autovector<ColumnFamilyData*> empty_cfs;
|
||||
for (auto cf : *versions_->GetColumnFamilySet()) {
|
||||
if (cf->IsEmpty()) {
|
||||
empty_cfs.push_back(cf);
|
||||
}
|
||||
}
|
||||
|
||||
VersionEdit wal_deletion;
|
||||
wal_deletion.DeleteWalsBefore(min_wal_number_to_keep);
|
||||
s = versions_->LogAndApplyToDefaultColumnFamily(&wal_deletion, &mutex_);
|
||||
if (!s.ok() && versions_->io_status().IsIOError()) {
|
||||
s = error_handler_.SetBGError(versions_->io_status(),
|
||||
BackgroundErrorReason::kManifestWrite);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
for (auto cf : empty_cfs) {
|
||||
if (cf->IsEmpty()) {
|
||||
cf->SetLogNumber(logfile_number_);
|
||||
cf->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
} // cf may become non-empty.
|
||||
}
|
||||
empty_cf_updated = true;
|
||||
}
|
||||
}
|
||||
if (!empty_cf_updated) {
|
||||
for (auto cf : *versions_->GetColumnFamilySet()) {
|
||||
// all this is just optimization to delete logs that
|
||||
// are no longer needed -- if CF is empty, that means it
|
||||
// doesn't need that particular log to stay alive, so we just
|
||||
// advance the log number. no need to persist this in the manifest
|
||||
if (cf->IsEmpty()) {
|
||||
if (creating_new_log) {
|
||||
cf->SetLogNumber(logfile_number_);
|
||||
}
|
||||
cf->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
}
|
||||
loop_cfd->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ void DBSecondaryTest::CheckFileTypeCounts(const std::string& dir,
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
log_cnt += (type == kLogFile);
|
||||
log_cnt += (type == kWalFile);
|
||||
sst_cnt += (type == kTableFile);
|
||||
manifest_cnt += (type == kDescriptorFile);
|
||||
}
|
||||
@@ -748,10 +748,12 @@ TEST_F(DBSecondaryTest, SwitchWALMultiColumnFamilies) {
|
||||
}
|
||||
};
|
||||
for (int k = 0; k != 8; ++k) {
|
||||
ASSERT_OK(
|
||||
Put(0 /*cf*/, "key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
ASSERT_OK(
|
||||
Put(1 /*cf*/, "key" + std::to_string(k), "value" + std::to_string(k)));
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
ASSERT_OK(Put(0 /*cf*/, "key" + std::to_string(k),
|
||||
"value" + std::to_string(k)));
|
||||
ASSERT_OK(Put(1 /*cf*/, "key" + std::to_string(k),
|
||||
"value" + std::to_string(k)));
|
||||
}
|
||||
TEST_SYNC_POINT(
|
||||
"DBSecondaryTest::SwitchWALMultipleColumnFamilies:BeforeCatchUp");
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
@@ -883,6 +885,7 @@ TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
Status s = TryOpenSecondary(options1);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
@@ -894,6 +897,7 @@ TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
OpenSecondary(options1);
|
||||
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
dbname.c_str(), file.c_str());
|
||||
}
|
||||
break;
|
||||
case kLogFile:
|
||||
case kWalFile:
|
||||
if (env->GetFileSize(dbname + "/" + file, &file_size).ok()) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
@@ -118,7 +118,7 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
wal_info.clear();
|
||||
for (const std::string& file : files) {
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
if (type == kLogFile) {
|
||||
if (type == kWalFile) {
|
||||
if (env->GetFileSize(options.wal_dir + "/" + file, &file_size).ok()) {
|
||||
char str[16];
|
||||
snprintf(str, sizeof(str), "%" PRIu64, file_size);
|
||||
|
||||
+114
-86
@@ -36,16 +36,18 @@ namespace ROCKSDB_NAMESPACE {
|
||||
DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* cmp, InternalIterator* iter, SequenceNumber s,
|
||||
bool arena_mode, uint64_t max_sequential_skip_in_iterations,
|
||||
const Comparator* cmp, InternalIterator* iter,
|
||||
const Version* version, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob)
|
||||
ColumnFamilyData* cfd, bool expose_blob_index)
|
||||
: prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
env_(_env),
|
||||
logger_(cf_options.info_log),
|
||||
user_comparator_(cmp),
|
||||
merge_operator_(cf_options.merge_operator),
|
||||
iter_(iter),
|
||||
version_(version),
|
||||
read_callback_(read_callback),
|
||||
sequence_(s),
|
||||
statistics_(cf_options.statistics),
|
||||
@@ -65,7 +67,9 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
expect_total_order_inner_iter_(prefix_extractor_ == nullptr ||
|
||||
read_options.total_order_seek ||
|
||||
read_options.auto_prefix_mode),
|
||||
allow_blob_(allow_blob),
|
||||
read_tier_(read_options.read_tier),
|
||||
verify_checksums_(read_options.verify_checksums),
|
||||
expose_blob_index_(expose_blob_index),
|
||||
is_blob_(false),
|
||||
arena_mode_(arena_mode),
|
||||
range_del_agg_(&cf_options.internal_comparator, s),
|
||||
@@ -107,11 +111,12 @@ Status DBIter::GetProperty(std::string prop_name, std::string* prop) {
|
||||
}
|
||||
|
||||
bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
if (ParseInternalKey(iter_.key(), ikey) != Status::OK()) {
|
||||
status_ = Status::Corruption("corrupted internal key in DBIter");
|
||||
Status s =
|
||||
ParseInternalKey(iter_.key(), ikey, false /* log_err_key */); // TODO
|
||||
if (!s.ok()) {
|
||||
status_ = Status::Corruption("In DBIter: ", s.getState());
|
||||
valid_ = false;
|
||||
ROCKS_LOG_ERROR(logger_, "corrupted internal key in DBIter: %s",
|
||||
iter_.key().ToString(true).c_str());
|
||||
ROCKS_LOG_ERROR(logger_, "In DBIter: %s", status_.getState());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@@ -164,6 +169,40 @@ void DBIter::Next() {
|
||||
}
|
||||
}
|
||||
|
||||
bool DBIter::SetBlobValueIfNeeded(const Slice& user_key,
|
||||
const Slice& blob_index) {
|
||||
assert(!is_blob_);
|
||||
|
||||
if (expose_blob_index_) { // Stacked BlobDB implementation
|
||||
is_blob_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!version_) {
|
||||
status_ = Status::Corruption("Encountered unexpected blob index.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: consider moving ReadOptions from ArenaWrappedDBIter to DBIter to
|
||||
// avoid having to copy options back and forth.
|
||||
ReadOptions read_options;
|
||||
read_options.read_tier = read_tier_;
|
||||
read_options.verify_checksums = verify_checksums_;
|
||||
|
||||
const Status s =
|
||||
version_->GetBlob(read_options, user_key, blob_index, &blob_value_);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
is_blob_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// PRE: saved_key_ has the current user key if skipping_saved_key
|
||||
// POST: saved_key_ should have the next user key if valid_,
|
||||
// if the current entry is a result of merge
|
||||
@@ -220,25 +259,28 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
is_key_seqnum_zero_ = false;
|
||||
return false;
|
||||
}
|
||||
Slice user_key_without_ts =
|
||||
StripTimestampFromUserKey(ikey_.user_key, timestamp_size_);
|
||||
|
||||
is_key_seqnum_zero_ = (ikey_.sequence == 0);
|
||||
|
||||
assert(iterate_upper_bound_ == nullptr ||
|
||||
iter_.UpperBoundCheckResult() != IterBoundCheck::kInbound ||
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, /*a_has_ts=*/true, *iterate_upper_bound_,
|
||||
user_key_without_ts, /*a_has_ts=*/false, *iterate_upper_bound_,
|
||||
/*b_has_ts=*/false) < 0);
|
||||
if (iterate_upper_bound_ != nullptr &&
|
||||
iter_.UpperBoundCheckResult() != IterBoundCheck::kInbound &&
|
||||
user_comparator_.CompareWithoutTimestamp(
|
||||
ikey_.user_key, /*a_has_ts=*/true, *iterate_upper_bound_,
|
||||
user_key_without_ts, /*a_has_ts=*/false, *iterate_upper_bound_,
|
||||
/*b_has_ts=*/false) >= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
assert(prefix == nullptr || prefix_extractor_ != nullptr);
|
||||
if (prefix != nullptr &&
|
||||
prefix_extractor_->Transform(ikey_.user_key).compare(*prefix) != 0) {
|
||||
prefix_extractor_->Transform(user_key_without_ts).compare(*prefix) !=
|
||||
0) {
|
||||
assert(prefix_same_as_start_);
|
||||
break;
|
||||
}
|
||||
@@ -315,8 +357,14 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
case kTypeBlobIndex:
|
||||
if (start_seqnum_ > 0) {
|
||||
if (ikey_.sequence >= start_seqnum_) {
|
||||
assert(ikey_.type != kTypeBlobIndex);
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!SetBlobValueIfNeeded(ikey_.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
@@ -330,6 +378,13 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
}
|
||||
} else if (timestamp_lb_) {
|
||||
saved_key_.SetInternalKey(ikey_);
|
||||
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!SetBlobValueIfNeeded(ikey_.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
@@ -344,20 +399,13 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
num_skipped = 0;
|
||||
reseek_done = false;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
} else {
|
||||
if (ikey_.type == kTypeBlobIndex) {
|
||||
if (!SetBlobValueIfNeeded(ikey_.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
is_blob_ = true;
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else {
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
@@ -436,11 +484,11 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), 0, kTypeDeletion));
|
||||
} else {
|
||||
std::string min_ts(timestamp_size_, static_cast<char>(0));
|
||||
const std::string kTsMin(timestamp_size_, static_cast<char>(0));
|
||||
AppendInternalKeyWithDifferentTimestamp(
|
||||
&last_key,
|
||||
ParsedInternalKey(saved_key_.GetUserKey(), 0, kTypeDeletion),
|
||||
min_ts);
|
||||
kTsMin);
|
||||
}
|
||||
// Don't set skipping_saved_key = false because we may still see more
|
||||
// user-keys equal to saved_key_.
|
||||
@@ -497,7 +545,6 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
TEST_SYNC_POINT("DBIter::MergeValuesNewToOld:PushedFirstOperand");
|
||||
|
||||
ParsedInternalKey ikey;
|
||||
Status s;
|
||||
for (iter_.Next(); iter_.Valid(); iter_.Next()) {
|
||||
TEST_SYNC_POINT("DBIter::MergeValuesNewToOld:SteppedToNextOperand");
|
||||
if (!ParseKey(&ikey)) {
|
||||
@@ -525,7 +572,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
const Slice val = iter_.value();
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, ikey.user_key, &val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_, &pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
@@ -547,15 +594,7 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else if (kTypeBlobIndex == ikey.type) {
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
} else {
|
||||
status_ =
|
||||
Status::NotSupported("Blob DB does not support merge operator.");
|
||||
}
|
||||
status_ = Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
} else {
|
||||
@@ -576,10 +615,10 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// a deletion marker.
|
||||
// feed null as the existing value to the merge operator, such that
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
s = MergeHelper::TimedFullMerge(merge_operator_, saved_key_.GetUserKey(),
|
||||
nullptr, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_, true);
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, env_,
|
||||
&pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
@@ -703,7 +742,9 @@ void DBIter::PrevInternal(const Slice* prefix) {
|
||||
|
||||
assert(prefix == nullptr || prefix_extractor_ != nullptr);
|
||||
if (prefix != nullptr &&
|
||||
prefix_extractor_->Transform(saved_key_.GetUserKey())
|
||||
prefix_extractor_
|
||||
->Transform(StripTimestampFromUserKey(saved_key_.GetUserKey(),
|
||||
timestamp_size_))
|
||||
.compare(*prefix) != 0) {
|
||||
assert(prefix_same_as_start_);
|
||||
// Current key does not have the same prefix as start
|
||||
@@ -810,12 +851,19 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
ikey, RangeDelPositioningMode::kBackwardTraversal)) {
|
||||
last_key_entry_type = kTypeRangeDeletion;
|
||||
PERF_COUNTER_ADD(internal_delete_skipped_count, 1);
|
||||
} else {
|
||||
assert(iter_.iter()->IsValuePinned());
|
||||
} else if (iter_.iter()->IsValuePinned()) {
|
||||
pinned_value_ = iter_.value();
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = Status::NotSupported(
|
||||
"Backward iteration not supported if underlying iterator's value "
|
||||
"cannot be pinned.");
|
||||
}
|
||||
merge_context_.Clear();
|
||||
last_not_merge_type = last_key_entry_type;
|
||||
if (!status_.ok()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case kTypeDeletion:
|
||||
case kTypeSingleDeletion:
|
||||
@@ -875,15 +923,8 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
env_, &pinned_value_, true);
|
||||
} else if (last_not_merge_type == kTypeBlobIndex) {
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
} else {
|
||||
status_ =
|
||||
Status::NotSupported("Blob DB does not support merge operator.");
|
||||
}
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
} else {
|
||||
@@ -898,15 +939,10 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
// do nothing - we've already has value in pinned_value_
|
||||
break;
|
||||
case kTypeBlobIndex:
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
valid_ = false;
|
||||
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
is_blob_ = true;
|
||||
|
||||
break;
|
||||
default:
|
||||
valid_ = false;
|
||||
@@ -979,14 +1015,6 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
valid_ = false;
|
||||
return true;
|
||||
}
|
||||
if (ikey.type == kTypeBlobIndex && !allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!iter_.PrepareValue()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
@@ -994,7 +1022,12 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
if (ikey.type == kTypeValue || ikey.type == kTypeBlobIndex) {
|
||||
assert(iter_.iter()->IsValuePinned());
|
||||
pinned_value_ = iter_.value();
|
||||
is_blob_ = (ikey.type == kTypeBlobIndex);
|
||||
if (ikey.type == kTypeBlobIndex) {
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
valid_ = true;
|
||||
return true;
|
||||
}
|
||||
@@ -1050,15 +1083,7 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else if (ikey.type == kTypeBlobIndex) {
|
||||
if (!allow_blob_) {
|
||||
ROCKS_LOG_ERROR(logger_, "Encounter unexpected blob index.");
|
||||
status_ = Status::NotSupported(
|
||||
"Encounter unexpected blob index. Please open DB with "
|
||||
"ROCKSDB_NAMESPACE::blob_db::BlobDB instead.");
|
||||
} else {
|
||||
status_ =
|
||||
Status::NotSupported("Blob DB does not support merge operator.");
|
||||
}
|
||||
status_ = Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
} else {
|
||||
@@ -1391,7 +1416,8 @@ void DBIter::SeekToFirst() {
|
||||
}
|
||||
if (valid_ && prefix_same_as_start_) {
|
||||
assert(prefix_extractor_ != nullptr);
|
||||
prefix_.SetUserKey(prefix_extractor_->Transform(saved_key_.GetUserKey()));
|
||||
prefix_.SetUserKey(prefix_extractor_->Transform(
|
||||
StripTimestampFromUserKey(saved_key_.GetUserKey(), timestamp_size_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1442,7 +1468,8 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
if (valid_ && prefix_same_as_start_) {
|
||||
assert(prefix_extractor_ != nullptr);
|
||||
prefix_.SetUserKey(prefix_extractor_->Transform(saved_key_.GetUserKey()));
|
||||
prefix_.SetUserKey(prefix_extractor_->Transform(
|
||||
StripTimestampFromUserKey(saved_key_.GetUserKey(), timestamp_size_)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1450,15 +1477,16 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter,
|
||||
InternalIterator* internal_iter, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool allow_blob) {
|
||||
DBIter* db_iter = new DBIter(
|
||||
env, read_options, cf_options, mutable_cf_options, user_key_comparator,
|
||||
internal_iter, sequence, false, max_sequential_skip_in_iterations,
|
||||
read_callback, db_impl, cfd, allow_blob);
|
||||
ColumnFamilyData* cfd, bool expose_blob_index) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
user_key_comparator, internal_iter, version, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, db_impl, cfd,
|
||||
expose_blob_index);
|
||||
return db_iter;
|
||||
}
|
||||
|
||||
|
||||
+25
-9
@@ -22,6 +22,8 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Version;
|
||||
|
||||
// This file declares the factory functions of DBIter, in its original form
|
||||
// or a wrapped form with class ArenaWrappedDBIter, which is defined here.
|
||||
// Class DBIter, which is declared and implemented inside db_iter.cc, is
|
||||
@@ -114,10 +116,10 @@ class DBIter final : public Iterator {
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, SequenceNumber s, bool arena_mode,
|
||||
uint64_t max_sequential_skip_in_iterations,
|
||||
InternalIterator* iter, const Version* version, SequenceNumber s,
|
||||
bool arena_mode, uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl, ColumnFamilyData* cfd,
|
||||
bool allow_blob);
|
||||
bool expose_blob_index);
|
||||
|
||||
// No copying allowed
|
||||
DBIter(const DBIter&) = delete;
|
||||
@@ -159,7 +161,10 @@ class DBIter final : public Iterator {
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(valid_);
|
||||
if (current_entry_is_merged_) {
|
||||
|
||||
if (!expose_blob_index_ && is_blob_) {
|
||||
return blob_value_;
|
||||
} else if (current_entry_is_merged_) {
|
||||
// If pinned_value_ is set then the result of merge operator is one of
|
||||
// the merge operands and we should return it.
|
||||
return pinned_value_.data() ? pinned_value_ : saved_value_;
|
||||
@@ -185,7 +190,7 @@ class DBIter final : public Iterator {
|
||||
return ExtractTimestampFromUserKey(ukey_and_ts, timestamp_size_);
|
||||
}
|
||||
bool IsBlob() const {
|
||||
assert(valid_ && (allow_blob_ || !is_blob_));
|
||||
assert(valid_);
|
||||
return is_blob_;
|
||||
}
|
||||
|
||||
@@ -287,12 +292,17 @@ class DBIter final : public Iterator {
|
||||
: user_comparator_.CompareWithoutTimestamp(a, b);
|
||||
}
|
||||
|
||||
// Retrieves the blob value for the specified user key using the given blob
|
||||
// index when using the integrated BlobDB implementation.
|
||||
bool SetBlobValueIfNeeded(const Slice& user_key, const Slice& blob_index);
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
Logger* logger_;
|
||||
UserComparatorWrapper user_comparator_;
|
||||
const MergeOperator* const merge_operator_;
|
||||
IteratorWrapper iter_;
|
||||
const Version* version_;
|
||||
ReadCallback* read_callback_;
|
||||
// Max visible sequence number. It is normally the snapshot seq unless we have
|
||||
// uncommitted data in db as in WriteUnCommitted.
|
||||
@@ -306,6 +316,7 @@ class DBIter final : public Iterator {
|
||||
std::string saved_value_;
|
||||
Slice pinned_value_;
|
||||
// for prefix seek mode to support prev()
|
||||
PinnableSlice blob_value_;
|
||||
Statistics* statistics_;
|
||||
uint64_t max_skip_;
|
||||
uint64_t max_skippable_internal_keys_;
|
||||
@@ -335,7 +346,11 @@ class DBIter final : public Iterator {
|
||||
// Expect the inner iterator to maintain a total order.
|
||||
// prefix_extractor_ must be non-NULL if the value is false.
|
||||
const bool expect_total_order_inner_iter_;
|
||||
bool allow_blob_;
|
||||
ReadTier read_tier_;
|
||||
bool verify_checksums_;
|
||||
// Whether the iterator is allowed to expose blob references. Set to true when
|
||||
// the stacked BlobDB implementation is used, false otherwise.
|
||||
bool expose_blob_index_;
|
||||
bool is_blob_;
|
||||
bool arena_mode_;
|
||||
// List of operands for merge operator.
|
||||
@@ -367,8 +382,9 @@ extern Iterator* NewDBIterator(
|
||||
const ImmutableCFOptions& cf_options,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
ReadCallback* read_callback, DBImpl* db_impl = nullptr,
|
||||
ColumnFamilyData* cfd = nullptr, bool allow_blob = false);
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, ReadCallback* read_callback,
|
||||
DBImpl* db_impl = nullptr, ColumnFamilyData* cfd = nullptr,
|
||||
bool expose_blob_index = false);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -513,7 +513,7 @@ TEST_F(DBIteratorStressTest, StressTest) {
|
||||
db_iter.reset(NewDBIterator(
|
||||
env_, ropt, ImmutableCFOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(),
|
||||
internal_iter, sequence,
|
||||
internal_iter, nullptr /* version */, sequence,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
}
|
||||
|
||||
+229
-153
@@ -99,10 +99,11 @@ class TestIterator : public InternalIterator {
|
||||
}
|
||||
for (auto it = data_.begin(); it != data_.end(); ++it) {
|
||||
ParsedInternalKey ikey;
|
||||
Status pikStatus = ParseInternalKey(it->first, &ikey);
|
||||
pikStatus.PermitUncheckedError();
|
||||
assert(pikStatus.ok());
|
||||
if (!pikStatus.ok() || ikey.user_key != _key) {
|
||||
Status pik_status =
|
||||
ParseInternalKey(it->first, &ikey, true /* log_err_key */);
|
||||
pik_status.PermitUncheckedError();
|
||||
assert(pik_status.ok());
|
||||
if (!pik_status.ok() || ikey.user_key != _key) {
|
||||
continue;
|
||||
}
|
||||
if (valid_ && data_.begin() + iter_ > it) {
|
||||
@@ -252,8 +253,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -285,8 +287,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -312,8 +315,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -345,8 +349,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -381,8 +386,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -411,8 +417,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 7, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -449,8 +456,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 4, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -475,8 +483,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
@@ -498,8 +507,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -534,8 +544,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 7, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_TRUE(GetPerfLevel() == kEnableCount);
|
||||
@@ -564,8 +575,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -607,8 +619,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -639,8 +652,9 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -670,8 +684,9 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
@@ -682,8 +697,9 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
@@ -705,8 +721,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkipCountSkips) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 2,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -750,8 +767,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, i + 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -786,8 +804,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, i + 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -815,8 +834,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 202, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 202 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -848,8 +868,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, i, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, i /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
|
||||
@@ -865,8 +886,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 200, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 200 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -900,8 +922,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, i + 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -935,8 +958,9 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, i + 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -985,8 +1009,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 0;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1032,8 +1057,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1077,8 +1103,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1116,8 +1143,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1152,8 +1180,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1183,8 +1212,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1221,8 +1251,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1259,8 +1290,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 2 * i + 1, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1313,8 +1345,9 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 2 * i + 1, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -1352,8 +1385,9 @@ TEST_F(DBIteratorTest, DBIterator1) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 1,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
1 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1380,8 +1414,9 @@ TEST_F(DBIteratorTest, DBIterator2) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 0,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
0 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1405,8 +1440,9 @@ TEST_F(DBIteratorTest, DBIterator3) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 2,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1430,8 +1466,9 @@ TEST_F(DBIteratorTest, DBIterator4) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 4,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
4 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1464,8 +1501,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1487,8 +1525,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 1, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1510,8 +1549,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1533,8 +1573,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 3, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1556,8 +1597,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 4, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1579,8 +1621,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 5, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1602,8 +1645,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 6, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1623,8 +1667,9 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 10, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -1654,8 +1699,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1677,8 +1723,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 1, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1700,8 +1747,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1723,8 +1771,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 3, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(!db_iter->Valid());
|
||||
}
|
||||
@@ -1742,8 +1791,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 4, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1765,8 +1815,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 5, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1788,8 +1839,9 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 6, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1831,8 +1883,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 0, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -1866,8 +1919,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 2, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1907,8 +1961,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 4, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1948,8 +2003,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 5, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -1994,8 +2050,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 6, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2041,8 +2098,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 7, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2082,8 +2140,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 9, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 9 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2129,8 +2188,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 13, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 13 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2177,8 +2237,9 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, 14, options.max_sequential_skip_in_iterations,
|
||||
nullptr /*read_callback*/));
|
||||
internal_iter, nullptr /* version */, 14 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
@@ -2208,8 +2269,9 @@ TEST_F(DBIteratorTest, DBIterator8) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2239,8 +2301,9 @@ TEST_F(DBIteratorTest, DBIterator9) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2306,8 +2369,9 @@ TEST_F(DBIteratorTest, DBIterator10) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->Seek("c");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
@@ -2346,8 +2410,8 @@ TEST_F(DBIteratorTest, SeekToLastOccurrenceSeq0) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10, 0 /* force seek */,
|
||||
nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2375,8 +2439,9 @@ TEST_F(DBIteratorTest, DBIterator11) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 1,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
1 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->SeekToFirst();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "a");
|
||||
@@ -2402,7 +2467,8 @@ TEST_F(DBIteratorTest, DBIterator12) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10, 0, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, 0 /* force seek */, nullptr /* read_callback */));
|
||||
db_iter->SeekToLast();
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "c");
|
||||
@@ -2439,7 +2505,9 @@ TEST_F(DBIteratorTest, DBIterator13) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 2, 3, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
2 /* sequence */, 3 /* max_sequential_skip_in_iterations */,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), key);
|
||||
@@ -2467,7 +2535,9 @@ TEST_F(DBIteratorTest, DBIterator14) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 4, 1, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
4 /* sequence */, 1 /* max_sequential_skip_in_iterations */,
|
||||
nullptr /* read_callback */));
|
||||
db_iter->Seek("b");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
ASSERT_EQ(db_iter->key().ToString(), "b");
|
||||
@@ -2494,8 +2564,9 @@ TEST_F(DBIteratorTest, DBIteratorTestDifferentialSnapshots) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 13,
|
||||
options.max_sequential_skip_in_iterations, nullptr));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
13 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
// Expecting InternalKeys in [5,8] range with correct type
|
||||
int seqnums[4] = {5,8,11,13};
|
||||
std::string user_keys[4] = {"1","2","3","4"};
|
||||
@@ -2529,8 +2600,9 @@ TEST_F(DBIteratorTest, DBIteratorTestDifferentialSnapshots) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 13,
|
||||
options.max_sequential_skip_in_iterations, nullptr));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
13 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
// Expecting InternalKeys in [5,8] range with correct type
|
||||
int seqnums[4] = {5,8,11,13};
|
||||
EntryType key_types[4] = {EntryType::kEntryDelete,EntryType::kEntryDelete,
|
||||
@@ -2579,9 +2651,9 @@ class DBIterWithMergeIterTest : public testing::Test {
|
||||
|
||||
db_iter_.reset(NewDBIterator(
|
||||
env_, ro_, ImmutableCFOptions(options_), MutableCFOptions(options_),
|
||||
BytewiseComparator(), merge_iter,
|
||||
BytewiseComparator(), merge_iter, nullptr /* version */,
|
||||
8 /* read data earlier than seqId 8 */,
|
||||
3 /* max iterators before reseek */, nullptr /*read_callback*/));
|
||||
3 /* max iterators before reseek */, nullptr /* read_callback */));
|
||||
}
|
||||
|
||||
Env* env_;
|
||||
@@ -3019,8 +3091,9 @@ TEST_F(DBIteratorTest, SeekPrefixTombstones) {
|
||||
ro.prefix_same_as_start = true;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
int skipped_keys = 0;
|
||||
|
||||
@@ -3055,8 +3128,8 @@ TEST_F(DBIteratorTest, SeekToFirstLowerBound) {
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToFirst();
|
||||
@@ -3094,8 +3167,9 @@ TEST_F(DBIteratorTest, PrevLowerBound) {
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations, nullptr /* read_callback */));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekToLast();
|
||||
for (int i = kNumKeys; i >= kLowerBound; --i) {
|
||||
@@ -3122,8 +3196,9 @@ TEST_F(DBIteratorTest, SeekLessLowerBound) {
|
||||
Options options;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, ImmutableCFOptions(options), MutableCFOptions(options),
|
||||
BytewiseComparator(), internal_iter, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations, nullptr /* read_callback */));
|
||||
BytewiseComparator(), internal_iter, nullptr /* version */,
|
||||
10 /* sequence */, options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
auto before_lower_bound_str = std::to_string(kLowerBound - 1);
|
||||
Slice before_lower_bound(lower_bound_str);
|
||||
@@ -3147,8 +3222,9 @@ TEST_F(DBIteratorTest, ReverseToForwardWithDisappearingKeys) {
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ReadOptions(), ImmutableCFOptions(options),
|
||||
MutableCFOptions(options), BytewiseComparator(), internal_iter, 10,
|
||||
options.max_sequential_skip_in_iterations, nullptr /*read_callback*/));
|
||||
MutableCFOptions(options), BytewiseComparator(), internal_iter,
|
||||
nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations, nullptr /* read_callback */));
|
||||
|
||||
db_iter->SeekForPrev("a");
|
||||
ASSERT_TRUE(db_iter->Valid());
|
||||
|
||||
+216
-49
@@ -67,8 +67,8 @@ TEST_P(DBIteratorTest, IteratorProperty) {
|
||||
// The test needs to be changed if kPersistedTier is supported in iterator.
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
Put(1, "1", "2");
|
||||
Delete(1, "2");
|
||||
ASSERT_OK(Put(1, "1", "2"));
|
||||
ASSERT_OK(Delete(1, "2"));
|
||||
ReadOptions ropt;
|
||||
ropt.pin_data = false;
|
||||
{
|
||||
@@ -172,10 +172,10 @@ TEST_P(DBIteratorTest, NonBlockingIteration) {
|
||||
TEST_P(DBIteratorTest, IterSeekBeforePrev) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("0", "f"));
|
||||
ASSERT_OK(Put("1", "h"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("2", "j"));
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
iter->Seek(Slice("c"));
|
||||
@@ -199,7 +199,7 @@ TEST_P(DBIteratorTest, IterReseekNewUpperBound) {
|
||||
ASSERT_OK(Put("aabb", rnd.RandomString(400)));
|
||||
ASSERT_OK(Put("aaef", rnd.RandomString(400)));
|
||||
ASSERT_OK(Put("b", rnd.RandomString(400)));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ReadOptions opts;
|
||||
Slice ub = Slice("aa");
|
||||
opts.iterate_upper_bound = &ub;
|
||||
@@ -215,10 +215,10 @@ TEST_P(DBIteratorTest, IterReseekNewUpperBound) {
|
||||
TEST_P(DBIteratorTest, IterSeekForPrevBeforeNext) {
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("0", "f"));
|
||||
ASSERT_OK(Put("1", "h"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("2", "j"));
|
||||
auto iter = NewIterator(ReadOptions());
|
||||
iter->SeekForPrev(Slice("0"));
|
||||
@@ -238,7 +238,7 @@ TEST_P(DBIteratorTest, IterLongKeys) {
|
||||
ASSERT_OK(Put(MakeLongKey(20, 0), "0"));
|
||||
ASSERT_OK(Put(MakeLongKey(32, 2), "2"));
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put(MakeLongKey(50, 1), "1"));
|
||||
ASSERT_OK(Put(MakeLongKey(127, 3), "3"));
|
||||
ASSERT_OK(Put(MakeLongKey(64, 4), "4"));
|
||||
@@ -276,7 +276,7 @@ TEST_P(DBIteratorTest, IterLongKeys) {
|
||||
|
||||
TEST_P(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
ASSERT_OK(Put("0", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("d", "e"));
|
||||
@@ -302,7 +302,7 @@ TEST_P(DBIteratorTest, IterNextWithNewerSeq) {
|
||||
|
||||
TEST_P(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
ASSERT_OK(Put("0", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("d", "e"));
|
||||
@@ -333,7 +333,7 @@ TEST_P(DBIteratorTest, IterPrevWithNewerSeq) {
|
||||
|
||||
TEST_P(DBIteratorTest, IterPrevWithNewerSeq2) {
|
||||
ASSERT_OK(Put("0", "0"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("a", "b"));
|
||||
ASSERT_OK(Put("c", "d"));
|
||||
ASSERT_OK(Put("e", "f"));
|
||||
@@ -377,6 +377,8 @@ TEST_P(DBIteratorTest, IterEmpty) {
|
||||
iter->SeekForPrev("foo");
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
delete iter;
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
@@ -783,18 +785,18 @@ TEST_P(DBIteratorTest, IterWithSnapshot) {
|
||||
TEST_P(DBIteratorTest, IteratorPinsRef) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
Put(1, "foo", "hello");
|
||||
ASSERT_OK(Put(1, "foo", "hello"));
|
||||
|
||||
// Get iterator that will yield the current contents of the DB.
|
||||
Iterator* iter = NewIterator(ReadOptions(), handles_[1]);
|
||||
|
||||
// Write to force compactions
|
||||
Put(1, "foo", "newvalue1");
|
||||
ASSERT_OK(Put(1, "foo", "newvalue1"));
|
||||
for (int i = 0; i < 100; i++) {
|
||||
// 100K values
|
||||
ASSERT_OK(Put(1, Key(i), Key(i) + std::string(100000, 'v')));
|
||||
}
|
||||
Put(1, "foo", "newvalue2");
|
||||
ASSERT_OK(Put(1, "foo", "newvalue2"));
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -809,8 +811,8 @@ TEST_P(DBIteratorTest, IteratorPinsRef) {
|
||||
TEST_P(DBIteratorTest, IteratorDeleteAfterCfDelete) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
|
||||
Put(1, "foo", "delete-cf-then-delete-iter");
|
||||
Put(1, "hello", "value2");
|
||||
ASSERT_OK(Put(1, "foo", "delete-cf-then-delete-iter"));
|
||||
ASSERT_OK(Put(1, "hello", "value2"));
|
||||
|
||||
ColumnFamilyHandle* cf = handles_[1];
|
||||
ReadOptions ro;
|
||||
@@ -820,7 +822,7 @@ TEST_P(DBIteratorTest, IteratorDeleteAfterCfDelete) {
|
||||
ASSERT_EQ(IterStatus(iter), "foo->delete-cf-then-delete-iter");
|
||||
|
||||
// delete CF handle
|
||||
db_->DestroyColumnFamilyHandle(cf);
|
||||
EXPECT_OK(db_->DestroyColumnFamilyHandle(cf));
|
||||
handles_.erase(std::begin(handles_) + 1);
|
||||
|
||||
// delete Iterator after CF handle is deleted
|
||||
@@ -832,7 +834,7 @@ TEST_P(DBIteratorTest, IteratorDeleteAfterCfDelete) {
|
||||
TEST_P(DBIteratorTest, IteratorDeleteAfterCfDrop) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
|
||||
Put(1, "foo", "drop-cf-then-delete-iter");
|
||||
ASSERT_OK(Put(1, "foo", "drop-cf-then-delete-iter"));
|
||||
|
||||
ReadOptions ro;
|
||||
ColumnFamilyHandle* cf = handles_[1];
|
||||
@@ -842,8 +844,8 @@ TEST_P(DBIteratorTest, IteratorDeleteAfterCfDrop) {
|
||||
ASSERT_EQ(IterStatus(iter), "foo->drop-cf-then-delete-iter");
|
||||
|
||||
// drop and delete CF
|
||||
db_->DropColumnFamily(cf);
|
||||
db_->DestroyColumnFamilyHandle(cf);
|
||||
EXPECT_OK(db_->DropColumnFamily(cf));
|
||||
EXPECT_OK(db_->DestroyColumnFamilyHandle(cf));
|
||||
handles_.erase(std::begin(handles_) + 1);
|
||||
|
||||
// delete Iterator after CF handle is dropped
|
||||
@@ -1307,9 +1309,9 @@ TEST_P(DBIteratorTest, PrevAfterAndNextAfterMerge) {
|
||||
|
||||
// write three entries with different keys using Merge()
|
||||
WriteOptions wopts;
|
||||
db_->Merge(wopts, "1", "data1");
|
||||
db_->Merge(wopts, "2", "data2");
|
||||
db_->Merge(wopts, "3", "data3");
|
||||
ASSERT_OK(db_->Merge(wopts, "1", "data1"));
|
||||
ASSERT_OK(db_->Merge(wopts, "2", "data2"));
|
||||
ASSERT_OK(db_->Merge(wopts, "3", "data3"));
|
||||
|
||||
std::unique_ptr<Iterator> it(NewIterator(ReadOptions()));
|
||||
|
||||
@@ -1393,7 +1395,7 @@ class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
|
||||
if (run_config == TestConfig::FLUSH_EVERY_1000) {
|
||||
if (i && i % 1000 == 0) {
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1402,7 +1404,7 @@ class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
Close();
|
||||
Reopen(options);
|
||||
} else if (run_config == TestConfig::COMPACT_BEFORE_READ) {
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
}
|
||||
|
||||
ReadOptions ro;
|
||||
@@ -1768,6 +1770,7 @@ TEST_P(DBIteratorTest, IterSeekForPrevCrossingFiles) {
|
||||
Iterator* iter = NewIterator(ro);
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
@@ -1823,6 +1826,7 @@ TEST_P(DBIteratorTest, IterSeekForPrevCrossingFilesCustomPrefixExtractor) {
|
||||
Iterator* iter = NewIterator(ro);
|
||||
iter->SeekForPrev("c2");
|
||||
ASSERT_TRUE(!iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
delete iter;
|
||||
}
|
||||
}
|
||||
@@ -2151,19 +2155,19 @@ TEST_P(DBIteratorTest, ReadAhead) {
|
||||
|
||||
std::string value(1024, 'a');
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Put(Key(i), value);
|
||||
ASSERT_OK(Put(Key(i), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Put(Key(i), value);
|
||||
ASSERT_OK(Put(Key(i), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Put(Key(i), value);
|
||||
ASSERT_OK(Put(Key(i), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -2270,6 +2274,7 @@ TEST_P(DBIteratorTest, Refresh) {
|
||||
ASSERT_OK(Put("x", "y"));
|
||||
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ReadOptions()));
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_EQ(iter->key().compare(Slice("x")), 0);
|
||||
@@ -2284,7 +2289,8 @@ TEST_P(DBIteratorTest, Refresh) {
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Refresh();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(iter->Refresh());
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -2295,7 +2301,7 @@ TEST_P(DBIteratorTest, Refresh) {
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
|
||||
ASSERT_OK(Put("m", "n"));
|
||||
|
||||
@@ -2308,7 +2314,8 @@ TEST_P(DBIteratorTest, Refresh) {
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
iter->Refresh();
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_OK(iter->Refresh());
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -2331,6 +2338,7 @@ TEST_P(DBIteratorTest, RefreshWithSnapshot) {
|
||||
ReadOptions options;
|
||||
options.snapshot = snapshot;
|
||||
Iterator* iter = NewIterator(options);
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
iter->Seek(Slice("a"));
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
@@ -2346,8 +2354,8 @@ TEST_P(DBIteratorTest, RefreshWithSnapshot) {
|
||||
iter->Next();
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
|
||||
Status s;
|
||||
s = iter->Refresh();
|
||||
ASSERT_OK(iter->status());
|
||||
Status s = iter->Refresh();
|
||||
ASSERT_TRUE(s.IsNotSupported());
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
delete iter;
|
||||
@@ -2405,14 +2413,14 @@ TEST_P(DBIteratorTest, UpperBoundWithChangeDirection) {
|
||||
|
||||
TEST_P(DBIteratorTest, TableFilter) {
|
||||
ASSERT_OK(Put("a", "1"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("b", "2"));
|
||||
ASSERT_OK(Put("c", "3"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
ASSERT_OK(Put("d", "4"));
|
||||
ASSERT_OK(Put("e", "5"));
|
||||
ASSERT_OK(Put("f", "6"));
|
||||
dbfull()->Flush(FlushOptions());
|
||||
EXPECT_OK(dbfull()->Flush(FlushOptions()));
|
||||
|
||||
// Ensure the table_filter callback is called once for each table.
|
||||
{
|
||||
@@ -2597,13 +2605,13 @@ TEST_P(DBIteratorTest, SeekAfterHittingManyInternalKeys) {
|
||||
ReadOptions ropts;
|
||||
ropts.max_skippable_internal_keys = 2;
|
||||
|
||||
Put("1", "val_1");
|
||||
ASSERT_OK(Put("1", "val_1"));
|
||||
// Add more tombstones than max_skippable_internal_keys so that Next() fails.
|
||||
Delete("2");
|
||||
Delete("3");
|
||||
Delete("4");
|
||||
Delete("5");
|
||||
Put("6", "val_6");
|
||||
ASSERT_OK(Delete("2"));
|
||||
ASSERT_OK(Delete("3"));
|
||||
ASSERT_OK(Delete("4"));
|
||||
ASSERT_OK(Delete("5"));
|
||||
ASSERT_OK(Put("6", "val_6"));
|
||||
|
||||
std::unique_ptr<Iterator> iter(NewIterator(ropts));
|
||||
iter->SeekToFirst();
|
||||
@@ -2645,9 +2653,9 @@ TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Two records in sst file, each in its own block.
|
||||
Put("b", "");
|
||||
Put("d", "");
|
||||
Flush();
|
||||
ASSERT_OK(Put("b", ""));
|
||||
ASSERT_OK(Put("d", ""));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Create a nonblocking iterator before writing to memtable.
|
||||
ReadOptions ropt;
|
||||
@@ -2657,7 +2665,7 @@ TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {
|
||||
// Overwrite a key in memtable many times to hit
|
||||
// max_sequential_skip_in_iterations (which is 8 by default).
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
Put("c", "");
|
||||
ASSERT_OK(Put("c", ""));
|
||||
}
|
||||
|
||||
// Load the second block in sst file into the block cache.
|
||||
@@ -2674,9 +2682,9 @@ TEST_P(DBIteratorTest, NonBlockingIterationBugRepro) {
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, SeekBackwardAfterOutOfUpperBound) {
|
||||
Put("a", "");
|
||||
Put("b", "");
|
||||
Flush();
|
||||
ASSERT_OK(Put("a", ""));
|
||||
ASSERT_OK(Put("b", ""));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions ropt;
|
||||
Slice ub = "b";
|
||||
@@ -2883,6 +2891,127 @@ TEST_P(DBIteratorTest, IterateWithLowerBoundAcrossFileBoundary) {
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
TEST_P(DBIteratorTest, Blob) {
|
||||
Options options = CurrentOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.max_sequential_skip_in_iterations = 2;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// Note: we have 4 KVs (3 of which are hidden) for key "b" and
|
||||
// max_sequential_skip_in_iterations is set to 2. Thus, we need to do a reseek
|
||||
// anytime we move from "b" to "c" or vice versa.
|
||||
ASSERT_OK(Put("a", "va"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("b", "vb0"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("b", "vb1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("b", "vb2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("b", "vb3"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("c", "vc"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::unique_ptr<Iterator> iter_guard(NewIterator(ReadOptions()));
|
||||
Iterator* const iter = iter_guard.get();
|
||||
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 0);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 1);
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 1);
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 1);
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 1);
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 1);
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
iter->Seek("");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Seek("a");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "a->va");
|
||||
iter->Seek("ax");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
|
||||
iter->SeekForPrev("d");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->SeekForPrev("c");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 2);
|
||||
ASSERT_EQ(IterStatus(iter), "c->vc");
|
||||
iter->SeekForPrev("bx");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 3);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
|
||||
iter->Seek("b");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 3);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
iter->Seek("z");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 3);
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
iter->SeekForPrev("b");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 4);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
iter->SeekForPrev("");
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 4);
|
||||
ASSERT_EQ(IterStatus(iter), "(invalid)");
|
||||
|
||||
// Switch from reverse to forward
|
||||
iter->SeekToLast();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 4);
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
|
||||
// Switch from forward to reverse
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 5);
|
||||
iter->Next();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 6);
|
||||
iter->Prev();
|
||||
ASSERT_EQ(TestGetTickerCount(options, NUMBER_OF_RESEEKS_IN_ITERATION), 7);
|
||||
ASSERT_EQ(IterStatus(iter), "b->vb3");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBIteratorTestInstance, DBIteratorTest,
|
||||
testing::Values(true, false));
|
||||
|
||||
@@ -3021,6 +3150,44 @@ TEST_F(DBIteratorWithReadCallbackTest, ReadCallback) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
TEST_F(DBIteratorTest, BackwardIterationOnInplaceUpdateMemtable) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.inplace_update_support = false;
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
constexpr int kNumKeys = 10;
|
||||
|
||||
// Write kNumKeys to WAL.
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), "val"));
|
||||
}
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
int count = 0;
|
||||
for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {
|
||||
++count;
|
||||
}
|
||||
ASSERT_EQ(kNumKeys, count);
|
||||
}
|
||||
|
||||
// Reopen and rebuild the memtable from WAL.
|
||||
options.create_if_missing = false;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.inplace_update_support = true;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
Reopen(options);
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->SeekToLast();
|
||||
// Backward iteration not supported due to inplace_update_support = true.
|
||||
ASSERT_TRUE(iter->status().IsNotSupported());
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -401,7 +401,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithDifferentPaths) {
|
||||
ColumnFamilyOptions cf_options0;
|
||||
cf_options0.cf_paths = {{cf_path_0_, 1024}};
|
||||
ColumnFamilyHandle* cf0;
|
||||
db0->CreateColumnFamily(cf_options0, "cf", &cf0);
|
||||
ASSERT_OK(db0->CreateColumnFamily(cf_options0, "cf", &cf0));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
@@ -421,7 +421,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithDifferentPaths) {
|
||||
ColumnFamilyOptions cf_options1;
|
||||
cf_options1.cf_paths = {{cf_path_1_, 1024}};
|
||||
ColumnFamilyHandle* cf1;
|
||||
db1->CreateColumnFamily(cf_options1, "cf", &cf1);
|
||||
ASSERT_OK(db1->CreateColumnFamily(cf_options1, "cf", &cf1));
|
||||
ASSERT_EQ(4, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
@@ -432,7 +432,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithDifferentPaths) {
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_1_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_1_));
|
||||
|
||||
db0->DestroyColumnFamilyHandle(cf0);
|
||||
ASSERT_OK(db0->DestroyColumnFamilyHandle(cf0));
|
||||
delete db0;
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_1_));
|
||||
@@ -441,7 +441,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithDifferentPaths) {
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_1_));
|
||||
ASSERT_OK(DestroyDB(data_path_0_, options, {{"cf", cf_options0}}));
|
||||
|
||||
db1->DestroyColumnFamilyHandle(cf1);
|
||||
ASSERT_OK(db1->DestroyColumnFamilyHandle(cf1));
|
||||
delete db1;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(data_path_1_, options, {{"cf", cf_options1}}));
|
||||
@@ -466,7 +466,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithSamePaths) {
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
|
||||
ColumnFamilyHandle* cf0;
|
||||
db0->CreateColumnFamily(cf_options, "cf", &cf0);
|
||||
ASSERT_OK(db0->CreateColumnFamily(cf_options, "cf", &cf0));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(1, cache_->GetRefCount(data_path_0_));
|
||||
@@ -482,14 +482,14 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithSamePaths) {
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
ColumnFamilyHandle* cf1;
|
||||
db1->CreateColumnFamily(cf_options, "cf", &cf1);
|
||||
ASSERT_OK(db1->CreateColumnFamily(cf_options, "cf", &cf1));
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(data_path_0_));
|
||||
ASSERT_TRUE(cache_->Contains(cf_path_0_));
|
||||
ASSERT_EQ(2, cache_->GetRefCount(cf_path_0_));
|
||||
|
||||
db0->DestroyColumnFamilyHandle(cf0);
|
||||
ASSERT_OK(db0->DestroyColumnFamilyHandle(cf0));
|
||||
delete db0;
|
||||
ASSERT_EQ(2, cache_->Size());
|
||||
ASSERT_TRUE(cache_->Contains(data_path_0_));
|
||||
@@ -498,7 +498,7 @@ TEST_F(DBLogicalBlockSizeCacheTest, MultiDBWithSamePaths) {
|
||||
ASSERT_EQ(1, cache_->GetRefCount(cf_path_0_));
|
||||
ASSERT_OK(DestroyDB(dbname_ + "/db0", options, {{"cf", cf_options}}));
|
||||
|
||||
db1->DestroyColumnFamilyHandle(cf1);
|
||||
ASSERT_OK(db1->DestroyColumnFamilyHandle(cf1));
|
||||
delete db1;
|
||||
ASSERT_EQ(0, cache_->Size());
|
||||
ASSERT_OK(DestroyDB(dbname_ + "/db1", options, {{"cf", cf_options}}));
|
||||
|
||||
+25
-36
@@ -129,7 +129,6 @@ class TestPrefixExtractor : public SliceTransform {
|
||||
TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
SequenceNumber seq = 123;
|
||||
std::string value;
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
Options options;
|
||||
InternalKeyComparator ikey_cmp(options.comparator);
|
||||
@@ -146,22 +145,15 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
|
||||
// Write some keys and make sure it returns false on duplicates
|
||||
bool res;
|
||||
res = mem->Add(seq, kTypeValue, "key", "value2");
|
||||
ASSERT_TRUE(res);
|
||||
res = mem->Add(seq, kTypeValue, "key", "value2");
|
||||
ASSERT_FALSE(res);
|
||||
ASSERT_OK(mem->Add(seq, kTypeValue, "key", "value2"));
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeValue, "key", "value2").IsTryAgain());
|
||||
// Changing the type should still cause the duplicatae key
|
||||
res = mem->Add(seq, kTypeMerge, "key", "value2");
|
||||
ASSERT_FALSE(res);
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeMerge, "key", "value2").IsTryAgain());
|
||||
// Changing the seq number will make the key fresh
|
||||
res = mem->Add(seq + 1, kTypeMerge, "key", "value2");
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_OK(mem->Add(seq + 1, kTypeMerge, "key", "value2"));
|
||||
// Test with different types for duplicate keys
|
||||
res = mem->Add(seq, kTypeDeletion, "key", "");
|
||||
ASSERT_FALSE(res);
|
||||
res = mem->Add(seq, kTypeSingleDeletion, "key", "");
|
||||
ASSERT_FALSE(res);
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeDeletion, "key", "").IsTryAgain());
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeSingleDeletion, "key", "").IsTryAgain());
|
||||
|
||||
// Test the duplicate keys under stress
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
@@ -169,11 +161,11 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
if (!insert_dup) {
|
||||
seq++;
|
||||
}
|
||||
res = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq));
|
||||
Status s = mem->Add(seq, kTypeValue, "foo", "value" + ToString(seq));
|
||||
if (insert_dup) {
|
||||
ASSERT_FALSE(res);
|
||||
ASSERT_TRUE(s.IsTryAgain());
|
||||
} else {
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
delete mem;
|
||||
@@ -185,10 +177,8 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
// Insert a duplicate key with _ in it
|
||||
res = mem->Add(seq, kTypeValue, "key_1", "value");
|
||||
ASSERT_TRUE(res);
|
||||
res = mem->Add(seq, kTypeValue, "key_1", "value");
|
||||
ASSERT_FALSE(res);
|
||||
ASSERT_OK(mem->Add(seq, kTypeValue, "key_1", "value"));
|
||||
ASSERT_TRUE(mem->Add(seq, kTypeValue, "key_1", "value").IsTryAgain());
|
||||
delete mem;
|
||||
|
||||
// Test when InsertConcurrently will be invoked
|
||||
@@ -197,10 +187,11 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
mem = new MemTable(cmp, ioptions, MutableCFOptions(options), &wb,
|
||||
kMaxSequenceNumber, 0 /* column_family_id */);
|
||||
MemTablePostProcessInfo post_process_info;
|
||||
res = mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info);
|
||||
ASSERT_TRUE(res);
|
||||
res = mem->Add(seq, kTypeValue, "key", "value", true, &post_process_info);
|
||||
ASSERT_FALSE(res);
|
||||
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());
|
||||
delete mem;
|
||||
}
|
||||
|
||||
@@ -208,7 +199,6 @@ TEST_F(DBMemTableTest, DuplicateSeq) {
|
||||
TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
int num_ops = 1000;
|
||||
std::string value;
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
Options options;
|
||||
// A merge operator that is not sensitive to concurrent writes since in this
|
||||
@@ -227,8 +217,7 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
|
||||
// Put 0 as the base
|
||||
PutFixed64(&value, static_cast<uint64_t>(0));
|
||||
bool res = mem->Add(0, kTypeValue, "key", value);
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_OK(mem->Add(0, kTypeValue, "key", value));
|
||||
value.clear();
|
||||
|
||||
// Write Merge concurrently
|
||||
@@ -237,9 +226,8 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
std::string v1;
|
||||
for (int seq = 1; seq < num_ops / 2; seq++) {
|
||||
PutFixed64(&v1, seq);
|
||||
bool res1 =
|
||||
mem->Add(seq, kTypeMerge, "key", v1, true, &post_process_info1);
|
||||
ASSERT_TRUE(res1);
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeMerge, "key", v1, true, &post_process_info1));
|
||||
v1.clear();
|
||||
}
|
||||
});
|
||||
@@ -248,9 +236,8 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
std::string v2;
|
||||
for (int seq = num_ops / 2; seq < num_ops; seq++) {
|
||||
PutFixed64(&v2, seq);
|
||||
bool res2 =
|
||||
mem->Add(seq, kTypeMerge, "key", v2, true, &post_process_info2);
|
||||
ASSERT_TRUE(res2);
|
||||
ASSERT_OK(
|
||||
mem->Add(seq, kTypeMerge, "key", v2, true, &post_process_info2));
|
||||
v2.clear();
|
||||
}
|
||||
});
|
||||
@@ -261,8 +248,9 @@ TEST_F(DBMemTableTest, ConcurrentMergeWrite) {
|
||||
ReadOptions roptions;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
LookupKey lkey("key", kMaxSequenceNumber);
|
||||
res = mem->Get(lkey, &value, /*timestamp=*/nullptr, &status, &merge_context,
|
||||
&max_covering_tombstone_seq, roptions);
|
||||
bool res = mem->Get(lkey, &value, /*timestamp=*/nullptr, &status,
|
||||
&merge_context, &max_covering_tombstone_seq, roptions);
|
||||
ASSERT_OK(status);
|
||||
ASSERT_TRUE(res);
|
||||
uint64_t ivalue = DecodeFixed64(Slice(value).data());
|
||||
uint64_t sum = 0;
|
||||
@@ -316,6 +304,7 @@ TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
TEST_F(DBMemTableTest, ColumnFamilyId) {
|
||||
// Verifies MemTableRepFactory is told the right column family id.
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.allow_concurrent_memtable_write = false;
|
||||
options.create_if_missing = true;
|
||||
options.memtable_factory.reset(new MockMemTableRepFactory());
|
||||
|
||||
+47
-47
@@ -59,29 +59,29 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
// k0 value in memtable
|
||||
Put("k0", "PutARock");
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k0",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(Put("k0", "PutARock"));
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k0", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "PutARock");
|
||||
|
||||
// k0.1 value in SST
|
||||
Put("k0.1", "RockInSST");
|
||||
ASSERT_OK(Put("k0.1", "RockInSST"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k0.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k0.1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "RockInSST");
|
||||
|
||||
// All k1 values are in memtable.
|
||||
ASSERT_OK(Merge("k1", "a"));
|
||||
Put("k1", "x");
|
||||
ASSERT_OK(Put("k1", "x"));
|
||||
ASSERT_OK(Merge("k1", "b"));
|
||||
ASSERT_OK(Merge("k1", "c"));
|
||||
ASSERT_OK(Merge("k1", "d"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "x");
|
||||
ASSERT_EQ(values[1], "b");
|
||||
ASSERT_EQ(values[2], "c");
|
||||
@@ -98,13 +98,13 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
|
||||
// All k1.1 values are in memtable.
|
||||
ASSERT_OK(Merge("k1.1", "r"));
|
||||
Delete("k1.1");
|
||||
ASSERT_OK(Delete("k1.1"));
|
||||
ASSERT_OK(Merge("k1.1", "c"));
|
||||
ASSERT_OK(Merge("k1.1", "k"));
|
||||
ASSERT_OK(Merge("k1.1", "s"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k1.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k1.1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "c");
|
||||
ASSERT_EQ(values[1], "k");
|
||||
ASSERT_EQ(values[2], "s");
|
||||
@@ -115,9 +115,9 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(Merge("k2", "e"));
|
||||
ASSERT_OK(Merge("k2", "r"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k2",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k2", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "q");
|
||||
ASSERT_EQ(values[1], "w");
|
||||
ASSERT_EQ(values[2], "e");
|
||||
@@ -125,30 +125,30 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
|
||||
// All k2.1 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Merge("k2.1", "m"));
|
||||
Put("k2.1", "l");
|
||||
ASSERT_OK(Put("k2.1", "l"));
|
||||
ASSERT_OK(Merge("k2.1", "n"));
|
||||
ASSERT_OK(Merge("k2.1", "o"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k2.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k2.1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "l,n,o");
|
||||
|
||||
// All k2.2 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Merge("k2.2", "g"));
|
||||
Delete("k2.2");
|
||||
ASSERT_OK(Delete("k2.2"));
|
||||
ASSERT_OK(Merge("k2.2", "o"));
|
||||
ASSERT_OK(Merge("k2.2", "t"));
|
||||
ASSERT_OK(Flush());
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k2.2",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k2.2", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "o,t");
|
||||
|
||||
// Do some compaction that will make the following tests more predictable
|
||||
// Slice start("PutARock");
|
||||
// Slice end("t");
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// All k3 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3", "ab"));
|
||||
@@ -158,9 +158,9 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(Merge("k3", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "de"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k3",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k3", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "ab");
|
||||
ASSERT_EQ(values[1], "bc");
|
||||
ASSERT_EQ(values[2], "cd");
|
||||
@@ -169,14 +169,14 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
// All k3.1 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3.1", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
Put("k3.1", "bc");
|
||||
ASSERT_OK(Put("k3.1", "bc"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.1", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.1", "de"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k3.1",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k3.1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "bc");
|
||||
ASSERT_EQ(values[1], "cd");
|
||||
ASSERT_EQ(values[2], "de");
|
||||
@@ -184,14 +184,14 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
// All k3.2 values are flushed and are in different files.
|
||||
ASSERT_OK(Merge("k3.2", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
Delete("k3.2");
|
||||
ASSERT_OK(Delete("k3.2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.2", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3.2", "de"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k3.2",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k3.2", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "cd");
|
||||
ASSERT_EQ(values[1], "de");
|
||||
|
||||
@@ -206,9 +206,9 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Merge("k4", "ed"));
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k4",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k4", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "ba");
|
||||
ASSERT_EQ(values[1], "cb");
|
||||
ASSERT_EQ(values[2], "dc");
|
||||
@@ -219,14 +219,14 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_OK(Merge("k5", "am"));
|
||||
ASSERT_OK(Merge("k5", "i"));
|
||||
ASSERT_OK(Flush());
|
||||
Put("k5", "remember");
|
||||
ASSERT_OK(Put("k5", "remember"));
|
||||
ASSERT_OK(Merge("k5", "i"));
|
||||
ASSERT_OK(Merge("k5", "am"));
|
||||
ASSERT_OK(Merge("k5", "rocks"));
|
||||
dbfull()->TEST_SwitchMemtable();
|
||||
db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(), "k5",
|
||||
values.data(), &merge_operands_info,
|
||||
&number_of_operands);
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable());
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k5", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "remember");
|
||||
ASSERT_EQ(values[1], "i");
|
||||
ASSERT_EQ(values[2], "am");
|
||||
|
||||
@@ -37,7 +37,7 @@ class TestReadCallback : public ReadCallback {
|
||||
class DBMergeOperatorTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperatorTest()
|
||||
: DBTestBase("/db_merge_operator_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("/db_merge_operator_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
std::string GetWithReadCallback(SnapshotChecker* snapshot_checker,
|
||||
const Slice& key,
|
||||
@@ -94,7 +94,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
ASSERT_OK(Merge("k1", "c"));
|
||||
ASSERT_OK(Merge("k1", "d"));
|
||||
std::string value;
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k1", &value).ok());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k1", &value));
|
||||
// Make sure that only the latest two merge operands are used. If this was
|
||||
// not the case the value would be "a,b,c,d".
|
||||
ASSERT_EQ(value, "c,d");
|
||||
@@ -105,7 +105,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
ASSERT_OK(Merge("k2", "c"));
|
||||
ASSERT_OK(Merge("k2", "d"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k2", &value).ok());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k2", &value));
|
||||
ASSERT_EQ(value, "c,d");
|
||||
|
||||
// All K3 values are flushed and are in different files.
|
||||
@@ -116,7 +116,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
ASSERT_OK(Merge("k3", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "de"));
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k3", &value).ok());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k3", &value));
|
||||
ASSERT_EQ(value, "cd,de");
|
||||
|
||||
// All K4 values are in different levels
|
||||
@@ -130,7 +130,7 @@ TEST_F(DBMergeOperatorTest, LimitMergeOperands) {
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Merge("k4", "de"));
|
||||
ASSERT_TRUE(db_->Get(ReadOptions(), "k4", &value).ok());
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "k4", &value));
|
||||
ASSERT_EQ(value, "cd,de");
|
||||
}
|
||||
|
||||
@@ -344,8 +344,9 @@ TEST_P(MergeOperatorPinningTest, EvictCacheBeforeMerge) {
|
||||
// Code executed before merge operation
|
||||
merge_hook->before_merge_ = [&]() {
|
||||
// Evict all tables from cache before every merge operation
|
||||
auto* table_cache = dbfull()->TEST_table_cache();
|
||||
for (uint64_t num : file_numbers) {
|
||||
TableCache::Evict(dbfull()->TEST_table_cache(), num);
|
||||
TableCache::Evict(table_cache, num);
|
||||
}
|
||||
// Decrease cache capacity to force all unrefed blocks to be evicted
|
||||
if (bbto.block_cache) {
|
||||
@@ -366,7 +367,7 @@ TEST_P(MergeOperatorPinningTest, EvictCacheBeforeMerge) {
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
ASSERT_EQ(merge_cnt, total_reads);
|
||||
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
VerifyDBFromMap(true_data, &total_reads);
|
||||
}
|
||||
@@ -385,7 +386,7 @@ TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
std::function<void()> writer_func = [&]() {
|
||||
int k = 0;
|
||||
for (int i = 0; i < kNumWrites; i++) {
|
||||
db_->Merge(WriteOptions(), Key(k), Key(k));
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), Key(k), Key(k)));
|
||||
|
||||
if (i && i % kNumOperands == 0) {
|
||||
k++;
|
||||
@@ -403,7 +404,7 @@ TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
ReadOptions ro;
|
||||
ro.tailing = true;
|
||||
Iterator* iter = db_->NewIterator(ro);
|
||||
|
||||
ASSERT_OK(iter->status());
|
||||
iter->SeekToFirst();
|
||||
for (int i = 0; i < (kNumWrites / kNumOperands); i++) {
|
||||
while (!iter->Valid()) {
|
||||
@@ -416,6 +417,7 @@ TEST_P(MergeOperatorPinningTest, TailingIterator) {
|
||||
|
||||
iter->Next();
|
||||
}
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
delete iter;
|
||||
};
|
||||
@@ -449,12 +451,13 @@ TEST_F(DBMergeOperatorTest, TailingIteratorMemtableUnrefedBySomeoneElse) {
|
||||
// ForwardIterator to not pin it in some circumstances. This test
|
||||
// reproduces it.
|
||||
|
||||
db_->Merge(WriteOptions(), "key", "sst");
|
||||
db_->Flush(FlushOptions()); // Switch to SuperVersion A
|
||||
db_->Merge(WriteOptions(), "key", "memtable");
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", "sst"));
|
||||
ASSERT_OK(db_->Flush(FlushOptions())); // Switch to SuperVersion A
|
||||
ASSERT_OK(db_->Merge(WriteOptions(), "key", "memtable"));
|
||||
|
||||
// Pin SuperVersion A
|
||||
std::unique_ptr<Iterator> someone_else(db_->NewIterator(ReadOptions()));
|
||||
ASSERT_OK(someone_else->status());
|
||||
|
||||
bool pushed_first_operand = false;
|
||||
bool stepped_to_next_operand = false;
|
||||
@@ -462,7 +465,7 @@ TEST_F(DBMergeOperatorTest, TailingIteratorMemtableUnrefedBySomeoneElse) {
|
||||
"DBIter::MergeValuesNewToOld:PushedFirstOperand", [&](void*) {
|
||||
EXPECT_FALSE(pushed_first_operand);
|
||||
pushed_first_operand = true;
|
||||
db_->Flush(FlushOptions()); // Switch to SuperVersion B
|
||||
EXPECT_OK(db_->Flush(FlushOptions())); // Switch to SuperVersion B
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBIter::MergeValuesNewToOld:SteppedToNextOperand", [&](void*) {
|
||||
@@ -477,7 +480,7 @@ TEST_F(DBMergeOperatorTest, TailingIteratorMemtableUnrefedBySomeoneElse) {
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(ro));
|
||||
iter->Seek("key");
|
||||
|
||||
ASSERT_TRUE(iter->status().ok());
|
||||
ASSERT_OK(iter->status());
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
EXPECT_EQ(std::string("sst,memtable"), iter->value().ToString());
|
||||
EXPECT_TRUE(pushed_first_operand);
|
||||
|
||||
@@ -81,6 +81,7 @@ class DBOptionsTest : public DBTestBase {
|
||||
|
||||
TEST_F(DBOptionsTest, ImmutableTrackAndVerifyWalsInManifest) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
|
||||
ImmutableDBOptions db_options(options);
|
||||
@@ -621,6 +622,7 @@ TEST_F(DBOptionsTest, MaxOpenFilesChange) {
|
||||
|
||||
TEST_F(DBOptionsTest, SanitizeDelayedWriteRate) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.delayed_write_rate = 0;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(16 * 1024 * 1024, dbfull()->GetDBOptions().delayed_write_rate);
|
||||
@@ -632,6 +634,7 @@ TEST_F(DBOptionsTest, SanitizeDelayedWriteRate) {
|
||||
|
||||
TEST_F(DBOptionsTest, SanitizeUniversalTTLCompaction) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
options.ttl = 0;
|
||||
@@ -661,6 +664,7 @@ TEST_F(DBOptionsTest, SanitizeUniversalTTLCompaction) {
|
||||
|
||||
TEST_F(DBOptionsTest, SanitizeTtlDefault) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(30 * 24 * 60 * 60, dbfull()->GetOptions().ttl);
|
||||
|
||||
@@ -677,6 +681,7 @@ TEST_F(DBOptionsTest, SanitizeTtlDefault) {
|
||||
TEST_F(DBOptionsTest, SanitizeFIFOPeriodicCompaction) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.env = CurrentOptions().env;
|
||||
options.ttl = 0;
|
||||
Reopen(options);
|
||||
ASSERT_EQ(30 * 24 * 60 * 60, dbfull()->GetOptions().ttl);
|
||||
@@ -702,6 +707,7 @@ TEST_F(DBOptionsTest, SanitizeFIFOPeriodicCompaction) {
|
||||
|
||||
TEST_F(DBOptionsTest, SetFIFOCompactionOptions) {
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.arena_block_size = 4096;
|
||||
@@ -841,6 +847,7 @@ TEST_F(DBOptionsTest, FIFOTtlBackwardCompatible) {
|
||||
options.compaction_style = kCompactionStyleFIFO;
|
||||
options.write_buffer_size = 10 << 10; // 10KB
|
||||
options.create_if_missing = true;
|
||||
options.env = CurrentOptions().env;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
@@ -894,6 +901,7 @@ TEST_F(DBOptionsTest, ChangeCompression) {
|
||||
options.bottommost_compression = CompressionType::kNoCompression;
|
||||
options.bottommost_compression_opts.level = 2;
|
||||
options.bottommost_compression_opts.parallel_threads = 1;
|
||||
options.env = CurrentOptions().env;
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
@@ -944,6 +952,66 @@ TEST_F(DBOptionsTest, ChangeCompression) {
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBOptionsTest, BottommostCompressionOptsWithFallbackType) {
|
||||
// Verify the bottommost compression options still take effect even when the
|
||||
// bottommost compression type is left at its default value. Verify for both
|
||||
// automatic and manual compaction.
|
||||
if (!Snappy_Supported() || !LZ4_Supported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int kUpperCompressionLevel = 1;
|
||||
constexpr int kBottommostCompressionLevel = 2;
|
||||
constexpr int kNumL0Files = 2;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.compression = CompressionType::kLZ4Compression;
|
||||
options.compression_opts.level = kUpperCompressionLevel;
|
||||
options.bottommost_compression_opts.level = kBottommostCompressionLevel;
|
||||
options.bottommost_compression_opts.enabled = true;
|
||||
Reopen(options);
|
||||
|
||||
CompressionType compression_used = CompressionType::kDisableCompressionOption;
|
||||
CompressionOptions compression_opt_used;
|
||||
bool compacted = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::RegisterCompaction:Registered", [&](void* arg) {
|
||||
Compaction* c = static_cast<Compaction*>(arg);
|
||||
compression_used = c->output_compression();
|
||||
compression_opt_used = c->output_compression_opts();
|
||||
compacted = true;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// First, verify for automatic compaction.
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
ASSERT_OK(Put("foo", "foofoofoo"));
|
||||
ASSERT_OK(Put("bar", "foofoofoo"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ASSERT_TRUE(compacted);
|
||||
ASSERT_EQ(CompressionType::kLZ4Compression, compression_used);
|
||||
ASSERT_EQ(kBottommostCompressionLevel, compression_opt_used.level);
|
||||
|
||||
// Second, verify for manual compaction.
|
||||
compacted = false;
|
||||
compression_used = CompressionType::kDisableCompressionOption;
|
||||
compression_opt_used = CompressionOptions();
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_TRUE(compacted);
|
||||
ASSERT_EQ(CompressionType::kLZ4Compression, compression_used);
|
||||
ASSERT_EQ(kBottommostCompressionLevel, compression_opt_used.level);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -336,7 +336,7 @@ TEST_F(DBPropertiesTest, AggregatedTableProperties) {
|
||||
table_options.filter_policy.reset(
|
||||
NewBloomFilterPolicy(kBloomBitsPerKey, false));
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -536,7 +536,7 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
table_options.filter_policy.reset(
|
||||
NewBloomFilterPolicy(kBloomBitsPerKey, false));
|
||||
table_options.block_size = 1024;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -1414,7 +1414,7 @@ TEST_F(DBPropertiesTest, NeedCompactHintPersistentTest) {
|
||||
}
|
||||
|
||||
TEST_F(DBPropertiesTest, EstimateNumKeysUnderflow) {
|
||||
Options options;
|
||||
Options options = CurrentOptions();
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Delete("foo"));
|
||||
@@ -1524,6 +1524,7 @@ TEST_F(DBPropertiesTest, SstFilesSize) {
|
||||
std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
|
||||
|
||||
Options options;
|
||||
options.env = CurrentOptions().env;
|
||||
options.disable_auto_compactions = true;
|
||||
options.listeners.push_back(listener);
|
||||
Reopen(options);
|
||||
@@ -1608,6 +1609,8 @@ TEST_F(DBPropertiesTest, BlockCacheProperties) {
|
||||
Options options;
|
||||
uint64_t value;
|
||||
|
||||
options.env = CurrentOptions().env;
|
||||
|
||||
// Block cache properties are not available for tables other than
|
||||
// block-based table.
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class DBRangeDelTest : public DBTestBase {
|
||||
public:
|
||||
DBRangeDelTest() : DBTestBase("/db_range_del_test", /*env_do_fsync=*/true) {}
|
||||
DBRangeDelTest() : DBTestBase("/db_range_del_test", /*env_do_fsync=*/false) {}
|
||||
|
||||
std::string GetNumericStr(int key) {
|
||||
uint64_t uint64_key = static_cast<uint64_t>(key);
|
||||
@@ -159,7 +159,7 @@ TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
// Want max_compaction_bytes to trigger the end of compaction output file, not
|
||||
// target_file_size_base, so make the latter much bigger
|
||||
opts.target_file_size_base = 100 * opts.max_compaction_bytes;
|
||||
Reopen(opts);
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
// snapshot protects range tombstone from dropping due to becoming obsolete.
|
||||
const Snapshot* snapshot = db_->GetSnapshot();
|
||||
@@ -269,7 +269,7 @@ TEST_F(DBRangeDelTest, FlushRemovesCoveredKeys) {
|
||||
const int kNum = 300, kRangeBegin = 50, kRangeEnd = 250;
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
Reopen(opts);
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
// Write a third before snapshot, a third between snapshot and tombstone, and
|
||||
// a third after the tombstone. Keys older than snapshot or newer than the
|
||||
@@ -309,7 +309,7 @@ TEST_F(DBRangeDelTest, CompactionRemovesCoveredKeys) {
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
opts.num_levels = 2;
|
||||
opts.statistics = CreateDBStatistics();
|
||||
Reopen(opts);
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
for (int i = 0; i < kNumFiles; ++i) {
|
||||
if (i > 0) {
|
||||
@@ -603,7 +603,7 @@ TEST_F(DBRangeDelTest, TableEvictedDuringScan) {
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.block_cache = NewLRUCache(8 << 20);
|
||||
opts.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(opts);
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
// Hold a snapshot so range deletions can't become obsolete during compaction
|
||||
// to bottommost level (i.e., L1).
|
||||
@@ -761,7 +761,7 @@ TEST_F(DBRangeDelTest, IteratorRemovesCoveredKeys) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
Reopen(opts);
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
// Write half of the keys before the tombstone and half after the tombstone.
|
||||
// Only covered keys (i.e., within the range and older than the tombstone)
|
||||
@@ -794,7 +794,7 @@ TEST_F(DBRangeDelTest, IteratorOverUserSnapshot) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.comparator = test::Uint64Comparator();
|
||||
opts.memtable_factory.reset(new SpecialSkipListFactory(kNumPerFile));
|
||||
Reopen(opts);
|
||||
DestroyAndReopen(opts);
|
||||
|
||||
const Snapshot* snapshot = nullptr;
|
||||
// Put a snapshot before the range tombstone, verify an iterator using that
|
||||
|
||||
+2
-1
@@ -141,6 +141,7 @@ TEST_F(DBSSTTest, SkipCheckingSSTFileSizesOnDBOpen) {
|
||||
|
||||
// Just open the DB with the option set to true and check that we don't crash.
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.skip_checking_sst_file_sizes_on_db_open = true;
|
||||
Reopen(options);
|
||||
|
||||
@@ -519,7 +520,7 @@ TEST_P(DBWALTestWithParam, WALTrashCleanupOnOpen) {
|
||||
bool fake_log_delete;
|
||||
};
|
||||
|
||||
std::unique_ptr<MyEnv> env(new MyEnv(Env::Default()));
|
||||
std::unique_ptr<MyEnv> env(new MyEnv(env_));
|
||||
Destroy(last_options_);
|
||||
|
||||
env->set_fake_log_delete(true);
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "table/format.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/random.h"
|
||||
@@ -50,7 +52,7 @@ class DBTablePropertiesTest : public DBTestBase,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBTablePropertiesTest()
|
||||
: DBTestBase("/db_table_properties_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("/db_table_properties_test", /*env_do_fsync=*/false) {}
|
||||
TablePropertiesCollection TestGetPropertiesOfTablesInRange(
|
||||
std::vector<Range> ranges, std::size_t* num_properties = nullptr,
|
||||
std::size_t* num_files = nullptr);
|
||||
@@ -274,6 +276,55 @@ TEST_F(DBTablePropertiesTest, GetDbIdentifiersProperty) {
|
||||
}
|
||||
}
|
||||
|
||||
class DBTableHostnamePropertyTest
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<std::tuple<int, std::string>> {
|
||||
public:
|
||||
DBTableHostnamePropertyTest()
|
||||
: DBTestBase("/db_table_hostname_property_test",
|
||||
/*env_do_fsync=*/false) {}
|
||||
};
|
||||
|
||||
TEST_P(DBTableHostnamePropertyTest, DbHostLocationProperty) {
|
||||
option_config_ = std::get<0>(GetParam());
|
||||
Options opts = CurrentOptions();
|
||||
std::string expected_host_id = std::get<1>(GetParam());
|
||||
;
|
||||
if (expected_host_id == kHostnameForDbHostId) {
|
||||
ASSERT_OK(env_->GetHostNameString(&expected_host_id));
|
||||
} else {
|
||||
opts.db_host_id = expected_host_id;
|
||||
}
|
||||
CreateAndReopenWithCF({"goku"}, opts);
|
||||
|
||||
for (uint32_t cf = 0; cf < 2; ++cf) {
|
||||
Put(cf, "key", "val");
|
||||
Put(cf, "foo", "bar");
|
||||
Flush(cf);
|
||||
|
||||
TablePropertiesCollection fname_to_props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(handles_[cf], &fname_to_props));
|
||||
ASSERT_EQ(1U, fname_to_props.size());
|
||||
|
||||
ASSERT_EQ(fname_to_props.begin()->second->db_host_id, expected_host_id);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTableHostnamePropertyTest, DBTableHostnamePropertyTest,
|
||||
::testing::Values(
|
||||
// OptionConfig, override db_host_location
|
||||
std::make_tuple(DBTestBase::OptionConfig::kDefault,
|
||||
kHostnameForDbHostId),
|
||||
std::make_tuple(DBTestBase::OptionConfig::kDefault, "foobar"),
|
||||
std::make_tuple(DBTestBase::OptionConfig::kDefault, ""),
|
||||
std::make_tuple(DBTestBase::OptionConfig::kPlainTableFirstBytePrefix,
|
||||
kHostnameForDbHostId),
|
||||
std::make_tuple(DBTestBase::OptionConfig::kPlainTableFirstBytePrefix,
|
||||
"foobar"),
|
||||
std::make_tuple(DBTestBase::OptionConfig::kPlainTableFirstBytePrefix,
|
||||
"")));
|
||||
|
||||
class DeletionTriggeredCompactionTestListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
|
||||
@@ -370,6 +421,54 @@ TEST_P(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
|
||||
ASSERT_LT(0, opts.statistics->getTickerCount(COMPACT_READ_BYTES_MARKED));
|
||||
}
|
||||
|
||||
TEST_P(DBTablePropertiesTest, RatioBasedDeletionTriggeredCompactionMarking) {
|
||||
constexpr int kNumKeys = 1000;
|
||||
constexpr int kWindowSize = 0;
|
||||
constexpr int kNumDelsTrigger = 0;
|
||||
constexpr double kDeletionRatio = 0.1;
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> compact_on_del =
|
||||
NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger,
|
||||
kDeletionRatio);
|
||||
|
||||
Options opts = CurrentOptions();
|
||||
opts.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
opts.table_properties_collector_factories.emplace_back(compact_on_del);
|
||||
|
||||
Reopen(opts);
|
||||
|
||||
// Add an L2 file to prevent tombstones from dropping due to obsolescence
|
||||
// during flush
|
||||
Put(Key(0), "val");
|
||||
Flush();
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
auto* listener = new DeletionTriggeredCompactionTestListener();
|
||||
opts.listeners.emplace_back(listener);
|
||||
Reopen(opts);
|
||||
|
||||
// Generate one L0 with kNumKeys Put.
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Put(Key(i), "not important"));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Generate another L0 with kNumKeys Delete.
|
||||
// This file, due to deletion ratio, will trigger compaction: 2@0 files to L1.
|
||||
// The resulting L1 file has only one tombstone for user key 'Key(0)'.
|
||||
// Again, due to deletion ratio, a compaction will be triggered: 1@1 + 1@2
|
||||
// files to L2. However, the resulting file is empty because the tombstone
|
||||
// and value are both dropped.
|
||||
for (int i = 0; i < kNumKeys; ++i) {
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(i));
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTablePropertiesTest,
|
||||
DBTablePropertiesTest,
|
||||
|
||||
@@ -2391,6 +2391,7 @@ TEST_F(DBTest, PurgeInfoLogs) {
|
||||
Options options = CurrentOptions();
|
||||
options.keep_log_file_num = 5;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
for (int mode = 0; mode <= 1; mode++) {
|
||||
if (mode == 1) {
|
||||
options.db_log_dir = dbname_ + "_logs";
|
||||
@@ -3947,6 +3948,7 @@ TEST_F(DBTest, WriteSingleThreadEntry) {
|
||||
TEST_F(DBTest, ConcurrentFlushWAL) {
|
||||
const size_t cnt = 100;
|
||||
Options options;
|
||||
options.env = env_;
|
||||
WriteOptions wopt;
|
||||
ReadOptions ropt;
|
||||
for (bool two_write_queues : {false, true}) {
|
||||
@@ -4615,6 +4617,7 @@ TEST_F(DBTest, DynamicLevelCompressionPerLevel) {
|
||||
|
||||
Random rnd(301);
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.db_write_buffer_size = 20480;
|
||||
options.write_buffer_size = 20480;
|
||||
@@ -5017,6 +5020,7 @@ TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
Options options;
|
||||
options.ttl = 0;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Initial defaults
|
||||
@@ -5078,6 +5082,7 @@ TEST_F(DBTest, DynamicFIFOCompactionOptions) {
|
||||
TEST_F(DBTest, DynamicUniversalCompactionOptions) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Initial defaults
|
||||
|
||||
+226
-29
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/read_callback.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/persistent_cache.h"
|
||||
@@ -104,6 +105,7 @@ class TestReadOnlyWithCompressedCache
|
||||
|
||||
TEST_P(TestReadOnlyWithCompressedCache, ReadOnlyWithCompressedCache) {
|
||||
if (use_mmap_ && !IsMemoryMappedAccessSupported()) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires MMAP support");
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
@@ -172,7 +174,9 @@ TEST_F(DBTest2, PartitionedIndexUserToInternalKey) {
|
||||
ASSERT_OK(Put("keykey_" + std::to_string(j), value));
|
||||
snapshots.push_back(db_->GetSnapshot());
|
||||
}
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
|
||||
for (auto s : snapshots) {
|
||||
db_->ReleaseSnapshot(s);
|
||||
}
|
||||
@@ -291,7 +295,7 @@ TEST_F(DBTest2, CacheIndexAndFilterWithDBRestart) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
Put(1, "a", "begin");
|
||||
@@ -1344,7 +1348,7 @@ TEST_F(DBTest2, PresetCompressionDictLocality) {
|
||||
options.target_file_size_base = kNumEntriesPerFile * kNumBytesPerEntry;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -1389,6 +1393,178 @@ TEST_F(DBTest2, PresetCompressionDictLocality) {
|
||||
}
|
||||
}
|
||||
|
||||
class PresetCompressionDictTest
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<CompressionType, bool>> {
|
||||
public:
|
||||
PresetCompressionDictTest()
|
||||
: DBTestBase("/db_test2", false /* env_do_fsync */),
|
||||
compression_type_(std::get<0>(GetParam())),
|
||||
bottommost_(std::get<1>(GetParam())) {}
|
||||
|
||||
protected:
|
||||
const CompressionType compression_type_;
|
||||
const bool bottommost_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTest2, PresetCompressionDictTest,
|
||||
::testing::Combine(::testing::ValuesIn(GetSupportedDictCompressions()),
|
||||
::testing::Bool()));
|
||||
|
||||
TEST_P(PresetCompressionDictTest, Flush) {
|
||||
// Verifies that dictionary is generated and written during flush only when
|
||||
// `ColumnFamilyOptions::compression` enables dictionary.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 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;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
}
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kKeysPerFile));
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
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;
|
||||
if (bottommost_) {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses;
|
||||
} else {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses + 1;
|
||||
}
|
||||
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.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 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;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
}
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (size_t j = 0; j <= kKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (size_t j = 0; j <= kKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,0,1", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_compression_dict_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
// 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.
|
||||
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
|
||||
#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;
|
||||
if (bottommost_) {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses;
|
||||
} else {
|
||||
expected_compression_dict_misses = prev_compression_dict_misses + 1;
|
||||
}
|
||||
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.
|
||||
const size_t kValueLen = 256;
|
||||
const size_t kKeysPerFile = 1 << 10;
|
||||
const size_t kDictLen = 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;
|
||||
} else {
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.max_dict_bytes = kDictLen;
|
||||
}
|
||||
options.disable_auto_compactions = true;
|
||||
options.statistics = CreateDBStatistics();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (size_t j = 0; j <= kKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(static_cast<int>(j)), rnd.RandomString(kValueLen)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2", FilesPerLevel(0));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
uint64_t prev_compression_dict_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_COMPRESSION_DICT_MISS);
|
||||
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));
|
||||
}
|
||||
|
||||
class CompactionCompressionListener : public EventListener {
|
||||
public:
|
||||
explicit CompactionCompressionListener(Options* db_options)
|
||||
@@ -1470,7 +1646,7 @@ TEST_P(CompressionFailuresTest, CompressionFailures) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 512;
|
||||
table_options.verify_compression = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.parallel_threads = compression_parallel_threads_;
|
||||
@@ -1808,7 +1984,7 @@ class PinL0IndexAndFilterBlocksTest
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options->table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options->table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, *options);
|
||||
|
||||
Put(1, "a", "begin");
|
||||
@@ -1848,7 +2024,7 @@ TEST_P(PinL0IndexAndFilterBlocksTest,
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, "key", "val"));
|
||||
@@ -2485,26 +2661,30 @@ TEST_F(DBTest2, ReadAmpBitmapLiveInCacheAfterDBClose) {
|
||||
{
|
||||
const int kIdBufLen = 100;
|
||||
char id_buf[kIdBufLen];
|
||||
Status s = Status::NotSupported();
|
||||
#ifndef OS_WIN
|
||||
// You can't open a directory on windows using random access file
|
||||
std::unique_ptr<RandomAccessFile> file;
|
||||
ASSERT_OK(env_->NewRandomAccessFile(dbname_, &file, EnvOptions()));
|
||||
if (file->GetUniqueId(id_buf, kIdBufLen) == 0) {
|
||||
// fs holding db directory doesn't support getting a unique file id,
|
||||
// this means that running this test will fail because lru_cache will load
|
||||
// the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
#else
|
||||
std::unique_ptr<Directory> dir;
|
||||
ASSERT_OK(env_->NewDirectory(dbname_, &dir));
|
||||
if (dir->GetUniqueId(id_buf, kIdBufLen) == 0) {
|
||||
// fs holding db directory doesn't support getting a unique file id,
|
||||
// this means that running this test will fail because lru_cache will load
|
||||
// the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
s = env_->NewRandomAccessFile(dbname_, &file, EnvOptions());
|
||||
if (s.ok()) {
|
||||
if (file->GetUniqueId(id_buf, kIdBufLen) == 0) {
|
||||
// fs holding db directory doesn't support getting a unique file id,
|
||||
// this means that running this test will fail because lru_cache will
|
||||
// load the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!s.ok()) {
|
||||
std::unique_ptr<Directory> dir;
|
||||
ASSERT_OK(env_->NewDirectory(dbname_, &dir));
|
||||
if (dir->GetUniqueId(id_buf, kIdBufLen) == 0) {
|
||||
// fs holding db directory doesn't support getting a unique file id,
|
||||
// this means that running this test will fail because lru_cache will
|
||||
// load the blocks again regardless of them being already in the cache
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint32_t bytes_per_bit[2] = {1, 16};
|
||||
for (size_t k = 0; k < 2; k++) {
|
||||
@@ -3297,7 +3477,7 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 16384;
|
||||
bbto.no_block_cache = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
@@ -3342,6 +3522,7 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
// is on levels higher than the new num_levels.
|
||||
TEST_F(DBTest2, ReduceLevel) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
Reopen(options);
|
||||
@@ -3370,6 +3551,7 @@ TEST_F(DBTest2, ReadCallbackTest) {
|
||||
Options options;
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
std::vector<const Snapshot*> snapshots;
|
||||
// Try to create a db with multiple layers and a memtable
|
||||
@@ -3629,7 +3811,9 @@ TEST_F(DBTest2, TraceAndReplay) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname2, column_families, &handles, &db2));
|
||||
DBOptions db_opts;
|
||||
db_opts.env = env_;
|
||||
ASSERT_OK(DB::Open(db_opts, dbname2, column_families, &handles, &db2));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
// Verify that the keys don't already exist
|
||||
@@ -3704,7 +3888,9 @@ TEST_F(DBTest2, TraceWithLimit) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname2, column_families, &handles, &db2));
|
||||
DBOptions db_opts;
|
||||
db_opts.env = env_;
|
||||
ASSERT_OK(DB::Open(db_opts, dbname2, column_families, &handles, &db2));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
// Verify that the keys don't already exist
|
||||
@@ -3772,7 +3958,9 @@ TEST_F(DBTest2, TraceWithSampling) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname2, column_families, &handles, &db2));
|
||||
DBOptions db_opts;
|
||||
db_opts.env = env_;
|
||||
ASSERT_OK(DB::Open(db_opts, dbname2, column_families, &handles, &db2));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
ASSERT_TRUE(db2->Get(ro, handles[0], "a", &value).IsNotFound());
|
||||
@@ -3872,7 +4060,9 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor("pikachu", ColumnFamilyOptions()));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname2, column_families, &handles, &db2));
|
||||
DBOptions db_opts;
|
||||
db_opts.env = env_;
|
||||
ASSERT_OK(DB::Open(db_opts, dbname2, column_families, &handles, &db2));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
// Verify that the keys don't already exist
|
||||
@@ -3918,7 +4108,7 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
handles.clear();
|
||||
|
||||
DB* db3 = nullptr;
|
||||
ASSERT_OK(DB::Open(DBOptions(), dbname3, column_families, &handles, &db3));
|
||||
ASSERT_OK(DB::Open(db_opts, dbname3, column_families, &handles, &db3));
|
||||
|
||||
env_->SleepForMicroseconds(100);
|
||||
// Verify that the keys don't already exist
|
||||
@@ -3974,6 +4164,11 @@ TEST_F(DBTest2, TraceWithFilter) {
|
||||
|
||||
TEST_F(DBTest2, PinnableSliceAndMmapReads) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
if (!IsMemoryMappedAccessSupported()) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires default environment");
|
||||
return;
|
||||
}
|
||||
options.allow_mmap_reads = true;
|
||||
options.max_open_files = 100;
|
||||
options.compression = kNoCompression;
|
||||
@@ -4026,7 +4221,7 @@ TEST_F(DBTest2, DISABLED_IteratorPinnedMemory) {
|
||||
bbto.cache_index_and_filter_blocks = false;
|
||||
bbto.block_cache = NewLRUCache(100000);
|
||||
bbto.block_size = 400; // small block size
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
@@ -4252,6 +4447,7 @@ TEST_F(DBTest2, TestCompactFiles) {
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.num_levels = 2;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
@@ -4569,7 +4765,7 @@ TEST_F(DBTest2, CrashInRecoveryMultipleCF) {
|
||||
for (const auto& f : filenames) {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(f, &number, &type) && type == FileType::kLogFile) {
|
||||
if (ParseFileName(f, &number, &type) && type == FileType::kWalFile) {
|
||||
std::string fname = dbname_ + "/" + f;
|
||||
std::string file_content;
|
||||
ASSERT_OK(ReadFileToString(env_, fname, &file_content));
|
||||
@@ -4807,6 +5003,7 @@ TEST_F(DBTest2, BlockBasedTablePrefixIndexSeekForPrev) {
|
||||
|
||||
TEST_F(DBTest2, PartitionedIndexPrefetchFailure) {
|
||||
Options options = last_options_;
|
||||
options.env = env_;
|
||||
options.max_open_files = 20;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
|
||||
+21
-16
@@ -200,28 +200,28 @@ bool DBTestBase::ChangeCompactOptions() {
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
TryReopen(options);
|
||||
Reopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kUniversalCompaction) {
|
||||
option_config_ = kUniversalCompactionMultiLevel;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
TryReopen(options);
|
||||
Reopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kUniversalCompactionMultiLevel) {
|
||||
option_config_ = kLevelSubcompactions;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
assert(options.max_subcompactions > 1);
|
||||
TryReopen(options);
|
||||
Reopen(options);
|
||||
return true;
|
||||
} else if (option_config_ == kLevelSubcompactions) {
|
||||
option_config_ = kUniversalSubcompactions;
|
||||
Destroy(last_options_);
|
||||
auto options = CurrentOptions();
|
||||
assert(options.max_subcompactions > 1);
|
||||
TryReopen(options);
|
||||
Reopen(options);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -331,7 +331,7 @@ Options DBTestBase::CurrentOptions(
|
||||
return GetOptions(option_config_, default_options, options_override);
|
||||
}
|
||||
|
||||
Options DBTestBase::GetDefaultOptions() {
|
||||
Options DBTestBase::GetDefaultOptions() const {
|
||||
Options options;
|
||||
options.write_buffer_size = 4090 * 4096;
|
||||
options.target_file_size_base = 2 * 1024 * 1024;
|
||||
@@ -339,6 +339,10 @@ Options DBTestBase::GetDefaultOptions() {
|
||||
options.max_open_files = 5000;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options.compaction_pri = CompactionPri::kByCompensatedSize;
|
||||
options.env = env_;
|
||||
if (!env_->skip_fsync_) {
|
||||
options.track_and_verify_wals_in_manifest = true;
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -367,28 +371,28 @@ Options DBTestBase::GetOptions(
|
||||
options.unordered_write = false;
|
||||
break;
|
||||
case kPlainTableFirstBytePrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(1));
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
case kPlainTableCappedPrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(8));
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
case kPlainTableCappedPrefixNonMmap:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(8));
|
||||
options.allow_mmap_reads = false;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
set_block_based_table_factory = false;
|
||||
break;
|
||||
case kPlainTableAllBytesPrefix:
|
||||
options.table_factory.reset(new PlainTableFactory());
|
||||
options.table_factory.reset(NewPlainTableFactory());
|
||||
options.prefix_extractor.reset(NewNoopTransform());
|
||||
options.allow_mmap_reads = can_allow_mmap;
|
||||
options.max_sequential_skip_in_iterations = 999999;
|
||||
@@ -676,7 +680,7 @@ void DBTestBase::Close() {
|
||||
void DBTestBase::DestroyAndReopen(const Options& options) {
|
||||
// Destroy using last options
|
||||
Destroy(last_options_);
|
||||
ASSERT_OK(TryReopen(options));
|
||||
Reopen(options);
|
||||
}
|
||||
|
||||
void DBTestBase::Destroy(const Options& options, bool delete_cf_paths) {
|
||||
@@ -704,9 +708,9 @@ Status DBTestBase::TryReopen(const Options& options) {
|
||||
// Note: operator= is an unsafe approach here since it destructs
|
||||
// std::shared_ptr in the same order of their creation, in contrast to
|
||||
// destructors which destructs them in the opposite order of creation. One
|
||||
// particular problme is that the cache destructor might invoke callback
|
||||
// particular problem is that the cache destructor might invoke callback
|
||||
// functions that use Option members such as statistics. To work around this
|
||||
// problem, we manually call destructor of table_facotry which eventually
|
||||
// problem, we manually call destructor of table_factory which eventually
|
||||
// clears the block cache.
|
||||
last_options_ = options;
|
||||
MaybeInstallTimeElapseOnlySleep(options);
|
||||
@@ -963,7 +967,8 @@ std::string DBTestBase::AllEntriesFor(const Slice& user_key, int cf) {
|
||||
bool first = true;
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey(Slice(), 0, kTypeValue);
|
||||
if (ParseInternalKey(iter->key(), &ikey) != Status::OK()) {
|
||||
if (ParseInternalKey(iter->key(), &ikey, true /* log_err_key */) !=
|
||||
Status::OK()) {
|
||||
result += "CORRUPTED";
|
||||
} else {
|
||||
if (!last_options_.comparator->Equal(ikey.user_key, user_key)) {
|
||||
@@ -1370,12 +1375,12 @@ void DBTestBase::validateNumberOfEntries(int numValues, int cf) {
|
||||
kMaxSequenceNumber));
|
||||
}
|
||||
iter->SeekToFirst();
|
||||
ASSERT_EQ(iter->status().ok(), true);
|
||||
ASSERT_OK(iter->status());
|
||||
int seq = numValues;
|
||||
while (iter->Valid()) {
|
||||
ParsedInternalKey ikey;
|
||||
ikey.clear();
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey));
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey, true /* log_err_key */));
|
||||
|
||||
// checks sequence number for updates
|
||||
ASSERT_EQ(ikey.sequence, (unsigned)seq--);
|
||||
@@ -1580,7 +1585,7 @@ void DBTestBase::VerifyDBInternal(
|
||||
for (auto p : true_data) {
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ParsedInternalKey ikey;
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey));
|
||||
ASSERT_OK(ParseInternalKey(iter->key(), &ikey, true /* log_err_key */));
|
||||
ASSERT_EQ(p.first, ikey.user_key);
|
||||
ASSERT_EQ(p.second, iter->value());
|
||||
iter->Next();
|
||||
|
||||
+6
-3
@@ -949,10 +949,13 @@ class DBTestBase : public testing::Test {
|
||||
const anon::OptionsOverride& options_override =
|
||||
anon::OptionsOverride()) const;
|
||||
|
||||
static Options GetDefaultOptions();
|
||||
Options GetDefaultOptions() const;
|
||||
|
||||
Options GetOptions(int option_config,
|
||||
const Options& default_options = GetDefaultOptions(),
|
||||
Options GetOptions(int option_config) const {
|
||||
return GetOptions(option_config, GetDefaultOptions());
|
||||
}
|
||||
|
||||
Options GetOptions(int option_config, const Options& default_options,
|
||||
const anon::OptionsOverride& options_override =
|
||||
anon::OptionsOverride()) const;
|
||||
|
||||
|
||||
@@ -2129,7 +2129,7 @@ TEST_F(DBTestUniversalCompaction2, IngestBehind) {
|
||||
TEST_F(DBTestUniversalCompaction2, PeriodicCompactionDefault) {
|
||||
Options options;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
options.env = env_;
|
||||
KeepFilterFactory* filter = new KeepFilterFactory(true);
|
||||
options.compaction_filter_factory.reset(filter);
|
||||
Reopen(options);
|
||||
|
||||
+95
-96
@@ -47,8 +47,8 @@ class EnrichedSpecialEnv : public SpecialEnv {
|
||||
InstrumentedMutexLock l(&env_mutex_);
|
||||
if (f == skipped_wal) {
|
||||
deleted_wal_reopened = true;
|
||||
if (IsWAL(f) && largetest_deleted_wal.size() != 0 &&
|
||||
f.compare(largetest_deleted_wal) <= 0) {
|
||||
if (IsWAL(f) && largest_deleted_wal.size() != 0 &&
|
||||
f.compare(largest_deleted_wal) <= 0) {
|
||||
gap_in_wals = true;
|
||||
}
|
||||
}
|
||||
@@ -62,9 +62,9 @@ class EnrichedSpecialEnv : public SpecialEnv {
|
||||
// remember its name partly because the application might attempt to
|
||||
// delete the file again.
|
||||
if (skipped_wal.size() != 0 && skipped_wal != fname) {
|
||||
if (largetest_deleted_wal.size() == 0 ||
|
||||
largetest_deleted_wal.compare(fname) < 0) {
|
||||
largetest_deleted_wal = fname;
|
||||
if (largest_deleted_wal.size() == 0 ||
|
||||
largest_deleted_wal.compare(fname) < 0) {
|
||||
largest_deleted_wal = fname;
|
||||
}
|
||||
} else {
|
||||
skipped_wal = fname;
|
||||
@@ -82,7 +82,7 @@ class EnrichedSpecialEnv : public SpecialEnv {
|
||||
// the wal whose actual delete was skipped by the env
|
||||
std::string skipped_wal = "";
|
||||
// the largest WAL that was requested to be deleted
|
||||
std::string largetest_deleted_wal = "";
|
||||
std::string largest_deleted_wal = "";
|
||||
// number of WALs that were successfully deleted
|
||||
std::atomic<size_t> deleted_wal_cnt = {0};
|
||||
// the WAL whose delete from fs was skipped is reopened during recovery
|
||||
@@ -358,16 +358,16 @@ TEST_F(DBWALTest, RecoverWithBlob) {
|
||||
// There should be no files just yet since we haven't flushed.
|
||||
{
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
ASSERT_NE(versions, nullptr);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
ASSERT_NE(cfd, nullptr);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
ASSERT_NE(current, nullptr);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
ASSERT_NE(storage_info, nullptr);
|
||||
|
||||
ASSERT_EQ(storage_info->num_non_empty_levels(), 0);
|
||||
ASSERT_TRUE(storage_info->GetBlobFiles().empty());
|
||||
@@ -380,37 +380,36 @@ TEST_F(DBWALTest, RecoverWithBlob) {
|
||||
options.min_blob_size = min_blob_size;
|
||||
options.avoid_flush_during_recovery = false;
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_EQ(Get("key1"), short_value);
|
||||
|
||||
// TODO: enable once Get support is implemented for blobs
|
||||
// ASSERT_EQ(Get("key2"), long_value);
|
||||
ASSERT_EQ(Get("key2"), long_value);
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
ASSERT_NE(versions, nullptr);
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
ASSERT_NE(cfd, nullptr);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
ASSERT_NE(current, nullptr);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
ASSERT_NE(storage_info, nullptr);
|
||||
|
||||
const auto& l0_files = storage_info->LevelFiles(0);
|
||||
ASSERT_EQ(l0_files.size(), 1);
|
||||
|
||||
const FileMetaData* const table_file = l0_files[0];
|
||||
assert(table_file);
|
||||
ASSERT_NE(table_file, nullptr);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
ASSERT_EQ(blob_files.size(), 1);
|
||||
|
||||
const auto& blob_file = blob_files.begin()->second;
|
||||
assert(blob_file);
|
||||
ASSERT_NE(blob_file, nullptr);
|
||||
|
||||
ASSERT_EQ(table_file->smallest.user_key(), "key1");
|
||||
ASSERT_EQ(table_file->largest.user_key(), "key2");
|
||||
@@ -423,7 +422,7 @@ TEST_F(DBWALTest, RecoverWithBlob) {
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
const InternalStats* const internal_stats = cfd->internal_stats();
|
||||
assert(internal_stats);
|
||||
ASSERT_NE(internal_stats, nullptr);
|
||||
|
||||
const uint64_t expected_bytes =
|
||||
table_file->fd.GetFileSize() + blob_file->GetTotalBlobBytes();
|
||||
@@ -442,10 +441,12 @@ class DBRecoveryTestBlobError
|
||||
: public DBWALTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
DBRecoveryTestBlobError() : fault_injection_env_(env_) {}
|
||||
DBRecoveryTestBlobError()
|
||||
: fault_injection_env_(env_), sync_point_(GetParam()) {}
|
||||
~DBRecoveryTestBlobError() { Close(); }
|
||||
|
||||
FaultInjectionTestEnv fault_injection_env_;
|
||||
std::string sync_point_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBRecoveryTestBlobError, DBRecoveryTestBlobError,
|
||||
@@ -459,11 +460,12 @@ TEST_P(DBRecoveryTestBlobError, RecoverWithBlobError) {
|
||||
|
||||
// Reopen with blob files enabled but make blob file writing fail during
|
||||
// recovery.
|
||||
SyncPoint::GetInstance()->SetCallBack(GetParam(), [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false, Status::IOError());
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
Status::IOError(sync_point_));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable", [this](void* /* arg */) {
|
||||
"BuildTable:BeforeDeleteFile", [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(true);
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -500,12 +502,12 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
|
||||
do {
|
||||
// delete old files in backup_logs directory
|
||||
env_->CreateDirIfMissing(backup_logs);
|
||||
ASSERT_OK(env_->CreateDirIfMissing(backup_logs));
|
||||
std::vector<std::string> old_files;
|
||||
env_->GetChildren(backup_logs, &old_files);
|
||||
ASSERT_OK(env_->GetChildren(backup_logs, &old_files));
|
||||
for (auto& file : old_files) {
|
||||
if (file != "." && file != "..") {
|
||||
env_->DeleteFile(backup_logs + "/" + file);
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + file));
|
||||
}
|
||||
}
|
||||
Options options = CurrentOptions();
|
||||
@@ -524,7 +526,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
|
||||
// copy the logs to backup
|
||||
std::vector<std::string> logs;
|
||||
env_->GetChildren(options.wal_dir, &logs);
|
||||
ASSERT_OK(env_->GetChildren(options.wal_dir, &logs));
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(options.wal_dir + "/" + log, backup_logs + "/" + log);
|
||||
@@ -555,7 +557,7 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
Close();
|
||||
|
||||
// copy the logs from backup back to wal dir
|
||||
env_->CreateDirIfMissing(options.wal_dir);
|
||||
ASSERT_OK(env_->CreateDirIfMissing(options.wal_dir));
|
||||
for (auto& log : logs) {
|
||||
if (log != ".." && log != ".") {
|
||||
CopyFile(backup_logs + "/" + log, options.wal_dir + "/" + log);
|
||||
@@ -570,16 +572,16 @@ TEST_F(DBWALTest, IgnoreRecoveredLog) {
|
||||
// Recovery will fail if DB directory doesn't exist.
|
||||
Destroy(options);
|
||||
// copy the logs from backup back to wal dir
|
||||
env_->CreateDirIfMissing(options.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
|
||||
env_->DeleteFile(backup_logs + "/" + log);
|
||||
ASSERT_OK(env_->DeleteFile(backup_logs + "/" + log));
|
||||
}
|
||||
}
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(!s.ok());
|
||||
ASSERT_NOK(s);
|
||||
Destroy(options);
|
||||
} while (ChangeWalOptions());
|
||||
}
|
||||
@@ -617,9 +619,9 @@ TEST_F(DBWALTest, PreallocateBlock) {
|
||||
called.fetch_add(1);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
ASSERT_OK(Put("", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("", ""));
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
@@ -636,9 +638,9 @@ TEST_F(DBWALTest, PreallocateBlock) {
|
||||
called.fetch_add(1);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
ASSERT_OK(Put("", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("", ""));
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
@@ -656,9 +658,9 @@ TEST_F(DBWALTest, PreallocateBlock) {
|
||||
called.fetch_add(1);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
ASSERT_OK(Put("", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("", ""));
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
@@ -677,9 +679,9 @@ TEST_F(DBWALTest, PreallocateBlock) {
|
||||
called.fetch_add(1);
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
Put("", "");
|
||||
Flush();
|
||||
Put("", "");
|
||||
ASSERT_OK(Put("", ""));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Put("", ""));
|
||||
Close();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(2, called.load());
|
||||
@@ -905,7 +907,7 @@ TEST_F(DBWALTest, RecoverCheckFileAmountWithSmallWriteBuffer) {
|
||||
// Make 'dobrynia' to be flushed and new WAL file to be created
|
||||
ASSERT_OK(Put(2, Key(10), DummyString(7500000)));
|
||||
ASSERT_OK(Put(2, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[2]);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[2]));
|
||||
{
|
||||
auto tables = ListTableFiles(env_, dbname_);
|
||||
ASSERT_EQ(tables.size(), static_cast<size_t>(1));
|
||||
@@ -959,7 +961,7 @@ TEST_F(DBWALTest, RecoverCheckFileAmount) {
|
||||
// Make 'nikitich' memtable to be flushed
|
||||
ASSERT_OK(Put(3, Key(10), DummyString(1002400)));
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[3]));
|
||||
// 4 memtable are not flushed, 1 sst file
|
||||
{
|
||||
auto tables = ListTableFiles(env_, dbname_);
|
||||
@@ -979,7 +981,7 @@ TEST_F(DBWALTest, RecoverCheckFileAmount) {
|
||||
ASSERT_OK(Put(3, Key(10), DummyString(1002400)));
|
||||
// make it flush
|
||||
ASSERT_OK(Put(3, Key(1), DummyString(1)));
|
||||
dbfull()->TEST_WaitForFlushMemTable(handles_[3]);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[3]));
|
||||
// There are still 4 memtable not flushed, and 2 sst tables
|
||||
ASSERT_OK(Put(0, Key(1), DummyString(1)));
|
||||
ASSERT_OK(Put(1, Key(1), DummyString(1)));
|
||||
@@ -1027,10 +1029,10 @@ TEST_F(DBWALTest, SyncMultipleLogs) {
|
||||
for (uint64_t b = 0; b < kNumBatches; b++) {
|
||||
batch.Clear();
|
||||
for (int i = 0; i < kBatchSize; i++) {
|
||||
batch.Put(Key(i), DummyString(128));
|
||||
ASSERT_OK(batch.Put(Key(i), DummyString(128)));
|
||||
}
|
||||
|
||||
dbfull()->Write(wo, &batch);
|
||||
ASSERT_OK(dbfull()->Write(wo, &batch));
|
||||
}
|
||||
|
||||
ASSERT_OK(dbfull()->SyncWAL());
|
||||
@@ -1058,7 +1060,7 @@ TEST_F(DBWALTest, PartOfWritesWithWALDisabled) {
|
||||
ASSERT_OK(Flush(0));
|
||||
ASSERT_OK(Put(0, "key", "v5", wal_on)); // seq id 5
|
||||
ASSERT_EQ("v5", Get(0, "key"));
|
||||
dbfull()->FlushWAL(false);
|
||||
ASSERT_OK(dbfull()->FlushWAL(false));
|
||||
// Simulate a crash.
|
||||
fault_env->SetFilesystemActive(false);
|
||||
Close();
|
||||
@@ -1126,12 +1128,13 @@ class RecoveryTestHelper {
|
||||
for (int i = 0; i < kKeysPerWALFile; i++) {
|
||||
std::string key = "key" + ToString((*count)++);
|
||||
std::string value = test->DummyString(kValueSize);
|
||||
assert(current_log_writer.get() != nullptr);
|
||||
ASSERT_NE(current_log_writer.get(), nullptr);
|
||||
uint64_t seq = versions->LastSequence() + 1;
|
||||
batch.Clear();
|
||||
batch.Put(key, value);
|
||||
ASSERT_OK(batch.Put(key, value));
|
||||
WriteBatchInternal::SetSequence(&batch, seq);
|
||||
current_log_writer->AddRecord(WriteBatchInternal::Contents(&batch));
|
||||
ASSERT_OK(current_log_writer->AddRecord(
|
||||
WriteBatchInternal::Contents(&batch)));
|
||||
versions->SetLastAllocatedSequence(seq);
|
||||
versions->SetLastPublishedSequence(seq);
|
||||
versions->SetLastSequence(seq);
|
||||
@@ -1179,32 +1182,13 @@ class RecoveryTestHelper {
|
||||
test->Close();
|
||||
#endif
|
||||
if (trunc) {
|
||||
ASSERT_EQ(0, truncate(fname.c_str(), static_cast<int64_t>(size * off)));
|
||||
ASSERT_OK(
|
||||
test::TruncateFile(env, fname, static_cast<uint64_t>(size * off)));
|
||||
} else {
|
||||
InduceCorruption(fname, static_cast<size_t>(size * off + 8),
|
||||
static_cast<size_t>(size * len));
|
||||
ASSERT_OK(test::CorruptFile(env, fname, static_cast<int>(size * off + 8),
|
||||
static_cast<int>(size * len), false));
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite data with 'a' from offset for length len
|
||||
static void InduceCorruption(const std::string& filename, size_t offset,
|
||||
size_t len) {
|
||||
ASSERT_GT(len, 0U);
|
||||
|
||||
int fd = open(filename.c_str(), O_RDWR);
|
||||
|
||||
// On windows long is 32-bit
|
||||
ASSERT_LE(offset, std::numeric_limits<long>::max());
|
||||
|
||||
ASSERT_GT(fd, 0);
|
||||
ASSERT_EQ(offset, lseek(fd, static_cast<long>(offset), SEEK_SET));
|
||||
|
||||
void* buf = alloca(len);
|
||||
memset(buf, 'b', len);
|
||||
ASSERT_EQ(len, write(fd, buf, static_cast<unsigned int>(len)));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
};
|
||||
|
||||
class DBWALTestWithParams
|
||||
@@ -1326,11 +1310,11 @@ TEST_F(DBWALTest, kPointInTimeRecoveryCFConsistency) {
|
||||
|
||||
ASSERT_OK(Put(1, "key3", "val3"));
|
||||
// Corrupt WAL at location of key3
|
||||
RecoveryTestHelper::InduceCorruption(
|
||||
fname, static_cast<size_t>(offset_to_corrupt), static_cast<size_t>(4));
|
||||
ASSERT_OK(test::CorruptFile(env, fname, static_cast<int>(offset_to_corrupt),
|
||||
4, false));
|
||||
ASSERT_OK(Put(2, "key4", "val4"));
|
||||
ASSERT_OK(Put(1, "key5", "val5"));
|
||||
Flush(2);
|
||||
ASSERT_OK(Flush(2));
|
||||
|
||||
// PIT recovery & verify
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
@@ -1366,13 +1350,19 @@ TEST_P(DBWALTestWithParams, kPointInTimeRecovery) {
|
||||
size_t recovered_row_count = RecoveryTestHelper::GetData(this);
|
||||
ASSERT_LT(recovered_row_count, row_count);
|
||||
|
||||
bool expect_data = true;
|
||||
for (size_t k = 0; k < maxkeys; ++k) {
|
||||
bool found = Get("key" + ToString(corrupt_offset)) != "NOT_FOUND";
|
||||
if (expect_data && !found) {
|
||||
expect_data = false;
|
||||
// Verify a prefix of keys were recovered. But not in the case of full WAL
|
||||
// truncation, because we have no way to know there was a corruption when
|
||||
// truncation happened on record boundaries (preventing recovery holes in
|
||||
// that case requires using `track_and_verify_wals_in_manifest`).
|
||||
if (!trunc || corrupt_offset != 0) {
|
||||
bool expect_data = true;
|
||||
for (size_t k = 0; k < maxkeys; ++k) {
|
||||
bool found = Get("key" + ToString(k)) != "NOT_FOUND";
|
||||
if (expect_data && !found) {
|
||||
expect_data = false;
|
||||
}
|
||||
ASSERT_EQ(found, expect_data);
|
||||
}
|
||||
ASSERT_EQ(found, expect_data);
|
||||
}
|
||||
|
||||
const size_t min = RecoveryTestHelper::kKeysPerWALFile *
|
||||
@@ -1478,7 +1468,7 @@ TEST_F(DBWALTest, WalCleanupAfterAvoidFlushDuringRecovery) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (i > 0) {
|
||||
// Flush() triggers deletion of obsolete tracked files
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
VectorLogPtr log_files;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files));
|
||||
@@ -1520,7 +1510,7 @@ TEST_F(DBWALTest, RecoverWithoutFlush) {
|
||||
ASSERT_EQ(Get("foo"), "foo_v2");
|
||||
ASSERT_EQ(Get("bar"), "bar_v2");
|
||||
// manual flush and insert again
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(Get("foo"), "foo_v2");
|
||||
ASSERT_EQ(Get("bar"), "bar_v2");
|
||||
ASSERT_OK(Put("foo", "foo_v3"));
|
||||
@@ -1541,7 +1531,9 @@ TEST_F(DBWALTest, RecoverWithoutFlushMultipleCF) {
|
||||
|
||||
auto countWalFiles = [this]() {
|
||||
VectorLogPtr log_files;
|
||||
dbfull()->GetSortedWalFiles(log_files);
|
||||
if (!dbfull()->GetSortedWalFiles(log_files).ok()) {
|
||||
return size_t{0};
|
||||
}
|
||||
return log_files.size();
|
||||
};
|
||||
|
||||
@@ -1549,11 +1541,11 @@ TEST_F(DBWALTest, RecoverWithoutFlushMultipleCF) {
|
||||
CreateAndReopenWithCF({"one", "two"}, options);
|
||||
ASSERT_OK(Put(0, "key1", kSmallValue));
|
||||
ASSERT_OK(Put(1, "key2", kLargeValue));
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_EQ(1, countWalFiles());
|
||||
ASSERT_OK(Put(0, "key3", kSmallValue));
|
||||
ASSERT_OK(Put(2, "key4", kLargeValue));
|
||||
Flush(2);
|
||||
ASSERT_OK(Flush(2));
|
||||
ASSERT_EQ(2, countWalFiles());
|
||||
|
||||
// Reopen, insert and flush.
|
||||
@@ -1567,9 +1559,9 @@ TEST_F(DBWALTest, RecoverWithoutFlushMultipleCF) {
|
||||
ASSERT_OK(Put(0, "key5", kLargeValue));
|
||||
ASSERT_OK(Put(1, "key6", kLargeValue));
|
||||
ASSERT_EQ(3, countWalFiles());
|
||||
Flush(1);
|
||||
ASSERT_OK(Flush(1));
|
||||
ASSERT_OK(Put(2, "key7", kLargeValue));
|
||||
dbfull()->FlushWAL(false);
|
||||
ASSERT_OK(dbfull()->FlushWAL(false));
|
||||
ASSERT_EQ(4, countWalFiles());
|
||||
|
||||
// Reopen twice and validate.
|
||||
@@ -1658,7 +1650,7 @@ TEST_F(DBWALTest, RestoreTotalLogSizeAfterRecoverWithoutFlush) {
|
||||
|
||||
void OnFlushBegin(DB* /*db*/, const FlushJobInfo& flush_job_info) override {
|
||||
count++;
|
||||
assert(FlushReason::kWriteBufferManager == flush_job_info.flush_reason);
|
||||
ASSERT_EQ(FlushReason::kWriteBufferManager, flush_job_info.flush_reason);
|
||||
}
|
||||
};
|
||||
std::shared_ptr<TestFlushListener> test_listener =
|
||||
@@ -1702,7 +1694,9 @@ TEST_F(DBWALTest, RestoreTotalLogSizeAfterRecoverWithoutFlush) {
|
||||
1 * kMB);
|
||||
// Write one more key to trigger flush.
|
||||
ASSERT_OK(Put(0, "foo", "v2"));
|
||||
dbfull()->TEST_WaitForFlushMemTable();
|
||||
for (auto* h : handles_) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(h));
|
||||
}
|
||||
// Flushed two column families.
|
||||
ASSERT_EQ(2, test_listener->count.load());
|
||||
}
|
||||
@@ -1714,7 +1708,12 @@ TEST_F(DBWALTest, RestoreTotalLogSizeAfterRecoverWithoutFlush) {
|
||||
TEST_F(DBWALTest, TruncateLastLogAfterRecoverWithoutFlush) {
|
||||
constexpr size_t kKB = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
if (mem_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem environment");
|
||||
return;
|
||||
}
|
||||
// Test fallocate support of running file system.
|
||||
// Skip this test if fallocate is not supported.
|
||||
std::string fname_test_fallocate = dbname_ + "/preallocate_testfile";
|
||||
@@ -1771,9 +1770,9 @@ TEST_F(DBWALTest, WalTermTest) {
|
||||
wo.disableWAL = false;
|
||||
|
||||
WriteBatch batch;
|
||||
batch.Put("foo", "bar");
|
||||
ASSERT_OK(batch.Put("foo", "bar"));
|
||||
batch.MarkWalTerminationPoint();
|
||||
batch.Put("foo2", "bar2");
|
||||
ASSERT_OK(batch.Put("foo2", "bar2"));
|
||||
|
||||
ASSERT_OK(dbfull()->Write(wo, &batch));
|
||||
|
||||
|
||||
@@ -32,6 +32,22 @@ class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::string KeyWithPrefix(std::string prefix, uint64_t k) {
|
||||
std::string ret;
|
||||
PutFixed64(&ret, k);
|
||||
std::reverse(ret.begin(), ret.end());
|
||||
return prefix + ret;
|
||||
}
|
||||
|
||||
static std::vector<Slice> ConvertStrToSlice(
|
||||
std::vector<std::string>& strings) {
|
||||
std::vector<Slice> ret;
|
||||
for (const auto& s : strings) {
|
||||
ret.emplace_back(s);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class TestComparator : public Comparator {
|
||||
private:
|
||||
const Comparator* cmp_without_ts_;
|
||||
@@ -141,7 +157,8 @@ class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
ukey_and_ts.assign(expected_ukey.data(), expected_ukey.size());
|
||||
ukey_and_ts.append(expected_ts.data(), expected_ts.size());
|
||||
ParsedInternalKey parsed_ikey;
|
||||
ASSERT_OK(ParseInternalKey(it->key(), &parsed_ikey));
|
||||
ASSERT_OK(
|
||||
ParseInternalKey(it->key(), &parsed_ikey, true /* log_err_key */));
|
||||
ASSERT_EQ(ukey_and_ts, parsed_ikey.user_key);
|
||||
ASSERT_EQ(expected_val_type, parsed_ikey.type);
|
||||
ASSERT_EQ(expected_seq, parsed_ikey.sequence);
|
||||
@@ -161,7 +178,8 @@ class DBBasicTestWithTimestampBase : public DBTestBase {
|
||||
ukey_and_ts.append(expected_ts.data(), expected_ts.size());
|
||||
|
||||
ParsedInternalKey parsed_ikey;
|
||||
ASSERT_OK(ParseInternalKey(it->key(), &parsed_ikey));
|
||||
ASSERT_OK(
|
||||
ParseInternalKey(it->key(), &parsed_ikey, true /* log_err_key */));
|
||||
ASSERT_EQ(expected_val_type, parsed_ikey.type);
|
||||
ASSERT_EQ(Slice(ukey_and_ts), parsed_ikey.user_key);
|
||||
if (expected_val_type == kTypeValue) {
|
||||
@@ -177,6 +195,113 @@ class DBBasicTestWithTimestamp : public DBBasicTestWithTimestampBase {
|
||||
: DBBasicTestWithTimestampBase("db_basic_test_with_timestamp") {}
|
||||
};
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, CompactRangeWithSpecifiedRange) {
|
||||
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);
|
||||
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo1", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo2", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::string start_str = "foo";
|
||||
std::string end_str = "foo2";
|
||||
Slice start(start_str), end(end_str);
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, GetApproximateSizes) {
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 100000000; // Large write buffer
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
auto default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
const int N = 128;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(db_->Put(write_opts, Key(i), rnd.RandomString(1024)));
|
||||
}
|
||||
|
||||
uint64_t size;
|
||||
std::string start = Key(50);
|
||||
std::string end = Key(60);
|
||||
Range r(start, end);
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = true;
|
||||
size_approx_options.include_files = true;
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_GT(size, 6000);
|
||||
ASSERT_LT(size, 204800);
|
||||
|
||||
// test multiple ranges
|
||||
std::vector<Range> ranges;
|
||||
std::string start_tmp = Key(10);
|
||||
std::string end_tmp = Key(20);
|
||||
ranges.emplace_back(Range(start_tmp, end_tmp));
|
||||
ranges.emplace_back(Range(start, end));
|
||||
uint64_t range_sizes[2];
|
||||
ASSERT_OK(db_->GetApproximateSizes(size_approx_options, default_cf,
|
||||
ranges.data(), 2, range_sizes));
|
||||
|
||||
ASSERT_EQ(range_sizes[1], size);
|
||||
|
||||
// Zero if not including mem table
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
start = Key(500);
|
||||
end = Key(600);
|
||||
r = Range(start, end);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
|
||||
// Test range boundaries
|
||||
ASSERT_OK(db_->Put(write_opts, Key(1000), rnd.RandomString(1024)));
|
||||
// Should include start key
|
||||
start = Key(1000);
|
||||
end = Key(1100);
|
||||
r = Range(start, end);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_GT(size, 0);
|
||||
|
||||
// Should exclude end key
|
||||
start = Key(900);
|
||||
end = Key(1000);
|
||||
r = Range(start, end);
|
||||
ASSERT_OK(
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size));
|
||||
ASSERT_EQ(size, 0);
|
||||
std::cout << size << std::endl;
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
const int kNumKeysPerFile = 128;
|
||||
const uint64_t kMaxKey = 1024;
|
||||
@@ -242,6 +367,166 @@ TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLessThanKey) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(3));
|
||||
options.memtable_whole_key_filtering = true;
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo1", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo2", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Move sst file to next level
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo3", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts = Timestamp(1, 0);
|
||||
ts = read_ts;
|
||||
read_opts.timestamp = &ts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
iter->Seek("bbb");
|
||||
ASSERT_FALSE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SeekWithPrefixLargerThanKey) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(20));
|
||||
options.memtable_whole_key_filtering = true;
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo1", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo2", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Move sst file to next level
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo3", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts = Timestamp(2, 0);
|
||||
ts = read_ts;
|
||||
read_opts.timestamp = &ts;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
// Make sure the prefix extractor doesn't include timestamp, otherwise it
|
||||
// may return invalid result.
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
iter->Next();
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SeekWithBound) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewFixedPrefixTransform(2));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo1", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo2", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Move sst file to next level
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo3", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions read_opts;
|
||||
std::string read_ts = Timestamp(2, 0);
|
||||
ts = read_ts;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string up_bound = "foo5";
|
||||
Slice up_bound_slice = up_bound;
|
||||
read_opts.iterate_upper_bound = &up_bound_slice;
|
||||
read_opts.auto_prefix_mode = true;
|
||||
{
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
// Make sure the prefix extractor doesn't include timestamp, otherwise it
|
||||
// may return invalid result.
|
||||
iter->Seek("foo");
|
||||
ASSERT_TRUE(iter->Valid());
|
||||
ASSERT_OK(iter->status());
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, SimpleForwardIterateLowerTsBound) {
|
||||
constexpr int kNumKeysPerFile = 128;
|
||||
constexpr uint64_t kMaxKey = 1024;
|
||||
@@ -490,6 +775,228 @@ TEST_F(DBBasicTestWithTimestamp, ReseekToNextUserKey) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithFastLocalBloom) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write any value
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo", "bar"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Read with MultiGet
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
size_t batch_size = 1;
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<PinnableSlice> values(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
keys[0] = "foo";
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
db_->MultiGet(read_opts, cfh, batch_size, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithPrefix) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(5));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write any value
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo", "bar"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Read with MultiGet
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
size_t batch_size = 1;
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<PinnableSlice> values(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
keys[0] = "foo";
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
db_->MultiGet(read_opts, cfh, batch_size, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetWithMemBloomFilter) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(5));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write any value
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo", "bar"));
|
||||
|
||||
// Read with MultiGet
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
size_t batch_size = 1;
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<PinnableSlice> values(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
keys[0] = "foo";
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
db_->MultiGet(read_opts, cfh, batch_size, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetRangeFiltering) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Write any value
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
// random data
|
||||
for (int i = 0; i < 3; i++) {
|
||||
auto key = ToString(i * 10);
|
||||
auto value = ToString(i * 10);
|
||||
Slice key_slice = key;
|
||||
Slice value_slice = value;
|
||||
ASSERT_OK(db_->Put(write_opts, key_slice, value_slice));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
// Make num_levels to 2 to do key range filtering of sst files
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo", "bar"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Read with MultiGet
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
size_t batch_size = 1;
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<PinnableSlice> values(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
keys[0] = "foo";
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
db_->MultiGet(read_opts, cfh, batch_size, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MultiGetPrefixFilter) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
options.prefix_extractor.reset(NewCappedPrefixTransform(5));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
bbto.cache_index_and_filter_blocks = true;
|
||||
bbto.whole_key_filtering = false;
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
ASSERT_OK(db_->Put(write_opts, "foo", "bar"));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
// Read with MultiGet
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
size_t batch_size = 1;
|
||||
std::vector<Slice> keys(batch_size);
|
||||
std::vector<std::string> values(batch_size);
|
||||
std::vector<std::string> timestamps(batch_size);
|
||||
keys[0] = "foo";
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
std::vector<ColumnFamilyHandle*> cfhs(keys.size(), cfh);
|
||||
std::vector<Status> statuses =
|
||||
db_->MultiGet(read_opts, cfhs, keys, &values, ×tamps);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, MaxKeysSkipped) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -588,6 +1095,131 @@ TEST_F(DBBasicTestWithTimestamp, CompactDeletionWithTimestampMarkerToBottom) {
|
||||
Close();
|
||||
}
|
||||
|
||||
class DBBasicTestWithTimestampFilterPrefixSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const FilterPolicy>, bool, bool,
|
||||
std::shared_ptr<const SliceTransform>, bool, double>> {
|
||||
public:
|
||||
DBBasicTestWithTimestampFilterPrefixSettings()
|
||||
: DBBasicTestWithTimestampBase(
|
||||
"db_basic_test_with_timestamp_filter_prefix") {}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampFilterPrefixSettings, GetAndMultiGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<0>(GetParam());
|
||||
bbto.whole_key_filtering = std::get<1>(GetParam());
|
||||
bbto.cache_index_and_filter_blocks = std::get<2>(GetParam());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.prefix_extractor = std::get<3>(GetParam());
|
||||
options.memtable_whole_key_filtering = std::get<4>(GetParam());
|
||||
options.memtable_prefix_bloom_size_ratio = std::get<5>(GetParam());
|
||||
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
const int kMaxKey = 1000;
|
||||
|
||||
// Write any value
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
int idx = 0;
|
||||
for (; idx < kMaxKey / 4; idx++) {
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(idx), "bar"));
|
||||
ASSERT_OK(db_->Put(write_opts, KeyWithPrefix("foo", idx), "bar"));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
for (; idx < kMaxKey / 2; idx++) {
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(idx), "bar"));
|
||||
ASSERT_OK(db_->Put(write_opts, KeyWithPrefix("foo", idx), "bar"));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
for (; idx < kMaxKey; idx++) {
|
||||
ASSERT_OK(db_->Put(write_opts, Key1(idx), "bar"));
|
||||
ASSERT_OK(db_->Put(write_opts, KeyWithPrefix("foo", idx), "bar"));
|
||||
}
|
||||
|
||||
// Read with MultiGet
|
||||
ReadOptions read_opts;
|
||||
read_opts.timestamp = &ts;
|
||||
|
||||
ReadOptions read_opts_total_order;
|
||||
read_opts_total_order.timestamp = &ts;
|
||||
read_opts_total_order.total_order_seek = true;
|
||||
|
||||
for (idx = 0; idx < kMaxKey; idx++) {
|
||||
size_t batch_size = 4;
|
||||
std::vector<std::string> keys_str(batch_size);
|
||||
std::vector<PinnableSlice> values(batch_size);
|
||||
std::vector<Status> statuses(batch_size);
|
||||
ColumnFamilyHandle* cfh = db_->DefaultColumnFamily();
|
||||
|
||||
keys_str[0] = Key1(idx);
|
||||
keys_str[1] = KeyWithPrefix("foo", idx);
|
||||
keys_str[2] = Key1(kMaxKey + idx);
|
||||
keys_str[3] = KeyWithPrefix("foo", kMaxKey + idx);
|
||||
|
||||
auto keys = ConvertStrToSlice(keys_str);
|
||||
|
||||
db_->MultiGet(read_opts, cfh, batch_size, keys.data(), values.data(),
|
||||
statuses.data());
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
ASSERT_OK(statuses[i]);
|
||||
}
|
||||
for (int i = 2; i < 4; i++) {
|
||||
ASSERT_TRUE(statuses[i].IsNotFound());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(read_opts, keys[i], &value));
|
||||
std::unique_ptr<Iterator> it1(db_->NewIterator(read_opts));
|
||||
ASSERT_NE(nullptr, it1);
|
||||
ASSERT_OK(it1->status());
|
||||
// TODO(zjay) Fix seek with prefix
|
||||
// it1->Seek(keys[i]);
|
||||
// ASSERT_TRUE(it1->Valid());
|
||||
}
|
||||
|
||||
for (int i = 2; i < 4; i++) {
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, keys[i], &value);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DBBasicTestWithTimestampFilterPrefixSettings,
|
||||
::testing::Combine(
|
||||
::testing::Values(
|
||||
std::shared_ptr<const FilterPolicy>(nullptr),
|
||||
std::shared_ptr<const FilterPolicy>(NewBloomFilterPolicy(10, true)),
|
||||
std::shared_ptr<const FilterPolicy>(NewBloomFilterPolicy(10,
|
||||
false))),
|
||||
::testing::Bool(), ::testing::Bool(),
|
||||
::testing::Values(
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(1)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(4)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(7)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(8))),
|
||||
::testing::Bool(), ::testing::Values(0, 0.1)));
|
||||
|
||||
class DataVisibilityTest : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
DataVisibilityTest() : DBBasicTestWithTimestampBase("data_visibility_test") {
|
||||
@@ -1051,7 +1683,7 @@ TEST_F(DataVisibilityTest, MultiGetWithTimestamp) {
|
||||
VerifyDefaultCF(snap0);
|
||||
VerifyDefaultCF(snap1);
|
||||
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
const Snapshot* snap2 = db_->GetSnapshot();
|
||||
PutTestData(2);
|
||||
@@ -1137,7 +1769,7 @@ TEST_F(DataVisibilityTest, MultiGetCrossCF) {
|
||||
VerifyDefaultCF(snap0);
|
||||
VerifyDefaultCF(snap1);
|
||||
|
||||
Flush();
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
const Snapshot* snap2 = db_->GetSnapshot();
|
||||
PutTestData(2);
|
||||
@@ -1518,6 +2150,8 @@ TEST_F(DBBasicTestWithTimestamp, BatchWriteAndMultiGet) {
|
||||
options.create_if_missing = true;
|
||||
options.env = env_;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
options.memtable_prefix_bloom_size_ratio = 0.1;
|
||||
options.memtable_whole_key_filtering = true;
|
||||
|
||||
size_t ts_sz = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(ts_sz);
|
||||
@@ -1772,6 +2406,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DBBasicTestWithTimestampPrefixSeek,
|
||||
::testing::Combine(
|
||||
::testing::Values(
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(1)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(4)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(7)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(8))),
|
||||
|
||||
+5
-5
@@ -260,7 +260,7 @@ TEST_P(DBWriteTest, WriteThreadHangOnWriteStall) {
|
||||
TEST_P(DBWriteTest, IOErrorOnWALWritePropagateToWriteThreadFollower) {
|
||||
constexpr int kNumThreads = 5;
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = GetOptions();
|
||||
options.env = mock_env.get();
|
||||
Reopen(options);
|
||||
@@ -320,7 +320,7 @@ TEST_P(DBWriteTest, ManualWalFlushInEffect) {
|
||||
ASSERT_TRUE(dbfull()->FlushWAL(false).ok());
|
||||
ASSERT_TRUE(dbfull()->TEST_WALBufferIsEmpty());
|
||||
// try the 2nd wal created during SwitchWAL
|
||||
dbfull()->TEST_SwitchWAL();
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_TRUE(Put("key" + ToString(0), "value").ok());
|
||||
ASSERT_TRUE(options.manual_wal_flush != dbfull()->TEST_WALBufferIsEmpty());
|
||||
ASSERT_TRUE(dbfull()->FlushWAL(false).ok());
|
||||
@@ -329,7 +329,7 @@ TEST_P(DBWriteTest, ManualWalFlushInEffect) {
|
||||
|
||||
TEST_P(DBWriteTest, IOErrorOnWALWriteTriggersReadOnlyMode) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = GetOptions();
|
||||
options.env = mock_env.get();
|
||||
Reopen(options);
|
||||
@@ -361,7 +361,7 @@ TEST_P(DBWriteTest, IOErrorOnWALWriteTriggersReadOnlyMode) {
|
||||
TEST_P(DBWriteTest, IOErrorOnSwitchMemtable) {
|
||||
Random rnd(301);
|
||||
std::unique_ptr<FaultInjectionTestEnv> mock_env(
|
||||
new FaultInjectionTestEnv(Env::Default()));
|
||||
new FaultInjectionTestEnv(env_));
|
||||
Options options = GetOptions();
|
||||
options.env = mock_env.get();
|
||||
options.writable_file_max_buffer_size = 4 * 1024 * 1024;
|
||||
@@ -395,7 +395,7 @@ TEST_P(DBWriteTest, LockWalInEffect) {
|
||||
ASSERT_TRUE(dbfull()->TEST_WALBufferIsEmpty(false));
|
||||
ASSERT_OK(dbfull()->UnlockWAL());
|
||||
// try the 2nd wal created during SwitchWAL
|
||||
dbfull()->TEST_SwitchWAL();
|
||||
ASSERT_OK(dbfull()->TEST_SwitchWAL());
|
||||
ASSERT_OK(Put("key" + ToString(0), "value"));
|
||||
ASSERT_TRUE(options.manual_wal_flush != dbfull()->TEST_WALBufferIsEmpty());
|
||||
ASSERT_OK(dbfull()->LockWAL());
|
||||
|
||||
+29
-6
@@ -49,7 +49,8 @@ EntryType GetEntryType(ValueType value_type) {
|
||||
|
||||
bool ParseFullKey(const Slice& internal_key, FullKey* fkey) {
|
||||
ParsedInternalKey ikey;
|
||||
if (ParseInternalKey(internal_key, &ikey) != Status::OK()) {
|
||||
if (!ParseInternalKey(internal_key, &ikey, false /*log_err_key */)
|
||||
.ok()) { // TODO
|
||||
return false;
|
||||
}
|
||||
fkey->user_key = ikey.user_key;
|
||||
@@ -77,12 +78,34 @@ void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
PutFixed64(result, PackSequenceAndType(s, t));
|
||||
}
|
||||
|
||||
std::string ParsedInternalKey::DebugString(bool hex) const {
|
||||
void AppendKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(ts_sz > 0);
|
||||
const std::string kTsMin(ts_sz, static_cast<unsigned char>(0));
|
||||
result->append(key.data(), key.size());
|
||||
result->append(kTsMin.data(), ts_sz);
|
||||
}
|
||||
|
||||
void AppendKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz) {
|
||||
assert(ts_sz > 0);
|
||||
const std::string kTsMax(ts_sz, static_cast<unsigned char>(0xff));
|
||||
result->append(key.data(), key.size());
|
||||
result->append(kTsMax.data(), ts_sz);
|
||||
}
|
||||
|
||||
std::string ParsedInternalKey::DebugString(bool log_err_key, bool hex) const {
|
||||
std::string result = "'";
|
||||
if (log_err_key) {
|
||||
result += user_key.ToString(hex);
|
||||
} else {
|
||||
result += "<redacted>";
|
||||
}
|
||||
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "' seq:%" PRIu64 ", type:%d", sequence,
|
||||
static_cast<int>(type));
|
||||
std::string result = "'";
|
||||
result += user_key.ToString(hex);
|
||||
|
||||
result += buf;
|
||||
return result;
|
||||
}
|
||||
@@ -90,8 +113,8 @@ std::string ParsedInternalKey::DebugString(bool hex) const {
|
||||
std::string InternalKey::DebugString(bool hex) const {
|
||||
std::string result;
|
||||
ParsedInternalKey parsed;
|
||||
if (ParseInternalKey(rep_, &parsed) == Status::OK()) {
|
||||
result = parsed.DebugString(hex);
|
||||
if (ParseInternalKey(rep_, &parsed, false /* log_err_key */).ok()) {
|
||||
result = parsed.DebugString(true /* log_err_key */, hex); // TODO
|
||||
} else {
|
||||
result = "(bad)";
|
||||
result.append(EscapeString(rep_));
|
||||
|
||||
+37
-11
@@ -112,13 +112,19 @@ struct ParsedInternalKey {
|
||||
// u contains timestamp if user timestamp feature is enabled.
|
||||
ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
|
||||
: user_key(u), sequence(seq), type(t) {}
|
||||
std::string DebugString(bool hex = false) const;
|
||||
std::string DebugString(bool log_err_key, bool hex) const;
|
||||
|
||||
void clear() {
|
||||
user_key.clear();
|
||||
sequence = 0;
|
||||
type = kTypeDeletion;
|
||||
}
|
||||
|
||||
void SetTimestamp(const Slice& ts) {
|
||||
assert(ts.size() <= user_key.size());
|
||||
const char* addr = user_key.data() - ts.size();
|
||||
memcpy(const_cast<char*>(addr), ts.data(), ts.size());
|
||||
}
|
||||
};
|
||||
|
||||
// Return the length of the encoding of "key".
|
||||
@@ -161,12 +167,20 @@ extern void AppendInternalKeyWithDifferentTimestamp(
|
||||
extern void AppendInternalKeyFooter(std::string* result, SequenceNumber s,
|
||||
ValueType t);
|
||||
|
||||
// Append the key and a minimal timestamp to *result
|
||||
extern void AppendKeyWithMinTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// Append the key and a maximal timestamp to *result
|
||||
extern void AppendKeyWithMaxTimestamp(std::string* result, const Slice& key,
|
||||
size_t ts_sz);
|
||||
|
||||
// Attempt to parse an internal key from "internal_key". On success,
|
||||
// stores the parsed data in "*result", and returns true.
|
||||
//
|
||||
// On error, returns false, leaves "*result" in an undefined state.
|
||||
extern Status ParseInternalKey(const Slice& internal_key,
|
||||
ParsedInternalKey* result);
|
||||
ParsedInternalKey* result, bool log_err_key);
|
||||
|
||||
// Returns the user key portion of an internal key.
|
||||
inline Slice ExtractUserKey(const Slice& internal_key) {
|
||||
@@ -285,8 +299,8 @@ class InternalKey {
|
||||
|
||||
bool Valid() const {
|
||||
ParsedInternalKey parsed;
|
||||
return (ParseInternalKey(Slice(rep_), &parsed) == Status::OK()) ? true
|
||||
: false;
|
||||
return (ParseInternalKey(Slice(rep_), &parsed, false /* log_err_key */)
|
||||
.ok()); // TODO
|
||||
}
|
||||
|
||||
void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }
|
||||
@@ -319,7 +333,7 @@ class InternalKey {
|
||||
AppendInternalKeyFooter(&rep_, s, t);
|
||||
}
|
||||
|
||||
std::string DebugString(bool hex = false) const;
|
||||
std::string DebugString(bool hex) const;
|
||||
};
|
||||
|
||||
inline int InternalKeyComparator::Compare(const InternalKey& a,
|
||||
@@ -328,20 +342,27 @@ inline int InternalKeyComparator::Compare(const InternalKey& a,
|
||||
}
|
||||
|
||||
inline Status ParseInternalKey(const Slice& internal_key,
|
||||
ParsedInternalKey* result) {
|
||||
ParsedInternalKey* result, bool log_err_key) {
|
||||
const size_t n = internal_key.size();
|
||||
|
||||
if (n < kNumInternalBytes) {
|
||||
return Status::Corruption("Internal Key too small");
|
||||
return Status::Corruption("Corrupted Key: Internal Key too small. Size=" +
|
||||
std::to_string(n) + ". ");
|
||||
}
|
||||
|
||||
uint64_t num = DecodeFixed64(internal_key.data() + n - kNumInternalBytes);
|
||||
unsigned char c = num & 0xff;
|
||||
result->sequence = num >> 8;
|
||||
result->type = static_cast<ValueType>(c);
|
||||
assert(result->type <= ValueType::kMaxValue);
|
||||
result->user_key = Slice(internal_key.data(), n - kNumInternalBytes);
|
||||
return IsExtendedValueType(result->type)
|
||||
? Status::OK()
|
||||
: Status::Corruption("Invalid Key Type");
|
||||
|
||||
if (IsExtendedValueType(result->type)) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
return Status::Corruption("Corrupted Key",
|
||||
result->DebugString(log_err_key, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Update the sequence number in the internal key.
|
||||
@@ -475,9 +496,14 @@ class IterKey {
|
||||
|
||||
// Update the sequence number in the internal key. Guarantees not to
|
||||
// invalidate slices to the key (and the user key).
|
||||
void UpdateInternalKey(uint64_t seq, ValueType t) {
|
||||
void UpdateInternalKey(uint64_t seq, ValueType t, const Slice* ts = nullptr) {
|
||||
assert(!IsKeyPinned());
|
||||
assert(key_size_ >= kNumInternalBytes);
|
||||
if (ts) {
|
||||
assert(key_size_ >= kNumInternalBytes + ts->size());
|
||||
memcpy(&buf_[key_size_ - kNumInternalBytes - ts->size()], ts->data(),
|
||||
ts->size());
|
||||
}
|
||||
uint64_t newval = (seq << 8) | t;
|
||||
EncodeFixed64(&buf_[key_size_ - kNumInternalBytes], newval);
|
||||
}
|
||||
|
||||
+3
-4
@@ -8,7 +8,6 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "logging/logging.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
@@ -41,12 +40,12 @@ static void TestKey(const std::string& key,
|
||||
Slice in(encoded);
|
||||
ParsedInternalKey decoded("", 0, kTypeValue);
|
||||
|
||||
ASSERT_OK(ParseInternalKey(in, &decoded));
|
||||
ASSERT_OK(ParseInternalKey(in, &decoded, true /* log_err_key */));
|
||||
ASSERT_EQ(key, decoded.user_key.ToString());
|
||||
ASSERT_EQ(seq, decoded.sequence);
|
||||
ASSERT_EQ(vt, decoded.type);
|
||||
|
||||
ASSERT_NOK(ParseInternalKey(Slice("bar"), &decoded));
|
||||
ASSERT_NOK(ParseInternalKey(Slice("bar"), &decoded, true /* log_err_key */));
|
||||
}
|
||||
|
||||
class FormatTest : public testing::Test {};
|
||||
@@ -186,7 +185,7 @@ TEST_F(FormatTest, UpdateInternalKey) {
|
||||
|
||||
Slice in(ikey);
|
||||
ParsedInternalKey decoded;
|
||||
ASSERT_OK(ParseInternalKey(in, &decoded));
|
||||
ASSERT_OK(ParseInternalKey(in, &decoded, true /* log_err_key */));
|
||||
ASSERT_EQ(user_key, decoded.user_key.ToString());
|
||||
ASSERT_EQ(new_seq, decoded.sequence);
|
||||
ASSERT_EQ(new_val_type, decoded.type);
|
||||
|
||||
@@ -112,7 +112,7 @@ class DeleteFileTest : public DBTestBase {
|
||||
uint64_t number;
|
||||
FileType type;
|
||||
if (ParseFileName(file, &number, &type)) {
|
||||
log_cnt += (type == kLogFile);
|
||||
log_cnt += (type == kWalFile);
|
||||
sst_cnt += (type == kTableFile);
|
||||
manifest_cnt += (type == kDescriptorFile);
|
||||
}
|
||||
|
||||
+97
-32
@@ -111,6 +111,23 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, Status::SubCode, bool>,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
false),
|
||||
Status::Severity::kFatalError},
|
||||
// Errors during MANIFEST write when WAL is disabled
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWriteNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
true),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWriteNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kNoSpace,
|
||||
false),
|
||||
Status::Severity::kHardError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWriteNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWriteNoWAL,
|
||||
Status::Code::kIOError, Status::SubCode::kIOFenced,
|
||||
false),
|
||||
Status::Severity::kFatalError},
|
||||
|
||||
};
|
||||
|
||||
@@ -175,6 +192,12 @@ std::map<std::tuple<BackgroundErrorReason, Status::Code, bool>,
|
||||
{std::make_tuple(BackgroundErrorReason::kFlushNoWAL,
|
||||
Status::Code::kIOError, false),
|
||||
Status::Severity::kNoError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWriteNoWAL,
|
||||
Status::Code::kIOError, true),
|
||||
Status::Severity::kFatalError},
|
||||
{std::make_tuple(BackgroundErrorReason::kManifestWriteNoWAL,
|
||||
Status::Code::kIOError, false),
|
||||
Status::Severity::kFatalError},
|
||||
};
|
||||
|
||||
std::map<std::tuple<BackgroundErrorReason, bool>, Status::Severity>
|
||||
@@ -244,10 +267,11 @@ void ErrorHandler::CancelErrorRecovery() {
|
||||
// This can also get called as part of a recovery operation. In that case, we
|
||||
// also track the error separately in recovery_error_ so we can tell in the
|
||||
// end whether recovery succeeded or not
|
||||
Status ErrorHandler::SetBGError(const Status& bg_err, BackgroundErrorReason reason) {
|
||||
const Status& ErrorHandler::SetBGError(const Status& bg_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_err.ok()) {
|
||||
return Status::OK();
|
||||
return bg_err;
|
||||
}
|
||||
|
||||
bool paranoid = db_options_.paranoid_checks;
|
||||
@@ -324,11 +348,32 @@ Status ErrorHandler::SetBGError(const Status& bg_err, BackgroundErrorReason reas
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason) {
|
||||
// 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 (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
|
||||
// to a soft error. Note that, it includes the case that IO error happens
|
||||
// in SST or manifest write during flush.
|
||||
// c) all other errors are mapped to hard error.
|
||||
// 3) for other cases, SetBGError(const Status& bg_err, BackgroundErrorReason
|
||||
// reason) will be called to handle other error cases.
|
||||
const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason) {
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_io_err.ok()) {
|
||||
return Status::OK();
|
||||
return bg_io_err;
|
||||
}
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "Background IO error %s",
|
||||
bg_io_err.ToString().c_str());
|
||||
@@ -336,37 +381,41 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
if (recovery_in_prog_ && recovery_io_error_.ok()) {
|
||||
recovery_io_error_ = bg_io_err;
|
||||
}
|
||||
if (BackgroundErrorReason::kManifestWrite == reason) {
|
||||
if (BackgroundErrorReason::kManifestWrite == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
// Always returns ok
|
||||
db_->DisableFileDeletionsWithLock().PermitUncheckedError();
|
||||
}
|
||||
|
||||
Status new_bg_io_err = bg_io_err;
|
||||
Status s;
|
||||
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;
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_err;
|
||||
}
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason, &s,
|
||||
db_mutex_, &auto_recovery);
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&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, &s,
|
||||
db_mutex_, &auto_recovery);
|
||||
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()) {
|
||||
@@ -374,7 +423,8 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
}
|
||||
recover_context_ = context;
|
||||
return bg_error_;
|
||||
} else if (BackgroundErrorReason::kFlushNoWAL == reason) {
|
||||
} else if (BackgroundErrorReason::kFlushNoWAL == reason ||
|
||||
BackgroundErrorReason::kManifestWriteNoWAL == reason) {
|
||||
// When the BG Retryable IO error reason is flush without WAL,
|
||||
// We map it to a soft error. At the same time, all the background work
|
||||
// should be stopped except the BG work from recovery. Therefore, we
|
||||
@@ -405,12 +455,11 @@ Status ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
}
|
||||
} else {
|
||||
s = SetBGError(new_bg_io_err, reason);
|
||||
return SetBGError(new_bg_io_err, reason);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status ErrorHandler::OverrideNoSpaceError(Status bg_error,
|
||||
Status ErrorHandler::OverrideNoSpaceError(const Status& bg_error,
|
||||
bool* auto_recovery) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (bg_error.severity() >= Status::Severity::kFatalError) {
|
||||
@@ -466,7 +515,11 @@ Status ErrorHandler::ClearBGError() {
|
||||
// Signal that recovery succeeded
|
||||
if (recovery_error_.ok()) {
|
||||
Status old_bg_error = bg_error_;
|
||||
// Clear and check the recovery IO and BG error
|
||||
bg_error_ = Status::OK();
|
||||
recovery_io_error_ = IOStatus::OK();
|
||||
bg_error_.PermitUncheckedError();
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
recovery_in_prog_ = false;
|
||||
soft_error_no_bg_work_ = false;
|
||||
EventHelpers::NotifyOnErrorRecoveryCompleted(db_options_.listeners,
|
||||
@@ -516,6 +569,7 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
// during the recovery process. While recovering, the only operations that
|
||||
// can generate background errors should be the flush operations
|
||||
recovery_error_ = Status::OK();
|
||||
recovery_error_.PermitUncheckedError();
|
||||
Status s = db_->ResumeImpl(recover_context_);
|
||||
if (s.ok()) {
|
||||
soft_error_no_bg_work_ = false;
|
||||
@@ -537,24 +591,34 @@ Status ErrorHandler::RecoverFromBGError(bool is_manual) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Status ErrorHandler::StartRecoverFromRetryableBGIOError(IOStatus io_error) {
|
||||
const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
const IOStatus& io_error) {
|
||||
#ifndef ROCKSDB_LITE
|
||||
db_mutex_->AssertHeld();
|
||||
if (bg_error_.ok() || io_error.ok()) {
|
||||
return Status::OK();
|
||||
}
|
||||
if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_ ||
|
||||
recovery_thread_) {
|
||||
if (bg_error_.ok()) {
|
||||
return bg_error_;
|
||||
} else if (io_error.ok()) {
|
||||
return io_error;
|
||||
} else if (db_options_.max_bgerror_resume_count <= 0 || recovery_in_prog_) {
|
||||
// Auto resume BG error is not enabled, directly return bg_error_.
|
||||
return bg_error_;
|
||||
}
|
||||
|
||||
if (recovery_thread_) {
|
||||
// In this case, if recovery_in_prog_ is false, current thread should
|
||||
// wait the previous recover thread to finish and create a new thread
|
||||
// to recover from the bg error.
|
||||
db_mutex_->Unlock();
|
||||
recovery_thread_->join();
|
||||
db_mutex_->Lock();
|
||||
}
|
||||
|
||||
recovery_in_prog_ = true;
|
||||
recovery_thread_.reset(
|
||||
new port::Thread(&ErrorHandler::RecoverFromRetryableBGIOError, this));
|
||||
|
||||
if (recovery_io_error_.ok() && recovery_error_.ok()) {
|
||||
return Status::OK();
|
||||
return recovery_error_;
|
||||
} else {
|
||||
TEST_SYNC_POINT("StartRecoverRetryableBGIOError:RecoverFail");
|
||||
return bg_error_;
|
||||
@@ -619,6 +683,7 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:RecoverSuccess");
|
||||
Status old_bg_error = bg_error_;
|
||||
bg_error_ = Status::OK();
|
||||
bg_error_.PermitUncheckedError();
|
||||
EventHelpers::NotifyOnErrorRecoveryCompleted(db_options_.listeners,
|
||||
old_bg_error, db_mutex_);
|
||||
recovery_in_prog_ = false;
|
||||
|
||||
+9
-11
@@ -31,17 +31,14 @@ class ErrorHandler {
|
||||
InstrumentedMutex* db_mutex)
|
||||
: db_(db),
|
||||
db_options_(db_options),
|
||||
bg_error_(Status::OK()),
|
||||
recovery_error_(Status::OK()),
|
||||
recovery_io_error_(IOStatus::OK()),
|
||||
cv_(db_mutex),
|
||||
end_recovery_(false),
|
||||
recovery_thread_(nullptr),
|
||||
db_mutex_(db_mutex),
|
||||
auto_recovery_(false),
|
||||
recovery_in_prog_(false),
|
||||
soft_error_no_bg_work_(false) {}
|
||||
~ErrorHandler() {
|
||||
soft_error_no_bg_work_(false) {
|
||||
// Clear the checked flag for uninitialized errors
|
||||
bg_error_.PermitUncheckedError();
|
||||
recovery_error_.PermitUncheckedError();
|
||||
recovery_io_error_.PermitUncheckedError();
|
||||
@@ -53,13 +50,14 @@ class ErrorHandler {
|
||||
Status::Code code,
|
||||
Status::SubCode subcode);
|
||||
|
||||
Status SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
const Status& SetBGError(const Status& bg_err, BackgroundErrorReason reason);
|
||||
|
||||
Status SetBGError(const IOStatus& bg_io_err, BackgroundErrorReason reason);
|
||||
const Status& SetBGError(const IOStatus& bg_io_err,
|
||||
BackgroundErrorReason reason);
|
||||
|
||||
Status GetBGError() { return bg_error_; }
|
||||
Status GetBGError() const { return bg_error_; }
|
||||
|
||||
Status GetRecoveryError() { return recovery_error_; }
|
||||
Status GetRecoveryError() const { return recovery_error_; }
|
||||
|
||||
Status ClearBGError();
|
||||
|
||||
@@ -110,9 +108,9 @@ class ErrorHandler {
|
||||
// Used to store the context for recover, such as flush reason.
|
||||
DBRecoverContext recover_context_;
|
||||
|
||||
Status OverrideNoSpaceError(Status bg_error, bool* auto_recovery);
|
||||
Status OverrideNoSpaceError(const Status& bg_error, bool* auto_recovery);
|
||||
void RecoverFromNoSpace();
|
||||
Status StartRecoverFromRetryableBGIOError(IOStatus io_error);
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
void RecoverFromRetryableBGIOError();
|
||||
};
|
||||
|
||||
|
||||
+436
-245
File diff suppressed because it is too large
Load Diff
@@ -24,13 +24,13 @@ class ExternalSSTFileBasicTest
|
||||
ExternalSSTFileBasicTest()
|
||||
: DBTestBase("/external_sst_file_basic_test", /*env_do_fsync=*/true) {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
fault_injection_test_env_.reset(new FaultInjectionTestEnv(Env::Default()));
|
||||
fault_injection_test_env_.reset(new FaultInjectionTestEnv(env_));
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
void DestroyAndRecreateExternalSSTFilesDir() {
|
||||
DestroyDir(env_, sst_files_dir_);
|
||||
env_->CreateDir(sst_files_dir_);
|
||||
ASSERT_OK(DestroyDir(env_, sst_files_dir_));
|
||||
ASSERT_OK(env_->CreateDir(sst_files_dir_));
|
||||
}
|
||||
|
||||
Status DeprecatedAddFile(const std::vector<std::string>& files,
|
||||
@@ -162,7 +162,9 @@ class ExternalSSTFileBasicTest
|
||||
write_global_seqno, verify_checksums_before_ingest, true_data);
|
||||
}
|
||||
|
||||
~ExternalSSTFileBasicTest() override { DestroyDir(env_, sst_files_dir_); }
|
||||
~ExternalSSTFileBasicTest() override {
|
||||
DestroyDir(env_, sst_files_dir_).PermitUncheckedError();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string sst_files_dir_;
|
||||
@@ -186,7 +188,7 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
@@ -202,14 +204,14 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, kUnknownFileChecksumFuncName);
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Put(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Add file using file path
|
||||
s = DeprecatedAddFile({file1});
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
@@ -286,7 +288,7 @@ TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::string file_checksum, file_checksum_func_name;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file1, &file_checksum, &file_checksum_func_name));
|
||||
@@ -305,14 +307,14 @@ TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name);
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Put(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Add file using file path
|
||||
s = DeprecatedAddFile({file1});
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
@@ -338,7 +340,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(1000));
|
||||
@@ -357,7 +359,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ExternalSstFileInfo file2_info;
|
||||
s = sst_file_writer.Finish(&file2_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 200);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(1100));
|
||||
@@ -376,7 +378,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
s = sst_file_writer.Finish(&file3_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 200);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(1300));
|
||||
@@ -395,7 +397,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ExternalSstFileInfo file4_info;
|
||||
s = sst_file_writer.Finish(&file4_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file4_info.file_path, file4);
|
||||
ASSERT_EQ(file4_info.num_entries, 300);
|
||||
ASSERT_EQ(file4_info.smallest_key, Key(1500));
|
||||
@@ -414,7 +416,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ExternalSstFileInfo file5_info;
|
||||
s = sst_file_writer.Finish(&file5_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file5_info.file_path, file5);
|
||||
ASSERT_EQ(file5_info.num_entries, 200);
|
||||
ASSERT_EQ(file5_info.smallest_key, Key(1800));
|
||||
@@ -433,7 +435,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
}
|
||||
ExternalSstFileInfo file6_info;
|
||||
s = sst_file_writer.Finish(&file6_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file6_info.file_path, file6);
|
||||
ASSERT_EQ(file6_info.num_entries, 200);
|
||||
ASSERT_EQ(file6_info.smallest_key, Key(2000));
|
||||
@@ -447,7 +449,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
s = AddFileWithFileChecksum({file1}, {file_checksum1, "xyz"},
|
||||
{file_checksum1}, true, false, false, false);
|
||||
// does not care the checksum input since db does not enable file checksum
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file1));
|
||||
std::vector<LiveFileMetaData> live_files;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files);
|
||||
@@ -465,26 +467,26 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
s = AddFileWithFileChecksum({file2}, {file_checksum2, "xyz"},
|
||||
{file_checksum_func_name2}, true, false, false,
|
||||
false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// The checksum name does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file2}, {file_checksum2}, {"xyz"}, true, false,
|
||||
false, false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// The checksum itself does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file2}, {"xyz"}, {file_checksum_func_name2},
|
||||
true, false, false, false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// All matches, ingestion is successful
|
||||
s = AddFileWithFileChecksum({file2}, {file_checksum2},
|
||||
{file_checksum_func_name2}, true, false, false,
|
||||
false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files1;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files1);
|
||||
for (auto f : live_files1) {
|
||||
@@ -501,7 +503,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
std::vector<std::string> checksum, checksum_func;
|
||||
s = AddFileWithFileChecksum({file3}, checksum, checksum_func, true, false,
|
||||
false, false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files2;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files2);
|
||||
for (auto f : live_files2) {
|
||||
@@ -511,20 +513,20 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file3));
|
||||
|
||||
// Does not enable verify_file_checksum options
|
||||
// The checksum name does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file4}, {file_checksum4}, {"xyz"}, false, false,
|
||||
false, false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
|
||||
// Does not enable verify_file_checksum options
|
||||
// Checksum function name matches, store the checksum being ingested.
|
||||
s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4},
|
||||
false, false, false, false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files3;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files3);
|
||||
for (auto f : live_files3) {
|
||||
@@ -535,7 +537,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file4));
|
||||
|
||||
// enable verify_file_checksum options, DB enable checksum, and enable
|
||||
@@ -544,8 +546,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
s = AddFileWithFileChecksum({file5}, {file_checksum5},
|
||||
{file_checksum_func_name5}, true, false, false,
|
||||
true);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files4;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files4);
|
||||
for (auto f : live_files4) {
|
||||
@@ -558,7 +559,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file5));
|
||||
|
||||
// Does not enable verify_file_checksum options and also the ingested file
|
||||
@@ -567,7 +568,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
std::vector<std::string> files_c6, files_name6;
|
||||
s = AddFileWithFileChecksum({file6}, files_c6, files_name6, false, false,
|
||||
false, false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files6;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files6);
|
||||
for (auto f : live_files6) {
|
||||
@@ -577,7 +578,7 @@ TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file6));
|
||||
}
|
||||
|
||||
@@ -595,7 +596,7 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0));
|
||||
@@ -609,7 +610,7 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
}
|
||||
ExternalSstFileInfo file2_info;
|
||||
s = sst_file_writer.Finish(&file2_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 200);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(100));
|
||||
@@ -623,23 +624,23 @@ TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
s = sst_file_writer.Finish(&file3_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 15);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(110));
|
||||
ASSERT_EQ(file3_info.largest_key, Key(124));
|
||||
|
||||
s = DeprecatedAddFile({file1}, true /* move file */);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(Status::NotFound(), env_->FileExists(file1));
|
||||
|
||||
s = DeprecatedAddFile({file2}, false /* copy file */);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file2));
|
||||
|
||||
// This file has overlapping values with the existing data
|
||||
s = DeprecatedAddFile({file3}, true /* move file */);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
ASSERT_NOK(s) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file3));
|
||||
|
||||
for (int k = 0; k < 300; k++) {
|
||||
@@ -1109,6 +1110,7 @@ TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
|
||||
}
|
||||
|
||||
Options sst_file_writer_options;
|
||||
sst_file_writer_options.env = env_;
|
||||
std::unique_ptr<SstFileWriter> sst_file_writer(
|
||||
new SstFileWriter(EnvOptions(), sst_file_writer_options));
|
||||
std::string file_name =
|
||||
@@ -1125,7 +1127,7 @@ TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
|
||||
if (i == 2) {
|
||||
ingest_opt.write_global_seqno = true;
|
||||
}
|
||||
ASSERT_FALSE(db_->IngestExternalFile({file_name}, ingest_opt).ok());
|
||||
ASSERT_NOK(db_->IngestExternalFile({file_name}, ingest_opt));
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -1137,11 +1139,12 @@ TEST_F(ExternalSSTFileBasicTest, SyncFailure) {
|
||||
TEST_F(ExternalSSTFileBasicTest, VerifyChecksumReadahead) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
SpecialEnv senv(Env::Default());
|
||||
SpecialEnv senv(env_);
|
||||
options.env = &senv;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Options sst_file_writer_options;
|
||||
sst_file_writer_options.env = env_;
|
||||
std::unique_ptr<SstFileWriter> sst_file_writer(
|
||||
new SstFileWriter(EnvOptions(), sst_file_writer_options));
|
||||
std::string file_name = sst_files_dir_ + "verify_checksum_readahead_test.sst";
|
||||
@@ -1324,7 +1327,7 @@ TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
|
||||
ASSERT_OK(sst_file_writer.DeleteRange(Key(300), Key(400)));
|
||||
ExternalSstFileInfo file8_info;
|
||||
Status s = sst_file_writer.Finish(&file8_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file8_info.file_path, file8);
|
||||
ASSERT_EQ(file8_info.num_entries, 0);
|
||||
ASSERT_EQ(file8_info.smallest_key, "");
|
||||
@@ -1339,7 +1342,7 @@ TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
|
||||
ASSERT_OK(sst_file_writer.DeleteRange(Key(400), Key(500)));
|
||||
ExternalSstFileInfo file9_info;
|
||||
s = sst_file_writer.Finish(&file9_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(file9_info.file_path, file9);
|
||||
ASSERT_EQ(file9_info.num_entries, 0);
|
||||
ASSERT_EQ(file9_info.smallest_key, "");
|
||||
@@ -1351,7 +1354,7 @@ TEST_F(ExternalSSTFileBasicTest, AdjacentRangeDeletionTombstones) {
|
||||
// Range deletion tombstones are exclusive on their end key, so these SSTs
|
||||
// should not be considered as overlapping.
|
||||
s = DeprecatedAddFile({file8, file9});
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(s) << s.ToString();
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
@@ -192,10 +192,13 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
// Step 1: generate the checksum for ingested sst file.
|
||||
if (need_generate_file_checksum_) {
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
|
||||
std::string generated_checksum, generated_checksum_func_name;
|
||||
std::string generated_checksum;
|
||||
std::string generated_checksum_func_name;
|
||||
std::string requested_checksum_func_name;
|
||||
IOStatus io_s = GenerateOneFileChecksum(
|
||||
fs_.get(), files_to_ingest_[i].internal_file_path,
|
||||
db_options_.file_checksum_gen_factory.get(), &generated_checksum,
|
||||
db_options_.file_checksum_gen_factory.get(),
|
||||
requested_checksum_func_name, &generated_checksum,
|
||||
&generated_checksum_func_name,
|
||||
ingestion_options_.verify_checksums_readahead_size,
|
||||
db_options_.allow_mmap_reads, io_tracer_);
|
||||
@@ -313,8 +316,8 @@ Status ExternalSstFileIngestionJob::NeedsFlush(bool* flush_needed,
|
||||
ranges.emplace_back(file_to_ingest.smallest_internal_key.user_key(),
|
||||
file_to_ingest.largest_internal_key.user_key());
|
||||
}
|
||||
Status status =
|
||||
cfd_->RangesOverlapWithMemtables(ranges, super_version, flush_needed);
|
||||
Status status = cfd_->RangesOverlapWithMemtables(
|
||||
ranges, super_version, db_options_.allow_data_in_errors, flush_needed);
|
||||
if (status.ok() && *flush_needed &&
|
||||
!ingestion_options_.allow_blocking_flush) {
|
||||
status = Status::InvalidArgument("External file requires flush");
|
||||
@@ -332,6 +335,12 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
// with the files we are ingesting
|
||||
bool need_flush = false;
|
||||
status = NeedsFlush(&need_flush, super_version);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (need_flush) {
|
||||
return Status::TryAgain();
|
||||
}
|
||||
assert(status.ok() && need_flush == false);
|
||||
#endif
|
||||
|
||||
@@ -599,22 +608,28 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
file_to_ingest->largest_internal_key =
|
||||
InternalKey("", 0, ValueType::kTypeValue);
|
||||
bool bounds_set = false;
|
||||
bool allow_data_in_errors = db_options_.allow_data_in_errors;
|
||||
iter->SeekToFirst();
|
||||
if (iter->Valid()) {
|
||||
if (ParseInternalKey(iter->key(), &key) != Status::OK()) {
|
||||
return Status::Corruption("external file have corrupted keys");
|
||||
Status pik_status =
|
||||
ParseInternalKey(iter->key(), &key, allow_data_in_errors);
|
||||
if (!pik_status.ok()) {
|
||||
return Status::Corruption("Corrupted key in external file. ",
|
||||
pik_status.getState());
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption("external file have non zero sequence number");
|
||||
return Status::Corruption("External file has non zero sequence number");
|
||||
}
|
||||
file_to_ingest->smallest_internal_key.SetFrom(key);
|
||||
|
||||
iter->SeekToLast();
|
||||
if (ParseInternalKey(iter->key(), &key) != Status::OK()) {
|
||||
return Status::Corruption("external file have corrupted keys");
|
||||
pik_status = ParseInternalKey(iter->key(), &key, allow_data_in_errors);
|
||||
if (!pik_status.ok()) {
|
||||
return Status::Corruption("Corrupted key in external file. ",
|
||||
pik_status.getState());
|
||||
}
|
||||
if (key.sequence != 0) {
|
||||
return Status::Corruption("external file have non zero sequence number");
|
||||
return Status::Corruption("External file has non zero sequence number");
|
||||
}
|
||||
file_to_ingest->largest_internal_key.SetFrom(key);
|
||||
|
||||
@@ -627,8 +642,11 @@ Status ExternalSstFileIngestionJob::GetIngestedFileInfo(
|
||||
if (range_del_iter != nullptr) {
|
||||
for (range_del_iter->SeekToFirst(); range_del_iter->Valid();
|
||||
range_del_iter->Next()) {
|
||||
if (ParseInternalKey(range_del_iter->key(), &key) != Status::OK()) {
|
||||
return Status::Corruption("external file have corrupted keys");
|
||||
Status pik_status =
|
||||
ParseInternalKey(range_del_iter->key(), &key, allow_data_in_errors);
|
||||
if (!pik_status.ok()) {
|
||||
return Status::Corruption("Corrupted key in external file. ",
|
||||
pik_status.getState());
|
||||
}
|
||||
RangeTombstone tombstone(key, range_del_iter->value());
|
||||
|
||||
@@ -830,11 +848,13 @@ IOStatus ExternalSstFileIngestionJob::GenerateChecksumForIngestedFile(
|
||||
// file checksum generated during Prepare(). This step will be skipped.
|
||||
return IOStatus::OK();
|
||||
}
|
||||
std::string file_checksum, file_checksum_func_name;
|
||||
std::string file_checksum;
|
||||
std::string file_checksum_func_name;
|
||||
std::string requested_checksum_func_name;
|
||||
IOStatus io_s = GenerateOneFileChecksum(
|
||||
fs_.get(), file_to_ingest->internal_file_path,
|
||||
db_options_.file_checksum_gen_factory.get(), &file_checksum,
|
||||
&file_checksum_func_name,
|
||||
db_options_.file_checksum_gen_factory.get(), requested_checksum_func_name,
|
||||
&file_checksum, &file_checksum_func_name,
|
||||
ingestion_options_.verify_checksums_readahead_size,
|
||||
db_options_.allow_mmap_reads, io_tracer_);
|
||||
if (!io_s.ok()) {
|
||||
|
||||
@@ -1345,15 +1345,18 @@ TEST_F(ExternalSSTFileTest, PickedLevelBug) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// While writing the MANIFEST start a thread that will ask for compaction
|
||||
Status bg_compact_status;
|
||||
ROCKSDB_NAMESPACE::port::Thread bg_compact([&]() {
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
bg_compact_status =
|
||||
db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
|
||||
});
|
||||
TEST_SYNC_POINT("ExternalSSTFileTest::PickedLevelBug:2");
|
||||
|
||||
// Start a thread that will ingest a new file
|
||||
Status bg_addfile_status;
|
||||
ROCKSDB_NAMESPACE::port::Thread bg_addfile([&]() {
|
||||
file_keys = {1, 2, 3};
|
||||
ASSERT_OK(GenerateAndAddExternalFile(options, file_keys, 1));
|
||||
bg_addfile_status = GenerateAndAddExternalFile(options, file_keys, 1);
|
||||
});
|
||||
|
||||
// Wait for AddFile to start picking levels and writing MANIFEST
|
||||
@@ -1374,6 +1377,9 @@ TEST_F(ExternalSSTFileTest, PickedLevelBug) {
|
||||
bg_addfile.join();
|
||||
bg_compact.join();
|
||||
|
||||
ASSERT_OK(bg_addfile_status);
|
||||
ASSERT_OK(bg_compact_status);
|
||||
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
|
||||
int total_keys = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user