mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcf3d75f3f | ||
|
|
417828ba84 | ||
|
|
1251252519 | ||
|
|
f181158b95 | ||
|
|
1217630bf3 | ||
|
|
8e588307fc | ||
|
|
5ac7843a03 | ||
|
|
0ddd74c708 | ||
|
|
db0435772f | ||
|
|
e15c8e6819 | ||
|
|
0b31668e0c | ||
|
|
57a817df76 | ||
|
|
8e43279f93 | ||
|
|
c48bae0968 | ||
|
|
d561432de8 | ||
|
|
4340e1ff6d | ||
|
|
8101643611 | ||
|
|
3a9f557451 | ||
|
|
43ac7a2774 | ||
|
|
dc5de45af8 | ||
|
|
cd4ea675e3 | ||
|
|
6cde8d2190 | ||
|
|
3ce4d4f558 | ||
|
|
ad40b0bee2 | ||
|
|
e12753eb71 | ||
|
|
12117b26a3 | ||
|
|
1e8322c0f5 | ||
|
|
8cf4294e25 | ||
|
|
4a7c1dc375 | ||
|
|
d94932323a | ||
|
|
230660be73 | ||
|
|
f4294669e0 | ||
|
|
74544d582f | ||
|
|
4f678b52e7 | ||
|
|
c9539ede76 | ||
|
|
2225f063d4 | ||
|
|
d95ffbaf4f | ||
|
|
b694cd0e0d | ||
|
|
1178d348aa | ||
|
|
f8c685c4fc | ||
|
|
28f54e71f3 | ||
|
|
230f18b6e1 | ||
|
|
cff7819dff | ||
|
|
2035798834 | ||
|
|
9bb13c56b3 | ||
|
|
3295e9f640 | ||
|
|
8689936310 | ||
|
|
17ce1ca48b | ||
|
|
afcd32533c | ||
|
|
937fbcbddc | ||
|
|
1a8eec461b | ||
|
|
fc93553e6d | ||
|
|
a113cecfc9 | ||
|
|
2fbe32b0c1 | ||
|
|
ffd6085e1f | ||
|
|
efaef9b40a | ||
|
|
a7478070f3 | ||
|
|
dddb791c18 | ||
|
|
78556c14dd | ||
|
|
e5b34f5867 | ||
|
|
be351f4754 | ||
|
|
8ef5b9ddfe | ||
|
|
9e788be4b7 | ||
|
|
5aad38f262 | ||
|
|
3fbddb1d27 | ||
|
|
3018a3e27e | ||
|
|
5237b39d2e | ||
|
|
7d74d3471c | ||
|
|
28bab0ef7e | ||
|
|
caadf09d53 | ||
|
|
8be121bb80 | ||
|
|
2a3511a0df | ||
|
|
1ababeb76a | ||
|
|
a64c8ca7a8 | ||
|
|
dfedc74d82 | ||
|
|
312d9c47b4 | ||
|
|
9b53f14a35 | ||
|
|
56810142a5 | ||
|
|
29102641dd | ||
|
|
081722780b | ||
|
|
2b60621f16 | ||
|
|
21f8a57f2a | ||
|
|
67a7b74b7f | ||
|
|
82afa01815 | ||
|
|
f72c834eab | ||
|
|
cfc57f55b5 | ||
|
|
ec9082d698 | ||
|
|
560fe70233 | ||
|
|
a2b9be42b6 | ||
|
|
a5ec5e3ea0 | ||
|
|
230c98f3ce | ||
|
|
b1c27a52d2 | ||
|
|
73e6b89fad | ||
|
|
2b70224f82 | ||
|
|
0e12b1d691 | ||
|
|
01bd86ad35 | ||
|
|
d263505417 | ||
|
|
8e59a1dc9a | ||
|
|
8f4f302316 | ||
|
|
49af999954 | ||
|
|
44d04582cb | ||
|
|
fdf2a0d7eb | ||
|
|
f2d11b3fdc | ||
|
|
92e2399669 | ||
|
|
a7d4bea43a | ||
|
|
f24c39ab3d | ||
|
|
5bf9a7d5ee | ||
|
|
82846f41d3 | ||
|
|
4ec31dc8ac | ||
|
|
dc00e4b120 | ||
|
|
e970248602 |
No files matched your search
+47
-40
@@ -48,7 +48,6 @@ commands:
|
||||
echo "export GTEST_THROW_ON_FAILURE=0" >> $BASH_ENV
|
||||
echo "export GTEST_OUTPUT=\"xml:/tmp/test-results/\"" >> $BASH_ENV
|
||||
echo "export SKIP_FORMAT_BUCK_CHECKS=1" >> $BASH_ENV
|
||||
echo "export PRINT_PARALLEL_OUTPUTS=1" >> $BASH_ENV
|
||||
echo "export GTEST_COLOR=1" >> $BASH_ENV
|
||||
echo "export CTEST_OUTPUT_ON_FAILURE=1" >> $BASH_ENV
|
||||
echo "export CTEST_TEST_TIMEOUT=300" >> $BASH_ENV
|
||||
@@ -98,6 +97,13 @@ commands:
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libbenchmark-dev
|
||||
|
||||
install-librados:
|
||||
steps:
|
||||
- run:
|
||||
name: Install librados
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y librados-dev
|
||||
|
||||
upgrade-cmake:
|
||||
steps:
|
||||
- run:
|
||||
@@ -145,7 +151,7 @@ jobs:
|
||||
- install-pyenv-on-macos
|
||||
- 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
|
||||
- run: ulimit -S -n 1048576 && OPT=-DCIRCLECI make V=1 J=32 -j32 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-cmake:
|
||||
@@ -158,7 +164,7 @@ jobs:
|
||||
- install-cmake-on-macos
|
||||
- install-gflags-on-macos
|
||||
- pre-steps-macos
|
||||
- run: ulimit -S -n 1048576 && (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j32 && ctest -j10) | .circleci/cat_ignore_eagain
|
||||
- run: ulimit -S -n 1048576 && (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j32 && ctest -j10) 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
@@ -168,17 +174,18 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- run: make V=1 J=32 -j32 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-mem-env:
|
||||
build-linux-mem-env-librados:
|
||||
machine:
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: MEM_ENV=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- install-librados
|
||||
- run: MEM_ENV=1 ROCKSDB_USE_LIBRADOS=1 make V=1 J=32 -j32 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-encrypted-env:
|
||||
@@ -188,7 +195,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: ENCRYPTED_ENV=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- run: ENCRYPTED_ENV=1 make V=1 J=32 -j32 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
@@ -198,7 +205,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- 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 check | .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 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-release:
|
||||
@@ -207,10 +214,10 @@ jobs:
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make V=1 -j8 release | .circleci/cat_ignore_eagain
|
||||
- run: make V=1 -j8 release 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- install-gflags
|
||||
- run: make V=1 -j8 release | .circleci/cat_ignore_eagain
|
||||
- run: make V=1 -j8 release 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- post-steps
|
||||
|
||||
@@ -221,11 +228,11 @@ jobs:
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench | .circleci/cat_ignore_eagain
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
- run: make clean
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench | .circleci/cat_ignore_eagain
|
||||
- run: USE_RTTI=1 DEBUG_LEVEL=0 make V=1 -j8 static_lib tools db_bench 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
|
||||
build-linux-lite:
|
||||
@@ -235,7 +242,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: LITE=1 make V=1 J=32 -j32 check | .circleci/cat_ignore_eagain
|
||||
- run: LITE=1 make V=1 J=32 -j32 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-lite-release:
|
||||
@@ -244,10 +251,10 @@ jobs:
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: LITE=1 make V=1 -j8 release | .circleci/cat_ignore_eagain
|
||||
- run: LITE=1 make V=1 -j8 release 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: if ./db_stress --version; then false; else true; fi # ensure without gflags
|
||||
- install-gflags
|
||||
- run: LITE=1 make V=1 -j8 release | .circleci/cat_ignore_eagain
|
||||
- run: LITE=1 make V=1 -j8 release 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: ./db_stress --version # ensure with gflags
|
||||
- post-steps
|
||||
|
||||
@@ -258,7 +265,7 @@ jobs:
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang libgflags-dev libtbb-dev
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all | .circleci/cat_ignore_eagain
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make V=1 -j16 all 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-asan:
|
||||
@@ -269,7 +276,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
- run: COMPILE_WITH_ASAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check 2>&1 | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-mini-tsan:
|
||||
@@ -280,7 +287,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out.
|
||||
- run: COMPILE_WITH_TSAN=1 CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 check 2>&1 | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out.
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-ubsan:
|
||||
@@ -291,7 +298,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
- run: COMPILE_WITH_UBSAN=1 OPT="-fsanitize-blacklist=.circleci/ubsan_suppression_list.txt" CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make V=1 -j32 ubsan_check 2>&1 | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out
|
||||
- post-steps
|
||||
|
||||
build-linux-clang10-clang-analyze:
|
||||
@@ -303,7 +310,7 @@ jobs:
|
||||
- install-gflags
|
||||
- install-clang-10
|
||||
- run: sudo apt-get update -y && sudo apt-get install -y clang-tools-10
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 CLANG_ANALYZER="/usr/bin/clang++-10" CLANG_SCAN_BUILD=scan-build-10 USE_CLANG=1 make V=1 -j32 analyze 2>&1 | .circleci/cat_ignore_eagain # aligned new doesn't work for reason we haven't figured out. For unknown, reason passing "clang++-10" as CLANG_ANALYZER doesn't work, and we need a full path.
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake:
|
||||
@@ -314,7 +321,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- upgrade-cmake
|
||||
- run: (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j20 && ctest -j20) | .circleci/cat_ignore_eagain
|
||||
- run: (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j20 && ctest -j20) 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-ubuntu-20:
|
||||
@@ -325,7 +332,7 @@ jobs:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- install-benchmark
|
||||
- run: (mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20 && make microbench) | .circleci/cat_ignore_eagain
|
||||
- run: (mkdir build && cd build && cmake -DWITH_GFLAGS=1 -DWITH_BENCHMARK=1 .. && make V=1 -j20 && ctest -j20 && make microbench) 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-unity-and-headers:
|
||||
@@ -335,8 +342,8 @@ jobs:
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: apt-get update -y && apt-get install -y libgflags-dev
|
||||
- run: TEST_TMPDIR=/dev/shm && make V=1 -j8 unity_test | .circleci/cat_ignore_eagain
|
||||
- run: make V=1 -j8 -k check-headers | .circleci/cat_ignore_eagain # could be moved to a different build
|
||||
- run: TEST_TMPDIR=/dev/shm && make V=1 -j8 unity_test 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run: make V=1 -j8 -k check-headers 2>&1 | .circleci/cat_ignore_eagain # could be moved to a different build
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-4_8-no_test_run:
|
||||
@@ -346,7 +353,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo apt-get update -y && sudo apt-get install gcc-4.8 g++-4.8 libgflags-dev
|
||||
- run: CC=gcc-4.8 CXX=g++-4.8 V=1 SKIP_LINK=1 make -j8 all | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-4.8 CXX=g++-4.8 V=1 SKIP_LINK=1 make -j8 all 2>&1 | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-8-no_test_run:
|
||||
@@ -356,7 +363,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo apt-get update -y && sudo apt-get install gcc-8 g++-8 libgflags-dev
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 SKIP_LINK=1 make -j8 all | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-8 CXX=g++-8 V=1 SKIP_LINK=1 make -j8 all 2>&1 | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-9-no_test_run:
|
||||
@@ -366,7 +373,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo apt-get update -y && sudo apt-get install gcc-9 g++-9 libgflags-dev
|
||||
- run: CC=gcc-9 CXX=g++-9 V=1 SKIP_LINK=1 make -j8 all | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-9 CXX=g++-9 V=1 SKIP_LINK=1 make -j8 all 2>&1 | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-10-cxx20-no_test_run:
|
||||
@@ -376,7 +383,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo apt-get update -y && sudo apt-get install gcc-10 g++-10 libgflags-dev
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 SKIP_LINK=1 ROCKSDB_CXX_STANDARD=c++20 make -j16 all | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-10 CXX=g++-10 V=1 SKIP_LINK=1 ROCKSDB_CXX_STANDARD=c++20 make -j16 all 2>&1 | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
build-linux-gcc-11-no_test_run:
|
||||
@@ -386,7 +393,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- run: sudo apt-get update -y && sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test && sudo apt-get install gcc-11 g++-11 libgflags-dev
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 SKIP_LINK=1 make -j16 all | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- run: CC=gcc-11 CXX=g++-11 V=1 SKIP_LINK=1 make -j16 all 2>&1 | .circleci/cat_ignore_eagain # Linking broken because libgflags compiled with newer ABI
|
||||
- post-steps
|
||||
|
||||
# This job is only to make sure the microbench tests are able to run, the benchmark result is not meaningful as the CI host is changing.
|
||||
@@ -397,7 +404,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: DEBUG_LEVEL=0 make microbench | .circleci/cat_ignore_eagain
|
||||
- run: DEBUG_LEVEL=0 make microbench 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
@@ -486,10 +493,10 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjava | .circleci/cat_ignore_eagain
|
||||
command: make V=1 J=8 -j8 rocksdbjava 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=8 -j8 jtest | .circleci/cat_ignore_eagain
|
||||
command: make V=1 J=8 -j8 jtest 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-java-static:
|
||||
@@ -510,7 +517,7 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic | .circleci/cat_ignore_eagain
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-java:
|
||||
@@ -533,10 +540,10 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Shared Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjava | .circleci/cat_ignore_eagain
|
||||
command: make V=1 J=8 -j8 rocksdbjava 2>&1 | .circleci/cat_ignore_eagain
|
||||
- run:
|
||||
name: "Test RocksDBJava"
|
||||
command: make V=1 J=8 -j8 jtest | .circleci/cat_ignore_eagain
|
||||
command: make V=1 J=8 -j8 jtest 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-macos-java-static:
|
||||
@@ -560,7 +567,7 @@ jobs:
|
||||
which javac && javac -version
|
||||
- run:
|
||||
name: "Build RocksDBJava Static Library"
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic | .circleci/cat_ignore_eagain
|
||||
command: make V=1 J=8 -j8 rocksdbjavastatic 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-examples:
|
||||
@@ -636,7 +643,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: make V=1 J=4 -j4 check | .circleci/cat_ignore_eagain
|
||||
- run: make V=1 J=4 -j4 check 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-arm:
|
||||
@@ -646,7 +653,7 @@ jobs:
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some | .circleci/cat_ignore_eagain
|
||||
- run: ROCKSDBTESTS_PLATFORM_DEPENDENT=only make V=1 J=4 -j4 all_but_some_tests check_some 2>&1 | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux-arm-cmake-no_test_run:
|
||||
@@ -708,9 +715,9 @@ workflows:
|
||||
jobs:
|
||||
- build-linux-cmake
|
||||
- build-linux-cmake-ubuntu-20
|
||||
build-linux-mem-env:
|
||||
build-linux-mem-env-librados:
|
||||
jobs:
|
||||
- build-linux-mem-env
|
||||
- build-linux-mem-env-librados
|
||||
build-linux-encrypted-env:
|
||||
jobs:
|
||||
- build-linux-encrypted-env
|
||||
|
||||
+51
@@ -5,6 +5,7 @@ os:
|
||||
arch:
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
@@ -59,6 +60,12 @@ matrix:
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
compiler: clang
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
@@ -73,6 +80,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=1
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -81,6 +92,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=2
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -89,6 +104,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=3
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -97,6 +116,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: TEST_GROUP=4
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -109,6 +132,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/ AND commit_message !~ /java/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=java_test
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -117,6 +144,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=lite_build
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -125,6 +156,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=examples
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -133,6 +168,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc8
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -141,6 +180,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc9
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -149,6 +192,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os : linux
|
||||
arch: arm64
|
||||
@@ -157,6 +204,10 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request AND commit_message !~ /FULL_CI/
|
||||
os: linux
|
||||
arch: s390x
|
||||
env: JOB_NAME=status_checked
|
||||
|
||||
install:
|
||||
- if [ "${JOB_NAME}" == cmake-gcc8 ]; then
|
||||
|
||||
+15
-1
@@ -267,6 +267,13 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|AARCH64")
|
||||
endif(HAS_ARMV8_CRC)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|AARCH64")
|
||||
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
|
||||
CHECK_C_COMPILER_FLAG("-march=native" HAS_S390X_MARCH_NATIVE)
|
||||
if(HAS_S390X_MARCH_NATIVE)
|
||||
message(STATUS " HAS_S390X_MARCH_NATIVE yes")
|
||||
endif(HAS_S390X_MARCH_NATIVE)
|
||||
endif(CMAKE_SYSTEM_PROCESSOR MATCHES "s390x")
|
||||
|
||||
option(PORTABLE "build a portable binary" OFF)
|
||||
option(FORCE_SSE42 "force building with SSE4.2, even when PORTABLE=ON" OFF)
|
||||
option(FORCE_AVX "force building with AVX, even when PORTABLE=ON" OFF)
|
||||
@@ -292,12 +299,17 @@ if(PORTABLE)
|
||||
if(FORCE_AVX2)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2 -mbmi -mlzcnt")
|
||||
endif()
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /arch:AVX2")
|
||||
else()
|
||||
if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390x" AND NOT HAS_S390X_MARCH_NATIVE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=z196")
|
||||
elseif(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64" AND NOT HAS_ARMV8_CRC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
|
||||
endif()
|
||||
endif()
|
||||
@@ -640,6 +652,7 @@ set(SOURCES
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_sequential_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
db/blob/prefetch_buffer_collection.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
@@ -865,6 +878,7 @@ set(SOURCES
|
||||
utilities/env_timed.cc
|
||||
utilities/fault_injection_env.cc
|
||||
utilities/fault_injection_fs.cc
|
||||
utilities/fault_injection_secondary_cache.cc
|
||||
utilities/leveldb_options/leveldb_options.cc
|
||||
utilities/memory/memory_util.cc
|
||||
utilities/merge_operators.cc
|
||||
|
||||
+60
@@ -1,4 +1,63 @@
|
||||
# Rocksdb Change Log
|
||||
## 6.27.3 (2021-12-10)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in TableOptions.prepopulate_block_cache which causes segmentation fault when used with TableOptions.partition_filters = true and TableOptions.cache_index_and_filter_blocks = true.
|
||||
* Fixed a bug affecting custom memtable factories which are not registered with the `ObjectRegistry`. The bug could result in failure to save the OPTIONS file.
|
||||
|
||||
## 6.27.2 (2021-12-01)
|
||||
### Bug Fixes
|
||||
* Fixed a bug in rocksdb automatic implicit prefetching which got broken because of new feature adaptive_readahead and internal prefetching got disabled when iterator moves from one file to next.
|
||||
|
||||
## 6.27.1 (2021-11-29)
|
||||
### Bug Fixes
|
||||
* Fixed a bug that could, with WAL enabled, cause backups, checkpoints, and `GetSortedWalFiles()` to fail randomly with an error like `IO error: 001234.log: No such file or directory`
|
||||
|
||||
## 6.27.0 (2021-11-19)
|
||||
### New Features
|
||||
* Added new ChecksumType kXXH3 which is faster than kCRC32c on almost all x86\_64 hardware.
|
||||
* Added a new online consistency check for BlobDB which validates that the number/total size of garbage blobs does not exceed the number/total size of all blobs in any given blob file.
|
||||
* Provided support for tracking per-sst user-defined timestamp information in MANIFEST.
|
||||
* Added new option "adaptive_readahead" in ReadOptions. For iterators, RocksDB does auto-readahead on noticing sequential reads and by enabling this option, readahead_size of current file (if reads are sequential) will be carried forward to next file instead of starting from the scratch at each level (except L0 level files). If reads are not sequential it will fall back to 8KB. This option is applicable only for RocksDB internal prefetch buffer and isn't supported with underlying file system prefetching.
|
||||
* Added the read count and read bytes related stats to Statistics for tiered storage hot, warm, and cold file reads.
|
||||
* Added an option to dynamically charge an updating estimated memory usage of block-based table building to block cache if block cache available. It currently only includes charging memory usage of constructing (new) Bloom Filter and Ribbon Filter to block cache. To enable this feature, set `BlockBasedTableOptions::reserve_table_builder_memory = true`.
|
||||
* Add a new API OnIOError in listener.h that notifies listeners when an IO error occurs during FileSystem operation along with filename, status etc.
|
||||
* Added compaction readahead support for blob files to the integrated BlobDB implementation, which can improve compaction performance when the database resides on higher-latency storage like HDDs or remote filesystems. Readahead can be configured using the column family option `blob_compaction_readahead_size`.
|
||||
|
||||
### Bug Fixes
|
||||
* Prevent a `CompactRange()` with `CompactRangeOptions::change_level == true` from possibly causing corruption to the LSM state (overlapping files within a level) when run in parallel with another manual compaction. Note that setting `force_consistency_checks == true` (the default) would cause the DB to enter read-only mode in this scenario and return `Status::Corruption`, rather than committing any corruption.
|
||||
* Fixed a bug in CompactionIterator when write-prepared transaction is used. A released earliest write conflict snapshot may cause assertion failure in dbg mode and unexpected key in opt mode.
|
||||
* Fix ticker WRITE_WITH_WAL("rocksdb.write.wal"), this bug is caused by a bad extra `RecordTick(stats_, WRITE_WITH_WAL)` (at 2 place), this fix remove the extra `RecordTick`s and fix the corresponding test case.
|
||||
* EventListener::OnTableFileCreated was previously called with OK status and file_size==0 in cases of no SST file contents written (because there was no content to add) and the empty file deleted before calling the listener. Now the status is Aborted.
|
||||
* Fixed a bug in CompactionIterator when write-preared transaction is used. Releasing earliest_snapshot during compaction may cause a SingleDelete to be output after a PUT of the same user key whose seq has been zeroed.
|
||||
* Added input sanitization on negative bytes passed into `GenericRateLimiter::Request`.
|
||||
* Fixed an assertion failure in CompactionIterator when write-prepared transaction is used. We prove that certain operations can lead to a Delete being followed by a SingleDelete (same user key). We can drop the SingleDelete.
|
||||
* Fixed a bug of timestamp-based GC which can cause all versions of a key under full_history_ts_low to be dropped. This bug will be triggered when some of the ikeys' timestamps are lower than full_history_ts_low, while others are newer.
|
||||
* In some cases outside of the DB read and compaction paths, SST block checksums are now checked where they were not before.
|
||||
* Explicitly check for and disallow the `BlockBasedTableOptions` if insertion into one of {`block_cache`, `block_cache_compressed`, `persistent_cache`} can show up in another of these. (RocksDB expects to be able to use the same key for different physical data among tiers.)
|
||||
* Users who configured a dedicated thread pool for bottommost compactions by explicitly adding threads to the `Env::Priority::BOTTOM` pool will no longer see RocksDB schedule automatic compactions exceeding the DB's compaction concurrency limit. For details on per-DB compaction concurrency limit, see API docs of `max_background_compactions` and `max_background_jobs`.
|
||||
* Fixed a bug of background flush thread picking more memtables to flush and prematurely advancing column family's log_number.
|
||||
* Fixed an assertion failure in ManifestTailer.
|
||||
|
||||
### Behavior Changes
|
||||
* `NUM_FILES_IN_SINGLE_COMPACTION` was only counting the first input level files, now it's including all input files.
|
||||
* `TransactionUtil::CheckKeyForConflicts` can also perform conflict-checking based on user-defined timestamps in addition to sequence numbers.
|
||||
* Removed `GenericRateLimiter`'s minimum refill bytes per period previously enforced.
|
||||
|
||||
### Public API change
|
||||
* When options.ttl is used with leveled compaction with compactinon priority kMinOverlappingRatio, files exceeding half of TTL value will be prioritized more, so that by the time TTL is reached, fewer extra compactions will be scheduled to clear them up. At the same time, when compacting files with data older than half of TTL, output files may be cut off based on those files' boundaries, in order for the early TTL compaction to work properly.
|
||||
* Made FileSystem extend the Customizable class and added a CreateFromString method. Implementations need to be registered with the ObjectRegistry and to implement a Name() method in order to be created via this method.
|
||||
* Clarified in API comments that RocksDB is not exception safe for callbacks and custom extensions. An exception propagating into RocksDB can lead to undefined behavior, including data loss, unreported corruption, deadlocks, and more.
|
||||
* Marked `WriteBufferManager` as `final` because it is not intended for extension.
|
||||
* Removed unimportant implementation details from table_properties.h
|
||||
* Add API `FSDirectory::FsyncWithDirOptions()`, which provides extra information like directory fsync reason in `DirFsyncOptions`. File system like btrfs is using that to skip directory fsync for creating a new file, or when renaming a file, fsync the target file instead of the directory, which improves the `DB::Open()` speed by ~20%.
|
||||
* `DB::Open()` is not going be blocked by obsolete file purge if `DBOptions::avoid_unnecessary_blocking_io` is set to true.
|
||||
* In builds where glibc provides `gettid()`, info log ("LOG" file) lines now print a system-wide thread ID from `gettid()` instead of the process-local `pthread_self()`. For all users, the thread ID format is changed from hexadecimal to decimal integer.
|
||||
* In builds where glibc provides `pthread_setname_np()`, the background thread names no longer contain an ID suffix. For example, "rocksdb:bottom7" (and all other threads in the `Env::Priority::BOTTOM` pool) are now named "rocksdb:bottom". Previously large thread pools could breach the name size limit (e.g., naming "rocksdb:bottom10" would fail).
|
||||
* Deprecating `ReadOptions::iter_start_seqnum` and `DBOptions::preserve_deletes`, please try using user defined timestamp feature instead. The options will be removed in a future release, currently it logs a warning message when using.
|
||||
|
||||
### Performance Improvements
|
||||
* Released some memory related to filter construction earlier in `BlockBasedTableBuilder` for `FullFilter` and `PartitionedFilter` case (#9070)
|
||||
|
||||
## 6.26.0 (2021-10-20)
|
||||
### Bug Fixes
|
||||
* Fixes a bug in directed IO mode when calling MultiGet() for blobs in the same blob file. The bug is caused by not sorting the blob read requests by file offsets.
|
||||
@@ -10,6 +69,7 @@
|
||||
* Fixed a bug where stalled writes would remain stalled forever after the user calls `WriteBufferManager::SetBufferSize()` with `new_size == 0` to dynamically disable memory limiting.
|
||||
* Make `DB::close()` thread-safe.
|
||||
* Fix a bug in atomic flush where one bg flush thread will wait forever for a preceding bg flush thread to commit its result to MANIFEST but encounters an error which is mapped to a soft error (DB not stopped).
|
||||
* Fix a bug in `BackupEngine` where some internal callers of `GenericRateLimiter::Request()` do not honor `bytes <= GetSingleBurstBytes()`.
|
||||
|
||||
### New Features
|
||||
* Print information about blob files when using "ldb list_live_files_metadata"
|
||||
|
||||
@@ -222,6 +222,7 @@ am__v_AR_1 =
|
||||
|
||||
ifdef ROCKSDB_USE_LIBRADOS
|
||||
LIB_SOURCES += utilities/env_librados.cc
|
||||
TEST_MAIN_SOURCES += utilities/env_librados_test.cc
|
||||
LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
@@ -305,6 +306,13 @@ ifdef COMPILE_WITH_ASAN
|
||||
EXEC_LDFLAGS += -fsanitize=address
|
||||
PLATFORM_CCFLAGS += -fsanitize=address
|
||||
PLATFORM_CXXFLAGS += -fsanitize=address
|
||||
ifeq ($(LIB_MODE),shared)
|
||||
ifdef USE_CLANG
|
||||
# Fix false ODR violation; see https://github.com/google/sanitizers/issues/1017
|
||||
EXEC_LDFLAGS += -mllvm -asan-use-private-alias=1
|
||||
PLATFORM_CXXFLAGS += -mllvm -asan-use-private-alias=1
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# TSAN doesn't work well with jemalloc. If we're compiling with TSAN, we should use regular malloc.
|
||||
@@ -507,6 +515,7 @@ STRESS_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(STRESS_LIB_SOURCES))
|
||||
ALL_SOURCES = $(filter-out util/build_version.cc, $(LIB_SOURCES)) $(TEST_LIB_SOURCES) $(MOCK_LIB_SOURCES) $(GTEST_DIR)/gtest/gtest-all.cc
|
||||
ALL_SOURCES += $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(CACHE_BENCH_LIB_SOURCES) $(ANALYZER_LIB_SOURCES) $(STRESS_LIB_SOURCES)
|
||||
ALL_SOURCES += $(TEST_MAIN_SOURCES) $(TOOL_MAIN_SOURCES) $(BENCH_MAIN_SOURCES)
|
||||
ALL_SOURCES += $(ROCKSDB_PLUGIN_SOURCES)
|
||||
|
||||
TESTS = $(patsubst %.cc, %, $(notdir $(TEST_MAIN_SOURCES)))
|
||||
TESTS += $(patsubst %.c, %, $(notdir $(TEST_MAIN_SOURCES_C)))
|
||||
@@ -823,14 +832,11 @@ parallel_tests = $(patsubst %,parallel_%,$(PARALLEL_TEST))
|
||||
$(parallel_tests): $(PARALLEL_TEST)
|
||||
$(AM_V_at)TEST_BINARY=$(patsubst parallel_%,%,$@); \
|
||||
TEST_NAMES=` \
|
||||
./$$TEST_BINARY --gtest_list_tests \
|
||||
| perl -n \
|
||||
-e 's/ *\#.*//;' \
|
||||
-e '/^(\s*)(\S+)/; !$$1 and do {$$p=$$2; break};' \
|
||||
-e 'print qq! $$p$$2!'`; \
|
||||
(./$$TEST_BINARY --gtest_list_tests || echo " $${TEST_BINARY}__list_tests_failure") \
|
||||
| awk '/^[^ ]/ { prefix = $$1 } /^[ ]/ { print prefix $$1 }'`; \
|
||||
echo " Generating parallel test scripts for $$TEST_BINARY"; \
|
||||
for TEST_NAME in $$TEST_NAMES; do \
|
||||
TEST_SCRIPT=t/run-$$TEST_BINARY-$${TEST_NAME//\//-}; \
|
||||
echo " GEN " $$TEST_SCRIPT; \
|
||||
printf '%s\n' \
|
||||
'#!/bin/sh' \
|
||||
"d=\$(TMPD)$$TEST_SCRIPT" \
|
||||
@@ -877,12 +883,18 @@ J ?= 100%
|
||||
|
||||
# Use this regexp to select the subset of tests whose names match.
|
||||
tests-regexp = .
|
||||
EXCLUDE_TESTS_REGEX ?= "^$"
|
||||
EXCLUDE_TESTS_REGEX ?= "^$$"
|
||||
|
||||
ifeq ($(PRINT_PARALLEL_OUTPUTS), 1)
|
||||
parallel_com = '{}'
|
||||
parallel_redir =
|
||||
else ifeq ($(QUIET_PARALLEL_TESTS), 1)
|
||||
parallel_redir = >& t/$(test_log_prefix)log-{/}
|
||||
else
|
||||
parallel_com = '{} >& t/log-{/}'
|
||||
# Default: print failure output only, as it happens
|
||||
# Note: gnu_parallel --eta is now always used, but has been modified to provide
|
||||
# only infrequent updates when not connected to a terminal. (CircleCI will
|
||||
# kill a job if no output for 10min.)
|
||||
parallel_redir = >& t/$(test_log_prefix)log-{/} || bash -c "cat t/$(test_log_prefix)log-{/}; exit $$?"
|
||||
endif
|
||||
|
||||
.PHONY: check_0
|
||||
@@ -891,7 +903,6 @@ check_0:
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out $(PARALLEL_TEST),$(TESTS)); \
|
||||
find t -name 'run-*' -print; \
|
||||
@@ -899,7 +910,7 @@ check_0:
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(EXCLUDE_TESTS_REGEX)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu $(parallel_com) ; \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu '{} $(parallel_redir)' ; \
|
||||
parallel_retcode=$$? ; \
|
||||
awk '{ if ($$7 != 0 || $$8 != 0) { if ($$7 == "Exitval") { h = $$0; } else { if (!f) print h; print; f = 1 } } } END { if(f) exit 1; }' < LOG ; \
|
||||
awk_retcode=$$?; \
|
||||
@@ -908,12 +919,12 @@ check_0:
|
||||
valgrind-exclude-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized|MySQLStyleTransactionTest.TransactionStressTest
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0: test_log_prefix := valgrind_
|
||||
valgrind_check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
printf '%s\n' '' \
|
||||
'To monitor subtest <duration,pass/fail,name>,' \
|
||||
' run "make watch-log" in a separate window' ''; \
|
||||
test -t 1 && eta=--eta || eta=; \
|
||||
{ \
|
||||
printf './%s\n' $(filter-out $(PARALLEL_TEST) %skiplist_test options_settable_test, $(TESTS)); \
|
||||
find t -name 'run-*' -print; \
|
||||
@@ -921,9 +932,9 @@ valgrind_check_0:
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(valgrind-exclude-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) ' \
|
||||
'>& t/valgrind_log-{/}'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG --eta --gnu \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) \
|
||||
$(parallel_redir)' \
|
||||
|
||||
CLEAN_FILES += t LOG $(TMPD)
|
||||
|
||||
@@ -977,15 +988,27 @@ check_some: $(ROCKSDBTESTS_SUBSET)
|
||||
ldb_tests: ldb
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
|
||||
crash_test: whitebox_crash_test blackbox_crash_test
|
||||
crash_test:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test
|
||||
$(MAKE) blackbox_crash_test
|
||||
|
||||
crash_test_with_atomic_flush: whitebox_crash_test_with_atomic_flush blackbox_crash_test_with_atomic_flush
|
||||
crash_test_with_atomic_flush:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_atomic_flush
|
||||
$(MAKE) blackbox_crash_test_with_atomic_flush
|
||||
|
||||
crash_test_with_txn: whitebox_crash_test_with_txn blackbox_crash_test_with_txn
|
||||
crash_test_with_txn:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_txn
|
||||
$(MAKE) blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
crash_test_with_ts: whitebox_crash_test_with_ts blackbox_crash_test_with_ts
|
||||
crash_test_with_ts:
|
||||
# Do not parallelize
|
||||
$(MAKE) whitebox_crash_test_with_ts
|
||||
$(MAKE) blackbox_crash_test_with_ts
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -1925,6 +1948,9 @@ clipping_iterator_test: $(OBJ_DIR)/db/compaction/clipping_iterator_test.o $(TEST
|
||||
ribbon_bench: $(OBJ_DIR)/microbench/ribbon_bench.o $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
db_basic_bench: $(OBJ_DIR)/microbench/db_basic_bench.o $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
cache_reservation_manager_test: $(OBJ_DIR)/cache/cache_reservation_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
#-------------------------------------------------
|
||||
@@ -1997,7 +2023,7 @@ JAVA_INCLUDE = -I$(JAVA_HOME)/include/ -I$(JAVA_HOME)/include/linux
|
||||
ifeq ($(PLATFORM), OS_SOLARIS)
|
||||
ARCH := $(shell isainfo -b)
|
||||
else ifeq ($(PLATFORM), OS_OPENBSD)
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ifneq (,$(filter amd64 ppc64 ppc64le s390x arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ARCH := 64
|
||||
else
|
||||
ARCH := 32
|
||||
@@ -2017,7 +2043,7 @@ ifneq ($(origin JNI_LIBC), undefined)
|
||||
JNI_LIBC_POSTFIX = -$(JNI_LIBC)
|
||||
endif
|
||||
|
||||
ifneq (,$(filter ppc% arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ifneq (,$(filter ppc% s390x arm64 aarch64 sparc64, $(MACHINE)))
|
||||
ROCKSDBJNILIB = librocksdbjni-linux-$(MACHINE)$(JNI_LIBC_POSTFIX).so
|
||||
else
|
||||
ROCKSDBJNILIB = librocksdbjni-linux$(ARCH)$(JNI_LIBC_POSTFIX).so
|
||||
@@ -2242,20 +2268,26 @@ rocksdbjavastaticpublish: rocksdbjavastaticrelease rocksdbjavastaticpublishcentr
|
||||
|
||||
rocksdbjavastaticpublishdocker: rocksdbjavastaticreleasedocker rocksdbjavastaticpublishcentral
|
||||
|
||||
ROCKSDB_JAVA_RELEASE_CLASSIFIERS = javadoc sources linux64 linux32 linux64-musl linux32-musl osx win64
|
||||
|
||||
rocksdbjavastaticpublishcentral: rocksdbjavageneratepom
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-javadoc.jar -Dclassifier=javadoc
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-sources.jar -Dclassifier=sources
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux64.jar -Dclassifier=linux64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux32.jar -Dclassifier=linux32
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux64-musl.jar -Dclassifier=linux64-musl
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-linux32-musl.jar -Dclassifier=linux32-musl
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-osx.jar -Dclassifier=osx
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-win64.jar -Dclassifier=win64
|
||||
mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), mvn gpg:sign-and-deploy-file -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ -DrepositoryId=sonatype-nexus-staging -DpomFile=java/pom.xml -Dfile=java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar -Dclassifier=$(classifier);)
|
||||
|
||||
rocksdbjavageneratepom:
|
||||
cd java;cat pom.xml.template | sed 's/\$${ROCKSDB_JAVA_VERSION}/$(ROCKSDB_JAVA_VERSION)/' > pom.xml
|
||||
|
||||
rocksdbjavastaticnexusbundlejar: rocksdbjavageneratepom
|
||||
openssl sha1 -r java/pom.xml | awk '{ print $$1 }' > java/target/pom.xml.sha1
|
||||
openssl sha1 -r java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar | awk '{ print $$1 }' > java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.sha1
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), openssl sha1 -r java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar | awk '{ print $$1 }' > java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.sha1;)
|
||||
gpg --yes --output java/target/pom.xml.asc -ab java/pom.xml
|
||||
gpg --yes -ab java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), gpg --yes -ab java/target/rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar;)
|
||||
jar cvf java/target/nexus-bundle-rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java pom.xml -C java/target pom.xml.sha1 -C java/target pom.xml.asc -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.sha1 -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar.asc
|
||||
$(foreach classifier, $(ROCKSDB_JAVA_RELEASE_CLASSIFIERS), jar uf java/target/nexus-bundle-rocksdbjni-$(ROCKSDB_JAVA_VERSION).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.sha1 -C java/target rocksdbjni-$(ROCKSDB_JAVA_VERSION)-$(classifier).jar.asc;)
|
||||
|
||||
|
||||
# A version of each $(LIBOBJECTS) compiled with -fPIC
|
||||
|
||||
jl/%.o: %.cc
|
||||
|
||||
@@ -31,7 +31,11 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
ROCKSDB_OS_DEPS_0 = [
|
||||
(
|
||||
"linux",
|
||||
["third-party//numa:numa", "third-party//liburing:uring", "third-party//tbb:tbb"],
|
||||
[
|
||||
"third-party//numa:numa",
|
||||
"third-party//liburing:uring",
|
||||
"third-party//tbb:tbb",
|
||||
],
|
||||
),
|
||||
(
|
||||
"macos",
|
||||
@@ -69,7 +73,13 @@ ROCKSDB_OS_PREPROCESSOR_FLAGS_0 = [
|
||||
),
|
||||
(
|
||||
"windows",
|
||||
[ "-DOS_WIN", "-DWIN32", "-D_MBCS", "-DWIN64", "-DNOMINMAX" ]
|
||||
[
|
||||
"-DOS_WIN",
|
||||
"-DWIN32",
|
||||
"-D_MBCS",
|
||||
"-DWIN64",
|
||||
"-DNOMINMAX",
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -149,6 +159,7 @@ cpp_library(
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/blob/prefetch_buffer_collection.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
@@ -385,6 +396,7 @@ cpp_library(
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/fault_injection_env.cc",
|
||||
"utilities/fault_injection_fs.cc",
|
||||
"utilities/fault_injection_secondary_cache.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators.cc",
|
||||
@@ -445,13 +457,14 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = False,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -475,6 +488,7 @@ cpp_library(
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/blob/prefetch_buffer_collection.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
@@ -711,6 +725,7 @@ cpp_library(
|
||||
"utilities/env_timed.cc",
|
||||
"utilities/fault_injection_env.cc",
|
||||
"utilities/fault_injection_fs.cc",
|
||||
"utilities/fault_injection_secondary_cache.cc",
|
||||
"utilities/leveldb_options/leveldb_options.cc",
|
||||
"utilities/memory/memory_util.cc",
|
||||
"utilities/merge_operators.cc",
|
||||
@@ -771,13 +786,14 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = True,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = True,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -795,15 +811,16 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = False,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -818,13 +835,14 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = False,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -833,13 +851,14 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = False,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -863,10 +882,11 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
@@ -875,10 +895,10 @@ 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,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
) if not is_opt_mode else None
|
||||
|
||||
@@ -897,13 +917,14 @@ cpp_library(
|
||||
auto_headers = AutoHeaders.RECURSIVE_GLOB,
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = False,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
link_whole = False,
|
||||
)
|
||||
|
||||
# [test_name, test_src, test_type, extra_deps, extra_compiler_flags]
|
||||
@@ -2220,10 +2241,10 @@ ROCKS_TESTS = [
|
||||
name = test_name,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
|
||||
@@ -84,10 +84,10 @@ 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,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"],
|
||||
) if not is_opt_mode else None
|
||||
|
||||
|
||||
@@ -38,7 +38,11 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
ROCKSDB_OS_DEPS_0 = [
|
||||
(
|
||||
"linux",
|
||||
["third-party//numa:numa", "third-party//liburing:uring", "third-party//tbb:tbb"],
|
||||
[
|
||||
"third-party//numa:numa",
|
||||
"third-party//liburing:uring",
|
||||
"third-party//tbb:tbb",
|
||||
],
|
||||
),
|
||||
(
|
||||
"macos",
|
||||
@@ -76,7 +80,13 @@ ROCKSDB_OS_PREPROCESSOR_FLAGS_0 = [
|
||||
),
|
||||
(
|
||||
"windows",
|
||||
[ "-DOS_WIN", "-DWIN32", "-D_MBCS", "-DWIN64", "-DNOMINMAX" ]
|
||||
[
|
||||
"-DOS_WIN",
|
||||
"-DWIN32",
|
||||
"-D_MBCS",
|
||||
"-DWIN64",
|
||||
"-DNOMINMAX",
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -144,13 +154,14 @@ cpp_library(
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
link_whole = {link_whole},
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS{extra_external_deps},
|
||||
link_whole = {link_whole},
|
||||
)
|
||||
"""
|
||||
|
||||
@@ -161,10 +172,11 @@ cpp_library(
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
unexported_deps_by_default = False,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
@@ -205,10 +217,10 @@ ROCKS_TESTS = [
|
||||
name = test_name,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS + extra_compiler_flags,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
include_paths = ROCKSDB_INCLUDE_PATHS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
|
||||
@@ -638,13 +638,19 @@ if test "0$PORTABLE" -eq 0; then
|
||||
# Tune for this POWER processor, treating '+' models as base models
|
||||
POWER=`LD_SHOW_AUXV=1 /bin/true | grep AT_PLATFORM | grep -E -o power[0-9]+`
|
||||
COMMON_FLAGS="$COMMON_FLAGS -mcpu=$POWER -mtune=$POWER "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z10 "
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep -e^arm -e^aarch64`"; then
|
||||
# TODO: Handle this with approprite options.
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^aarch64`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
if echo 'int main() {}' | $CXX $PLATFORM_CXXFLAGS -x c++ \
|
||||
-fsyntax-only -march=native - -o /dev/null 2>/dev/null; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=native "
|
||||
else
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "IOS" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS"
|
||||
elif [ "$TARGET_OS" == "AIX" ] || [ "$TARGET_OS" == "SunOS" ]; then
|
||||
@@ -661,11 +667,16 @@ else
|
||||
TRY_SSE_ETC="1"
|
||||
fi
|
||||
|
||||
if test -n "`echo $TARGET_ARCHITECTURE | grep ^s390x`"; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -march=z196 "
|
||||
fi
|
||||
|
||||
if [[ "${PLATFORM}" == "OS_MACOSX" ]]; then
|
||||
# For portability compile for macOS 10.12 (2016) or newer
|
||||
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"
|
||||
# -mmacosx-version-min must come first here.
|
||||
PLATFORM_SHARED_LDFLAGS="-mmacosx-version-min=10.12 $PLATFORM_SHARED_LDFLAGS"
|
||||
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"
|
||||
|
||||
@@ -127,11 +127,11 @@ uncommitted_code=`git diff HEAD`
|
||||
if [ -z "$uncommitted_code" ]
|
||||
then
|
||||
# Attempt to get name of facebook/rocksdb.git remote.
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE="$(LC_ALL=POSIX LANG=POSIX git remote -v | grep 'facebook/rocksdb.git' | head -n 1 | cut -f 1)"
|
||||
# Fall back on 'origin' if that fails
|
||||
[ "$FORMAT_REMOTE" ] || FORMAT_REMOTE=origin
|
||||
# Use main branch from that remote
|
||||
[ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/$(git remote show $FORMAT_REMOTE | sed -n '/HEAD branch/s/.*: //p')"
|
||||
[ "$FORMAT_UPSTREAM" ] || FORMAT_UPSTREAM="$FORMAT_REMOTE/$(LC_ALL=POSIX LANG=POSIX git remote show $FORMAT_REMOTE | sed -n '/HEAD branch/s/.*: //p')"
|
||||
# Get the common ancestor with that remote branch. Everything after that
|
||||
# common ancestor would be considered the contents of a pull request, so
|
||||
# should be relevant for formatting fixes.
|
||||
|
||||
@@ -1561,6 +1561,7 @@ sub save_stdin_stdout_stderr {
|
||||
::die_bug("Can't dup STDERR: $!");
|
||||
open $Global::original_stdin, "<&", "STDIN" or
|
||||
::die_bug("Can't dup STDIN: $!");
|
||||
$Global::is_terminal = (-t $Global::original_stderr);
|
||||
}
|
||||
|
||||
sub enough_file_handles {
|
||||
@@ -1840,12 +1841,17 @@ sub start_another_job {
|
||||
}
|
||||
}
|
||||
|
||||
$opt::min_progress_interval = 0;
|
||||
|
||||
sub init_progress {
|
||||
# Uses:
|
||||
# $opt::bar
|
||||
# Returns:
|
||||
# list of computers for progress output
|
||||
$|=1;
|
||||
if (not $Global::is_terminal) {
|
||||
$opt::min_progress_interval = 30;
|
||||
}
|
||||
if($opt::bar) {
|
||||
return("","");
|
||||
}
|
||||
@@ -1870,6 +1876,9 @@ sub drain_job_queue {
|
||||
}
|
||||
my $last_header="";
|
||||
my $sleep = 0.2;
|
||||
my $last_left = 1000000000;
|
||||
my $last_progress_time = 0;
|
||||
my $ps_reported = 0;
|
||||
do {
|
||||
while($Global::total_running > 0) {
|
||||
debug($Global::total_running, "==", scalar
|
||||
@@ -1880,14 +1889,38 @@ sub drain_job_queue {
|
||||
close $job->fh(0,"w");
|
||||
}
|
||||
}
|
||||
if($opt::progress) {
|
||||
# When not connected to terminal, assume CI (e.g. CircleCI). In
|
||||
# that case we want occasional progress output to prevent abort
|
||||
# due to timeout with no output, but we also need to stop sending
|
||||
# progress output if there has been no actual progress, so that
|
||||
# the job can time out appropriately (CirecleCI: 10m) in case of
|
||||
# a hung test. But without special output, it is extremely
|
||||
# annoying to diagnose which test is hung, so we add that using
|
||||
# `ps` below.
|
||||
if($opt::progress and
|
||||
($Global::is_terminal or (time() - $last_progress_time) >= 30)) {
|
||||
my %progress = progress();
|
||||
if($last_header ne $progress{'header'}) {
|
||||
print $Global::original_stderr "\n", $progress{'header'}, "\n";
|
||||
$last_header = $progress{'header'};
|
||||
}
|
||||
print $Global::original_stderr "\r",$progress{'status'};
|
||||
flush $Global::original_stderr;
|
||||
if ($Global::is_terminal) {
|
||||
print $Global::original_stderr "\r",$progress{'status'};
|
||||
}
|
||||
if ($last_left > $Global::left) {
|
||||
if (not $Global::is_terminal) {
|
||||
print $Global::original_stderr $progress{'status'},"\n";
|
||||
}
|
||||
$last_progress_time = time();
|
||||
$ps_reported = 0;
|
||||
} elsif (not $ps_reported and (time() - $last_progress_time) >= 60) {
|
||||
# No progress in at least 60 seconds: run ps
|
||||
print $Global::original_stderr "\n";
|
||||
system("ps", "-wf");
|
||||
$ps_reported = 1;
|
||||
}
|
||||
$last_left = $Global::left;
|
||||
flush $Global::original_stderr;
|
||||
}
|
||||
if($Global::total_running < $Global::max_jobs_running
|
||||
and not $Global::JobQueue->empty()) {
|
||||
@@ -1921,7 +1954,7 @@ sub drain_job_queue {
|
||||
not $Global::start_no_new_jobs and not $Global::JobQueue->empty());
|
||||
if($opt::progress) {
|
||||
my %progress = progress();
|
||||
print $Global::original_stderr "\r", $progress{'status'}, "\n";
|
||||
print $Global::original_stderr $opt::progress_sep, $progress{'status'}, "\n";
|
||||
flush $Global::original_stderr;
|
||||
}
|
||||
}
|
||||
@@ -1954,10 +1987,11 @@ sub progress {
|
||||
my $eta = "";
|
||||
my ($status,$header)=("","");
|
||||
if($opt::eta) {
|
||||
my($total, $completed, $left, $pctcomplete, $avgtime, $this_eta) =
|
||||
compute_eta();
|
||||
$eta = sprintf("ETA: %ds Left: %d AVG: %.2fs ",
|
||||
$this_eta, $left, $avgtime);
|
||||
my($total, $completed, $left, $pctcomplete, $avgtime, $this_eta) =
|
||||
compute_eta();
|
||||
$eta = sprintf("ETA: %ds Left: %d AVG: %.2fs ",
|
||||
$this_eta, $left, $avgtime);
|
||||
$Global::left = $left;
|
||||
}
|
||||
my $termcols = terminal_columns();
|
||||
my @workers = sort keys %Global::host;
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Vendored
+2
@@ -20,6 +20,7 @@ std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToCamelString{{
|
||||
"OtherBlock",
|
||||
"WriteBuffer",
|
||||
"CompressionDictionaryBuildingBuffer",
|
||||
"FilterConstruction",
|
||||
"Misc",
|
||||
}};
|
||||
|
||||
@@ -32,6 +33,7 @@ std::array<const char*, kNumCacheEntryRoles> kCacheEntryRoleToHyphenString{{
|
||||
"other-block",
|
||||
"write-buffer",
|
||||
"compression-dictionary-building-buffer",
|
||||
"filter-construction",
|
||||
"misc",
|
||||
}};
|
||||
|
||||
|
||||
Vendored
+11
-4
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rocksdb/cache.h"
|
||||
@@ -34,6 +36,9 @@ enum class CacheEntryRole {
|
||||
// BlockBasedTableBuilder reservations to account for
|
||||
// compression dictionary building buffer's memory usage
|
||||
kCompressionDictionaryBuildingBuffer,
|
||||
// Filter reservations to account for
|
||||
// (new) bloom and ribbon filter construction's memory usage
|
||||
kFilterConstruction,
|
||||
// Default bucket, for miscellaneous cache entries. Do not use for
|
||||
// entries that could potentially add up to large usage.
|
||||
kMisc,
|
||||
@@ -90,7 +95,9 @@ struct RegisteredDeleter {
|
||||
// These have global linkage to help ensure compiler optimizations do not
|
||||
// break uniqueness for each <T,R>
|
||||
static void Delete(const Slice& /* key */, void* value) {
|
||||
delete static_cast<T*>(value);
|
||||
// Supports T == Something[], unlike delete operator
|
||||
std::default_delete<T>()(
|
||||
static_cast<typename std::remove_extent<T>::type*>(value));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,9 +105,9 @@ template <CacheEntryRole R>
|
||||
struct RegisteredNoopDeleter {
|
||||
RegisteredNoopDeleter() { RegisterCacheDeleterRole(Delete, R); }
|
||||
|
||||
static void Delete(const Slice& /* key */, void* value) {
|
||||
(void)value;
|
||||
assert(value == nullptr);
|
||||
static void Delete(const Slice& /* key */, void* /* value */) {
|
||||
// Here was `assert(value == nullptr);` but we can also put pointers
|
||||
// to static data in Cache, for testing at least.
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Vendored
+63
-2
@@ -23,7 +23,9 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
CacheReservationManager::CacheReservationManager(std::shared_ptr<Cache> cache,
|
||||
bool delayed_decrease)
|
||||
: delayed_decrease_(delayed_decrease), cache_allocated_size_(0) {
|
||||
: delayed_decrease_(delayed_decrease),
|
||||
cache_allocated_size_(0),
|
||||
memory_used_(0) {
|
||||
assert(cache != nullptr);
|
||||
cache_ = cache;
|
||||
std::memset(cache_key_, 0, kCacheKeyPrefixSize + kMaxVarint64Length);
|
||||
@@ -39,6 +41,7 @@ CacheReservationManager::~CacheReservationManager() {
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManager::UpdateCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
memory_used_ = new_mem_used;
|
||||
std::size_t cur_cache_allocated_size =
|
||||
cache_allocated_size_.load(std::memory_order_relaxed);
|
||||
if (new_mem_used == cur_cache_allocated_size) {
|
||||
@@ -76,6 +79,28 @@ template Status CacheReservationManager::UpdateCacheReservation<
|
||||
template Status CacheReservationManager::UpdateCacheReservation<
|
||||
CacheEntryRole::kMisc>(std::size_t new_mem_used);
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManager::MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<R>>* handle) {
|
||||
assert(handle != nullptr);
|
||||
Status s =
|
||||
UpdateCacheReservation<R>(GetTotalMemoryUsed() + incremental_memory_used);
|
||||
(*handle).reset(new CacheReservationHandle<R>(incremental_memory_used,
|
||||
shared_from_this()));
|
||||
return s;
|
||||
}
|
||||
|
||||
template Status
|
||||
CacheReservationManager::MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<CacheEntryRole::kMisc>>* handle);
|
||||
template Status CacheReservationManager::MakeCacheReservation<
|
||||
CacheEntryRole::kFilterConstruction>(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<
|
||||
CacheReservationHandle<CacheEntryRole::kFilterConstruction>>* handle);
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Status CacheReservationManager::IncreaseCacheReservation(
|
||||
std::size_t new_mem_used) {
|
||||
@@ -118,6 +143,10 @@ std::size_t CacheReservationManager::GetTotalReservedCacheSize() {
|
||||
return cache_allocated_size_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
std::size_t CacheReservationManager::GetTotalMemoryUsed() {
|
||||
return memory_used_;
|
||||
}
|
||||
|
||||
Slice CacheReservationManager::GetNextCacheKey() {
|
||||
// Calling this function will have the side-effect of changing the
|
||||
// underlying cache_key_ that is shared among other keys generated from this
|
||||
@@ -128,4 +157,36 @@ Slice CacheReservationManager::GetNextCacheKey() {
|
||||
EncodeVarint64(cache_key_ + kCacheKeyPrefixSize, next_cache_key_id_++);
|
||||
return Slice(cache_key_, static_cast<std::size_t>(end - cache_key_));
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
template <CacheEntryRole R>
|
||||
Cache::DeleterFn CacheReservationManager::TEST_GetNoopDeleterForRole() {
|
||||
return GetNoopDeleterForRole<R>();
|
||||
}
|
||||
|
||||
template Cache::DeleterFn CacheReservationManager::TEST_GetNoopDeleterForRole<
|
||||
CacheEntryRole::kFilterConstruction>();
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationHandle<R>::CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr)
|
||||
: incremental_memory_used_(incremental_memory_used) {
|
||||
assert(cache_res_mgr != nullptr);
|
||||
cache_res_mgr_ = cache_res_mgr;
|
||||
}
|
||||
|
||||
template <CacheEntryRole R>
|
||||
CacheReservationHandle<R>::~CacheReservationHandle() {
|
||||
assert(cache_res_mgr_ != nullptr);
|
||||
assert(cache_res_mgr_->GetTotalMemoryUsed() >= incremental_memory_used_);
|
||||
|
||||
Status s = cache_res_mgr_->UpdateCacheReservation<R>(
|
||||
cache_res_mgr_->GetTotalMemoryUsed() - incremental_memory_used_);
|
||||
s.PermitUncheckedError();
|
||||
}
|
||||
|
||||
// Explicitly instantiate templates for "CacheEntryRole" values we use.
|
||||
// This makes it possible to keep the template definitions in the .cc file.
|
||||
template class CacheReservationHandle<CacheEntryRole::kMisc>;
|
||||
template class CacheReservationHandle<CacheEntryRole::kFilterConstruction>;
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+107
-8
@@ -24,10 +24,16 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationHandle;
|
||||
|
||||
// CacheReservationManager is for reserving cache space for the memory used
|
||||
// through inserting/releasing dummy entries in the cache.
|
||||
// This class is not thread-safe.
|
||||
class CacheReservationManager {
|
||||
//
|
||||
// This class is NOT thread-safe, except that GetTotalReservedCacheSize()
|
||||
// can be called without external synchronization.
|
||||
class CacheReservationManager
|
||||
: public std::enable_shared_from_this<CacheReservationManager> {
|
||||
public:
|
||||
// Construct a CacheReservationManager
|
||||
// @param cache The cache where dummy entries are inserted and released for
|
||||
@@ -52,9 +58,13 @@ class CacheReservationManager {
|
||||
|
||||
template <CacheEntryRole R>
|
||||
|
||||
// One of the two ways of reserving/releasing cache,
|
||||
// see CacheReservationManager::MakeCacheReservation() for the other.
|
||||
// Use ONLY one of them to prevent unexpected behavior.
|
||||
//
|
||||
// Insert and release dummy entries in the cache to
|
||||
// match the size of total dummy entries with the smallest multiple of
|
||||
// kSizeDummyEntry that is greater than or equal to new_mem_used
|
||||
// match the size of total dummy entries with the least multiple of
|
||||
// kSizeDummyEntry greater than or equal to new_mem_used
|
||||
//
|
||||
// Insert dummy entries if new_memory_used > cache_allocated_size_;
|
||||
//
|
||||
@@ -68,13 +78,84 @@ class CacheReservationManager {
|
||||
// is set true.
|
||||
//
|
||||
// @param new_memory_used The number of bytes used by new memory
|
||||
// The most recent new_memoy_used passed in will be returned
|
||||
// in GetTotalMemoryUsed() even when the call return non-ok status.
|
||||
//
|
||||
// Since the class is NOT thread-safe, external synchronization on the
|
||||
// order of calling UpdateCacheReservation() is needed if you want
|
||||
// GetTotalMemoryUsed() indeed returns the latest memory used.
|
||||
//
|
||||
// @return On inserting dummy entries, it returns Status::OK() if all dummy
|
||||
// entry insertions succeed. Otherwise, it returns the first non-ok status;
|
||||
// On releasing dummy entries, it always returns Status::OK().
|
||||
// On keeping dummy entries the same, it always returns Status::OK().
|
||||
// entry insertions succeed.
|
||||
// Otherwise, it returns the first non-ok status;
|
||||
// On releasing dummy entries, it always returns Status::OK().
|
||||
// On keeping dummy entries the same, it always returns Status::OK().
|
||||
Status UpdateCacheReservation(std::size_t new_memory_used);
|
||||
|
||||
// One of the two ways of reserving/releasing cache,
|
||||
// see CacheReservationManager::UpdateCacheReservation() for the other.
|
||||
// Use ONLY one of them to prevent unexpected behavior.
|
||||
//
|
||||
// Insert dummy entries in the cache for the incremental memory usage
|
||||
// to match the size of total dummy entries with the least multiple of
|
||||
// kSizeDummyEntry greater than or equal to the total memory used.
|
||||
//
|
||||
// A CacheReservationHandle is returned as an output parameter.
|
||||
// The reserved dummy entries are automatically released on the destruction of
|
||||
// this handle, which achieves better RAII per cache reservation.
|
||||
//
|
||||
// WARNING: Deallocate all the handles of the CacheReservationManager object
|
||||
// before deallocating the object to prevent unexpected behavior.
|
||||
//
|
||||
// @param incremental_memory_used The number of bytes increased in memory
|
||||
// usage.
|
||||
//
|
||||
// Calling GetTotalMemoryUsed() afterward will return the total memory
|
||||
// increased by this number, even when calling MakeCacheReservation()
|
||||
// returns non-ok status.
|
||||
//
|
||||
// Since the class is NOT thread-safe, external synchronization in
|
||||
// calling MakeCacheReservation() is needed if you want
|
||||
// GetTotalMemoryUsed() indeed returns the latest memory used.
|
||||
//
|
||||
// @param handle An pointer to std::unique_ptr<CacheReservationHandle<R>> that
|
||||
// manages the lifetime of the handle and its cache reservation.
|
||||
//
|
||||
// @return It returns Status::OK() if all dummy
|
||||
// entry insertions succeed.
|
||||
// Otherwise, it returns the first non-ok status;
|
||||
//
|
||||
// REQUIRES: handle != nullptr
|
||||
// REQUIRES: The CacheReservationManager object is NOT managed by
|
||||
// std::unique_ptr as CacheReservationHandle needs to
|
||||
// shares ownership to the CacheReservationManager object.
|
||||
template <CacheEntryRole R>
|
||||
Status MakeCacheReservation(
|
||||
std::size_t incremental_memory_used,
|
||||
std::unique_ptr<CacheReservationHandle<R>> *handle);
|
||||
|
||||
// Return the size of the cache (which is a multiple of kSizeDummyEntry)
|
||||
// successfully reserved by calling UpdateCacheReservation().
|
||||
//
|
||||
// When UpdateCacheReservation() returns non-ok status,
|
||||
// calling GetTotalReservedCacheSize() after that might return a slightly
|
||||
// smaller number than the actual reserved cache size due to
|
||||
// the returned number will always be a multiple of kSizeDummyEntry
|
||||
// and cache full might happen in the middle of inserting a dummy entry.
|
||||
std::size_t GetTotalReservedCacheSize();
|
||||
|
||||
// Return the latest total memory used indicated by the most recent call of
|
||||
// UpdateCacheReservation(std::size_t new_memory_used);
|
||||
std::size_t GetTotalMemoryUsed();
|
||||
|
||||
static constexpr std::size_t GetDummyEntrySize() { return kSizeDummyEntry; }
|
||||
|
||||
// For testing only - it is to help ensure the NoopDeleterForRole<R>
|
||||
// accessed from CacheReservationManager and the one accessed from the test
|
||||
// are from the same translation units
|
||||
template <CacheEntryRole R>
|
||||
static Cache::DeleterFn TEST_GetNoopDeleterForRole();
|
||||
|
||||
private:
|
||||
static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
// The key will be longer than keys for blocks in SST files so they won't
|
||||
@@ -90,9 +171,27 @@ class CacheReservationManager {
|
||||
std::shared_ptr<Cache> cache_;
|
||||
bool delayed_decrease_;
|
||||
std::atomic<std::size_t> cache_allocated_size_;
|
||||
std::size_t memory_used_;
|
||||
std::vector<Cache::Handle *> dummy_handles_;
|
||||
std::uint64_t next_cache_key_id_ = 0;
|
||||
// The non-prefix part will be updated according to the ID to use.
|
||||
char cache_key_[kCacheKeyPrefixSize + kMaxVarint64Length];
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
// CacheReservationHandle is for managing the lifetime of a cache reservation
|
||||
// This class is NOT thread-safe
|
||||
template <CacheEntryRole R>
|
||||
class CacheReservationHandle {
|
||||
public:
|
||||
// REQUIRES: cache_res_mgr != nullptr
|
||||
explicit CacheReservationHandle(
|
||||
std::size_t incremental_memory_used,
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr);
|
||||
|
||||
~CacheReservationHandle();
|
||||
|
||||
private:
|
||||
std::size_t incremental_memory_used_;
|
||||
std::shared_ptr<CacheReservationManager> cache_res_mgr_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+127
-24
@@ -22,15 +22,15 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class CacheReservationManagerTest : public ::testing::Test {
|
||||
protected:
|
||||
static constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
|
||||
static constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
static constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
static constexpr int kNumShardBits = 0; // 2^0 shard
|
||||
|
||||
static constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
static const std::size_t kCacheKeyPrefixSize =
|
||||
BlockBasedTable::kMaxCacheKeyPrefixSize + kMaxVarint64Length;
|
||||
static constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(kOneGigabyte, kNumShardBits);
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(kCacheCapacity, kNumShardBits);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng;
|
||||
|
||||
CacheReservationManagerTest() {
|
||||
@@ -81,6 +81,7 @@ TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
1 * kSizeDummyEntry);
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used);
|
||||
std::size_t initial_pinned_usage = cache->GetPinnedUsage();
|
||||
ASSERT_GE(initial_pinned_usage, 1 * kSizeDummyEntry);
|
||||
ASSERT_LT(initial_pinned_usage,
|
||||
@@ -96,6 +97,9 @@ TEST_F(CacheReservationManagerTest, KeepCacheReservationTheSame) {
|
||||
1 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep correctly when new_mem_used equals to current "
|
||||
"cache reservation";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly when new_mem_used "
|
||||
"equals to current cache reservation";
|
||||
EXPECT_EQ(cache->GetPinnedUsage(), initial_pinned_usage)
|
||||
<< "Failed to keep underlying dummy entries the same when new_mem_used "
|
||||
"equals to current cache reservation";
|
||||
@@ -113,6 +117,8 @@ TEST_F(CacheReservationManagerTest,
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep cache reservation increase correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 2 * kSizeDummyEntry)
|
||||
<< "Failed to increase underlying dummy entries in cache correctly";
|
||||
EXPECT_LT(cache->GetPinnedUsage(),
|
||||
@@ -132,6 +138,8 @@ TEST_F(CacheReservationManagerTest,
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
3 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep cache reservation increase correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 3 * kSizeDummyEntry)
|
||||
<< "Failed to increase underlying dummy entries in cache correctly";
|
||||
EXPECT_LT(cache->GetPinnedUsage(),
|
||||
@@ -141,20 +149,22 @@ TEST_F(CacheReservationManagerTest,
|
||||
|
||||
TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
IncreaseCacheReservationOnFullCache) {
|
||||
constexpr std::size_t kOneMegabyte = 1024 * 1024;
|
||||
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
|
||||
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
constexpr std::size_t kSmallCacheCapacity = 4 * kSizeDummyEntry;
|
||||
constexpr std::size_t kBigCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
LRUCacheOptions lo;
|
||||
lo.capacity = kOneMegabyte;
|
||||
lo.capacity = kSmallCacheCapacity;
|
||||
lo.num_shard_bits = 0; // 2^0 shard
|
||||
lo.strict_capacity_limit = true;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
new CacheReservationManager(cache));
|
||||
|
||||
std::size_t new_mem_used = kOneMegabyte + 1;
|
||||
std::size_t new_mem_used = kSmallCacheCapacity + 1;
|
||||
Status s =
|
||||
test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
@@ -166,18 +176,21 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
1 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep correctly before cache resevation failure happens "
|
||||
"due to full cache";
|
||||
EXPECT_LE(test_cache_rev_mng->GetTotalReservedCacheSize(), kOneMegabyte)
|
||||
EXPECT_LE(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
kSmallCacheCapacity)
|
||||
<< "Failed to bookkeep correctly (i.e, bookkeep only successful dummy "
|
||||
"entry insertions) when encountering cache resevation failure due to "
|
||||
"full cache";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry)
|
||||
<< "Failed to insert underlying dummy entries correctly when "
|
||||
"encountering cache resevation failure due to full cache";
|
||||
EXPECT_LE(cache->GetPinnedUsage(), kOneMegabyte)
|
||||
EXPECT_LE(cache->GetPinnedUsage(), kSmallCacheCapacity)
|
||||
<< "Failed to insert underlying dummy entries correctly when "
|
||||
"encountering cache resevation failure due to full cache";
|
||||
|
||||
new_mem_used = kOneMegabyte / 2; // 2 dummy entries
|
||||
new_mem_used = kSmallCacheCapacity / 2; // 2 dummy entries
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
@@ -188,6 +201,8 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
2 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep cache reservation decrease correctly after "
|
||||
"encountering cache reservation due to full cache";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 2 * kSizeDummyEntry)
|
||||
<< "Failed to release underlying dummy entries correctly on cache "
|
||||
"reservation decrease after encountering cache resevation failure due "
|
||||
@@ -199,7 +214,7 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
"to full cache";
|
||||
|
||||
// Create cache full again for subsequent tests
|
||||
new_mem_used = kOneMegabyte + 1;
|
||||
new_mem_used = kSmallCacheCapacity + 1;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
@@ -210,21 +225,24 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
1 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep correctly before cache resevation failure happens "
|
||||
"due to full cache";
|
||||
EXPECT_LE(test_cache_rev_mng->GetTotalReservedCacheSize(), kOneMegabyte)
|
||||
EXPECT_LE(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
kSmallCacheCapacity)
|
||||
<< "Failed to bookkeep correctly (i.e, bookkeep only successful dummy "
|
||||
"entry insertions) when encountering cache resevation failure due to "
|
||||
"full cache";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry)
|
||||
<< "Failed to insert underlying dummy entries correctly when "
|
||||
"encountering cache resevation failure due to full cache";
|
||||
EXPECT_LE(cache->GetPinnedUsage(), kOneMegabyte)
|
||||
EXPECT_LE(cache->GetPinnedUsage(), kSmallCacheCapacity)
|
||||
<< "Failed to insert underlying dummy entries correctly when "
|
||||
"encountering cache resevation failure due to full cache";
|
||||
|
||||
// Increase cache capacity so the previously failed insertion can fully
|
||||
// succeed
|
||||
cache->SetCapacity(kOneGigabyte);
|
||||
new_mem_used = kOneMegabyte + 1;
|
||||
cache->SetCapacity(kBigCacheCapacity);
|
||||
new_mem_used = kSmallCacheCapacity + 1;
|
||||
s = test_cache_rev_mng
|
||||
->UpdateCacheReservation<ROCKSDB_NAMESPACE::CacheEntryRole::kMisc>(
|
||||
new_mem_used);
|
||||
@@ -235,6 +253,8 @@ TEST(CacheReservationManagerIncreaseReservcationOnFullCacheTest,
|
||||
5 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep cache reservation increase correctly after "
|
||||
"increasing cache capacity and mitigating cache full error";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 5 * kSizeDummyEntry)
|
||||
<< "Failed to insert underlying dummy entries correctly after increasing "
|
||||
"cache capacity and mitigating cache full error";
|
||||
@@ -254,6 +274,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry);
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used);
|
||||
ASSERT_GE(cache->GetPinnedUsage(), 2 * kSizeDummyEntry);
|
||||
ASSERT_LT(cache->GetPinnedUsage(),
|
||||
2 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
@@ -267,6 +288,8 @@ TEST_F(CacheReservationManagerTest,
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
1 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep cache reservation decrease correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry)
|
||||
<< "Failed to decrease underlying dummy entries in cache correctly";
|
||||
EXPECT_LT(cache->GetPinnedUsage(),
|
||||
@@ -284,6 +307,7 @@ TEST_F(CacheReservationManagerTest,
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
2 * kSizeDummyEntry);
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used);
|
||||
ASSERT_GE(cache->GetPinnedUsage(), 2 * kSizeDummyEntry);
|
||||
ASSERT_LT(cache->GetPinnedUsage(),
|
||||
2 * kSizeDummyEntry + kMetaDataChargeOverhead);
|
||||
@@ -297,6 +321,8 @@ TEST_F(CacheReservationManagerTest,
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
1 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep cache reservation decrease correctly";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 1 * kSizeDummyEntry)
|
||||
<< "Failed to decrease underlying dummy entries in cache correctly";
|
||||
EXPECT_LT(cache->GetPinnedUsage(),
|
||||
@@ -306,12 +332,13 @@ TEST_F(CacheReservationManagerTest,
|
||||
|
||||
TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
DecreaseCacheReservationWithDelayedDecrease) {
|
||||
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
|
||||
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
LRUCacheOptions lo;
|
||||
lo.capacity = kOneGigabyte;
|
||||
lo.capacity = kCacheCapacity;
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
std::unique_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
@@ -325,6 +352,7 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(),
|
||||
8 * kSizeDummyEntry);
|
||||
ASSERT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used);
|
||||
std::size_t initial_pinned_usage = cache->GetPinnedUsage();
|
||||
ASSERT_GE(initial_pinned_usage, 8 * kSizeDummyEntry);
|
||||
ASSERT_LT(initial_pinned_usage,
|
||||
@@ -339,6 +367,8 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
8 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep correctly when delaying cache reservation "
|
||||
"decrease";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_EQ(cache->GetPinnedUsage(), initial_pinned_usage)
|
||||
<< "Failed to delay decreasing underlying dummy entries in cache";
|
||||
|
||||
@@ -351,6 +381,8 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
8 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep correctly when delaying cache reservation "
|
||||
"decrease";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_EQ(cache->GetPinnedUsage(), initial_pinned_usage)
|
||||
<< "Failed to delay decreasing underlying dummy entries in cache";
|
||||
|
||||
@@ -365,6 +397,8 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
6 * kSizeDummyEntry)
|
||||
<< "Failed to bookkeep correctly when new_mem_used < "
|
||||
"GetTotalReservedCacheSize() * 3 / 4 on delayed decrease mode";
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), new_mem_used)
|
||||
<< "Failed to bookkeep the used memory correctly";
|
||||
EXPECT_GE(cache->GetPinnedUsage(), 6 * kSizeDummyEntry)
|
||||
<< "Failed to decrease underlying dummy entries in cache when "
|
||||
"new_mem_used < GetTotalReservedCacheSize() * 3 / 4 on delayed "
|
||||
@@ -378,12 +412,13 @@ TEST(CacheReservationManagerWithDelayedDecreaseTest,
|
||||
|
||||
TEST(CacheReservationManagerDestructorTest,
|
||||
ReleaseRemainingDummyEntriesOnDestruction) {
|
||||
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
|
||||
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
constexpr std::size_t kSizeDummyEntry =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
constexpr std::size_t kCacheCapacity = 4096 * kSizeDummyEntry;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
LRUCacheOptions lo;
|
||||
lo.capacity = kOneGigabyte;
|
||||
lo.capacity = kCacheCapacity;
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
{
|
||||
@@ -403,9 +438,77 @@ TEST(CacheReservationManagerDestructorTest,
|
||||
<< "Failed to release remaining underlying dummy entries in cache in "
|
||||
"CacheReservationManager's destructor";
|
||||
}
|
||||
|
||||
TEST(CacheReservationHandleTest, HandleTest) {
|
||||
constexpr std::size_t kOneGigabyte = 1024 * 1024 * 1024;
|
||||
constexpr std::size_t kSizeDummyEntry = 256 * 1024;
|
||||
constexpr std::size_t kMetaDataChargeOverhead = 10000;
|
||||
|
||||
LRUCacheOptions lo;
|
||||
lo.capacity = kOneGigabyte;
|
||||
lo.num_shard_bits = 0;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(lo);
|
||||
|
||||
std::shared_ptr<CacheReservationManager> test_cache_rev_mng(
|
||||
std::make_shared<CacheReservationManager>(cache));
|
||||
|
||||
std::size_t mem_used = 0;
|
||||
const std::size_t incremental_mem_used_handle_1 = 1 * kSizeDummyEntry;
|
||||
const std::size_t incremental_mem_used_handle_2 = 2 * kSizeDummyEntry;
|
||||
std::unique_ptr<CacheReservationHandle<CacheEntryRole::kMisc>> handle_1,
|
||||
handle_2;
|
||||
|
||||
// To test consecutive CacheReservationManager::MakeCacheReservation works
|
||||
// correctly in terms of returning the handle as well as updating cache
|
||||
// reservation and the latest total memory used
|
||||
Status s = test_cache_rev_mng->MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
incremental_mem_used_handle_1, &handle_1);
|
||||
mem_used = mem_used + incremental_mem_used_handle_1;
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
EXPECT_TRUE(handle_1 != nullptr);
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(), mem_used);
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), mem_used);
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
s = test_cache_rev_mng->MakeCacheReservation<CacheEntryRole::kMisc>(
|
||||
incremental_mem_used_handle_2, &handle_2);
|
||||
mem_used = mem_used + incremental_mem_used_handle_2;
|
||||
ASSERT_EQ(s, Status::OK());
|
||||
EXPECT_TRUE(handle_2 != nullptr);
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(), mem_used);
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), mem_used);
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
// To test CacheReservationHandle::~CacheReservationHandle() works correctly
|
||||
// in releasing the cache reserved for the handle
|
||||
handle_1.reset();
|
||||
EXPECT_TRUE(handle_1 == nullptr);
|
||||
mem_used = mem_used - incremental_mem_used_handle_1;
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalReservedCacheSize(), mem_used);
|
||||
EXPECT_EQ(test_cache_rev_mng->GetTotalMemoryUsed(), mem_used);
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
// To test the actual CacheReservationManager object won't be deallocated
|
||||
// as long as there remain handles pointing to it.
|
||||
// We strongly recommend deallocating CacheReservationManager object only
|
||||
// after all its handles are deallocated to keep things easy to reasonate
|
||||
test_cache_rev_mng.reset();
|
||||
EXPECT_GE(cache->GetPinnedUsage(), mem_used);
|
||||
EXPECT_LT(cache->GetPinnedUsage(), mem_used + kMetaDataChargeOverhead);
|
||||
|
||||
handle_2.reset();
|
||||
// The CacheReservationManager object is now deallocated since all the handles
|
||||
// and its original pointer is gone
|
||||
mem_used = mem_used - incremental_mem_used_handle_2;
|
||||
EXPECT_EQ(mem_used, 0);
|
||||
EXPECT_EQ(cache->GetPinnedUsage(), mem_used);
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
}
|
||||
+20
-8
@@ -9,14 +9,26 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status BlobFetcher::FetchBlob(const Slice& user_key, const Slice& blob_index,
|
||||
PinnableSlice* blob_value) {
|
||||
Status s;
|
||||
Status BlobFetcher::FetchBlob(const Slice& user_key,
|
||||
const Slice& blob_index_slice,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* blob_value,
|
||||
uint64_t* bytes_read) const {
|
||||
assert(version_);
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
s = version_->GetBlob(read_options_, user_key, blob_index, blob_value,
|
||||
bytes_read);
|
||||
return s;
|
||||
|
||||
return version_->GetBlob(read_options_, user_key, blob_index_slice,
|
||||
prefetch_buffer, blob_value, bytes_read);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Status BlobFetcher::FetchBlob(const Slice& user_key,
|
||||
const BlobIndex& blob_index,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* blob_value,
|
||||
uint64_t* bytes_read) const {
|
||||
assert(version_);
|
||||
|
||||
return version_->GetBlob(read_options_, user_key, blob_index, prefetch_buffer,
|
||||
blob_value, bytes_read);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+17
-6
@@ -9,18 +9,29 @@
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class Version;
|
||||
|
||||
class Version;
|
||||
class Slice;
|
||||
class FilePrefetchBuffer;
|
||||
class PinnableSlice;
|
||||
class BlobIndex;
|
||||
|
||||
// A thin wrapper around the blob retrieval functionality of Version.
|
||||
class BlobFetcher {
|
||||
public:
|
||||
BlobFetcher(Version* version, const ReadOptions& read_options)
|
||||
BlobFetcher(const Version* version, const ReadOptions& read_options)
|
||||
: version_(version), read_options_(read_options) {}
|
||||
|
||||
Status FetchBlob(const Slice& user_key, const Slice& blob_index,
|
||||
PinnableSlice* blob_value);
|
||||
Status FetchBlob(const Slice& user_key, const Slice& blob_index_slice,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* blob_value, uint64_t* bytes_read) const;
|
||||
|
||||
Status FetchBlob(const Slice& user_key, const BlobIndex& blob_index,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* blob_value, uint64_t* bytes_read) const;
|
||||
|
||||
private:
|
||||
Version* version_;
|
||||
const Version* version_;
|
||||
ReadOptions read_options_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/file_prefetch_buffer.h"
|
||||
#include "file/filename.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "options/cf_options.h"
|
||||
@@ -282,6 +283,7 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
const Slice& user_key, uint64_t offset,
|
||||
uint64_t value_size,
|
||||
CompressionType compression_type,
|
||||
FilePrefetchBuffer* prefetch_buffer,
|
||||
PinnableSlice* value,
|
||||
uint64_t* bytes_read) const {
|
||||
assert(value);
|
||||
@@ -313,7 +315,21 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
Buffer buf;
|
||||
AlignedBuf aligned_buf;
|
||||
|
||||
{
|
||||
bool prefetched = false;
|
||||
|
||||
if (prefetch_buffer) {
|
||||
Status s;
|
||||
constexpr bool for_compaction = true;
|
||||
|
||||
prefetched = prefetch_buffer->TryReadFromCache(
|
||||
IOOptions(), file_reader_.get(), record_offset,
|
||||
static_cast<size_t>(record_size), &record_slice, &s, for_compaction);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (!prefetched) {
|
||||
TEST_SYNC_POINT("BlobFileReader::GetBlob:ReadFromFile");
|
||||
|
||||
const Status s = ReadFromFile(file_reader_.get(), record_offset,
|
||||
@@ -322,11 +338,11 @@ Status BlobFileReader::GetBlob(const ReadOptions& read_options,
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileReader::GetBlob:TamperWithResult",
|
||||
&record_slice);
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("BlobFileReader::GetBlob:TamperWithResult",
|
||||
&record_slice);
|
||||
|
||||
if (read_options.verify_checksums) {
|
||||
const Status s = VerifyBlob(record_slice, user_key, value_size);
|
||||
if (!s.ok()) {
|
||||
|
||||
@@ -21,6 +21,7 @@ struct FileOptions;
|
||||
class HistogramImpl;
|
||||
struct ReadOptions;
|
||||
class Slice;
|
||||
class FilePrefetchBuffer;
|
||||
class PinnableSlice;
|
||||
class Statistics;
|
||||
|
||||
@@ -41,7 +42,8 @@ class BlobFileReader {
|
||||
|
||||
Status GetBlob(const ReadOptions& read_options, const Slice& user_key,
|
||||
uint64_t offset, uint64_t value_size,
|
||||
CompressionType compression_type, PinnableSlice* value,
|
||||
CompressionType compression_type,
|
||||
FilePrefetchBuffer* prefetch_buffer, PinnableSlice* value,
|
||||
uint64_t* bytes_read) const;
|
||||
|
||||
// offsets must be sorted in ascending order by caller.
|
||||
|
||||
@@ -179,13 +179,15 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = false;
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, keys[0], blob_offsets[0],
|
||||
blob_sizes[0], kNoCompression, &value,
|
||||
&bytes_read));
|
||||
blob_sizes[0], kNoCompression, prefetch_buffer,
|
||||
&value, &bytes_read));
|
||||
ASSERT_EQ(value, blobs[0]);
|
||||
ASSERT_EQ(bytes_read, blob_sizes[0]);
|
||||
|
||||
@@ -222,8 +224,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, keys[1], blob_offsets[1],
|
||||
blob_sizes[1], kNoCompression, &value,
|
||||
&bytes_read));
|
||||
blob_sizes[1], kNoCompression, prefetch_buffer,
|
||||
&value, &bytes_read));
|
||||
ASSERT_EQ(value, blobs[1]);
|
||||
|
||||
const uint64_t key_size = keys[1].size();
|
||||
@@ -239,8 +241,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, keys[0], blob_offsets[0] - 1,
|
||||
blob_sizes[0], kNoCompression, &value,
|
||||
&bytes_read)
|
||||
blob_sizes[0], kNoCompression, prefetch_buffer,
|
||||
&value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
@@ -252,8 +254,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, keys[2], blob_offsets[2] + 1,
|
||||
blob_sizes[2], kNoCompression, &value,
|
||||
&bytes_read)
|
||||
blob_sizes[2], kNoCompression, prefetch_buffer,
|
||||
&value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
@@ -265,7 +267,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, keys[0], blob_offsets[0],
|
||||
blob_sizes[0], kZSTD, &value, &bytes_read)
|
||||
blob_sizes[0], kZSTD, prefetch_buffer, &value,
|
||||
&bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
@@ -280,8 +283,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
->GetBlob(read_options, shorter_key,
|
||||
blob_offsets[0] -
|
||||
(keys[0].size() - sizeof(shorter_key) + 1),
|
||||
blob_sizes[0], kNoCompression, &value,
|
||||
&bytes_read)
|
||||
blob_sizes[0], kNoCompression, prefetch_buffer,
|
||||
&value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -323,8 +326,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, incorrect_key, blob_offsets[0],
|
||||
blob_sizes[0], kNoCompression, &value,
|
||||
&bytes_read)
|
||||
blob_sizes[0], kNoCompression, prefetch_buffer,
|
||||
&value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -363,8 +366,8 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(read_options, keys[1], blob_offsets[1],
|
||||
blob_sizes[1] + 1, kNoCompression, &value,
|
||||
&bytes_read)
|
||||
blob_sizes[1] + 1, kNoCompression,
|
||||
prefetch_buffer, &value, &bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -642,12 +645,14 @@ TEST_F(BlobFileReaderTest, BlobCRCError) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kNoCompression, &value, &bytes_read)
|
||||
kNoCompression, prefetch_buffer, &value,
|
||||
&bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -695,12 +700,15 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = false;
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
|
||||
{
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, key, blob_offset, blob_size,
|
||||
kSnappyCompression, &value, &bytes_read));
|
||||
kSnappyCompression, prefetch_buffer, &value,
|
||||
&bytes_read));
|
||||
ASSERT_EQ(value, blob);
|
||||
ASSERT_EQ(bytes_read, blob_size);
|
||||
}
|
||||
@@ -712,7 +720,8 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_OK(reader->GetBlob(read_options, key, blob_offset, blob_size,
|
||||
kSnappyCompression, &value, &bytes_read));
|
||||
kSnappyCompression, prefetch_buffer, &value,
|
||||
&bytes_read));
|
||||
ASSERT_EQ(value, blob);
|
||||
|
||||
constexpr uint64_t key_size = sizeof(key) - 1;
|
||||
@@ -770,12 +779,14 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kSnappyCompression, &value, &bytes_read)
|
||||
kSnappyCompression, prefetch_buffer, &value,
|
||||
&bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
|
||||
@@ -854,12 +865,14 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kNoCompression, &value, &bytes_read)
|
||||
kNoCompression, prefetch_buffer, &value,
|
||||
&bytes_read)
|
||||
.IsIOError());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
@@ -937,12 +950,14 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
} else {
|
||||
ASSERT_OK(s);
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
PinnableSlice value;
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
ASSERT_TRUE(reader
|
||||
->GetBlob(ReadOptions(), key, blob_offset, blob_size,
|
||||
kNoCompression, &value, &bytes_read)
|
||||
kNoCompression, prefetch_buffer, &value,
|
||||
&bytes_read)
|
||||
.IsCorruption());
|
||||
ASSERT_EQ(bytes_read, 0);
|
||||
}
|
||||
|
||||
@@ -1018,14 +1018,6 @@ TEST_P(DBBlobBasicIOErrorTest, CompactionFilterReadBlob_IOError) {
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -590,15 +590,125 @@ TEST_F(DBBlobCompactionTest, MergeBlobWithBase) {
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
TEST_F(DBBlobCompactionTest, CompactionReadaheadGarbageCollection) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.enable_blob_garbage_collection = true;
|
||||
options.blob_garbage_collection_age_cutoff = 1.0;
|
||||
options.blob_compaction_readahead_size = 1 << 10;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("key", "lime"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Put("key", "pie"));
|
||||
ASSERT_OK(Put("foo", "baz"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
size_t num_non_prefetch_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::GetBlob:ReadFromFile",
|
||||
[&num_non_prefetch_reads](void* /* arg */) { ++num_non_prefetch_reads; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_EQ(Get("key"), "pie");
|
||||
ASSERT_EQ(Get("foo"), "baz");
|
||||
ASSERT_EQ(num_non_prefetch_reads, 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
TEST_F(DBBlobCompactionTest, CompactionReadaheadFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new ValueMutationFilter("pie"));
|
||||
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.blob_compaction_readahead_size = 1 << 10;
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("key", "lime"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
size_t num_non_prefetch_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::GetBlob:ReadFromFile",
|
||||
[&num_non_prefetch_reads](void* /* arg */) { ++num_non_prefetch_reads; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_EQ(Get("key"), "limepie");
|
||||
ASSERT_EQ(Get("foo"), "barpie");
|
||||
ASSERT_EQ(num_non_prefetch_reads, 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, CompactionReadaheadMerge) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.blob_compaction_readahead_size = 1 << 10;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("key", "lime"));
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Merge("key", "pie"));
|
||||
ASSERT_OK(Merge("foo", "baz"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
size_t num_non_prefetch_reads = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::GetBlob:ReadFromFile",
|
||||
[&num_non_prefetch_reads](void* /* arg */) { ++num_non_prefetch_reads; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_EQ(Get("key"), "lime,pie");
|
||||
ASSERT_EQ(Get("foo"), "bar,baz");
|
||||
ASSERT_EQ(num_non_prefetch_reads, 0);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
|
||||
@@ -74,14 +74,6 @@ TEST_F(DBBlobCorruptionTest, VerifyWholeBlobFileChecksum) {
|
||||
#endif // !ROCKSDB_LITE
|
||||
} // 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);
|
||||
|
||||
@@ -564,14 +564,6 @@ TEST_F(DBBlobIndexTest, IntegratedBlobIterate) {
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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/prefetch_buffer_collection.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
FilePrefetchBuffer* PrefetchBufferCollection::GetOrCreatePrefetchBuffer(
|
||||
uint64_t file_number) {
|
||||
auto& prefetch_buffer = prefetch_buffers_[file_number];
|
||||
if (!prefetch_buffer) {
|
||||
prefetch_buffer.reset(
|
||||
new FilePrefetchBuffer(readahead_size_, readahead_size_));
|
||||
}
|
||||
|
||||
return prefetch_buffer.get();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 <cstdint>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "file/file_prefetch_buffer.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// A class that owns a collection of FilePrefetchBuffers using the file number
|
||||
// as key. Used for implementing compaction readahead for blob files. Designed
|
||||
// to be accessed by a single thread only: every (sub)compaction needs its own
|
||||
// buffers since they are guaranteed to read different blobs from different
|
||||
// positions even when reading the same file.
|
||||
class PrefetchBufferCollection {
|
||||
public:
|
||||
explicit PrefetchBufferCollection(uint64_t readahead_size)
|
||||
: readahead_size_(readahead_size) {
|
||||
assert(readahead_size_ > 0);
|
||||
}
|
||||
|
||||
FilePrefetchBuffer* GetOrCreatePrefetchBuffer(uint64_t file_number);
|
||||
|
||||
private:
|
||||
uint64_t readahead_size_;
|
||||
std::unordered_map<uint64_t, std::unique_ptr<FilePrefetchBuffer>>
|
||||
prefetch_buffers_; // maps file number to prefetch buffer
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+6
-1
@@ -380,14 +380,19 @@ Status BuildTable(
|
||||
}
|
||||
}
|
||||
|
||||
Status status_for_listener = s;
|
||||
if (meta->fd.GetFileSize() == 0) {
|
||||
fname = "(nil)";
|
||||
if (s.ok()) {
|
||||
status_for_listener = Status::Aborted("Empty SST file not kept");
|
||||
}
|
||||
}
|
||||
// Output to event logger and fire events.
|
||||
EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
event_logger, ioptions.listeners, dbname, tboptions.column_family_name,
|
||||
fname, job_id, meta->fd, meta->oldest_blob_file_number, tp,
|
||||
tboptions.reason, s, file_checksum, file_checksum_func_name);
|
||||
tboptions.reason, status_for_listener, file_checksum,
|
||||
file_checksum_func_name);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -2760,6 +2760,16 @@ double rocksdb_options_get_blob_gc_force_threshold(rocksdb_options_t* opt) {
|
||||
return opt->rep.blob_garbage_collection_force_threshold;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_blob_compaction_readahead_size(rocksdb_options_t* opt,
|
||||
uint64_t val) {
|
||||
opt->rep.blob_compaction_readahead_size = val;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_blob_compaction_readahead_size(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.blob_compaction_readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_num_levels(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.num_levels = n;
|
||||
}
|
||||
|
||||
@@ -1799,6 +1799,10 @@ int main(int argc, char** argv) {
|
||||
rocksdb_options_set_blob_gc_force_threshold(o, 0.75);
|
||||
CheckCondition(0.75 == rocksdb_options_get_blob_gc_force_threshold(o));
|
||||
|
||||
rocksdb_options_set_blob_compaction_readahead_size(o, 262144);
|
||||
CheckCondition(262144 ==
|
||||
rocksdb_options_get_blob_compaction_readahead_size(o));
|
||||
|
||||
// Create a copy that should be equal to the original.
|
||||
rocksdb_options_t* copy;
|
||||
copy = rocksdb_options_create_copy(o);
|
||||
|
||||
+2
-6
@@ -75,11 +75,6 @@ ColumnFamilyHandleImpl::~ColumnFamilyHandleImpl() {
|
||||
bool defer_purge =
|
||||
db_->immutable_db_options().avoid_unnecessary_blocking_io;
|
||||
db_->PurgeObsoleteFiles(job_context, defer_purge);
|
||||
if (defer_purge) {
|
||||
mutex_->Lock();
|
||||
db_->SchedulePurge();
|
||||
mutex_->Unlock();
|
||||
}
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
@@ -211,7 +206,8 @@ ColumnFamilyOptions SanitizeOptions(const ImmutableDBOptions& db_options,
|
||||
size_t clamp_max = std::conditional<
|
||||
sizeof(size_t) == 4, std::integral_constant<size_t, 0xffffffff>,
|
||||
std::integral_constant<uint64_t, 64ull << 30>>::type::value;
|
||||
ClipToRange(&result.write_buffer_size, ((size_t)64) << 10, clamp_max);
|
||||
ClipToRange(&result.write_buffer_size, (static_cast<size_t>(64)) << 10,
|
||||
clamp_max);
|
||||
// if user sets arena_block_size, we trust user to use this value. Otherwise,
|
||||
// calculate a proper value from writer_buffer_size;
|
||||
if (result.arena_block_size <= 0) {
|
||||
|
||||
@@ -3433,14 +3433,6 @@ TEST(ColumnFamilyTest, ValidateBlobGCForceThreshold) {
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -612,10 +612,19 @@ bool Compaction::DoesInputReferenceBlobFiles() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime() const {
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime(
|
||||
const InternalKey* start, const InternalKey* end) const {
|
||||
uint64_t min_oldest_ancester_time = port::kMaxUint64;
|
||||
const InternalKeyComparator& icmp =
|
||||
column_family_data()->internal_comparator();
|
||||
for (const auto& level_files : inputs_) {
|
||||
for (const auto& file : level_files.files) {
|
||||
if (start != nullptr && icmp.Compare(file->largest, *start) < 0) {
|
||||
continue;
|
||||
}
|
||||
if (end != nullptr && icmp.Compare(file->smallest, *end) > 0) {
|
||||
continue;
|
||||
}
|
||||
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
|
||||
if (oldest_ancester_time != 0) {
|
||||
min_oldest_ancester_time =
|
||||
|
||||
@@ -304,7 +304,10 @@ class Compaction {
|
||||
|
||||
uint32_t max_subcompactions() const { return max_subcompactions_; }
|
||||
|
||||
uint64_t MinInputFileOldestAncesterTime() const;
|
||||
// start and end are sub compact range. Null if no boundary.
|
||||
// This is used to filter out some input files' ancester's time range.
|
||||
uint64_t MinInputFileOldestAncesterTime(const InternalKey* start,
|
||||
const InternalKey* end) const;
|
||||
|
||||
// Called by DBImpl::NotifyOnCompactionCompleted to make sure number of
|
||||
// compaction begin and compaction completion callbacks match.
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
struct CompactionIterationStats {
|
||||
// Compaction statistics
|
||||
|
||||
@@ -43,3 +45,5 @@ struct CompactionIterationStats {
|
||||
uint64_t num_blobs_relocated = 0;
|
||||
uint64_t total_blob_bytes_relocated = 0;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -8,8 +8,10 @@
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
#include "db/blob/blob_fetcher.h"
|
||||
#include "db/blob/blob_file_builder.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/prefetch_buffer_collection.h"
|
||||
#include "db/snapshot_checker.h"
|
||||
#include "logging/logging.h"
|
||||
#include "port/likely.h"
|
||||
@@ -88,6 +90,9 @@ CompactionIterator::CompactionIterator(
|
||||
merge_out_iter_(merge_helper_),
|
||||
blob_garbage_collection_cutoff_file_number_(
|
||||
ComputeBlobGarbageCollectionCutoffFileNumber(compaction_.get())),
|
||||
blob_fetcher_(CreateBlobFetcherIfNeeded(compaction_.get())),
|
||||
prefetch_buffers_(
|
||||
CreatePrefetchBufferCollectionIfNeeded(compaction_.get())),
|
||||
current_key_committed_(false),
|
||||
cmp_with_history_ts_low_(0),
|
||||
level_(compaction_ == nullptr ? 0 : compaction_->level()) {
|
||||
@@ -225,6 +230,13 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
compaction_filter_skip_until_.rep());
|
||||
if (CompactionFilter::Decision::kUndetermined == filter &&
|
||||
!compaction_filter_->IsStackedBlobDbInternalCompactionFilter()) {
|
||||
if (compaction_ == nullptr) {
|
||||
status_ =
|
||||
Status::Corruption("Unexpected blob index outside of compaction");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// For integrated BlobDB impl, CompactionIterator reads blob value.
|
||||
// For Stacked BlobDB impl, the corresponding CompactionFilter's
|
||||
// FilterV2 method should read the blob value.
|
||||
@@ -235,23 +247,19 @@ bool CompactionIterator::InvokeFilterIfNeeded(bool* need_skip,
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (blob_index.HasTTL() || blob_index.IsInlined()) {
|
||||
status_ = Status::Corruption("Unexpected TTL/inlined blob index");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (compaction_ == nullptr) {
|
||||
status_ =
|
||||
Status::Corruption("Unexpected blob index outside of compaction");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
const Version* const version = compaction_->input_version();
|
||||
assert(version);
|
||||
|
||||
FilePrefetchBuffer* prefetch_buffer =
|
||||
prefetch_buffers_ ? prefetch_buffers_->GetOrCreatePrefetchBuffer(
|
||||
blob_index.file_number())
|
||||
: nullptr;
|
||||
|
||||
uint64_t bytes_read = 0;
|
||||
s = version->GetBlob(ReadOptions(), ikey_.user_key, blob_index,
|
||||
&blob_value_, &bytes_read);
|
||||
|
||||
assert(blob_fetcher_);
|
||||
|
||||
s = blob_fetcher_->FetchBlob(ikey_.user_key, blob_index,
|
||||
prefetch_buffer, &blob_value_,
|
||||
&bytes_read);
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
valid_ = false;
|
||||
@@ -407,6 +415,12 @@ void CompactionIterator::NextFromInput() {
|
||||
// Copy key for output
|
||||
key_ = current_key_.SetInternalKey(key_, &ikey_);
|
||||
|
||||
int prev_cmp_with_ts_low =
|
||||
!full_history_ts_low_ ? 0
|
||||
: curr_ts_.empty()
|
||||
? 0
|
||||
: cmp_->CompareTimestamp(curr_ts_, *full_history_ts_low_);
|
||||
|
||||
// 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();
|
||||
@@ -416,14 +430,16 @@ void CompactionIterator::NextFromInput() {
|
||||
// (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_
|
||||
// (5) timestamp is NO older than *full_history_ts_low_, OR
|
||||
// (6) timestamp is the largest one 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_ ||
|
||||
!user_key_equal_without_ts || cmp_with_history_ts_low_ >= 0) {
|
||||
!user_key_equal_without_ts || cmp_with_history_ts_low_ >= 0 ||
|
||||
prev_cmp_with_ts_low >= 0) {
|
||||
// Initialize for future comparison for rule (A) and etc.
|
||||
current_user_key_sequence_ = kMaxSequenceNumber;
|
||||
current_user_key_snapshot_ = 0;
|
||||
@@ -433,6 +449,8 @@ void CompactionIterator::NextFromInput() {
|
||||
|
||||
has_outputted_key_ = false;
|
||||
|
||||
last_key_seq_zeroed_ = false;
|
||||
|
||||
current_key_committed_ = KeyCommitted(ikey_.sequence);
|
||||
|
||||
// Apply the compaction filter to the first committed version of the user
|
||||
@@ -520,6 +538,25 @@ void CompactionIterator::NextFromInput() {
|
||||
// 2) We've already returned a record in this snapshot -OR-
|
||||
// there are no earlier earliest_write_conflict_snapshot.
|
||||
//
|
||||
// A note about 2) above:
|
||||
// we try to determine whether there is any earlier write conflict
|
||||
// checking snapshot by calling DefinitelyInSnapshot() with seq and
|
||||
// earliest_write_conflict_snapshot as arguments. For write-prepared
|
||||
// and write-unprepared transactions, if earliest_write_conflict_snapshot
|
||||
// is evicted from WritePreparedTxnDB::commit_cache, then
|
||||
// DefinitelyInSnapshot(seq, earliest_write_conflict_snapshot) returns
|
||||
// false, even if the seq is actually visible within
|
||||
// earliest_write_conflict_snapshot. Consequently, CompactionIterator
|
||||
// may try to zero out its sequence number, thus hitting assertion error
|
||||
// in debug mode or cause incorrect DBIter return result.
|
||||
// We observe that earliest_write_conflict_snapshot >= earliest_snapshot,
|
||||
// and the seq zeroing logic depends on
|
||||
// DefinitelyInSnapshot(seq, earliest_snapshot). Therefore, if we cannot
|
||||
// determine whether seq is **definitely** in
|
||||
// earliest_write_conflict_snapshot, then we can additionally check if
|
||||
// seq is definitely in earliest_snapshot. If the latter holds, then the
|
||||
// former holds too.
|
||||
//
|
||||
// Rule 1 is needed for SingleDelete correctness. Rule 2 is needed to
|
||||
// allow Transactions to do write-conflict checking (if we compacted away
|
||||
// all keys, then we wouldn't know that a write happened in this
|
||||
@@ -575,17 +612,31 @@ void CompactionIterator::NextFromInput() {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:SingleDelete:1",
|
||||
const_cast<Compaction*>(c));
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
if (prev_snapshot == 0 ||
|
||||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot)) {
|
||||
if (last_key_seq_zeroed_) {
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
assert(bottommost_level_);
|
||||
AdvanceInputIter();
|
||||
} else if (prev_snapshot == 0 ||
|
||||
DefinitelyNotInSnapshot(next_ikey.sequence, prev_snapshot)) {
|
||||
// Check whether the next key belongs to the same snapshot as the
|
||||
// SingleDelete.
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:SingleDelete:2", nullptr);
|
||||
if (next_ikey.type == kTypeSingleDeletion) {
|
||||
if (next_ikey.type == kTypeSingleDeletion ||
|
||||
next_ikey.type == kTypeDeletion) {
|
||||
// We encountered two SingleDeletes for same key in a row. This
|
||||
// could be due to unexpected user input. Skip the first
|
||||
// SingleDelete and let the next iteration decide how to handle the
|
||||
// second SingleDelete
|
||||
// could be due to unexpected user input. If write-(un)prepared
|
||||
// transaction is used, this could also be due to releasing an old
|
||||
// snapshot between a Put and its matching SingleDelete.
|
||||
// Furthermore, if write-(un)prepared transaction is rolled back
|
||||
// after prepare, we will write a Delete to cancel a prior Put. If
|
||||
// old snapshot is released between a later Put and its matching
|
||||
// SingleDelete, we will end up with a Delete followed by
|
||||
// SingleDelete.
|
||||
// Skip the first SingleDelete and let the next iteration decide
|
||||
// how to handle the second SingleDelete or Delete.
|
||||
|
||||
// First SingleDelete has been skipped since we already called
|
||||
// input_.Next().
|
||||
@@ -598,7 +649,10 @@ void CompactionIterator::NextFromInput() {
|
||||
valid_ = true;
|
||||
} else if (has_outputted_key_ ||
|
||||
DefinitelyInSnapshot(ikey_.sequence,
|
||||
earliest_write_conflict_snapshot_)) {
|
||||
earliest_write_conflict_snapshot_) ||
|
||||
(earliest_snapshot_ < earliest_write_conflict_snapshot_ &&
|
||||
DefinitelyInSnapshot(ikey_.sequence,
|
||||
earliest_snapshot_))) {
|
||||
// Found a matching value, we can drop the single delete and the
|
||||
// value. It is safe to drop both records since we've already
|
||||
// outputted a key in this snapshot, or there is no earlier
|
||||
@@ -639,6 +693,9 @@ void CompactionIterator::NextFromInput() {
|
||||
// We hit the next snapshot without hitting a put, so the iterator
|
||||
// returns the single delete.
|
||||
valid_ = true;
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"CompactionIterator::NextFromInput:SingleDelete:3",
|
||||
const_cast<Compaction*>(c));
|
||||
}
|
||||
} else {
|
||||
// We are at the end of the input, could not parse the next key, or hit
|
||||
@@ -661,6 +718,11 @@ void CompactionIterator::NextFromInput() {
|
||||
if (!bottommost_level_) {
|
||||
++iter_stats_.num_optimized_del_drop_obsolete;
|
||||
}
|
||||
} else if (last_key_seq_zeroed_) {
|
||||
// Skip.
|
||||
++iter_stats_.num_record_drop_hidden;
|
||||
++iter_stats_.num_record_drop_obsolete;
|
||||
assert(bottommost_level_);
|
||||
} else {
|
||||
// Output SingleDelete
|
||||
valid_ = true;
|
||||
@@ -777,15 +839,15 @@ void CompactionIterator::NextFromInput() {
|
||||
}
|
||||
|
||||
pinned_iters_mgr_.StartPinning();
|
||||
Version* version = compaction_ ? compaction_->input_version() : nullptr;
|
||||
|
||||
// We know the merge type entry is not hidden, otherwise we would
|
||||
// have hit (A)
|
||||
// We encapsulate the merge related state machine in a different
|
||||
// object to minimize change to the existing flow.
|
||||
Status s = merge_helper_->MergeUntil(&input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_,
|
||||
allow_data_in_errors_, version);
|
||||
Status s = merge_helper_->MergeUntil(
|
||||
&input_, range_del_agg_, prev_snapshot, bottommost_level_,
|
||||
allow_data_in_errors_, blob_fetcher_.get(), prefetch_buffers_.get(),
|
||||
&iter_stats_);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
if (!s.ok() && !s.IsMergeInProgress()) {
|
||||
@@ -905,26 +967,23 @@ void CompactionIterator::GarbageCollectBlobIfNeeded() {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
FilePrefetchBuffer* prefetch_buffer =
|
||||
prefetch_buffers_ ? prefetch_buffers_->GetOrCreatePrefetchBuffer(
|
||||
blob_index.file_number())
|
||||
: nullptr;
|
||||
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
{
|
||||
const Status s = version->GetBlob(ReadOptions(), user_key(), blob_index,
|
||||
&blob_value_, &bytes_read);
|
||||
assert(blob_fetcher_);
|
||||
|
||||
const Status s = blob_fetcher_->FetchBlob(
|
||||
user_key(), blob_index, prefetch_buffer, &blob_value_, &bytes_read);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
@@ -1016,6 +1075,9 @@ void CompactionIterator::PrepareOutput() {
|
||||
ikey_.type);
|
||||
}
|
||||
ikey_.sequence = 0;
|
||||
last_key_seq_zeroed_ = true;
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionIterator::PrepareOutput:ZeroingSeq",
|
||||
&ikey_);
|
||||
if (!timestamp_size_) {
|
||||
current_key_.UpdateInternalKey(0, ikey_.type);
|
||||
} else if (full_history_ts_low_ && cmp_with_history_ts_low_ < 0) {
|
||||
@@ -1084,41 +1146,6 @@ inline bool CompactionIterator::ikeyNotNeededForIncrementalSnapshot() {
|
||||
(ikey_.sequence < preserve_deletes_seqnum_);
|
||||
}
|
||||
|
||||
bool CompactionIterator::IsInCurrentEarliestSnapshot(SequenceNumber sequence) {
|
||||
assert(snapshot_checker_ != nullptr);
|
||||
bool pre_condition = (earliest_snapshot_ == kMaxSequenceNumber ||
|
||||
(earliest_snapshot_iter_ != snapshots_->end() &&
|
||||
*earliest_snapshot_iter_ == earliest_snapshot_));
|
||||
assert(pre_condition);
|
||||
if (!pre_condition) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"Pre-Condition is not hold in IsInEarliestSnapshot");
|
||||
}
|
||||
auto in_snapshot =
|
||||
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
|
||||
while (UNLIKELY(in_snapshot == SnapshotCheckerResult::kSnapshotReleased)) {
|
||||
// Avoid the the current earliest_snapshot_ being return as
|
||||
// earliest visible snapshot for the next value. So if a value's sequence
|
||||
// is zero-ed out by PrepareOutput(), the next value will be compact out.
|
||||
released_snapshots_.insert(earliest_snapshot_);
|
||||
earliest_snapshot_iter_++;
|
||||
|
||||
if (earliest_snapshot_iter_ == snapshots_->end()) {
|
||||
earliest_snapshot_ = kMaxSequenceNumber;
|
||||
} else {
|
||||
earliest_snapshot_ = *earliest_snapshot_iter_;
|
||||
}
|
||||
in_snapshot =
|
||||
snapshot_checker_->CheckInSnapshot(sequence, earliest_snapshot_);
|
||||
}
|
||||
assert(in_snapshot != SnapshotCheckerResult::kSnapshotReleased);
|
||||
if (in_snapshot == SnapshotCheckerResult::kSnapshotReleased) {
|
||||
ROCKS_LOG_FATAL(info_log_,
|
||||
"Unexpected released snapshot in IsInEarliestSnapshot");
|
||||
}
|
||||
return in_snapshot == SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
uint64_t CompactionIterator::ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
const CompactionProxy* compaction) {
|
||||
if (!compaction) {
|
||||
@@ -1129,7 +1156,7 @@ uint64_t CompactionIterator::ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
return 0;
|
||||
}
|
||||
|
||||
Version* const version = compaction->input_version();
|
||||
const Version* const version = compaction->input_version();
|
||||
assert(version);
|
||||
|
||||
const VersionStorageInfo* const storage_info = version->storage_info();
|
||||
@@ -1145,4 +1172,42 @@ uint64_t CompactionIterator::ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
: std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
std::unique_ptr<BlobFetcher> CompactionIterator::CreateBlobFetcherIfNeeded(
|
||||
const CompactionProxy* compaction) {
|
||||
if (!compaction) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Version* const version = compaction->input_version();
|
||||
if (!version) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<BlobFetcher>(new BlobFetcher(version, ReadOptions()));
|
||||
}
|
||||
|
||||
std::unique_ptr<PrefetchBufferCollection>
|
||||
CompactionIterator::CreatePrefetchBufferCollectionIfNeeded(
|
||||
const CompactionProxy* compaction) {
|
||||
if (!compaction) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!compaction->input_version()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (compaction->allow_mmap_reads()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint64_t readahead_size = compaction->blob_compaction_readahead_size();
|
||||
if (!readahead_size) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::unique_ptr<PrefetchBufferCollection>(
|
||||
new PrefetchBufferCollection(readahead_size));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -23,6 +23,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class BlobFileBuilder;
|
||||
class BlobFetcher;
|
||||
class PrefetchBufferCollection;
|
||||
|
||||
// A wrapper of internal iterator whose purpose is to count how
|
||||
// many entries there are in the iterator.
|
||||
@@ -92,11 +94,15 @@ class CompactionIterator {
|
||||
|
||||
virtual bool preserve_deletes() const = 0;
|
||||
|
||||
virtual bool allow_mmap_reads() 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;
|
||||
virtual uint64_t blob_compaction_readahead_size() const = 0;
|
||||
|
||||
virtual const Version* input_version() const = 0;
|
||||
|
||||
virtual bool DoesInputReferenceBlobFiles() const = 0;
|
||||
|
||||
@@ -137,6 +143,10 @@ class CompactionIterator {
|
||||
return compaction_->immutable_options()->preserve_deletes;
|
||||
}
|
||||
|
||||
bool allow_mmap_reads() const override {
|
||||
return compaction_->immutable_options()->allow_mmap_reads;
|
||||
}
|
||||
|
||||
bool enable_blob_garbage_collection() const override {
|
||||
return compaction_->mutable_cf_options()->enable_blob_garbage_collection;
|
||||
}
|
||||
@@ -146,7 +156,11 @@ class CompactionIterator {
|
||||
->blob_garbage_collection_age_cutoff;
|
||||
}
|
||||
|
||||
Version* input_version() const override {
|
||||
uint64_t blob_compaction_readahead_size() const override {
|
||||
return compaction_->mutable_cf_options()->blob_compaction_readahead_size;
|
||||
}
|
||||
|
||||
const Version* input_version() const override {
|
||||
return compaction_->input_version();
|
||||
}
|
||||
|
||||
@@ -271,14 +285,10 @@ class CompactionIterator {
|
||||
SnapshotCheckerResult::kInSnapshot;
|
||||
}
|
||||
|
||||
bool IsInCurrentEarliestSnapshot(SequenceNumber sequence);
|
||||
|
||||
bool DefinitelyInSnapshot(SequenceNumber seq, SequenceNumber snapshot);
|
||||
|
||||
bool DefinitelyNotInSnapshot(SequenceNumber seq, SequenceNumber snapshot);
|
||||
|
||||
bool InCurrentEarliestSnapshot(SequenceNumber seq);
|
||||
|
||||
// Extract user-defined timestamp from user key if possible and compare it
|
||||
// with *full_history_ts_low_ if applicable.
|
||||
inline void UpdateTimestampAndCompareWithFullHistoryLow() {
|
||||
@@ -295,6 +305,10 @@ class CompactionIterator {
|
||||
|
||||
static uint64_t ComputeBlobGarbageCollectionCutoffFileNumber(
|
||||
const CompactionProxy* compaction);
|
||||
static std::unique_ptr<BlobFetcher> CreateBlobFetcherIfNeeded(
|
||||
const CompactionProxy* compaction);
|
||||
static std::unique_ptr<PrefetchBufferCollection>
|
||||
CreatePrefetchBufferCollectionIfNeeded(const CompactionProxy* compaction);
|
||||
|
||||
SequenceIterWrapper input_;
|
||||
const Comparator* cmp_;
|
||||
@@ -383,6 +397,9 @@ class CompactionIterator {
|
||||
|
||||
uint64_t blob_garbage_collection_cutoff_file_number_;
|
||||
|
||||
std::unique_ptr<BlobFetcher> blob_fetcher_;
|
||||
std::unique_ptr<PrefetchBufferCollection> prefetch_buffers_;
|
||||
|
||||
std::string blob_index_;
|
||||
PinnableSlice blob_value_;
|
||||
std::string compaction_filter_value_;
|
||||
@@ -405,6 +422,10 @@ class CompactionIterator {
|
||||
|
||||
const int level_;
|
||||
|
||||
// True if the previous internal key (same user key)'s sequence number has
|
||||
// just been zeroed out during bottommost compaction.
|
||||
bool last_key_seq_zeroed_{false};
|
||||
|
||||
void AdvanceInputIter() { input_.Next(); }
|
||||
|
||||
void SkipUntil(const Slice& skip_until) { input_.Seek(skip_until); }
|
||||
@@ -439,10 +460,4 @@ inline bool CompactionIterator::DefinitelyNotInSnapshot(
|
||||
SnapshotCheckerResult::kNotInSnapshot)));
|
||||
}
|
||||
|
||||
inline bool CompactionIterator::InCurrentEarliestSnapshot(SequenceNumber seq) {
|
||||
return ((seq) <= earliest_snapshot_ &&
|
||||
(snapshot_checker_ == nullptr ||
|
||||
LIKELY(IsInCurrentEarliestSnapshot(seq))));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -168,11 +168,15 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
|
||||
bool preserve_deletes() const override { return false; }
|
||||
|
||||
bool allow_mmap_reads() 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; }
|
||||
uint64_t blob_compaction_readahead_size() const override { return 0; }
|
||||
|
||||
const Version* input_version() const override { return nullptr; }
|
||||
|
||||
bool DoesInputReferenceBlobFiles() const override { return false; }
|
||||
|
||||
@@ -1171,9 +1175,10 @@ TEST_P(CompactionIteratorTsGcTest, NewHidesOldSameSnapshot) {
|
||||
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"};
|
||||
const std::vector<std::string> expected_keys = {
|
||||
input_keys[0], input_keys[1], input_keys[2]};
|
||||
const std::vector<std::string> expected_values = {"", input_values[1],
|
||||
input_values[2]};
|
||||
RunTest(input_keys, input_values, expected_keys, expected_values,
|
||||
/*last_committed_seq=*/kMaxSequenceNumber,
|
||||
/*merge_operator=*/nullptr, /*compaction_filter=*/nullptr,
|
||||
|
||||
@@ -170,6 +170,13 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
}
|
||||
|
||||
// Some identified files with old oldest ancester time and the range should be
|
||||
// isolated out so that the output file(s) in that range can be merged down
|
||||
// for TTL and clear the timestamps for the range.
|
||||
std::vector<FileMetaData*> files_to_cut_for_ttl;
|
||||
int cur_files_to_cut_for_ttl = -1;
|
||||
int next_files_to_cut_for_ttl = 0;
|
||||
|
||||
uint64_t current_output_file_size = 0;
|
||||
|
||||
// State during the subcompaction
|
||||
@@ -212,6 +219,8 @@ struct CompactionJob::SubcompactionState {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void FillFilesToCutForTtl();
|
||||
|
||||
// Returns true iff we should stop building the current output
|
||||
// before processing "internal_key".
|
||||
bool ShouldStopBefore(const Slice& internal_key, uint64_t curr_file_size) {
|
||||
@@ -244,6 +253,40 @@ struct CompactionJob::SubcompactionState {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!files_to_cut_for_ttl.empty()) {
|
||||
if (cur_files_to_cut_for_ttl != -1) {
|
||||
// Previous key is inside the range of a file
|
||||
if (icmp->Compare(internal_key,
|
||||
files_to_cut_for_ttl[cur_files_to_cut_for_ttl]
|
||||
->largest.Encode()) > 0) {
|
||||
next_files_to_cut_for_ttl = cur_files_to_cut_for_ttl + 1;
|
||||
cur_files_to_cut_for_ttl = -1;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Look for the key position
|
||||
while (next_files_to_cut_for_ttl <
|
||||
static_cast<int>(files_to_cut_for_ttl.size())) {
|
||||
if (icmp->Compare(internal_key,
|
||||
files_to_cut_for_ttl[next_files_to_cut_for_ttl]
|
||||
->smallest.Encode()) >= 0) {
|
||||
if (icmp->Compare(internal_key,
|
||||
files_to_cut_for_ttl[next_files_to_cut_for_ttl]
|
||||
->largest.Encode()) <= 0) {
|
||||
// With in the current file
|
||||
cur_files_to_cut_for_ttl = next_files_to_cut_for_ttl;
|
||||
return true;
|
||||
}
|
||||
// Beyond the current file
|
||||
next_files_to_cut_for_ttl++;
|
||||
} else {
|
||||
// Still fall into the gap
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -256,6 +299,46 @@ struct CompactionJob::SubcompactionState {
|
||||
}
|
||||
};
|
||||
|
||||
void CompactionJob::SubcompactionState::FillFilesToCutForTtl() {
|
||||
if (compaction->immutable_options()->compaction_style !=
|
||||
CompactionStyle::kCompactionStyleLevel ||
|
||||
compaction->immutable_options()->compaction_pri !=
|
||||
CompactionPri::kMinOverlappingRatio ||
|
||||
compaction->mutable_cf_options()->ttl == 0 ||
|
||||
compaction->num_input_levels() < 2 || compaction->bottommost_level()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We define new file with oldest ancestor time to be younger than 1/4 TTL,
|
||||
// and an old one to be older than 1/2 TTL time.
|
||||
int64_t temp_current_time;
|
||||
auto get_time_status = compaction->immutable_options()->clock->GetCurrentTime(
|
||||
&temp_current_time);
|
||||
if (!get_time_status.ok()) {
|
||||
return;
|
||||
}
|
||||
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
|
||||
if (current_time < compaction->mutable_cf_options()->ttl) {
|
||||
return;
|
||||
}
|
||||
uint64_t old_age_thres =
|
||||
current_time - compaction->mutable_cf_options()->ttl / 2;
|
||||
|
||||
const std::vector<FileMetaData*>& olevel =
|
||||
*(compaction->inputs(compaction->num_input_levels() - 1));
|
||||
for (FileMetaData* file : olevel) {
|
||||
// Worth filtering out by start and end?
|
||||
uint64_t oldest_ancester_time = file->TryGetOldestAncesterTime();
|
||||
// We put old files if they are not too small to prevent a flood
|
||||
// of small files.
|
||||
if (oldest_ancester_time < old_age_thres &&
|
||||
file->fd.GetFileSize() >
|
||||
compaction->mutable_cf_options()->target_file_size_base / 2) {
|
||||
files_to_cut_for_ttl.push_back(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maintains state for the entire compaction
|
||||
struct CompactionJob::CompactionState {
|
||||
Compaction* const compaction;
|
||||
@@ -680,12 +763,16 @@ Status CompactionJob::Run() {
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
|
||||
if (output_directory_) {
|
||||
io_s = output_directory_->Fsync(IOOptions(), dbg);
|
||||
io_s = output_directory_->FsyncWithDirOptions(
|
||||
IOOptions(), dbg,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
|
||||
if (io_s.ok() && wrote_new_blob_files && blob_output_directory_ &&
|
||||
blob_output_directory_ != output_directory_) {
|
||||
io_s = blob_output_directory_->Fsync(IOOptions(), dbg);
|
||||
io_s = blob_output_directory_->FsyncWithDirOptions(
|
||||
IOOptions(), dbg,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
}
|
||||
if (io_status_.ok()) {
|
||||
@@ -1291,6 +1378,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
sub_compact->FillFilesToCutForTtl();
|
||||
// ShouldStopBefore() maintains state based on keys processed so far. The
|
||||
// compaction loop always calls it on the "next" key, thus won't tell it the
|
||||
// first key. So we do that here.
|
||||
@@ -1739,7 +1827,6 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
meta->UpdateBoundariesForRange(smallest_candidate, largest_candidate,
|
||||
tombstone.seq_,
|
||||
cfd->internal_comparator());
|
||||
|
||||
// The smallest key in a file is used for range tombstone truncation, so
|
||||
// it cannot have a seqnum of 0 (unless the smallest data key in a file
|
||||
// has a seqnum of 0). Otherwise, the truncated tombstone may expose
|
||||
@@ -1763,6 +1850,23 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
if (s.ok()) {
|
||||
meta->fd.file_size = current_bytes;
|
||||
meta->marked_for_compaction = sub_compact->builder->NeedCompact();
|
||||
// With accurate smallest and largest key, we can get a slightly more
|
||||
// accurate oldest ancester time.
|
||||
// This makes oldest ancester time in manifest more accurate than in
|
||||
// table properties. Not sure how to resolve it.
|
||||
if (meta->smallest.size() > 0 && meta->largest.size() > 0) {
|
||||
uint64_t refined_oldest_ancester_time;
|
||||
Slice new_smallest = meta->smallest.user_key();
|
||||
Slice new_largest = meta->largest.user_key();
|
||||
if (!new_largest.empty() && !new_smallest.empty()) {
|
||||
refined_oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
&(meta->smallest), &(meta->largest));
|
||||
if (refined_oldest_ancester_time != port::kMaxUint64) {
|
||||
meta->oldest_ancester_time = refined_oldest_ancester_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sub_compact->current_output()->finished = true;
|
||||
sub_compact->total_bytes += current_bytes;
|
||||
@@ -2033,8 +2137,17 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
get_time_status.ToString().c_str());
|
||||
}
|
||||
uint64_t current_time = static_cast<uint64_t>(temp_current_time);
|
||||
InternalKey tmp_start, tmp_end;
|
||||
if (sub_compact->start != nullptr) {
|
||||
tmp_start.SetMinPossibleForUserKey(*(sub_compact->start));
|
||||
}
|
||||
if (sub_compact->end != nullptr) {
|
||||
tmp_end.SetMinPossibleForUserKey(*(sub_compact->end));
|
||||
}
|
||||
uint64_t oldest_ancester_time =
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime();
|
||||
sub_compact->compaction->MinInputFileOldestAncesterTime(
|
||||
(sub_compact->start != nullptr) ? &tmp_start : nullptr,
|
||||
(sub_compact->end != nullptr) ? &tmp_end : nullptr);
|
||||
if (oldest_ancester_time == port::kMaxUint64) {
|
||||
oldest_ancester_time = current_time;
|
||||
}
|
||||
@@ -2333,7 +2446,8 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
|
||||
if (output_directory_) {
|
||||
io_s = output_directory_->Fsync(IOOptions(), dbg);
|
||||
io_s = output_directory_->FsyncWithDirOptions(IOOptions(), dbg,
|
||||
DirFsyncOptions());
|
||||
}
|
||||
}
|
||||
if (io_status_.ok()) {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "db/version_set.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/file_system.h"
|
||||
#include "rocksdb/options.h"
|
||||
@@ -73,9 +74,7 @@ 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_(env_->GetFileSystem()),
|
||||
dbname_(std::move(dbname)),
|
||||
: dbname_(std::move(dbname)),
|
||||
ucmp_(ucmp),
|
||||
db_options_(),
|
||||
mutable_cf_options_(cf_options_),
|
||||
@@ -91,7 +90,13 @@ class CompactionJobTestBase : public testing::Test {
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
error_handler_(nullptr, db_options_, &mutex_),
|
||||
encode_u64_ts_(std::move(encode_u64_ts)) {}
|
||||
encode_u64_ts_(std::move(encode_u64_ts)) {
|
||||
Env* base_env = Env::Default();
|
||||
EXPECT_OK(
|
||||
test::CreateEnvFromSystem(ConfigOptions(), &base_env, &env_guard_));
|
||||
env_ = base_env;
|
||||
fs_ = env_->GetFileSystem();
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
EXPECT_OK(env_->CreateDirIfMissing(dbname_));
|
||||
@@ -200,7 +205,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
edit.AddFile(level, file_number, 0, 10, smallest_key, largest_key,
|
||||
smallest_seqno, largest_seqno, false, oldest_blob_file_number,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kDisableUserTimestamp, kDisableUserTimestamp);
|
||||
|
||||
mutex_.Lock();
|
||||
EXPECT_OK(
|
||||
@@ -386,6 +392,7 @@ class CompactionJobTestBase : public testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
Env* env_;
|
||||
std::shared_ptr<FileSystem> fs_;
|
||||
std::string dbname_;
|
||||
@@ -1392,6 +1399,7 @@ TEST_F(CompactionJobTimestampTest, SomeKeysExpired) {
|
||||
|
||||
auto expected_results =
|
||||
mock::MakeMockFile({{KeyStr("a", 5, ValueType::kTypeValue, 50), "a5"},
|
||||
{KeyStr("a", 0, ValueType::kTypeValue, 0), "a3"},
|
||||
{KeyStr("b", 6, ValueType::kTypeValue, 49), "b6"}});
|
||||
const auto& files = cfd_->current()->storage_info()->LevelFiles(0);
|
||||
|
||||
@@ -1403,6 +1411,7 @@ TEST_F(CompactionJobTimestampTest, SomeKeysExpired) {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "db/compaction/compaction.h"
|
||||
#include "db/compaction/compaction_picker_fifo.h"
|
||||
#include "db/compaction/compaction_picker_level.h"
|
||||
#include "db/compaction/compaction_picker_universal.h"
|
||||
|
||||
#include "db/compaction/file_pri.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -114,7 +114,8 @@ class CompactionPickerTest : public testing::Test {
|
||||
InternalKey(largest, largest_seq, kTypeValue), smallest_seq,
|
||||
largest_seq, marked_for_compact, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName,
|
||||
kDisableUserTimestamp, kDisableUserTimestamp);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
f->temperature = temperature;
|
||||
@@ -149,7 +150,7 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_ = std::move(temp_vstorage_);
|
||||
}
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
vstorage_->GenerateFileIndexer();
|
||||
vstorage_->GenerateLevelFilesBrief();
|
||||
@@ -1673,6 +1674,66 @@ TEST_F(CompactionPickerTest, OverlappingUserKeys11) {
|
||||
ASSERT_EQ(7U, compaction->input(1, 0)->fd.GetNumber());
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, FileTtlBooster) {
|
||||
// Set TTL to 2048
|
||||
// TTL boosting for all levels starts at 1024,
|
||||
// Whole TTL range is 2048 * 31 / 32 - 1024 = 1984 - 1024 = 960.
|
||||
// From second last level (L5), range starts at
|
||||
// 1024 + 480, 1024 + 240, 1024 + 120 (which is L3).
|
||||
// Boosting step 124 / 16 = 7.75 -> 7
|
||||
//
|
||||
const uint64_t kCurrentTime = 1000000;
|
||||
FileMetaData meta;
|
||||
|
||||
{
|
||||
FileTtlBooster booster(kCurrentTime, 2048, 7, 3);
|
||||
|
||||
// Not triggering if the file is younger than ttl/2
|
||||
meta.oldest_ancester_time = kCurrentTime - 1023;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancester_time = kCurrentTime - 1024;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancester_time = kCurrentTime + 10;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
|
||||
// Within one boosting step
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 6);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
|
||||
// One boosting step
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 7);
|
||||
ASSERT_EQ(2, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 8);
|
||||
ASSERT_EQ(2, booster.GetBoostScore(&meta));
|
||||
|
||||
// Multiple boosting steps
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 30);
|
||||
ASSERT_EQ(5, booster.GetBoostScore(&meta));
|
||||
|
||||
// Very high boosting steps
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 120 + 700);
|
||||
ASSERT_EQ(101, booster.GetBoostScore(&meta));
|
||||
}
|
||||
{
|
||||
// Test second last level
|
||||
FileTtlBooster booster(kCurrentTime, 2048, 7, 5);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
|
||||
ASSERT_EQ(3, booster.GetBoostScore(&meta));
|
||||
}
|
||||
{
|
||||
// Test last level
|
||||
FileTtlBooster booster(kCurrentTime, 2048, 7, 6);
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancester_time = kCurrentTime - (1024 + 480 + 60);
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
meta.oldest_ancester_time = kCurrentTime - 3000;
|
||||
ASSERT_EQ(1, booster.GetBoostScore(&meta));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, NotScheduleL1IfL0WithHigherPri1) {
|
||||
NewVersionStorage(6, kCompactionStyleLevel);
|
||||
mutable_cf_options_.level0_file_num_compaction_trigger = 2;
|
||||
|
||||
@@ -806,14 +806,6 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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 <algorithm>
|
||||
|
||||
#include "db/version_edit.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// We boost files that are closer to TTL limit. This boosting could be
|
||||
// through FileMetaData.compensated_file_size but this compensated size
|
||||
// is widely used as something similar to file size so dramatically boost
|
||||
// the value might cause unintended consequences.
|
||||
//
|
||||
// This boosting algorithm can go very fancy, but here we use a simple
|
||||
// formula which can satisify:
|
||||
// (1) Different levels are triggered slightly differently to avoid
|
||||
// too many cascading cases
|
||||
// (2) Files in the same level get boosting more when TTL gets closer.
|
||||
//
|
||||
// Don't do any boosting before TTL has past by half. This is to make
|
||||
// sure lower write amp for most of the case. And all levels should be
|
||||
// fully boosted when total TTL compaction threshold triggers.
|
||||
// Differientiate boosting ranges of each level by 1/2. This will make
|
||||
// range for each level exponentially increasing. We could do it by
|
||||
// having them to be equal, or go even fancier. We can adjust it after
|
||||
// we observe the behavior in production.
|
||||
// The threshold starting boosting:
|
||||
// +------------------------------------------------------------------ +
|
||||
// ^ ^ ^ ^ ^ ^
|
||||
// Age 0 ... | | second last level thresold
|
||||
// | |
|
||||
// | third last level
|
||||
// |
|
||||
// forth last level
|
||||
//
|
||||
// We arbitrarily set with 0 when a file is aged boost_age_start and
|
||||
// grow linearly. The ratio is arbitrarily set so that when the next level
|
||||
// starts to boost, the previous level's boosting amount is 16.
|
||||
class FileTtlBooster {
|
||||
public:
|
||||
FileTtlBooster(uint64_t current_time, uint64_t ttl, int num_non_empty_levels,
|
||||
int level)
|
||||
: current_time_(current_time) {
|
||||
if (ttl == 0 || level == 0 || level >= num_non_empty_levels - 1) {
|
||||
enabled_ = false;
|
||||
boost_age_start_ = 0;
|
||||
boost_step_ = 1;
|
||||
} else {
|
||||
enabled_ = true;
|
||||
uint64_t all_boost_start_age = ttl / 2;
|
||||
uint64_t all_boost_age_range = (ttl / 32) * 31 - all_boost_start_age;
|
||||
uint64_t boost_age_range =
|
||||
all_boost_age_range >> (num_non_empty_levels - level - 1);
|
||||
boost_age_start_ = all_boost_start_age + boost_age_range;
|
||||
const uint64_t kBoostRatio = 16;
|
||||
// prevent 0 value to avoid divide 0 error.
|
||||
boost_step_ = std::max(boost_age_range / kBoostRatio, uint64_t{1});
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t GetBoostScore(FileMetaData* f) {
|
||||
if (!enabled_) {
|
||||
return 1;
|
||||
}
|
||||
uint64_t oldest_ancester_time = f->TryGetOldestAncesterTime();
|
||||
if (oldest_ancester_time >= current_time_) {
|
||||
return 1;
|
||||
}
|
||||
uint64_t age = current_time_ - oldest_ancester_time;
|
||||
if (age > boost_age_start_) {
|
||||
// Use integer just for convenience.
|
||||
// We could make all file_to_order double if we want.
|
||||
// Technically this can overflow if users override timing and
|
||||
// give a very high current time. Ignore the case for simplicity.
|
||||
// Boosting is addition to current value, so +1. This will effectively
|
||||
// make boosting to kick in after the first boost_step_ is reached.
|
||||
return (age - boost_age_start_) / boost_step_ + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private:
|
||||
bool enabled_;
|
||||
uint64_t current_time_;
|
||||
uint64_t boost_age_start_;
|
||||
uint64_t boost_step_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -544,7 +544,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
fs->GetFileSize(filename, file_opts.io_options, &file_size, nullptr));
|
||||
|
||||
BlockHandle range_del_handle;
|
||||
ASSERT_OK(FindMetaBlock(
|
||||
ASSERT_OK(FindMetaBlockInFile(
|
||||
file_reader.get(), file_size, kBlockBasedTableMagicNumber,
|
||||
ImmutableOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
@@ -890,14 +890,6 @@ TEST_F(CorruptionTest, VerifyWholeTableChecksum) {
|
||||
|
||||
} // 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);
|
||||
|
||||
+17
-15
@@ -10,6 +10,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/flush_block_policy.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
@@ -974,13 +975,14 @@ TEST_F(DBBasicTest, MultiGetEmpty) {
|
||||
TEST_F(DBBasicTest, ChecksumTest) {
|
||||
BlockBasedTableOptions table_options;
|
||||
Options options = CurrentOptions();
|
||||
// change when new checksum type added
|
||||
int max_checksum = static_cast<int>(kxxHash64);
|
||||
const int kNumPerFile = 2;
|
||||
|
||||
const auto algs = GetSupportedChecksums();
|
||||
const int algs_size = static_cast<int>(algs.size());
|
||||
|
||||
// generate one table with each type of checksum
|
||||
for (int i = 0; i <= max_checksum; ++i) {
|
||||
table_options.checksum = static_cast<ChecksumType>(i);
|
||||
for (int i = 0; i < algs_size; ++i) {
|
||||
table_options.checksum = algs[i];
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
for (int j = 0; j < kNumPerFile; ++j) {
|
||||
@@ -990,15 +992,20 @@ TEST_F(DBBasicTest, ChecksumTest) {
|
||||
}
|
||||
|
||||
// with each valid checksum type setting...
|
||||
for (int i = 0; i <= max_checksum; ++i) {
|
||||
table_options.checksum = static_cast<ChecksumType>(i);
|
||||
for (int i = 0; i < algs_size; ++i) {
|
||||
table_options.checksum = algs[i];
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
// verify every type of checksum (should be regardless of that setting)
|
||||
for (int j = 0; j < (max_checksum + 1) * kNumPerFile; ++j) {
|
||||
for (int j = 0; j < algs_size * kNumPerFile; ++j) {
|
||||
ASSERT_EQ(Key(j), Get(Key(j)));
|
||||
}
|
||||
}
|
||||
|
||||
// Now test invalid checksum type
|
||||
table_options.checksum = static_cast<ChecksumType>(123);
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_TRUE(TryReopen(options).IsInvalidArgument());
|
||||
}
|
||||
|
||||
// On Windows you can have either memory mapped file or a file
|
||||
@@ -3289,6 +3296,9 @@ class DeadlineFS : public FileSystemWrapper {
|
||||
ignore_deadline_(false),
|
||||
error_on_delay_(error_on_delay) {}
|
||||
|
||||
static const char* kClassName() { return "DeadlineFileSystem"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
IOStatus NewRandomAccessFile(const std::string& fname,
|
||||
const FileOptions& opts,
|
||||
std::unique_ptr<FSRandomAccessFile>* result,
|
||||
@@ -3850,14 +3860,6 @@ INSTANTIATE_TEST_CASE_P(DBBasicTestDeadline, DBBasicTestDeadline,
|
||||
std::make_tuple(true, true)));
|
||||
} // 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);
|
||||
|
||||
+185
-11
@@ -336,6 +336,143 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
CheckCacheCounters(options, 1, 0, 1, 0);
|
||||
CheckCompressedCacheCounters(options, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class PersistentCacheFromCache : public PersistentCache {
|
||||
public:
|
||||
PersistentCacheFromCache(std::shared_ptr<Cache> cache, bool read_only)
|
||||
: cache_(cache), read_only_(read_only) {}
|
||||
|
||||
Status Insert(const Slice& key, const char* data,
|
||||
const size_t size) override {
|
||||
if (read_only_) {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
std::unique_ptr<char[]> copy{new char[size]};
|
||||
std::copy_n(data, size, copy.get());
|
||||
Status s = cache_->Insert(
|
||||
key, copy.get(), size,
|
||||
GetCacheEntryDeleterForRole<char[], CacheEntryRole::kMisc>());
|
||||
if (s.ok()) {
|
||||
copy.release();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status Lookup(const Slice& key, std::unique_ptr<char[]>* data,
|
||||
size_t* size) override {
|
||||
auto handle = cache_->Lookup(key);
|
||||
if (handle) {
|
||||
char* ptr = static_cast<char*>(cache_->Value(handle));
|
||||
*size = cache_->GetCharge(handle);
|
||||
data->reset(new char[*size]);
|
||||
std::copy_n(ptr, *size, data->get());
|
||||
cache_->Release(handle);
|
||||
return Status::OK();
|
||||
} else {
|
||||
return Status::NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsCompressed() override { return false; }
|
||||
|
||||
StatsType Stats() override { return StatsType(); }
|
||||
|
||||
std::string GetPrintableOptions() const override { return ""; }
|
||||
|
||||
uint64_t NewId() override { return cache_->NewId(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Cache> cache_;
|
||||
bool read_only_;
|
||||
};
|
||||
|
||||
class ReadOnlyCacheWrapper : public CacheWrapper {
|
||||
using CacheWrapper::CacheWrapper;
|
||||
|
||||
using Cache::Insert;
|
||||
Status Insert(const Slice& /*key*/, void* /*value*/, size_t /*charge*/,
|
||||
void (*)(const Slice& key, void* value) /*deleter*/,
|
||||
Handle** /*handle*/, Priority /*priority*/) override {
|
||||
return Status::NotSupported();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(DBBlockCacheTest, TestWithSameCompressed) {
|
||||
auto table_options = GetTableOptions();
|
||||
auto options = GetOptions(table_options);
|
||||
InitTable(options);
|
||||
|
||||
std::shared_ptr<Cache> rw_cache{NewLRUCache(1000000)};
|
||||
std::shared_ptr<PersistentCacheFromCache> rw_pcache{
|
||||
new PersistentCacheFromCache(rw_cache, /*read_only*/ false)};
|
||||
// Exercise some obscure behavior with read-only wrappers
|
||||
std::shared_ptr<Cache> ro_cache{new ReadOnlyCacheWrapper(rw_cache)};
|
||||
std::shared_ptr<PersistentCacheFromCache> ro_pcache{
|
||||
new PersistentCacheFromCache(rw_cache, /*read_only*/ true)};
|
||||
|
||||
// Simple same pointer
|
||||
table_options.block_cache = rw_cache;
|
||||
table_options.block_cache_compressed = rw_cache;
|
||||
table_options.persistent_cache.reset();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: block_cache same as block_cache_compressed not "
|
||||
"currently supported, and would be bad for performance anyway");
|
||||
|
||||
// Other cases
|
||||
table_options.block_cache = ro_cache;
|
||||
table_options.block_cache_compressed = rw_cache;
|
||||
table_options.persistent_cache.reset();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: block_cache and block_cache_compressed share "
|
||||
"the same key space, which is not supported");
|
||||
|
||||
table_options.block_cache = rw_cache;
|
||||
table_options.block_cache_compressed = ro_cache;
|
||||
table_options.persistent_cache.reset();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: block_cache_compressed and block_cache share "
|
||||
"the same key space, which is not supported");
|
||||
|
||||
table_options.block_cache = ro_cache;
|
||||
table_options.block_cache_compressed.reset();
|
||||
table_options.persistent_cache = rw_pcache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: block_cache and persistent_cache share the same "
|
||||
"key space, which is not supported");
|
||||
|
||||
table_options.block_cache = rw_cache;
|
||||
table_options.block_cache_compressed.reset();
|
||||
table_options.persistent_cache = ro_pcache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: persistent_cache and block_cache share the same "
|
||||
"key space, which is not supported");
|
||||
|
||||
table_options.block_cache.reset();
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_cache_compressed = ro_cache;
|
||||
table_options.persistent_cache = rw_pcache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: block_cache_compressed and persistent_cache "
|
||||
"share the same key space, which is not supported");
|
||||
|
||||
table_options.block_cache.reset();
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_cache_compressed = rw_cache;
|
||||
table_options.persistent_cache = ro_pcache;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
ASSERT_EQ(TryReopen(options).ToString(),
|
||||
"Invalid argument: persistent_cache and block_cache_compressed "
|
||||
"share the same key space, which is not supported");
|
||||
}
|
||||
#endif // SNAPPY
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
@@ -513,13 +650,32 @@ TEST_F(DBBlockCacheTest, WarmCacheWithDataBlocksDuringFlush) {
|
||||
}
|
||||
|
||||
// This test cache data, index and filter blocks during flush.
|
||||
TEST_F(DBBlockCacheTest, WarmCacheWithBlocksDuringFlush) {
|
||||
class DBBlockCacheTest1 : public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
public:
|
||||
const size_t kNumBlocks = 10;
|
||||
const size_t kValueSize = 100;
|
||||
DBBlockCacheTest1() : DBTestBase("db_block_cache_test1", true) {}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBBlockCacheTest1, DBBlockCacheTest1,
|
||||
::testing::Bool());
|
||||
|
||||
TEST_P(DBBlockCacheTest1, WarmCacheWithBlocksDuringFlush) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewLRUCache(1 << 25, 0, false);
|
||||
|
||||
bool use_partition = GetParam();
|
||||
if (use_partition) {
|
||||
table_options.partition_filters = true;
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
}
|
||||
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.prepopulate_block_cache =
|
||||
BlockBasedTableOptions::PrepopulateBlockCache::kFlushOnly;
|
||||
@@ -532,9 +688,15 @@ TEST_F(DBBlockCacheTest, WarmCacheWithBlocksDuringFlush) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
|
||||
if (use_partition) {
|
||||
ASSERT_EQ(2 * i,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(2 * i,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
} else {
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
}
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
@@ -542,11 +704,16 @@ TEST_F(DBBlockCacheTest, WarmCacheWithBlocksDuringFlush) {
|
||||
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_MISS));
|
||||
ASSERT_EQ(i * 3, options.statistics->getTickerCount(BLOCK_CACHE_INDEX_HIT));
|
||||
|
||||
if (use_partition) {
|
||||
ASSERT_EQ(i * 3,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(i * 2,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT));
|
||||
}
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_FILTER_MISS));
|
||||
ASSERT_EQ(i * 2,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_HIT));
|
||||
}
|
||||
|
||||
// Verify compaction not counted
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
@@ -555,10 +722,17 @@ TEST_F(DBBlockCacheTest, WarmCacheWithBlocksDuringFlush) {
|
||||
// Index and filter blocks are automatically warmed when the new table file
|
||||
// is automatically opened at the end of compaction. This is not easily
|
||||
// disabled so results in the new index and filter blocks being warmed.
|
||||
EXPECT_EQ(1 + kNumBlocks,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(1 + kNumBlocks,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
if (use_partition) {
|
||||
EXPECT_EQ(2 * (1 + kNumBlocks),
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(2 * (1 + kNumBlocks),
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
} else {
|
||||
EXPECT_EQ(1 + kNumBlocks,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_INDEX_ADD));
|
||||
EXPECT_EQ(1 + kNumBlocks,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_FILTER_ADD));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlockCacheTest, DynamicallyWarmCacheDuringFlush) {
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
#include "cache/cache_entry_roles.h"
|
||||
#include "cache/cache_reservation_manager.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "table/block_based/filter_policy_internal.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -667,6 +670,439 @@ TEST_F(DBBloomFilterTest, BloomFilterReverseCompatibility) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A cache wrapper that tracks peaks and increments of filter
|
||||
* construction cache reservation.
|
||||
* p0
|
||||
* / \ p1
|
||||
* / \ /\
|
||||
* / \/ \
|
||||
* a / b \
|
||||
* peaks = {p0, p1}
|
||||
* increments = {p1-a, p2-b}
|
||||
*/
|
||||
class FilterConstructResPeakTrackingCache : public CacheWrapper {
|
||||
public:
|
||||
explicit FilterConstructResPeakTrackingCache(std::shared_ptr<Cache> target)
|
||||
: CacheWrapper(std::move(target)),
|
||||
cur_cache_res_(0),
|
||||
cache_res_peak_(0),
|
||||
cache_res_increment_(0),
|
||||
last_peak_tracked_(false),
|
||||
cache_res_increments_sum_(0) {}
|
||||
|
||||
using Cache::Insert;
|
||||
Status Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value),
|
||||
Handle** handle = nullptr,
|
||||
Priority priority = Priority::LOW) override {
|
||||
Status s = target_->Insert(key, value, charge, deleter, handle, priority);
|
||||
if (deleter == kNoopDeleterForFilterConstruction) {
|
||||
if (last_peak_tracked_) {
|
||||
cache_res_peak_ = 0;
|
||||
cache_res_increment_ = 0;
|
||||
last_peak_tracked_ = false;
|
||||
}
|
||||
cur_cache_res_ += charge;
|
||||
cache_res_peak_ = std::max(cache_res_peak_, cur_cache_res_);
|
||||
cache_res_increment_ += charge;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
using Cache::Release;
|
||||
bool Release(Handle* handle, bool force_erase = false) override {
|
||||
auto deleter = GetDeleter(handle);
|
||||
if (deleter == kNoopDeleterForFilterConstruction) {
|
||||
if (!last_peak_tracked_) {
|
||||
cache_res_peaks_.push_back(cache_res_peak_);
|
||||
cache_res_increments_sum_ += cache_res_increment_;
|
||||
last_peak_tracked_ = true;
|
||||
}
|
||||
cur_cache_res_ -= GetCharge(handle);
|
||||
}
|
||||
bool is_successful = target_->Release(handle, force_erase);
|
||||
return is_successful;
|
||||
}
|
||||
|
||||
std::deque<std::size_t> GetReservedCachePeaks() { return cache_res_peaks_; }
|
||||
|
||||
std::size_t GetReservedCacheIncrementSum() {
|
||||
return cache_res_increments_sum_;
|
||||
}
|
||||
|
||||
private:
|
||||
static const Cache::DeleterFn kNoopDeleterForFilterConstruction;
|
||||
|
||||
std::size_t cur_cache_res_;
|
||||
std::size_t cache_res_peak_;
|
||||
std::size_t cache_res_increment_;
|
||||
bool last_peak_tracked_;
|
||||
std::deque<std::size_t> cache_res_peaks_;
|
||||
std::size_t cache_res_increments_sum_;
|
||||
};
|
||||
|
||||
const Cache::DeleterFn
|
||||
FilterConstructResPeakTrackingCache::kNoopDeleterForFilterConstruction =
|
||||
CacheReservationManager::TEST_GetNoopDeleterForRole<
|
||||
CacheEntryRole::kFilterConstruction>();
|
||||
|
||||
// To align with the type of hash entry being reserved in implementation.
|
||||
using FilterConstructionReserveMemoryHash = uint64_t;
|
||||
|
||||
class DBFilterConstructionReserveMemoryTestWithParam
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<bool, BloomFilterPolicy::Mode, bool>> {
|
||||
public:
|
||||
DBFilterConstructionReserveMemoryTestWithParam()
|
||||
: DBTestBase("db_bloom_filter_tests",
|
||||
/*env_do_fsync=*/true),
|
||||
num_key_(0),
|
||||
reserve_table_builder_memory_(std::get<0>(GetParam())),
|
||||
policy_(std::get<1>(GetParam())),
|
||||
partition_filters_(std::get<2>(GetParam())) {
|
||||
if (!reserve_table_builder_memory_ ||
|
||||
policy_ == BloomFilterPolicy::Mode::kDeprecatedBlock ||
|
||||
policy_ == BloomFilterPolicy::Mode::kLegacyBloom) {
|
||||
// For these cases, we only interested in whether filter construction
|
||||
// cache resevation happens instead of its accuracy. Therefore we don't
|
||||
// need many keys.
|
||||
num_key_ = 5;
|
||||
} else if (partition_filters_) {
|
||||
// For PartitionFilter case, since we set
|
||||
// table_options.metadata_block_size big enough such that each partition
|
||||
// trigger at least 1 dummy entry reservation each for hash entries and
|
||||
// final filter, we need a large number of keys to ensure we have at least
|
||||
// two partitions.
|
||||
num_key_ = 18 * CacheReservationManager::GetDummyEntrySize() /
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
} else if (policy_ == BloomFilterPolicy::Mode::kFastLocalBloom) {
|
||||
// For Bloom Filter + FullFilter case, since we design the num_key_ to
|
||||
// make hash entry cache reservation be a multiple of dummy entries, the
|
||||
// correct behavior of charging final filter on top of it will trigger at
|
||||
// least another dummy entry insertion. Therefore we can assert that
|
||||
// behavior and we don't need a large number of keys to verify we
|
||||
// indeed charge the final filter for cache reservation, even though final
|
||||
// filter is a lot smaller than hash entries.
|
||||
num_key_ = 1 * CacheReservationManager::GetDummyEntrySize() /
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
} else {
|
||||
// For Ribbon Filter + FullFilter case, we need a large enough number of
|
||||
// keys so that charging final filter after releasing the hash entries
|
||||
// reservation will trigger at least another dummy entry (or equivalently
|
||||
// to saying, causing another peak in cache reservation) as banding
|
||||
// reservation might not be a multiple of dummy entry.
|
||||
num_key_ = 12 * CacheReservationManager::GetDummyEntrySize() /
|
||||
sizeof(FilterConstructionReserveMemoryHash);
|
||||
}
|
||||
}
|
||||
|
||||
BlockBasedTableOptions GetBlockBasedTableOptions() {
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
// We set cache capacity big enough to prevent cache full for convenience in
|
||||
// calculation.
|
||||
constexpr std::size_t kCacheCapacity = 100 * 1024 * 1024;
|
||||
|
||||
table_options.reserve_table_builder_memory = reserve_table_builder_memory_;
|
||||
table_options.filter_policy.reset(new BloomFilterPolicy(10, policy_));
|
||||
table_options.partition_filters = partition_filters_;
|
||||
if (table_options.partition_filters) {
|
||||
table_options.index_type =
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
// We set table_options.metadata_block_size big enough so that each
|
||||
// partition trigger at least 1 dummy entry insertion each for hash
|
||||
// entries and final filter.
|
||||
table_options.metadata_block_size = 409000;
|
||||
}
|
||||
|
||||
LRUCacheOptions lo;
|
||||
lo.capacity = kCacheCapacity;
|
||||
lo.num_shard_bits = 0; // 2^0 shard
|
||||
lo.strict_capacity_limit = true;
|
||||
cache_ = std::make_shared<FilterConstructResPeakTrackingCache>(
|
||||
(NewLRUCache(lo)));
|
||||
table_options.block_cache = cache_;
|
||||
|
||||
return table_options;
|
||||
}
|
||||
|
||||
std::size_t GetNumKey() { return num_key_; }
|
||||
|
||||
bool ReserveTableBuilderMemory() { return reserve_table_builder_memory_; }
|
||||
|
||||
BloomFilterPolicy::Mode GetFilterPolicy() { return policy_; }
|
||||
|
||||
bool PartitionFilters() { return partition_filters_; }
|
||||
|
||||
std::shared_ptr<FilterConstructResPeakTrackingCache>
|
||||
GetFilterConstructResPeakTrackingCache() {
|
||||
return cache_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t num_key_;
|
||||
bool reserve_table_builder_memory_;
|
||||
BloomFilterPolicy::Mode policy_;
|
||||
bool partition_filters_;
|
||||
std::shared_ptr<FilterConstructResPeakTrackingCache> cache_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
BlockBasedTableOptions, DBFilterConstructionReserveMemoryTestWithParam,
|
||||
::testing::Values(
|
||||
std::make_tuple(false, BloomFilterPolicy::Mode::kFastLocalBloom, false),
|
||||
std::make_tuple(true, BloomFilterPolicy::Mode::kFastLocalBloom, false),
|
||||
std::make_tuple(true, BloomFilterPolicy::Mode::kFastLocalBloom, true),
|
||||
std::make_tuple(true, BloomFilterPolicy::Mode::kStandard128Ribbon,
|
||||
false),
|
||||
std::make_tuple(true, BloomFilterPolicy::Mode::kStandard128Ribbon,
|
||||
true),
|
||||
std::make_tuple(true, BloomFilterPolicy::Mode::kDeprecatedBlock, false),
|
||||
std::make_tuple(true, BloomFilterPolicy::Mode::kLegacyBloom, false)));
|
||||
|
||||
// TODO: Speed up this test.
|
||||
// The current test inserts many keys (on the scale of dummy entry size)
|
||||
// in order to make small memory user (e.g, final filter, partitioned hash
|
||||
// entries/filter/banding) , which is proportional to the number of
|
||||
// keys, big enough so that its cache reservation triggers dummy entry insertion
|
||||
// and becomes observable in the test.
|
||||
//
|
||||
// However, inserting that many keys slows down this test and leaves future
|
||||
// developers an opportunity to speed it up.
|
||||
//
|
||||
// Possible approaches & challenges:
|
||||
// 1. Use sync point during cache reservation of filter construction
|
||||
//
|
||||
// Benefit: It does not rely on triggering dummy entry insertion
|
||||
// but the sync point to verify small memory user is charged correctly.
|
||||
//
|
||||
// Challenge: this approach is intrusive.
|
||||
//
|
||||
// 2. Make dummy entry size configurable and set it small in the test
|
||||
//
|
||||
// Benefit: It increases the precision of cache reservation and therefore
|
||||
// small memory usage can still trigger insertion of dummy entry.
|
||||
//
|
||||
// Challenge: change CacheReservationManager related APIs and a hack
|
||||
// might be needed to control the size of dummmy entry of
|
||||
// CacheReservationManager used in filter construction for testing
|
||||
// since CacheReservationManager is not exposed at the high level.
|
||||
//
|
||||
TEST_P(DBFilterConstructionReserveMemoryTestWithParam, ReserveMemory) {
|
||||
Options options = CurrentOptions();
|
||||
// We set write_buffer_size big enough so that in the case where there is
|
||||
// filter construction cache reservation, flush won't be triggered before we
|
||||
// manually trigger it for clean testing
|
||||
options.write_buffer_size = 640 << 20;
|
||||
options.table_factory.reset(
|
||||
NewBlockBasedTableFactory(GetBlockBasedTableOptions()));
|
||||
std::shared_ptr<FilterConstructResPeakTrackingCache> cache =
|
||||
GetFilterConstructResPeakTrackingCache();
|
||||
options.create_if_missing = true;
|
||||
// Disable auto compaction to prevent its unexpected side effect
|
||||
// to the number of keys per partition designed by us in the test
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
int num_key = static_cast<int>(GetNumKey());
|
||||
for (int i = 0; i < num_key; i++) {
|
||||
ASSERT_OK(Put(Key(i), Key(i)));
|
||||
}
|
||||
|
||||
ASSERT_EQ(cache->GetReservedCacheIncrementSum(), 0)
|
||||
<< "Flush was triggered too early in the test case with filter "
|
||||
"construction cache reservation - please make sure no flush triggered "
|
||||
"during the key insertions above";
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
bool reserve_table_builder_memory = ReserveTableBuilderMemory();
|
||||
BloomFilterPolicy::Mode policy = GetFilterPolicy();
|
||||
bool partition_filters = PartitionFilters();
|
||||
|
||||
std::deque<std::size_t> filter_construction_cache_res_peaks =
|
||||
cache->GetReservedCachePeaks();
|
||||
std::size_t filter_construction_cache_res_increments_sum =
|
||||
cache->GetReservedCacheIncrementSum();
|
||||
|
||||
if (!reserve_table_builder_memory) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (policy == BloomFilterPolicy::Mode::kDeprecatedBlock ||
|
||||
policy == BloomFilterPolicy::Mode::kLegacyBloom) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 0)
|
||||
<< "There shouldn't be filter construction cache reservation as this "
|
||||
"feature does not support BloomFilterPolicy::Mode::kDeprecatedBlock "
|
||||
"nor BloomFilterPolicy::Mode::kLegacyBloom";
|
||||
return;
|
||||
}
|
||||
|
||||
const std::size_t kDummyEntrySize =
|
||||
CacheReservationManager::GetDummyEntrySize();
|
||||
|
||||
const std::size_t predicted_hash_entries_cache_res =
|
||||
num_key * sizeof(FilterConstructionReserveMemoryHash);
|
||||
ASSERT_EQ(predicted_hash_entries_cache_res % kDummyEntrySize, 0)
|
||||
<< "It's by this test's design that predicted_hash_entries_cache_res is "
|
||||
"a multipe of dummy entry";
|
||||
|
||||
const std::size_t predicted_hash_entries_cache_res_dummy_entry_num =
|
||||
predicted_hash_entries_cache_res / kDummyEntrySize;
|
||||
const std::size_t predicted_final_filter_cache_res =
|
||||
static_cast<std::size_t>(std::ceil(
|
||||
1.0 * predicted_hash_entries_cache_res_dummy_entry_num / 6 *
|
||||
(policy == BloomFilterPolicy::Mode::kStandard128Ribbon ? 0.7 : 1))) *
|
||||
kDummyEntrySize;
|
||||
const std::size_t predicted_banding_cache_res =
|
||||
static_cast<std::size_t>(
|
||||
std::ceil(predicted_hash_entries_cache_res_dummy_entry_num * 2.5)) *
|
||||
kDummyEntrySize;
|
||||
|
||||
if (policy == BloomFilterPolicy::Mode::kFastLocalBloom) {
|
||||
/* BloomFilterPolicy::Mode::kFastLocalBloom + FullFilter
|
||||
* p0
|
||||
* / \
|
||||
* b / \
|
||||
* / \
|
||||
* / \
|
||||
* 0/ \
|
||||
* hash entries = b - 0, final filter = p0 - b
|
||||
* p0 = hash entries + final filter
|
||||
*
|
||||
* The test is designed in a way such that the reservation for b is a
|
||||
* multiple of dummy entries so that reservation for (p0 - b)
|
||||
* will trigger at least another dummy entry insertion.
|
||||
*
|
||||
* BloomFilterPolicy::Mode::kFastLocalBloom + PartitionedFilter
|
||||
* p1
|
||||
* / \
|
||||
* p0 b'/ \
|
||||
* / \ / \
|
||||
* b / \ / \
|
||||
* / \ / \
|
||||
* / a \
|
||||
* 0/ \
|
||||
* partitioned hash entries1 = b - 0, partitioned hash entries1 = b' - a
|
||||
* parittioned final filter1 = p0 - b, parittioned final filter2 = p1 - b'
|
||||
*
|
||||
* (increment p0 - 0) + (increment p1 - a)
|
||||
* = partitioned hash entries1 + partitioned hash entries2
|
||||
* + parittioned final filter1 + parittioned final filter2
|
||||
* = hash entries + final filter
|
||||
*
|
||||
*/
|
||||
if (!partition_filters) {
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 1)
|
||||
<< "Filter construction cache reservation should have only 1 peak in "
|
||||
"case: BloomFilterPolicy::Mode::kFastLocalBloom + FullFilter";
|
||||
std::size_t filter_construction_cache_res_peak =
|
||||
filter_construction_cache_res_peaks[0];
|
||||
EXPECT_GT(filter_construction_cache_res_peak,
|
||||
predicted_hash_entries_cache_res)
|
||||
<< "The testing number of hash entries is designed to make hash "
|
||||
"entries cache reservation be multiples of dummy entries"
|
||||
" so the correct behavior of charging final filter on top of it"
|
||||
" should've triggered at least another dummy entry insertion";
|
||||
|
||||
std::size_t predicted_filter_construction_cache_res_peak =
|
||||
predicted_hash_entries_cache_res + predicted_final_filter_cache_res;
|
||||
EXPECT_GE(filter_construction_cache_res_peak,
|
||||
predicted_filter_construction_cache_res_peak * 0.9);
|
||||
EXPECT_LE(filter_construction_cache_res_peak,
|
||||
predicted_filter_construction_cache_res_peak * 1.1);
|
||||
return;
|
||||
} else {
|
||||
EXPECT_GE(filter_construction_cache_res_peaks.size(), 2)
|
||||
<< "Filter construction cache reservation should have multiple peaks "
|
||||
"in case: BloomFilterPolicy::Mode::kFastLocalBloom + "
|
||||
"PartitionedFilter";
|
||||
std::size_t predicted_filter_construction_cache_res_increments_sum =
|
||||
predicted_hash_entries_cache_res + predicted_final_filter_cache_res;
|
||||
EXPECT_GE(filter_construction_cache_res_increments_sum,
|
||||
predicted_filter_construction_cache_res_increments_sum * 0.9);
|
||||
EXPECT_LE(filter_construction_cache_res_increments_sum,
|
||||
predicted_filter_construction_cache_res_increments_sum * 1.1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (policy == BloomFilterPolicy::Mode::kStandard128Ribbon) {
|
||||
/* BloomFilterPolicy::Mode::kStandard128Ribbon + FullFilter
|
||||
* p0
|
||||
* / \ p1
|
||||
* / \/\
|
||||
* b / b' \
|
||||
* / \
|
||||
* 0/ \
|
||||
* hash entries = b - 0, banding = p0 - b, final filter = p1 - b'
|
||||
* p0 = hash entries + banding
|
||||
*
|
||||
* The test is designed in a way such that the reservation for (p1 - b')
|
||||
* will trigger at least another dummy entry insertion
|
||||
* (or equivelantly to saying, creating another peak).
|
||||
*
|
||||
* BloomFilterPolicy::Mode::kStandard128Ribbon + PartitionedFilter
|
||||
* p3
|
||||
* p0 /\ p4
|
||||
* / \ p1 / \ /\
|
||||
* / \/\ b''/ a' \
|
||||
* b / b' \ / \
|
||||
* / \ / \
|
||||
* 0/ a \
|
||||
* partitioned hash entries1 = b - 0, partitioned hash entries2 = b'' - a
|
||||
* partitioned banding1 = p0 - b, partitioned banding2 = p3 - b''
|
||||
* parittioned final filter1 = p1 - b',parittioned final filter2 = p4 - a'
|
||||
*
|
||||
* (increment p0 - 0) + (increment p1 - b')
|
||||
* + (increment p3 - a) + (increment p4 - a')
|
||||
* = partitioned hash entries1 + partitioned hash entries2
|
||||
* + parittioned banding1 + parittioned banding2
|
||||
* + parittioned final filter1 + parittioned final filter2
|
||||
* = hash entries + banding + final filter
|
||||
*/
|
||||
if (!partition_filters) {
|
||||
ASSERT_GE(std::floor(1.0 * predicted_final_filter_cache_res /
|
||||
CacheReservationManager::GetDummyEntrySize()),
|
||||
1)
|
||||
<< "Final filter cache reservation too small for this test - please "
|
||||
"increase the number of keys";
|
||||
EXPECT_EQ(filter_construction_cache_res_peaks.size(), 2)
|
||||
<< "Filter construction cache reservation should have 2 peaks in "
|
||||
"case: BloomFilterPolicy::Mode::kStandard128Ribbon + FullFilter. "
|
||||
"The second peak is resulted from charging the final filter after "
|
||||
"decreasing the hash entry reservation since the testing final "
|
||||
"filter reservation is designed to be at least 1 dummy entry size";
|
||||
|
||||
std::size_t filter_construction_cache_res_peak =
|
||||
filter_construction_cache_res_peaks[0];
|
||||
std::size_t predicted_filter_construction_cache_res_peak =
|
||||
predicted_hash_entries_cache_res + predicted_banding_cache_res;
|
||||
EXPECT_GE(filter_construction_cache_res_peak,
|
||||
predicted_filter_construction_cache_res_peak * 0.9);
|
||||
EXPECT_LE(filter_construction_cache_res_peak,
|
||||
predicted_filter_construction_cache_res_peak * 1.1);
|
||||
return;
|
||||
} else {
|
||||
EXPECT_GE(filter_construction_cache_res_peaks.size(), 3)
|
||||
<< "Filter construction cache reservation should have more than 3 "
|
||||
"peaks "
|
||||
"in case: BloomFilterPolicy::Mode::kStandard128Ribbon + "
|
||||
"PartitionedFilter";
|
||||
std::size_t predicted_filter_construction_cache_res_increments_sum =
|
||||
predicted_hash_entries_cache_res + predicted_banding_cache_res +
|
||||
predicted_final_filter_cache_res;
|
||||
EXPECT_GE(filter_construction_cache_res_increments_sum,
|
||||
predicted_filter_construction_cache_res_increments_sum * 0.9);
|
||||
EXPECT_LE(filter_construction_cache_res_increments_sum,
|
||||
predicted_filter_construction_cache_res_increments_sum * 1.1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// A wrapped bloom over block-based FilterPolicy
|
||||
class TestingWrappedBlockBasedFilterPolicy : public FilterPolicy {
|
||||
|
||||
@@ -4302,6 +4302,67 @@ TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelTtlBooster) {
|
||||
const int kNumKeysPerFile = 32;
|
||||
const int kNumLevelFiles = 3;
|
||||
const int kValueSize = 1000;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.ttl = 10 * 60 * 60; // 10 hours
|
||||
options.periodic_compaction_seconds = 480 * 60 * 60; // very long
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.max_bytes_for_level_base = 5 * uint64_t{kNumKeysPerFile * kValueSize};
|
||||
options.max_open_files = -1; // needed for both periodic and ttl compactions
|
||||
options.compaction_pri = CompactionPri::kMinOverlappingRatio;
|
||||
env_->SetMockSleep();
|
||||
options.env = env_;
|
||||
|
||||
// NOTE: Presumed unnecessary and removed: resetting mock time in env
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < kNumLevelFiles; ++i) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(
|
||||
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
ASSERT_EQ("0,0,3", FilesPerLevel());
|
||||
|
||||
// Create some files for L1
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(2 * j + i), rnd.RandomString(kValueSize)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
|
||||
ASSERT_EQ("0,1,3", FilesPerLevel());
|
||||
|
||||
// Make the new L0 files qualify TTL boosting and generate one more to trigger
|
||||
// L1 -> L2 compaction. Old files will be picked even if their priority is
|
||||
// lower without boosting.
|
||||
env_->MockSleepForSeconds(8 * 60 * 60);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(Put(Key(kNumKeysPerFile * 2 + 2 * j + i),
|
||||
rnd.RandomString(kValueSize * 2)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
}
|
||||
ASSERT_EQ("0,1,2", FilesPerLevel());
|
||||
|
||||
ASSERT_GT(SizeAtLevel(1), kNumKeysPerFile * 4 * kValueSize);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
|
||||
class TestCompactionFilter : public CompactionFilter {
|
||||
const char* Name() const override { return "TestCompactionFilter"; }
|
||||
@@ -6863,6 +6924,141 @@ TEST_F(DBCompactionTest,
|
||||
ASSERT_TRUE(callback_completed);
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ChangeLevelConflictsWithManual) {
|
||||
Options options = CurrentOptions();
|
||||
options.num_levels = 3;
|
||||
Reopen(options);
|
||||
|
||||
// Setup an LSM with L2 populated.
|
||||
Random rnd(301);
|
||||
ASSERT_OK(Put(Key(0), rnd.RandomString(990)));
|
||||
ASSERT_OK(Put(Key(1), rnd.RandomString(990)));
|
||||
{
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 2;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
}
|
||||
ASSERT_EQ("0,0,1", FilesPerLevel(0));
|
||||
|
||||
// The background thread will refit L2->L1 while the foreground thread will
|
||||
// attempt to run a compaction on new data. The following dependencies
|
||||
// ensure the background manual compaction's refitting phase disables manual
|
||||
// compaction immediately before the foreground manual compaction can register
|
||||
// itself. Manual compaction is kept disabled until the foreground manual
|
||||
// checks for the failure once.
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
||||
// Only do Put()s for foreground CompactRange() once the background
|
||||
// CompactRange() has reached the refitting phase.
|
||||
{
|
||||
"DBImpl::CompactRange:BeforeRefit:1",
|
||||
"DBCompactionTest::ChangeLevelConflictsWithManual:"
|
||||
"PreForegroundCompactRange",
|
||||
},
|
||||
// Right before we register the manual compaction, proceed with
|
||||
// the refitting phase so manual compactions are disabled. Stay in
|
||||
// the refitting phase with manual compactions disabled until it is
|
||||
// noticed.
|
||||
{
|
||||
"DBImpl::RunManualCompaction:0",
|
||||
"DBImpl::CompactRange:BeforeRefit:2",
|
||||
},
|
||||
{
|
||||
"DBImpl::CompactRange:PreRefitLevel",
|
||||
"DBImpl::RunManualCompaction:1",
|
||||
},
|
||||
{
|
||||
"DBImpl::RunManualCompaction:PausedAtStart",
|
||||
"DBImpl::CompactRange:PostRefitLevel",
|
||||
},
|
||||
// If compaction somehow were scheduled, let's let it run after reenabling
|
||||
// manual compactions. This dependency is not expected to be hit but is
|
||||
// here for speculatively coercing future bugs.
|
||||
{
|
||||
"DBImpl::CompactRange:PostRefitLevel:ManualCompactionEnabled",
|
||||
"BackgroundCallCompaction:0",
|
||||
},
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread refit_level_thread([&] {
|
||||
CompactRangeOptions cro;
|
||||
cro.change_level = true;
|
||||
cro.target_level = 1;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
});
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::ChangeLevelConflictsWithManual:"
|
||||
"PreForegroundCompactRange");
|
||||
ASSERT_OK(Put(Key(0), rnd.RandomString(990)));
|
||||
ASSERT_OK(Put(Key(1), rnd.RandomString(990)));
|
||||
ASSERT_TRUE(dbfull()
|
||||
->CompactRange(CompactRangeOptions(), nullptr, nullptr)
|
||||
.IsIncomplete());
|
||||
|
||||
refit_level_thread.join();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, BottomPriCompactionCountsTowardConcurrencyLimit) {
|
||||
// Flushes several files to trigger compaction while lock is released during
|
||||
// a bottom-pri compaction. Verifies it does not get scheduled to thread pool
|
||||
// because per-DB limit for compaction parallelism is one (default).
|
||||
const int kNumL0Files = 4;
|
||||
const int kNumLevels = 3;
|
||||
|
||||
env_->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = kNumL0Files;
|
||||
options.num_levels = kNumLevels;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Setup last level to be non-empty since it's a bit unclear whether
|
||||
// compaction to an empty level would be considered "bottommost".
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(kNumLevels - 1);
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BGWorkBottomCompaction",
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PreTriggerCompaction"},
|
||||
{"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PostTriggerCompaction",
|
||||
"BackgroundCallCompaction:0"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
port::Thread compact_range_thread([&] {
|
||||
CompactRangeOptions cro;
|
||||
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
|
||||
cro.exclusive_manual_compaction = false;
|
||||
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
|
||||
});
|
||||
|
||||
// Sleep in the low-pri thread so any newly scheduled compaction will be
|
||||
// queued. Otherwise it might finish before we check its existence.
|
||||
test::SleepingBackgroundTask sleeping_task_low;
|
||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||
Env::Priority::LOW);
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PreTriggerCompaction");
|
||||
for (int i = 0; i < kNumL0Files; ++i) {
|
||||
ASSERT_OK(Put(Key(0), "val"));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
ASSERT_EQ(0u, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
|
||||
TEST_SYNC_POINT(
|
||||
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
|
||||
"PostTriggerCompaction");
|
||||
|
||||
sleeping_task_low.WakeUp();
|
||||
sleeping_task_low.WaitUntilDone();
|
||||
compact_range_thread.join();
|
||||
}
|
||||
|
||||
#endif // !defined(ROCKSDB_LITE)
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+18
-19
@@ -127,31 +127,30 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
|
||||
Status DBImpl::GetSortedWalFiles(VectorLogPtr& files) {
|
||||
// If caller disabled deletions, this function should return files that are
|
||||
// guaranteed not to be deleted until deletions are re-enabled. We need to
|
||||
// wait for pending purges to finish since WalManager doesn't know which
|
||||
// files are going to be purged. Additional purges won't be scheduled as
|
||||
// long as deletions are disabled (so the below loop must terminate).
|
||||
// Also note that we disable deletions anyway to avoid the case where a
|
||||
// file is deleted in the middle of the scan, causing IO error.
|
||||
Status deletions_disabled = DisableFileDeletions();
|
||||
{
|
||||
// If caller disabled deletions, this function should return files that are
|
||||
// guaranteed not to be deleted until deletions are re-enabled. We need to
|
||||
// wait for pending purges to finish since WalManager doesn't know which
|
||||
// files are going to be purged. Additional purges won't be scheduled as
|
||||
// long as deletions are disabled (so the below loop must terminate).
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (disable_delete_obsolete_files_ > 0 &&
|
||||
(pending_purge_obsolete_files_ > 0 || bg_purge_scheduled_ > 0)) {
|
||||
while (pending_purge_obsolete_files_ > 0 || bg_purge_scheduled_ > 0) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
// Disable deletion in order to avoid the case where a file is deleted in
|
||||
// the middle of the process so IO error is returned.
|
||||
Status s = DisableFileDeletions();
|
||||
bool file_deletion_supported = !s.IsNotSupported();
|
||||
if (s.ok() || !file_deletion_supported) {
|
||||
s = wal_manager_.GetSortedWalFiles(files);
|
||||
if (file_deletion_supported) {
|
||||
Status s2 = EnableFileDeletions(false);
|
||||
if (!s2.ok() && s.ok()) {
|
||||
s = s2;
|
||||
}
|
||||
}
|
||||
Status s = wal_manager_.GetSortedWalFiles(files);
|
||||
|
||||
// DisableFileDeletions / EnableFileDeletions not supported in read-only DB
|
||||
if (deletions_disabled.ok()) {
|
||||
Status s2 = EnableFileDeletions(/*force*/ false);
|
||||
assert(s2.ok());
|
||||
s2.PermitUncheckedError();
|
||||
} else {
|
||||
assert(deletions_disabled.IsNotSupported());
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
+65
-23
@@ -783,8 +783,8 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
options.listeners.emplace_back(listener);
|
||||
#endif // !ROCKSDB_LITE
|
||||
ASSERT_OK(TryReopen(options));
|
||||
uint32_t mempurge_count = 0;
|
||||
uint32_t sst_count = 0;
|
||||
std::atomic<uint32_t> mempurge_count{0};
|
||||
std::atomic<uint32_t> sst_count{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:MemPurgeSuccessful",
|
||||
[&](void* /*arg*/) { mempurge_count++; });
|
||||
@@ -859,10 +859,8 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
// Check that there was no SST files created during flush.
|
||||
const uint32_t EXPECTED_SST_COUNT = 0;
|
||||
|
||||
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
|
||||
|
||||
const uint32_t mempurge_count_record = mempurge_count;
|
||||
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
|
||||
|
||||
// Insertion of of K-V pairs, no overwrites.
|
||||
for (size_t i = 0; i < NUM_REPEAT; i++) {
|
||||
@@ -893,9 +891,9 @@ TEST_F(DBFlushTest, MemPurgeBasic) {
|
||||
}
|
||||
|
||||
// Assert that at least one flush to storage has been performed
|
||||
ASSERT_GT(sst_count, EXPECTED_SST_COUNT);
|
||||
EXPECT_GT(sst_count.exchange(0), EXPECTED_SST_COUNT);
|
||||
// (which will consequently increase the number of mempurges recorded too).
|
||||
ASSERT_GE(mempurge_count, mempurge_count_record);
|
||||
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
|
||||
// Assert that there is no data corruption, even with
|
||||
// a flush to storage.
|
||||
@@ -935,8 +933,8 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
uint32_t mempurge_count = 0;
|
||||
uint32_t sst_count = 0;
|
||||
std::atomic<uint32_t> mempurge_count{0};
|
||||
std::atomic<uint32_t> sst_count{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:MemPurgeSuccessful",
|
||||
[&](void* /*arg*/) { mempurge_count++; });
|
||||
@@ -1024,8 +1022,8 @@ TEST_F(DBFlushTest, MemPurgeDeleteAndDeleteRange) {
|
||||
// Check that there was no SST files created during flush.
|
||||
const uint32_t EXPECTED_SST_COUNT = 0;
|
||||
|
||||
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
|
||||
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
|
||||
|
||||
// Additional test for the iterator+memPurge.
|
||||
ASSERT_OK(Put(KEY2, p_v2));
|
||||
@@ -1142,8 +1140,8 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
|
||||
|
||||
ASSERT_OK(TryReopen(options));
|
||||
|
||||
uint32_t mempurge_count = 0;
|
||||
uint32_t sst_count = 0;
|
||||
std::atomic<uint32_t> mempurge_count{0};
|
||||
std::atomic<uint32_t> sst_count{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:MemPurgeSuccessful",
|
||||
[&](void* /*arg*/) { mempurge_count++; });
|
||||
@@ -1189,8 +1187,8 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
|
||||
// Check that there was no SST files created during flush.
|
||||
const uint32_t EXPECTED_SST_COUNT = 0;
|
||||
|
||||
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
|
||||
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
|
||||
|
||||
// Verify that the ConditionalUpdateCompactionFilter
|
||||
// updated the values of KEY2 and KEY3, and not KEY4 and KEY5.
|
||||
@@ -1201,7 +1199,7 @@ TEST_F(DBFlushTest, MemPurgeAndCompactionFilter) {
|
||||
ASSERT_EQ(Get(KEY5), p_v5);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
TEST_F(DBFlushTest, DISABLED_MemPurgeWALSupport) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
options.statistics = CreateDBStatistics();
|
||||
@@ -1233,8 +1231,8 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
ASSERT_OK(Put(0, "bar", "v2"));
|
||||
ASSERT_OK(Put(1, "bar", "v2"));
|
||||
ASSERT_OK(Put(1, "foo", "v3"));
|
||||
uint32_t mempurge_count = 0;
|
||||
uint32_t sst_count = 0;
|
||||
std::atomic<uint32_t> mempurge_count{0};
|
||||
std::atomic<uint32_t> sst_count{0};
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushJob:MemPurgeSuccessful",
|
||||
[&](void* /*arg*/) { mempurge_count++; });
|
||||
@@ -1330,10 +1328,10 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
// Check that there was no SST files created during flush.
|
||||
const uint32_t EXPECTED_SST_COUNT = 0;
|
||||
|
||||
EXPECT_GE(mempurge_count, EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
EXPECT_GE(mempurge_count.exchange(0), EXPECTED_MIN_MEMPURGE_COUNT);
|
||||
if (options.experimental_mempurge_threshold ==
|
||||
std::numeric_limits<double>::max()) {
|
||||
EXPECT_EQ(sst_count, EXPECTED_SST_COUNT);
|
||||
EXPECT_EQ(sst_count.exchange(0), EXPECTED_SST_COUNT);
|
||||
}
|
||||
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
@@ -1359,7 +1357,7 @@ TEST_F(DBFlushTest, MemPurgeWALSupport) {
|
||||
ASSERT_OK(Put(1, RNDKEY, RNDVALUE));
|
||||
}
|
||||
// ASsert than there was at least one flush to storage.
|
||||
EXPECT_GT(sst_count, EXPECTED_SST_COUNT);
|
||||
EXPECT_GT(sst_count.exchange(0), EXPECTED_SST_COUNT);
|
||||
ReopenWithColumnFamilies({"default", "pikachu"}, options);
|
||||
ASSERT_EQ("v4", Get(1, "foo"));
|
||||
ASSERT_EQ("v2", Get(1, "bar"));
|
||||
@@ -1534,7 +1532,7 @@ TEST_F(DBFlushTest, FireOnFlushCompletedAfterCommittedResult) {
|
||||
std::shared_ptr<TestListener> listener = std::make_shared<TestListener>();
|
||||
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::BackgroundCallFlush:start",
|
||||
{{"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables",
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitFirst"},
|
||||
{"DBImpl::FlushMemTableToOutputFile:Finish",
|
||||
"DBFlushTest::FireOnFlushCompletedAfterCommittedResult:WaitSecond"}});
|
||||
@@ -1860,6 +1858,50 @@ TEST_F(DBFlushTest, FlushWithChecksumHandoffManifest2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBFlushTest, PickRightMemtables) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
options.create_if_missing = true;
|
||||
|
||||
const std::string test_cf_name = "test_cf";
|
||||
options.max_write_buffer_number = 128;
|
||||
CreateColumnFamilies({test_cf_name}, options);
|
||||
|
||||
Close();
|
||||
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, test_cf_name}, options);
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "key", "value"));
|
||||
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "key", "value"));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::SyncClosedLogs:BeforeReLock", [&](void* /*arg*/) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), handles_[1], "what", "v"));
|
||||
auto* cfhi =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(handles_[1]);
|
||||
assert(cfhi);
|
||||
ASSERT_OK(dbfull()->TEST_SwitchMemtable(cfhi->cfd()));
|
||||
});
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables", [&](void* arg) {
|
||||
auto* job = reinterpret_cast<FlushJob*>(arg);
|
||||
assert(job);
|
||||
const auto& mems = job->GetMemTables();
|
||||
assert(mems.size() == 1);
|
||||
assert(mems[0]);
|
||||
ASSERT_EQ(1, mems[0]->GetID());
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(db_->Flush(FlushOptions(), handles_[1]));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
class DBFlushTestBlobError : public DBFlushTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
public:
|
||||
|
||||
+64
-28
@@ -1389,7 +1389,9 @@ Status DBImpl::SyncWAL() {
|
||||
IOStatusCheck(io_s);
|
||||
}
|
||||
if (status.ok() && need_log_dir_sync) {
|
||||
status = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
status = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
TEST_SYNC_POINT("DBWALTest::SyncWALNotWaitWrite:2");
|
||||
|
||||
@@ -1544,6 +1546,8 @@ void DBImpl::BackgroundCallPurge() {
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
assert(bg_purge_scheduled_ > 0);
|
||||
|
||||
// Can't use iterator to go over purge_files_ because inside the loop we're
|
||||
// unlocking the mutex that protects purge_files_.
|
||||
while (!purge_files_.empty()) {
|
||||
@@ -1611,17 +1615,7 @@ static void CleanupIteratorState(void* arg1, void* /*arg2*/) {
|
||||
delete state->super_version;
|
||||
}
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
if (state->background_purge) {
|
||||
// PurgeObsoleteFiles here does not delete files. Instead, it adds the
|
||||
// files to be deleted to a job queue, and deletes it in a separate
|
||||
// background thread.
|
||||
state->db->PurgeObsoleteFiles(job_context, true /* schedule only */);
|
||||
state->mu->Lock();
|
||||
state->db->SchedulePurge();
|
||||
state->mu->Unlock();
|
||||
} else {
|
||||
state->db->PurgeObsoleteFiles(job_context);
|
||||
}
|
||||
state->db->PurgeObsoleteFiles(job_context, state->background_purge);
|
||||
}
|
||||
job_context.Clean();
|
||||
}
|
||||
@@ -2901,6 +2895,13 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
}
|
||||
// if iterator wants internal keys, we can only proceed if
|
||||
// we can guarantee the deletes haven't been processed yet
|
||||
if (read_options.iter_start_seqnum > 0 &&
|
||||
!iter_start_seqnum_deprecation_warned_.exchange(true)) {
|
||||
ROCKS_LOG_WARN(
|
||||
immutable_db_options_.info_log,
|
||||
"iter_start_seqnum is deprecated, will be removed in a future release. "
|
||||
"Please try using user-defined timestamp instead.");
|
||||
}
|
||||
if (immutable_db_options_.preserve_deletes &&
|
||||
read_options.iter_start_seqnum > 0 &&
|
||||
read_options.iter_start_seqnum < preserve_deletes_seqnum_.load()) {
|
||||
@@ -4288,6 +4289,14 @@ Status DBImpl::RenameTempFileToOptionsFile(const std::string& file_name) {
|
||||
if (s.ok()) {
|
||||
// Retry if the file name happen to conflict with an existing one.
|
||||
s = GetEnv()->RenameFile(file_name, options_file_name);
|
||||
std::unique_ptr<FSDirectory> dir_obj;
|
||||
if (s.ok()) {
|
||||
s = fs_->NewDirectory(GetName(), IOOptions(), &dir_obj, nullptr);
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = dir_obj->FsyncWithDirOptions(IOOptions(), nullptr,
|
||||
DirFsyncOptions(options_file_name));
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -4366,25 +4375,38 @@ SequenceNumber DBImpl::GetEarliestMemTableSequenceNumber(SuperVersion* sv,
|
||||
return earliest_seq;
|
||||
}
|
||||
|
||||
Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool cache_only,
|
||||
SequenceNumber lower_bound_seq,
|
||||
SequenceNumber* seq,
|
||||
bool* found_record_for_key,
|
||||
bool* is_blob_index) {
|
||||
Status DBImpl::GetLatestSequenceForKey(
|
||||
SuperVersion* sv, const Slice& key, bool cache_only,
|
||||
SequenceNumber lower_bound_seq, SequenceNumber* seq, std::string* timestamp,
|
||||
bool* found_record_for_key, bool* is_blob_index) {
|
||||
Status s;
|
||||
MergeContext merge_context;
|
||||
SequenceNumber max_covering_tombstone_seq = 0;
|
||||
|
||||
ReadOptions read_options;
|
||||
SequenceNumber current_seq = versions_->LastSequence();
|
||||
LookupKey lkey(key, current_seq);
|
||||
|
||||
ColumnFamilyData* cfd = sv->cfd;
|
||||
assert(cfd);
|
||||
const Comparator* const ucmp = cfd->user_comparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
std::string ts_buf;
|
||||
if (ts_sz > 0) {
|
||||
assert(timestamp);
|
||||
ts_buf.assign(ts_sz, '\xff');
|
||||
} else {
|
||||
assert(!timestamp);
|
||||
}
|
||||
Slice ts(ts_buf);
|
||||
|
||||
LookupKey lkey(key, current_seq, ts_sz == 0 ? nullptr : &ts);
|
||||
|
||||
*seq = kMaxSequenceNumber;
|
||||
*found_record_for_key = false;
|
||||
|
||||
// Check if there is a record for this key in the latest memtable
|
||||
sv->mem->Get(lkey, nullptr, nullptr, &s, &merge_context,
|
||||
sv->mem->Get(lkey, /*value=*/nullptr, timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
@@ -4396,6 +4418,10 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
|
||||
return s;
|
||||
}
|
||||
assert(!ts_sz ||
|
||||
(*seq != kMaxSequenceNumber &&
|
||||
*timestamp != std::string(ts_sz, '\xff')) ||
|
||||
(*seq == kMaxSequenceNumber && timestamp->empty()));
|
||||
|
||||
if (*seq != kMaxSequenceNumber) {
|
||||
// Found a sequence number, no need to check immutable memtables
|
||||
@@ -4411,7 +4437,7 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->Get(lkey, nullptr, nullptr, &s, &merge_context,
|
||||
sv->imm->Get(lkey, /*value=*/nullptr, timestamp, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
@@ -4424,6 +4450,11 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
return s;
|
||||
}
|
||||
|
||||
assert(!ts_sz ||
|
||||
(*seq != kMaxSequenceNumber &&
|
||||
*timestamp != std::string(ts_sz, '\xff')) ||
|
||||
(*seq == kMaxSequenceNumber && timestamp->empty()));
|
||||
|
||||
if (*seq != kMaxSequenceNumber) {
|
||||
// Found a sequence number, no need to check memtable history
|
||||
*found_record_for_key = true;
|
||||
@@ -4438,9 +4469,9 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
}
|
||||
|
||||
// Check if there is a record for this key in the immutable memtables
|
||||
sv->imm->GetFromHistory(lkey, nullptr, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, seq, read_options,
|
||||
is_blob_index);
|
||||
sv->imm->GetFromHistory(lkey, /*value=*/nullptr, timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq, seq,
|
||||
read_options, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading memtable.
|
||||
@@ -4452,8 +4483,13 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
return s;
|
||||
}
|
||||
|
||||
assert(!ts_sz ||
|
||||
(*seq != kMaxSequenceNumber &&
|
||||
*timestamp != std::string(ts_sz, '\xff')) ||
|
||||
(*seq == kMaxSequenceNumber && timestamp->empty()));
|
||||
if (*seq != kMaxSequenceNumber) {
|
||||
// Found a sequence number, no need to check SST files
|
||||
assert(0 == ts_sz || *timestamp != std::string(ts_sz, '\xff'));
|
||||
*found_record_for_key = true;
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -4466,10 +4502,10 @@ Status DBImpl::GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
// SST files if cache_only=true?
|
||||
if (!cache_only) {
|
||||
// Check tables
|
||||
sv->current->Get(read_options, lkey, nullptr, nullptr, &s, &merge_context,
|
||||
&max_covering_tombstone_seq, nullptr /* value_found */,
|
||||
found_record_for_key, seq, nullptr /*read_callback*/,
|
||||
is_blob_index);
|
||||
sv->current->Get(read_options, lkey, /*value=*/nullptr, timestamp, &s,
|
||||
&merge_context, &max_covering_tombstone_seq,
|
||||
nullptr /* value_found */, found_record_for_key, seq,
|
||||
nullptr /*read_callback*/, is_blob_index);
|
||||
|
||||
if (!(s.ok() || s.IsNotFound() || s.IsMergeInProgress())) {
|
||||
// unexpected error reading SST files
|
||||
|
||||
+16
-3
@@ -582,9 +582,15 @@ class DBImpl : public DB {
|
||||
// in the memtables, including memtable history. If cache_only is false,
|
||||
// SST files will also be checked.
|
||||
//
|
||||
// `key` should NOT have user-defined timestamp appended to user key even if
|
||||
// timestamp is enabled.
|
||||
//
|
||||
// If a key is found, *found_record_for_key will be set to true and
|
||||
// *seq will be set to the stored sequence number for the latest
|
||||
// operation on this key or kMaxSequenceNumber if unknown.
|
||||
// operation on this key or kMaxSequenceNumber if unknown. If user-defined
|
||||
// timestamp is enabled for this column family and timestamp is not nullptr,
|
||||
// then *timestamp will be set to the stored timestamp for the latest
|
||||
// operation on this key.
|
||||
// If no key is found, *found_record_for_key will be set to false.
|
||||
//
|
||||
// Note: If cache_only=false, it is possible for *seq to be set to 0 if
|
||||
@@ -608,9 +614,9 @@ class DBImpl : public DB {
|
||||
Status GetLatestSequenceForKey(SuperVersion* sv, const Slice& key,
|
||||
bool cache_only,
|
||||
SequenceNumber lower_bound_seq,
|
||||
SequenceNumber* seq,
|
||||
SequenceNumber* seq, std::string* timestamp,
|
||||
bool* found_record_for_key,
|
||||
bool* is_blob_index = nullptr);
|
||||
bool* is_blob_index);
|
||||
|
||||
Status TraceIteratorSeek(const uint32_t& cf_id, const Slice& key,
|
||||
const Slice& lower_bound, const Slice upper_bound);
|
||||
@@ -974,6 +980,9 @@ class DBImpl : public DB {
|
||||
// is only for the special test of CancelledCompactions
|
||||
Status TEST_WaitForCompact(bool waitUnscheduled = false);
|
||||
|
||||
// Wait for any background purge
|
||||
Status TEST_WaitForPurge();
|
||||
|
||||
// Get the background error status
|
||||
Status TEST_GetBGError();
|
||||
|
||||
@@ -2312,6 +2321,10 @@ class DBImpl : public DB {
|
||||
|
||||
// Pointer to WriteBufferManager stalling interface.
|
||||
std::unique_ptr<StallInterface> wbm_stall_;
|
||||
|
||||
// Indicate if deprecation warning message is logged before. Will be removed
|
||||
// soon with the deprecated feature.
|
||||
std::atomic_bool iter_start_seqnum_deprecation_warned_{false};
|
||||
};
|
||||
|
||||
extern Options SanitizeOptions(const std::string& db, const Options& src,
|
||||
|
||||
@@ -103,6 +103,8 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
if (!logs_to_sync.empty()) {
|
||||
mutex_.Unlock();
|
||||
|
||||
assert(job_context);
|
||||
|
||||
for (log::Writer* log : logs_to_sync) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"[JOB %d] Syncing log #%" PRIu64, job_context->job_id,
|
||||
@@ -120,9 +122,13 @@ IOStatus DBImpl::SyncClosedLogs(JobContext* job_context) {
|
||||
}
|
||||
}
|
||||
if (io_s.ok()) {
|
||||
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::SyncClosedLogs:BeforeReLock",
|
||||
/*arg=*/nullptr);
|
||||
mutex_.Lock();
|
||||
|
||||
// "number <= current_log_number - 1" is equivalent to
|
||||
@@ -151,14 +157,34 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
Env::Priority thread_pri) {
|
||||
mutex_.AssertHeld();
|
||||
assert(cfd);
|
||||
assert(cfd->imm());
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
assert(versions_);
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
// If there are more than one column families, we need to make sure that
|
||||
// all the log files except the most recent one are synced. Otherwise if
|
||||
// the host crashes after flushing and before WAL is persistent, the
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other (unflushed) column families are missing.
|
||||
const bool needs_to_sync_closed_wals =
|
||||
logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1;
|
||||
// If needs_to_sync_closed_wals is true, we need to record the current
|
||||
// maximum memtable ID of this column family so that a later PickMemtables()
|
||||
// call will not pick memtables whose IDs are higher. This is due to the fact
|
||||
// that SyncClosedLogs() may release the db mutex, and memtable switch can
|
||||
// happen for this column family in the meantime. The newly created memtables
|
||||
// have their data backed by unsynced WALs, thus they cannot be included in
|
||||
// this flush job.
|
||||
uint64_t max_memtable_id = needs_to_sync_closed_wals
|
||||
? cfd->imm()->GetLatestMemTableID()
|
||||
: port::kMaxUint64;
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
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),
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options, max_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,
|
||||
@@ -174,13 +200,7 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
Status s;
|
||||
bool need_cancel = false;
|
||||
IOStatus log_io_s = IOStatus::OK();
|
||||
if (logfile_number_ > 0 &&
|
||||
versions_->GetColumnFamilySet()->NumberOfColumnFamilies() > 1) {
|
||||
// If there are more than one column families, we need to make sure that
|
||||
// all the log files except the most recent one are synced. Otherwise if
|
||||
// the host crashes after flushing and before WAL is persistent, the
|
||||
// flushed SST may contain data from write batches whose updates to
|
||||
// other column families are missing.
|
||||
if (needs_to_sync_closed_wals) {
|
||||
// SyncClosedLogs() may unlock and re-lock the db_mutex.
|
||||
log_io_s = SyncClosedLogs(job_context);
|
||||
if (!log_io_s.ok() && !log_io_s.IsShutdownInProgress() &&
|
||||
@@ -199,7 +219,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
flush_job.PickMemTable();
|
||||
need_cancel = true;
|
||||
}
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:AfterPickMemtables");
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"DBImpl::FlushMemTableToOutputFile:AfterPickMemtables", &flush_job);
|
||||
bool switched_to_mempurge = false;
|
||||
// Within flush_job.Run, rocksdb may call event listener to notify
|
||||
// file creation and deletion.
|
||||
@@ -532,7 +553,9 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Sync on all distinct output directories.
|
||||
for (auto dir : distinct_output_dirs) {
|
||||
if (dir != nullptr) {
|
||||
Status error_status = dir->Fsync(IOOptions(), nullptr);
|
||||
Status error_status = dir->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
if (!error_status.ok()) {
|
||||
s = error_status;
|
||||
break;
|
||||
@@ -1110,6 +1133,8 @@ Status DBImpl::CompactRangeInternal(const CompactRangeOptions& options,
|
||||
assert(temp_s.ok());
|
||||
}
|
||||
EnableManualCompaction();
|
||||
TEST_SYNC_POINT(
|
||||
"DBImpl::CompactRange:PostRefitLevel:ManualCompactionEnabled");
|
||||
}
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
|
||||
@@ -1583,12 +1608,12 @@ Status DBImpl::ReFitLevel(ColumnFamilyData* cfd, int level, int target_level) {
|
||||
edit.SetColumnFamily(cfd->GetID());
|
||||
for (const auto& f : vstorage->LevelFiles(level)) {
|
||||
edit.DeleteFile(level, f->fd.GetNumber());
|
||||
edit.AddFile(to_level, f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest,
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction, f->oldest_blob_file_number,
|
||||
f->oldest_ancester_time, f->file_creation_time,
|
||||
f->file_checksum, f->file_checksum_func_name);
|
||||
edit.AddFile(
|
||||
to_level, f->fd.GetNumber(), f->fd.GetPathId(), f->fd.GetFileSize(),
|
||||
f->smallest, f->largest, f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction, f->oldest_blob_file_number,
|
||||
f->oldest_ancester_time, f->file_creation_time, f->file_checksum,
|
||||
f->file_checksum_func_name, f->min_timestamp, f->max_timestamp);
|
||||
}
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] Apply version edit:\n%s", cfd->GetName().c_str(),
|
||||
@@ -1743,6 +1768,17 @@ Status DBImpl::RunManualCompaction(
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:1");
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
|
||||
if (manual_compaction_paused_ > 0) {
|
||||
// Does not make sense to `AddManualCompaction()` in this scenario since
|
||||
// `DisableManualCompaction()` just waited for the manual compaction queue
|
||||
// to drain. So return immediately.
|
||||
TEST_SYNC_POINT("DBImpl::RunManualCompaction:PausedAtStart");
|
||||
manual.status =
|
||||
Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
manual.done = true;
|
||||
return manual.status;
|
||||
}
|
||||
|
||||
// When a manual compaction arrives, temporarily disable scheduling of
|
||||
// non-manual compactions and wait until the number of scheduled compaction
|
||||
// jobs drops to zero. This used to be needed to ensure that this manual
|
||||
@@ -1826,7 +1862,6 @@ Status DBImpl::RunManualCompaction(
|
||||
}
|
||||
ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
ca->compaction_pri_ = Env::Priority::LOW;
|
||||
ca->prepicked_compaction = new PrepickedCompaction;
|
||||
ca->prepicked_compaction->manual_compaction_state = &manual;
|
||||
ca->prepicked_compaction->compaction = compaction;
|
||||
@@ -1836,14 +1871,19 @@ Status DBImpl::RunManualCompaction(
|
||||
assert(false);
|
||||
}
|
||||
manual.incomplete = false;
|
||||
bg_compaction_scheduled_++;
|
||||
Env::Priority thread_pool_pri = Env::Priority::LOW;
|
||||
if (compaction->bottommost_level() &&
|
||||
env_->GetBackgroundThreads(Env::Priority::BOTTOM) > 0) {
|
||||
thread_pool_pri = Env::Priority::BOTTOM;
|
||||
bg_bottom_compaction_scheduled_++;
|
||||
ca->compaction_pri_ = Env::Priority::BOTTOM;
|
||||
env_->Schedule(&DBImpl::BGWorkBottomCompaction, ca,
|
||||
Env::Priority::BOTTOM, this,
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
} else {
|
||||
bg_compaction_scheduled_++;
|
||||
ca->compaction_pri_ = Env::Priority::LOW;
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, Env::Priority::LOW, this,
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
}
|
||||
env_->Schedule(&DBImpl::BGWorkCompaction, ca, thread_pool_pri, this,
|
||||
&DBImpl::UnscheduleCompactionCallback);
|
||||
scheduled = true;
|
||||
}
|
||||
}
|
||||
@@ -2358,7 +2398,8 @@ void DBImpl::MaybeScheduleFlushOrCompaction() {
|
||||
return;
|
||||
}
|
||||
|
||||
while (bg_compaction_scheduled_ < bg_job_limits.max_compactions &&
|
||||
while (bg_compaction_scheduled_ + bg_bottom_compaction_scheduled_ <
|
||||
bg_job_limits.max_compactions &&
|
||||
unscheduled_compactions_ > 0) {
|
||||
CompactionArg* ca = new CompactionArg;
|
||||
ca->db = this;
|
||||
@@ -2547,8 +2588,7 @@ void DBImpl::BGWorkBottomCompaction(void* arg) {
|
||||
IOSTATS_SET_THREAD_POOL_ID(Env::Priority::BOTTOM);
|
||||
TEST_SYNC_POINT("DBImpl::BGWorkBottomCompaction");
|
||||
auto* prepicked_compaction = ca.prepicked_compaction;
|
||||
assert(prepicked_compaction && prepicked_compaction->compaction &&
|
||||
!prepicked_compaction->manual_compaction_state);
|
||||
assert(prepicked_compaction && prepicked_compaction->compaction);
|
||||
ca.db->BackgroundCallCompaction(prepicked_compaction, Env::Priority::BOTTOM);
|
||||
delete prepicked_compaction;
|
||||
}
|
||||
@@ -3063,8 +3103,12 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
status = Status::CompactionTooLarge();
|
||||
} else {
|
||||
// update statistics
|
||||
RecordInHistogram(stats_, NUM_FILES_IN_SINGLE_COMPACTION,
|
||||
c->inputs(0)->size());
|
||||
size_t num_files = 0;
|
||||
for (auto& each_level : *c->inputs()) {
|
||||
num_files += each_level.files.size();
|
||||
}
|
||||
RecordInHistogram(stats_, NUM_FILES_IN_SINGLE_COMPACTION, num_files);
|
||||
|
||||
// There are three things that can change compaction score:
|
||||
// 1) When flush or compaction finish. This case is covered by
|
||||
// InstallSuperVersionAndScheduleWork
|
||||
@@ -3149,13 +3193,13 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
for (size_t i = 0; i < c->num_input_files(l); i++) {
|
||||
FileMetaData* f = c->input(l, i);
|
||||
c->edit()->DeleteFile(c->level(l), f->fd.GetNumber());
|
||||
c->edit()->AddFile(c->output_level(), f->fd.GetNumber(),
|
||||
f->fd.GetPathId(), f->fd.GetFileSize(), f->smallest,
|
||||
f->largest, f->fd.smallest_seqno,
|
||||
f->fd.largest_seqno, f->marked_for_compaction,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->file_checksum,
|
||||
f->file_checksum_func_name);
|
||||
c->edit()->AddFile(
|
||||
c->output_level(), f->fd.GetNumber(), f->fd.GetPathId(),
|
||||
f->fd.GetFileSize(), f->smallest, f->largest, f->fd.smallest_seqno,
|
||||
f->fd.largest_seqno, f->marked_for_compaction,
|
||||
f->oldest_blob_file_number, f->oldest_ancester_time,
|
||||
f->file_creation_time, f->file_checksum, f->file_checksum_func_name,
|
||||
f->min_timestamp, f->max_timestamp);
|
||||
|
||||
ROCKS_LOG_BUFFER(
|
||||
log_buffer,
|
||||
@@ -3384,6 +3428,7 @@ bool DBImpl::HasPendingManualCompaction() {
|
||||
}
|
||||
|
||||
void DBImpl::AddManualCompaction(DBImpl::ManualCompactionState* m) {
|
||||
assert(manual_compaction_paused_ == 0);
|
||||
manual_compaction_dequeue_.push_back(m);
|
||||
}
|
||||
|
||||
|
||||
@@ -184,6 +184,14 @@ Status DBImpl::TEST_WaitForCompact(bool wait_unscheduled) {
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_WaitForPurge() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
while (bg_purge_scheduled_ && error_handler_.GetBGError().ok()) {
|
||||
bg_cv_.Wait();
|
||||
}
|
||||
return error_handler_.GetBGError();
|
||||
}
|
||||
|
||||
Status DBImpl::TEST_GetBGError() {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
return error_handler_.GetBGError();
|
||||
|
||||
@@ -131,7 +131,8 @@ Status DBImpl::PromoteL0(ColumnFamilyHandle* column_family, int target_level) {
|
||||
f->fd.smallest_seqno, f->fd.largest_seqno,
|
||||
f->marked_for_compaction, f->oldest_blob_file_number,
|
||||
f->oldest_ancester_time, f->file_creation_time,
|
||||
f->file_checksum, f->file_checksum_func_name);
|
||||
f->file_checksum, f->file_checksum_func_name,
|
||||
f->min_timestamp, f->max_timestamp);
|
||||
}
|
||||
|
||||
status = versions_->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
|
||||
@@ -56,6 +56,8 @@ Status DBImpl::DisableFileDeletions() {
|
||||
return s;
|
||||
}
|
||||
|
||||
// FIXME: can be inconsistent with DisableFileDeletions in cases like
|
||||
// DBImplReadOnly
|
||||
Status DBImpl::DisableFileDeletionsWithLock() {
|
||||
mutex_.AssertHeld();
|
||||
++disable_delete_obsolete_files_;
|
||||
@@ -642,6 +644,11 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
--pending_purge_obsolete_files_;
|
||||
assert(pending_purge_obsolete_files_ >= 0);
|
||||
if (schedule_only) {
|
||||
// Must change from pending_purge_obsolete_files_ to bg_purge_scheduled_
|
||||
// while holding mutex (for GetSortedWalFiles() etc.)
|
||||
SchedulePurge();
|
||||
}
|
||||
if (pending_purge_obsolete_files_ == 0) {
|
||||
bg_cv_.SignalAll();
|
||||
}
|
||||
@@ -655,7 +662,8 @@ void DBImpl::DeleteObsoleteFiles() {
|
||||
|
||||
mutex_.Unlock();
|
||||
if (job_context.HaveSomethingToDelete()) {
|
||||
PurgeObsoleteFiles(job_context);
|
||||
bool defer_purge = immutable_db_options_.avoid_unnecessary_blocking_io;
|
||||
PurgeObsoleteFiles(job_context, defer_purge);
|
||||
}
|
||||
job_context.Clean();
|
||||
mutex_.Lock();
|
||||
|
||||
@@ -200,6 +200,13 @@ DBOptions SanitizeOptions(const std::string& dbname, const DBOptions& src,
|
||||
"file size check will be skipped during open.");
|
||||
}
|
||||
|
||||
if (result.preserve_deletes) {
|
||||
ROCKS_LOG_WARN(
|
||||
result.info_log,
|
||||
"preserve_deletes is deprecated, will be removed in a future release. "
|
||||
"Please try using user-defined timestamp instead.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1463,7 +1470,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
meta.fd.smallest_seqno, meta.fd.largest_seqno,
|
||||
meta.marked_for_compaction, meta.oldest_blob_file_number,
|
||||
meta.oldest_ancester_time, meta.file_creation_time,
|
||||
meta.file_checksum, meta.file_checksum_func_name);
|
||||
meta.file_checksum, meta.file_checksum_func_name,
|
||||
meta.min_timestamp, meta.max_timestamp);
|
||||
|
||||
for (const auto& blob : blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
@@ -1694,10 +1702,6 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
if (impl->two_write_queues_) {
|
||||
impl->log_write_mutex_.Unlock();
|
||||
}
|
||||
|
||||
impl->DeleteObsoleteFiles();
|
||||
s = impl->directories_.GetDbDir()->Fsync(IOOptions(), nullptr);
|
||||
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFilesAndSyncDir");
|
||||
}
|
||||
if (s.ok()) {
|
||||
// In WritePrepared there could be gap in sequence numbers. This breaks
|
||||
@@ -1770,6 +1774,8 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
|
||||
*dbptr = impl;
|
||||
impl->opened_successfully_ = true;
|
||||
impl->DeleteObsoleteFiles();
|
||||
TEST_SYNC_POINT("DBImpl::Open:AfterDeleteFiles");
|
||||
impl->MaybeScheduleFlushOrCompaction();
|
||||
} else {
|
||||
persist_options_status.PermitUncheckedError();
|
||||
|
||||
@@ -156,11 +156,6 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
PERF_TIMER_GUARD(write_pre_and_post_process_time);
|
||||
WriteThread::Writer w(write_options, my_batch, callback, log_ref,
|
||||
disable_memtable, batch_cnt, pre_release_callback);
|
||||
|
||||
if (!write_options.disableWAL) {
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
}
|
||||
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread_.JoinBatchGroup(&w);
|
||||
@@ -681,7 +676,6 @@ Status DBImpl::WriteImplWALOnly(
|
||||
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);
|
||||
RecordTick(stats_, WRITE_WITH_WAL);
|
||||
StopWatch write_sw(immutable_db_options_.clock, stats_, DB_WRITE);
|
||||
|
||||
write_thread->JoinBatchGroup(&w);
|
||||
@@ -1148,7 +1142,9 @@ IOStatus DBImpl::WriteToWAL(const WriteThread::WriteGroup& write_group,
|
||||
// We only sync WAL directory the first time WAL syncing is
|
||||
// requested, so that in case users never turn on WAL sync,
|
||||
// we can avoid the disk I/O in the write code path.
|
||||
io_s = directories_.GetWalDir()->Fsync(IOOptions(), nullptr);
|
||||
io_s = directories_.GetWalDir()->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -588,5 +588,6 @@ TEST_F(DBIOFailureTest, CompactionSstSyncError) {
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+2
-1
@@ -192,10 +192,11 @@ bool DBIter::SetBlobValueIfNeeded(const Slice& user_key,
|
||||
read_options.read_tier = read_tier_;
|
||||
read_options.verify_checksums = verify_checksums_;
|
||||
|
||||
constexpr FilePrefetchBuffer* prefetch_buffer = nullptr;
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
|
||||
const Status s = version_->GetBlob(read_options, user_key, blob_index,
|
||||
&blob_value_, bytes_read);
|
||||
prefetch_buffer, &blob_value_, bytes_read);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
|
||||
@@ -266,6 +266,50 @@ TEST_F(DBOptionsTest, SetMutableTableOptions) {
|
||||
ASSERT_EQ(c_bbto->block_restart_interval, 13);
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetWithCustomMemTableFactory) {
|
||||
class DummySkipListFactory : public SkipListFactory {
|
||||
public:
|
||||
static const char* kClassName() { return "DummySkipListFactory"; }
|
||||
const char* Name() const override { return kClassName(); }
|
||||
explicit DummySkipListFactory() : SkipListFactory(2) {}
|
||||
};
|
||||
{
|
||||
// Verify the DummySkipList cannot be created
|
||||
ConfigOptions config_options;
|
||||
config_options.ignore_unsupported_options = false;
|
||||
std::unique_ptr<MemTableRepFactory> factory;
|
||||
ASSERT_NOK(MemTableRepFactory::CreateFromString(
|
||||
config_options, DummySkipListFactory::kClassName(), &factory));
|
||||
}
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Try with fail_if_options_file_error=false/true to update the options
|
||||
for (bool on_error : {false, true}) {
|
||||
options.fail_if_options_file_error = on_error;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = false;
|
||||
|
||||
options.memtable_factory.reset(new DummySkipListFactory());
|
||||
Reopen(options);
|
||||
|
||||
ColumnFamilyHandle* cfh = dbfull()->DefaultColumnFamily();
|
||||
ASSERT_OK(
|
||||
dbfull()->SetOptions(cfh, {{"disable_auto_compactions", "true"}}));
|
||||
ColumnFamilyDescriptor cfd;
|
||||
ASSERT_OK(cfh->GetDescriptor(&cfd));
|
||||
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
|
||||
DummySkipListFactory::kClassName());
|
||||
ColumnFamilyHandle* test = nullptr;
|
||||
ASSERT_OK(dbfull()->CreateColumnFamily(options, "test", &test));
|
||||
ASSERT_OK(test->GetDescriptor(&cfd));
|
||||
ASSERT_STREQ(cfd.options.memtable_factory->Name(),
|
||||
DummySkipListFactory::kClassName());
|
||||
|
||||
ASSERT_OK(dbfull()->DropColumnFamily(test));
|
||||
delete test;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBOptionsTest, SetBytesPerSync) {
|
||||
const size_t kValueSize = 1024 * 1024; // 1MB
|
||||
Options options;
|
||||
|
||||
@@ -233,7 +233,7 @@ void VerifySimilar(uint64_t a, uint64_t b, double bias) {
|
||||
|
||||
void VerifyTableProperties(
|
||||
const TableProperties& base_tp, const TableProperties& new_tp,
|
||||
double filter_size_bias = CACHE_LINE_SIZE >= 256 ? 0.15 : 0.1,
|
||||
double filter_size_bias = CACHE_LINE_SIZE >= 256 ? 0.18 : 0.1,
|
||||
double index_size_bias = 0.1, double data_size_bias = 0.1,
|
||||
double num_data_blocks_bias = 0.05) {
|
||||
VerifySimilar(base_tp.data_size, new_tp.data_size, data_size_bias);
|
||||
@@ -627,7 +627,8 @@ TEST_F(DBPropertiesTest, AggregatedTablePropertiesAtLevel) {
|
||||
value_is_delta_encoded);
|
||||
// Gives larger bias here as index block size, filter block size,
|
||||
// and data block size become much harder to estimate in this test.
|
||||
VerifyTableProperties(expected_tp, tp, 0.5, 0.4, 0.4, 0.25);
|
||||
VerifyTableProperties(expected_tp, tp, CACHE_LINE_SIZE >= 256 ? 0.6 : 0.5,
|
||||
0.4, 0.4, 0.25);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-9
@@ -690,7 +690,7 @@ TEST_F(DBSecondaryTest, SwitchToNewManifestDuringOpen) {
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:0",
|
||||
"VersionSet::ProcessManifestWrites:BeforeNewManifest"},
|
||||
{"DBImpl::Open:AfterDeleteFilesAndSyncDir",
|
||||
{"DBImpl::Open:AfterDeleteFiles",
|
||||
"ReactiveVersionSet::MaybeSwitchManifest:AfterGetCurrentManifestPath:"
|
||||
"1"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
@@ -851,12 +851,14 @@ TEST_F(DBSecondaryTest, SwitchManifest) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.level0_file_num_compaction_trigger = 4;
|
||||
Reopen(options);
|
||||
const std::string cf1_name("test_cf");
|
||||
CreateAndReopenWithCF({cf1_name}, options);
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
OpenSecondary(options1);
|
||||
OpenSecondaryWithColumnFamilies({kDefaultColumnFamilyName, cf1_name},
|
||||
options1);
|
||||
|
||||
const int kNumFiles = options.level0_file_num_compaction_trigger - 1;
|
||||
// Keep it smaller than 10 so that key0, key1, ..., key9 are sorted as 0, 1,
|
||||
@@ -890,11 +892,11 @@ TEST_F(DBSecondaryTest, SwitchManifest) {
|
||||
// restart primary, performs full compaction, close again, restart again so
|
||||
// that next time secondary tries to catch up with primary, the secondary
|
||||
// will skip the MANIFEST in middle.
|
||||
Reopen(options);
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, cf1_name}, options);
|
||||
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
Reopen(options);
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, cf1_name}, options);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
@@ -905,12 +907,14 @@ TEST_F(DBSecondaryTest, SwitchManifestTwice) {
|
||||
Options options;
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
Reopen(options);
|
||||
const std::string cf1_name("test_cf");
|
||||
CreateAndReopenWithCF({cf1_name}, options);
|
||||
|
||||
Options options1;
|
||||
options1.env = env_;
|
||||
options1.max_open_files = -1;
|
||||
OpenSecondary(options1);
|
||||
OpenSecondaryWithColumnFamilies({kDefaultColumnFamilyName, cf1_name},
|
||||
options1);
|
||||
|
||||
ASSERT_OK(Put("0", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -921,9 +925,9 @@ TEST_F(DBSecondaryTest, SwitchManifestTwice) {
|
||||
ASSERT_OK(db_secondary_->Get(ropts, "0", &value));
|
||||
ASSERT_EQ("value0", value);
|
||||
|
||||
Reopen(options);
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, cf1_name}, options);
|
||||
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
|
||||
Reopen(options);
|
||||
ReopenWithColumnFamilies({kDefaultColumnFamilyName, cf1_name}, options);
|
||||
ASSERT_OK(Put("0", "value1"));
|
||||
ASSERT_OK(db_secondary_->TryCatchUpWithPrimary());
|
||||
|
||||
|
||||
@@ -1681,14 +1681,6 @@ TEST_F(DBSSTTest, DBWithSFMForBlobFilesAtomicFlush) {
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "rocksdb/utilities/table_properties_collectors.h"
|
||||
#include "table/format.h"
|
||||
#include "table/meta_blocks.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
#include "util/random.h"
|
||||
@@ -63,21 +65,49 @@ class DBTablePropertiesTest : public DBTestBase,
|
||||
TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 8;
|
||||
// Part of strategy to prevent pinning table files
|
||||
options.max_open_files = 42;
|
||||
Reopen(options);
|
||||
|
||||
// Create 4 tables
|
||||
for (int table = 0; table < 4; ++table) {
|
||||
// Use old meta name for table properties for one file
|
||||
if (table == 3) {
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:Meta", [&](void* meta) {
|
||||
*reinterpret_cast<const std::string**>(meta) =
|
||||
&kPropertiesBlockOldName;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
// Build file
|
||||
for (int i = 0; i < 10 + table; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), ToString(table * 100 + i), "val"));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
std::string original_session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(original_session_id));
|
||||
|
||||
// Part of strategy to prevent pinning table files
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionEditHandler::LoadTables:skip_load_table_files",
|
||||
[&](void* skip_load) { *reinterpret_cast<bool*>(skip_load) = true; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// 1. Read table properties directly from file
|
||||
Reopen(options);
|
||||
// Clear out auto-opened files
|
||||
dbfull()->TEST_table_cache()->EraseUnRefEntries();
|
||||
ASSERT_EQ(dbfull()->TEST_table_cache()->GetUsage(), 0U);
|
||||
VerifyTableProperties(db_, 10 + 11 + 12 + 13);
|
||||
|
||||
// 2. Put two tables to table cache and
|
||||
Reopen(options);
|
||||
// Clear out auto-opened files
|
||||
dbfull()->TEST_table_cache()->EraseUnRefEntries();
|
||||
ASSERT_EQ(dbfull()->TEST_table_cache()->GetUsage(), 0U);
|
||||
// fetch key from 1st and 2nd table, which will internally place that table to
|
||||
// the table cache.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
@@ -88,12 +118,82 @@ TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
|
||||
|
||||
// 3. Put all tables to table cache
|
||||
Reopen(options);
|
||||
// fetch key from 1st and 2nd table, which will internally place that table to
|
||||
// the table cache.
|
||||
// fetch key from all tables, which will place them in table cache.
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
Get(ToString(i * 100 + 0));
|
||||
}
|
||||
VerifyTableProperties(db_, 10 + 11 + 12 + 13);
|
||||
|
||||
// 4. Try to read CORRUPT properties (a) directly from file, and (b)
|
||||
// through reader on Get
|
||||
|
||||
// It's not practical to prevent table file read on Open, so we
|
||||
// corrupt after open and after purging table cache.
|
||||
for (bool direct : {true, false}) {
|
||||
Reopen(options);
|
||||
// Clear out auto-opened files
|
||||
dbfull()->TEST_table_cache()->EraseUnRefEntries();
|
||||
ASSERT_EQ(dbfull()->TEST_table_cache()->GetUsage(), 0U);
|
||||
|
||||
TablePropertiesCollection props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
|
||||
std::string sst_file = props.begin()->first;
|
||||
|
||||
// Corrupt the file's TableProperties using session id
|
||||
std::string contents;
|
||||
ASSERT_OK(
|
||||
ReadFileToString(env_->GetFileSystem().get(), sst_file, &contents));
|
||||
size_t pos = contents.find(original_session_id);
|
||||
ASSERT_NE(pos, std::string::npos);
|
||||
ASSERT_OK(test::CorruptFile(env_, sst_file, static_cast<int>(pos), 1,
|
||||
/*verify checksum fails*/ false));
|
||||
|
||||
// Try to read CORRUPT properties
|
||||
if (direct) {
|
||||
ASSERT_TRUE(db_->GetPropertiesOfAllTables(&props).IsCorruption());
|
||||
} else {
|
||||
bool found_corruption = false;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
std::string result = Get(ToString(i * 100 + 0));
|
||||
if (result.find_first_of("Corruption: block checksum mismatch") !=
|
||||
std::string::npos) {
|
||||
found_corruption = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(found_corruption);
|
||||
}
|
||||
|
||||
// UN-corrupt file for next iteration
|
||||
ASSERT_OK(test::CorruptFile(env_, sst_file, static_cast<int>(pos), 1,
|
||||
/*verify checksum fails*/ false));
|
||||
}
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, InvalidIgnored) {
|
||||
// RocksDB versions 2.5 - 2.7 generate some properties that Block considers
|
||||
// invalid in some way. This approximates that.
|
||||
|
||||
// Inject properties block data that Block considers invalid
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::WritePropertiesBlock:BlockData",
|
||||
[&](void* block_data) {
|
||||
*reinterpret_cast<Slice*>(block_data) = Slice("X");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Build file
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), ToString(i), "val"));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
// Not crashing is good enough
|
||||
TablePropertiesCollection props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, CreateOnDeletionCollectorFactory) {
|
||||
|
||||
@@ -6956,14 +6956,6 @@ TEST_F(DBTest, MemoryUsageWithMaxWriteBufferSizeToMaintain) {
|
||||
|
||||
} // 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);
|
||||
|
||||
+100
-9
@@ -3973,7 +3973,9 @@ TEST_F(DBTest2, RateLimitedCompactionReads) {
|
||||
ASSERT_OK(Put(Key(j), DummyString(kBytesPerKey)));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
if (i + 1 < kNumL0Files) {
|
||||
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_EQ(0, NumTableFilesAtLevel(0));
|
||||
@@ -6501,6 +6503,7 @@ TEST_F(DBTest2, BottommostTemperature) {
|
||||
Options options = CurrentOptions();
|
||||
options.bottommost_temperature = Temperature::kWarm;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.statistics = CreateDBStatistics();
|
||||
Reopen(options);
|
||||
|
||||
auto size = GetSstSizeHelper(Temperature::kUnknown);
|
||||
@@ -6532,6 +6535,9 @@ TEST_F(DBTest2, BottommostTemperature) {
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_read_count, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.warm_file_read_count, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_read_count, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_BYTES), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_BYTES), 0);
|
||||
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
|
||||
@@ -6541,6 +6547,12 @@ TEST_F(DBTest2, BottommostTemperature) {
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_bytes_read, 0);
|
||||
ASSERT_GT(iostats->file_io_stats_by_temperature.warm_file_bytes_read, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.cold_file_bytes_read, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_BYTES), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_COUNT), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_COUNT), 0);
|
||||
|
||||
// non-bottommost file still has unknown temperature
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
@@ -6553,6 +6565,12 @@ TEST_F(DBTest2, BottommostTemperature) {
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_bytes_read, 0);
|
||||
ASSERT_GT(iostats->file_io_stats_by_temperature.warm_file_bytes_read, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.cold_file_bytes_read, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_BYTES), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_COUNT), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_COUNT), 0);
|
||||
|
||||
db_->GetColumnFamilyMetaData(&metadata);
|
||||
ASSERT_EQ(2, metadata.file_count);
|
||||
@@ -6593,7 +6611,7 @@ TEST_F(DBTest2, BottommostTemperatureUniversal) {
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.level0_file_num_compaction_trigger = kTriggerNum;
|
||||
options.num_levels = kNumLevels;
|
||||
|
||||
options.statistics = CreateDBStatistics();
|
||||
DestroyAndReopen(options);
|
||||
|
||||
auto size = GetSstSizeHelper(Temperature::kUnknown);
|
||||
@@ -6624,6 +6642,12 @@ TEST_F(DBTest2, BottommostTemperatureUniversal) {
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_read_count, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.warm_file_read_count, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_read_count, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(WARM_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(WARM_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ("bar", Get("foo"));
|
||||
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_read_count, 0);
|
||||
@@ -6632,6 +6656,12 @@ TEST_F(DBTest2, BottommostTemperatureUniversal) {
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.hot_file_bytes_read, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.warm_file_bytes_read, 0);
|
||||
ASSERT_EQ(iostats->file_io_stats_by_temperature.cold_file_bytes_read, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(WARM_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(WARM_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_COUNT), 0);
|
||||
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("bar", "bar"));
|
||||
@@ -6667,6 +6697,12 @@ TEST_F(DBTest2, BottommostTemperatureUniversal) {
|
||||
ASSERT_EQ(size, 0);
|
||||
size = GetSstSizeHelper(Temperature::kWarm);
|
||||
ASSERT_GT(size, 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_BYTES), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_BYTES), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(HOT_FILE_READ_COUNT), 0);
|
||||
ASSERT_GT(options.statistics->getTickerCount(WARM_FILE_READ_COUNT), 0);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COLD_FILE_READ_COUNT), 0);
|
||||
|
||||
// non-bottommost file still has unknown temperature
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
@@ -6763,15 +6799,70 @@ TEST_F(DBTest2, RenameDirectory) {
|
||||
Destroy(options);
|
||||
dbname_ = old_dbname;
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
extern "C" {
|
||||
void RegisterCustomObjects(int argc, char** argv);
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, GetLatestSeqAndTsForKey) {
|
||||
Destroy(last_options_);
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.max_write_buffer_size_to_maintain = 64 << 10;
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
options.comparator = test::ComparatorWithU64Ts();
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
Reopen(options);
|
||||
|
||||
constexpr uint64_t kTsU64Value = 12;
|
||||
|
||||
for (uint64_t key = 0; key < 100; ++key) {
|
||||
std::string ts_str;
|
||||
PutFixed64(&ts_str, kTsU64Value);
|
||||
Slice ts = ts_str;
|
||||
WriteOptions write_opts;
|
||||
write_opts.timestamp = &ts;
|
||||
|
||||
std::string key_str;
|
||||
PutFixed64(&key_str, key);
|
||||
std::reverse(key_str.begin(), key_str.end());
|
||||
ASSERT_OK(Put(key_str, "value", write_opts));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
constexpr bool cache_only = true;
|
||||
constexpr SequenceNumber lower_bound_seq = 0;
|
||||
auto* cfhi = static_cast_with_check<ColumnFamilyHandleImpl>(
|
||||
dbfull()->DefaultColumnFamily());
|
||||
assert(cfhi);
|
||||
assert(cfhi->cfd());
|
||||
SuperVersion* sv = cfhi->cfd()->GetSuperVersion();
|
||||
for (uint64_t key = 0; key < 100; ++key) {
|
||||
std::string key_str;
|
||||
PutFixed64(&key_str, key);
|
||||
std::reverse(key_str.begin(), key_str.end());
|
||||
std::string ts;
|
||||
SequenceNumber seq = kMaxSequenceNumber;
|
||||
bool found_record_for_key = false;
|
||||
bool is_blob_index = false;
|
||||
|
||||
const Status s = dbfull()->GetLatestSequenceForKey(
|
||||
sv, key_str, cache_only, lower_bound_seq, &seq, &ts,
|
||||
&found_record_for_key, &is_blob_index);
|
||||
ASSERT_OK(s);
|
||||
std::string expected_ts;
|
||||
PutFixed64(&expected_ts, kTsU64Value);
|
||||
ASSERT_EQ(expected_ts, ts);
|
||||
ASSERT_TRUE(found_record_for_key);
|
||||
ASSERT_FALSE(is_blob_index);
|
||||
}
|
||||
|
||||
// Verify that no read to SST files.
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(GET_HIT_L0));
|
||||
}
|
||||
#else
|
||||
void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {}
|
||||
#endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
|
||||
+2
-6
@@ -475,12 +475,8 @@ Options DBTestBase::GetOptions(
|
||||
case kInfiniteMaxOpenFiles:
|
||||
options.max_open_files = -1;
|
||||
break;
|
||||
case kxxHashChecksum: {
|
||||
table_options.checksum = kxxHash;
|
||||
break;
|
||||
}
|
||||
case kxxHash64Checksum: {
|
||||
table_options.checksum = kxxHash64;
|
||||
case kXXH3Checksum: {
|
||||
table_options.checksum = kXXH3;
|
||||
break;
|
||||
}
|
||||
case kFIFOCompaction: {
|
||||
|
||||
+1
-2
@@ -854,7 +854,7 @@ class DBTestBase : public testing::Test {
|
||||
kUniversalCompactionMultiLevel = 20,
|
||||
kCompressedBlockCache = 21,
|
||||
kInfiniteMaxOpenFiles = 22,
|
||||
kxxHashChecksum = 23,
|
||||
kXXH3Checksum = 23,
|
||||
kFIFOCompaction = 24,
|
||||
kOptimizeFiltersForHits = 25,
|
||||
kRowCache = 26,
|
||||
@@ -869,7 +869,6 @@ class DBTestBase : public testing::Test {
|
||||
kBlockBasedTableWithPartitionedIndexFormat4,
|
||||
kPartitionedFilterWithNewTableReaderForCompactions,
|
||||
kUniversalSubcompactions,
|
||||
kxxHash64Checksum,
|
||||
kUnorderedWrite,
|
||||
// This must be the last line
|
||||
kEnd,
|
||||
|
||||
@@ -1680,6 +1680,7 @@ TEST_P(DBTestUniversalCompaction, ConcurrentBottomPriLowPriCompactions) {
|
||||
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
Options options = CurrentOptions();
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.max_background_compactions = 2;
|
||||
options.num_levels = num_levels_;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
@@ -1688,6 +1689,10 @@ TEST_P(DBTestUniversalCompaction, ConcurrentBottomPriLowPriCompactions) {
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// Need to get a token to enable compaction parallelism up to
|
||||
// `max_background_compactions` jobs.
|
||||
auto pressure_token =
|
||||
dbfull()->TEST_write_controler().GetCompactionPressureToken();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{// wait for the full compaction to be picked before adding files intended
|
||||
// for the second one.
|
||||
|
||||
@@ -287,6 +287,51 @@ TEST_F(DBBasicTestWithTimestamp, CompactRangeWithSpecifiedRange) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, GcPreserveLatestVersionBelowFullHistoryLow) {
|
||||
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);
|
||||
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
WriteOptions wopts;
|
||||
Slice ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(wopts, "k1", "v1"));
|
||||
ASSERT_OK(db_->Put(wopts, "k2", "v2"));
|
||||
ASSERT_OK(db_->Put(wopts, "k3", "v3"));
|
||||
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Delete(wopts, "k3"));
|
||||
|
||||
ts_str = Timestamp(4, 0);
|
||||
ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(wopts, "k1", "v5"));
|
||||
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
CompactRangeOptions cro;
|
||||
cro.full_history_ts_low = &ts;
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ReadOptions ropts;
|
||||
ropts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(ropts, "k1", &value);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("v1", value);
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLow) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -324,6 +369,7 @@ TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLow) {
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// TODO return a non-ok for read ts < current_ts_low and test it.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ReadOptions read_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
@@ -331,7 +377,7 @@ TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLow) {
|
||||
read_opts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status status = db_->Get(read_opts, kKey, &value);
|
||||
if (i < current_ts_low) {
|
||||
if (i < current_ts_low - 1) {
|
||||
ASSERT_TRUE(status.IsNotFound());
|
||||
} else {
|
||||
ASSERT_OK(status);
|
||||
@@ -358,19 +404,16 @@ TEST_F(DBBasicTestWithTimestamp, UpdateFullHistoryTsLow) {
|
||||
result_ts_low = cfd->GetFullHistoryTsLow();
|
||||
ASSERT_TRUE(test_cmp.CompareTimestamp(ts_low, result_ts_low) == 0);
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
// TODO return a non-ok for read ts < current_ts_low and test it.
|
||||
for (int i = current_ts_low; i < 20; i++) {
|
||||
ReadOptions read_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
Slice ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status status = db_->Get(read_opts, kKey, &value);
|
||||
if (i < current_ts_low) {
|
||||
ASSERT_TRUE(status.IsNotFound());
|
||||
} else {
|
||||
ASSERT_OK(status);
|
||||
ASSERT_TRUE(value.compare(Key(i)) == 0);
|
||||
}
|
||||
ASSERT_OK(status);
|
||||
ASSERT_TRUE(value.compare(Key(i)) == 0);
|
||||
}
|
||||
|
||||
// Test invalid compaction with range
|
||||
@@ -552,6 +595,41 @@ TEST_F(DBBasicTestWithTimestamp, SimpleIterate) {
|
||||
Close();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTestWithTimestamp, GetTimestampTableProperties) {
|
||||
Options options = CurrentOptions();
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
DestroyAndReopen(options);
|
||||
// Create 2 tables
|
||||
for (int table = 0; table < 2; ++table) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(i, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "key", Key(i)));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
TablePropertiesCollection props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(&props));
|
||||
ASSERT_EQ(2U, props.size());
|
||||
for (const auto& item : props) {
|
||||
auto& user_collected = item.second->user_collected_properties;
|
||||
ASSERT_TRUE(user_collected.find("rocksdb.timestamp_min") !=
|
||||
user_collected.end());
|
||||
ASSERT_TRUE(user_collected.find("rocksdb.timestamp_max") !=
|
||||
user_collected.end());
|
||||
ASSERT_EQ(user_collected.at("rocksdb.timestamp_min"), Timestamp(0, 0));
|
||||
ASSERT_EQ(user_collected.at("rocksdb.timestamp_max"), Timestamp(9, 0));
|
||||
}
|
||||
Close();
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
class DBBasicTestWithTimestampTableOptions
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<BlockBasedTableOptions::IndexType> {
|
||||
@@ -2632,6 +2710,8 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGetWithCompaction) {
|
||||
// incremental positions such that lowerlevel[1].smallest.userkey ==
|
||||
// higherlevel[0].largest.userkey
|
||||
ASSERT_OK(Flush(cf));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact()); // wait for flush (which
|
||||
// is also a compaction)
|
||||
|
||||
// compact files (2 at each level) to a lower level such that all
|
||||
// keys with the same timestamp is at one level, with newer versions
|
||||
@@ -3082,14 +3162,6 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -785,14 +785,6 @@ INSTANTIATE_TEST_CASE_P(DBWriteBufferManagerTest, DBWriteBufferManagerTest,
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -461,5 +461,6 @@ INSTANTIATE_TEST_CASE_P(DBWriteTestInstance, DBWriteTest,
|
||||
int main(int argc, char** argv) {
|
||||
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -28,6 +28,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// ValueType, not the lowest).
|
||||
const ValueType kValueTypeForSeek = kTypeDeletionWithTimestamp;
|
||||
const ValueType kValueTypeForSeekForPrev = kTypeDeletion;
|
||||
const std::string kDisableUserTimestamp("");
|
||||
|
||||
EntryType GetEntryType(ValueType value_type) {
|
||||
switch (value_type) {
|
||||
|
||||
@@ -93,6 +93,9 @@ static const SequenceNumber kDisableGlobalSequenceNumber = port::kMaxUint64;
|
||||
|
||||
constexpr uint64_t kNumInternalBytes = 8;
|
||||
|
||||
// Defined in dbformat.cc
|
||||
extern const std::string kDisableUserTimestamp;
|
||||
|
||||
// The data structure that represents an internal key in the way that user_key,
|
||||
// sequence number and type are stored in separated forms.
|
||||
struct ParsedInternalKey {
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "db/dbformat.h"
|
||||
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -202,5 +204,6 @@ TEST_F(FormatTest, RangeTombstoneSerializeEndKey) {
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+49
-14
@@ -117,9 +117,15 @@ class DeleteFileTest : public DBTestBase {
|
||||
manifest_cnt += (type == kDescriptorFile);
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(required_log, log_cnt);
|
||||
ASSERT_EQ(required_sst, sst_cnt);
|
||||
ASSERT_EQ(required_manifest, manifest_cnt);
|
||||
if (required_log >= 0) {
|
||||
ASSERT_EQ(required_log, log_cnt);
|
||||
}
|
||||
if (required_sst >= 0) {
|
||||
ASSERT_EQ(required_sst, sst_cnt);
|
||||
}
|
||||
if (required_manifest >= 0) {
|
||||
ASSERT_EQ(required_manifest, manifest_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static void DoSleep(void* arg) {
|
||||
@@ -264,6 +270,41 @@ TEST_F(DeleteFileTest, BackgroundPurgeIteratorTest) {
|
||||
CheckFileTypeCounts(dbname_, 0, 1, 1);
|
||||
}
|
||||
|
||||
TEST_F(DeleteFileTest, PurgeDuringOpen) {
|
||||
Options options = CurrentOptions();
|
||||
CheckFileTypeCounts(dbname_, -1, 0, -1);
|
||||
Close();
|
||||
std::unique_ptr<WritableFile> file;
|
||||
ASSERT_OK(options.env->NewWritableFile(dbname_ + "/000002.sst", &file,
|
||||
EnvOptions()));
|
||||
ASSERT_OK(file->Close());
|
||||
CheckFileTypeCounts(dbname_, -1, 1, -1);
|
||||
options.avoid_unnecessary_blocking_io = false;
|
||||
options.create_if_missing = false;
|
||||
Reopen(options);
|
||||
CheckFileTypeCounts(dbname_, -1, 0, -1);
|
||||
Close();
|
||||
|
||||
// test background purge
|
||||
options.avoid_unnecessary_blocking_io = true;
|
||||
options.create_if_missing = false;
|
||||
ASSERT_OK(options.env->NewWritableFile(dbname_ + "/000002.sst", &file,
|
||||
EnvOptions()));
|
||||
ASSERT_OK(file->Close());
|
||||
CheckFileTypeCounts(dbname_, -1, 1, -1);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DeleteFileTest::PurgeDuringOpen:1", "DBImpl::BGWorkPurge:start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Reopen(options);
|
||||
// the obsolete file is not deleted until the background purge job is ran
|
||||
CheckFileTypeCounts(dbname_, -1, 1, -1);
|
||||
TEST_SYNC_POINT("DeleteFileTest::PurgeDuringOpen:1");
|
||||
ASSERT_OK(dbfull()->TEST_WaitForPurge());
|
||||
CheckFileTypeCounts(dbname_, -1, 0, -1);
|
||||
}
|
||||
|
||||
TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
|
||||
Options options = CurrentOptions();
|
||||
SetOptions(&options);
|
||||
@@ -310,6 +351,11 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
|
||||
do_test(false);
|
||||
}
|
||||
|
||||
options.avoid_unnecessary_blocking_io = true;
|
||||
options.create_if_missing = false;
|
||||
Reopen(options);
|
||||
ASSERT_OK(dbfull()->TEST_WaitForPurge());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
@@ -317,9 +363,6 @@ TEST_F(DeleteFileTest, BackgroundPurgeCFDropTest) {
|
||||
"DBImpl::BGWorkPurge:start"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.avoid_unnecessary_blocking_io = true;
|
||||
options.create_if_missing = false;
|
||||
Reopen(options);
|
||||
{
|
||||
SCOPED_TRACE("avoid_unnecessary_blocking_io = true");
|
||||
do_test(true);
|
||||
@@ -553,14 +596,6 @@ TEST_F(DeleteFileTest, DeleteNonDefaultColumnFamily) {
|
||||
|
||||
} // 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);
|
||||
|
||||
+13
-23
@@ -406,10 +406,7 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
// 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;
|
||||
}
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_ERROR_COUNT);
|
||||
RecordTick(bg_error_stats_.get(), ERROR_HANDLER_BG_IO_ERROR_COUNT);
|
||||
@@ -468,24 +465,14 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
// to many small memtable being generated during auto resume, the flush
|
||||
// reason is set to kErrorRecoveryRetryFlush.
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kSoftError);
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_err;
|
||||
}
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
soft_error_no_bg_work_ = true;
|
||||
context.flush_reason = FlushReason::kErrorRecoveryRetryFlush;
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
} else {
|
||||
Status bg_err(new_bg_io_err, Status::Severity::kHardError);
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_err;
|
||||
}
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
CheckAndSetRecoveryAndBGError(bg_err);
|
||||
recover_context_ = context;
|
||||
return StartRecoverFromRetryableBGIOError(bg_io_err);
|
||||
}
|
||||
@@ -663,7 +650,6 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
if (recovery_io_error_.ok() && recovery_error_.ok()) {
|
||||
return recovery_error_;
|
||||
} else {
|
||||
TEST_SYNC_POINT("StartRecoverRetryableBGIOError:RecoverFail");
|
||||
return bg_error_;
|
||||
}
|
||||
#else
|
||||
@@ -677,7 +663,6 @@ const Status& ErrorHandler::StartRecoverFromRetryableBGIOError(
|
||||
void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeStart");
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeStart1");
|
||||
InstrumentedMutexLock l(db_mutex_);
|
||||
if (end_recovery_) {
|
||||
return;
|
||||
@@ -697,8 +682,6 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
recovery_error_ = Status::OK();
|
||||
retry_count++;
|
||||
Status s = db_->ResumeImpl(context);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterResume0");
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterResume1");
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordTick(bg_error_stats_.get(),
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT);
|
||||
@@ -707,7 +690,6 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
bg_error_.severity() >= Status::Severity::kFatalError) {
|
||||
// If DB shutdown in progress or the error severity is higher than
|
||||
// Hard Error, stop auto resume and returns.
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:RecoverFail0");
|
||||
recovery_in_prog_ = false;
|
||||
if (bg_error_stats_ != nullptr) {
|
||||
RecordInHistogram(bg_error_stats_.get(),
|
||||
@@ -725,7 +707,6 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:BeforeWait1");
|
||||
int64_t wait_until = db_options_.clock->NowMicros() + wait_interval;
|
||||
cv_.TimedWait(wait_until);
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:AfterWait0");
|
||||
} else {
|
||||
// There are three possibility: 1) recover_io_error is set during resume
|
||||
// and the error is not retryable, 2) recover is successful, 3) other
|
||||
@@ -751,7 +732,6 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
TEST_SYNC_POINT("RecoverFromRetryableBGIOError:RecoverFail1");
|
||||
// In this case: 1) recovery_io_error is more serious or not retryable
|
||||
// 2) other Non IO recovery_error happens. The auto recovery stops.
|
||||
recovery_in_prog_ = false;
|
||||
@@ -776,6 +756,16 @@ void ErrorHandler::RecoverFromRetryableBGIOError() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ErrorHandler::CheckAndSetRecoveryAndBGError(const Status& bg_err) {
|
||||
if (recovery_in_prog_ && recovery_error_.ok()) {
|
||||
recovery_error_ = bg_err;
|
||||
}
|
||||
if (bg_err.severity() > bg_error_.severity()) {
|
||||
bg_error_ = bg_err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void ErrorHandler::EndAutoRecovery() {
|
||||
db_mutex_->AssertHeld();
|
||||
if (!end_recovery_) {
|
||||
|
||||
@@ -116,6 +116,10 @@ class ErrorHandler {
|
||||
void RecoverFromNoSpace();
|
||||
const Status& StartRecoverFromRetryableBGIOError(const IOStatus& io_error);
|
||||
void RecoverFromRetryableBGIOError();
|
||||
// First, if it is in recovery and the recovery_error is ok. Set the
|
||||
// recovery_error_ to bg_err. Second, if the severity is higher than the
|
||||
// current bg_error_, overwrite it.
|
||||
void CheckAndSetRecoveryAndBGError(const Status& bg_err);
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -487,6 +487,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWALAtomicWriteRetryableError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// The flush error is injected before we finish the table build
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -542,6 +543,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableError1) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// The retryable IO error is injected before we sync table
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError2) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -585,6 +587,7 @@ TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// The retryable IO error is injected before we close the table file
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteNoWALRetryableError3) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
@@ -1874,6 +1877,8 @@ TEST_F(DBErrorHandlingFSTest, FLushWritNoWALRetryableErrorAutoRecover2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// Auto resume fromt the flush retryable IO error. Activate the FS before the
|
||||
// first resume. Resume is successful
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover1) {
|
||||
// Activate the FS before the first resume
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
@@ -1914,6 +1919,8 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover1) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// Auto resume fromt the flush retryable IO error and set the retry limit count.
|
||||
// Never activate the FS and auto resume should fail at the end
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover2) {
|
||||
// Fail all the resume and let user to resume
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
@@ -1963,6 +1970,8 @@ TEST_F(DBErrorHandlingFSTest, FLushWritRetryableErrorAutoRecover2) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// Auto resume fromt the flush retryable IO error and set the retry limit count.
|
||||
// Fail the first resume and let the second resume be successful.
|
||||
TEST_F(DBErrorHandlingFSTest, ManifestWriteRetryableErrorAutoRecover) {
|
||||
// Fail the first resume and let the second resume be successful
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
|
||||
@@ -1806,14 +1806,6 @@ INSTANTIATE_TEST_CASE_P(ExternalSSTFileBasicTest, ExternalSSTFileBasicTest,
|
||||
|
||||
} // 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);
|
||||
|
||||
@@ -168,7 +168,9 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
TEST_SYNC_POINT("ExternalSstFileIngestionJob::BeforeSyncDir");
|
||||
if (status.ok()) {
|
||||
for (auto path_id : ingestion_path_ids) {
|
||||
status = directories_->GetDataDir(path_id)->Fsync(IOOptions(), nullptr);
|
||||
status = directories_->GetDataDir(path_id)->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
if (!status.ok()) {
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Failed to sync directory %" ROCKSDB_PRIszt
|
||||
@@ -439,7 +441,8 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
f.fd.GetNumber(), f.fd.GetPathId(), f.fd.GetFileSize(),
|
||||
f.smallest_internal_key, f.largest_internal_key, f.assigned_seqno,
|
||||
f.assigned_seqno, false, kInvalidBlobFileNumber, oldest_ancester_time,
|
||||
current_time, f.file_checksum, f.file_checksum_func_name);
|
||||
current_time, f.file_checksum, f.file_checksum_func_name,
|
||||
kDisableUserTimestamp, kDisableUserTimestamp);
|
||||
f_metadata.temperature = f.file_temperature;
|
||||
edit_.AddFile(f.picked_level, f_metadata);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "file/filename.h"
|
||||
#include "options/options_helper.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_reader.h"
|
||||
@@ -2383,10 +2384,9 @@ TEST_F(ExternalSSTFileTest, IngestFileWrittenWithCompressionDictionary) {
|
||||
|
||||
// Very slow, not worth the cost to run regularly
|
||||
TEST_F(ExternalSSTFileTest, DISABLED_HugeBlockChecksum) {
|
||||
int max_checksum = static_cast<int>(kxxHash64);
|
||||
for (int i = 0; i <= max_checksum; ++i) {
|
||||
for (auto t : GetSupportedChecksums()) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.checksum = static_cast<ChecksumType>(i);
|
||||
table_options.checksum = t;
|
||||
Options options = CurrentOptions();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "env/mock_env.h"
|
||||
#include "file/filename.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/table.h"
|
||||
@@ -81,7 +82,11 @@ class FaultInjectionTest
|
||||
sync_use_compact_(true),
|
||||
base_env_(nullptr),
|
||||
env_(nullptr),
|
||||
db_(nullptr) {}
|
||||
db_(nullptr) {
|
||||
EXPECT_OK(
|
||||
test::CreateEnvFromSystem(ConfigOptions(), &system_env_, &env_guard_));
|
||||
EXPECT_NE(system_env_, nullptr);
|
||||
}
|
||||
|
||||
~FaultInjectionTest() override {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -94,7 +99,7 @@ class FaultInjectionTest
|
||||
return false;
|
||||
} else {
|
||||
if (option_config_ == kMultiLevels) {
|
||||
base_env_.reset(MockEnv::Create(Env::Default()));
|
||||
base_env_.reset(MockEnv::Create(system_env_));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -146,8 +151,7 @@ class FaultInjectionTest
|
||||
assert(tiny_cache_ == nullptr);
|
||||
assert(env_ == nullptr);
|
||||
|
||||
env_ =
|
||||
new FaultInjectionTestEnv(base_env_ ? base_env_.get() : Env::Default());
|
||||
env_ = new FaultInjectionTestEnv(base_env_ ? base_env_.get() : system_env_);
|
||||
|
||||
options_ = CurrentOptions();
|
||||
options_.env = env_;
|
||||
@@ -345,6 +349,10 @@ class FaultInjectionTest
|
||||
ASSERT_OK(static_cast<DBImpl*>(db_->GetRootDB())->TEST_WaitForCompact());
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "", ""));
|
||||
}
|
||||
|
||||
private:
|
||||
Env* system_env_;
|
||||
std::shared_ptr<Env> env_guard_;
|
||||
};
|
||||
|
||||
class FaultInjectionTestSplitted : public FaultInjectionTest {};
|
||||
@@ -552,5 +560,6 @@ INSTANTIATE_TEST_CASE_P(
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
RegisterCustomObjects(argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
+5
-2
@@ -937,7 +937,9 @@ Status FlushJob::WriteLevel0Table() {
|
||||
meta_.marked_for_compaction ? " (needs compaction)" : "");
|
||||
|
||||
if (s.ok() && output_file_directory_ != nullptr && sync_output_directory_) {
|
||||
s = output_file_directory_->Fsync(IOOptions(), nullptr);
|
||||
s = output_file_directory_->FsyncWithDirOptions(
|
||||
IOOptions(), nullptr,
|
||||
DirFsyncOptions(DirFsyncOptions::FsyncReason::kNewFileSynced));
|
||||
}
|
||||
TEST_SYNC_POINT_CALLBACK("FlushJob::WriteLevel0Table", &mems_);
|
||||
db_mutex_->Lock();
|
||||
@@ -960,7 +962,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
meta_.fd.smallest_seqno, meta_.fd.largest_seqno,
|
||||
meta_.marked_for_compaction, meta_.oldest_blob_file_number,
|
||||
meta_.oldest_ancester_time, meta_.file_creation_time,
|
||||
meta_.file_checksum, meta_.file_checksum_func_name);
|
||||
meta_.file_checksum, meta_.file_checksum_func_name,
|
||||
meta_.min_timestamp, meta_.max_timestamp);
|
||||
|
||||
edit_->SetBlobFileAdditions(std::move(blob_file_additions));
|
||||
}
|
||||
|
||||
@@ -155,7 +155,8 @@ Status ImportColumnFamilyJob::Run() {
|
||||
f.largest_internal_key, file_metadata.smallest_seqno,
|
||||
file_metadata.largest_seqno, false, kInvalidBlobFileNumber,
|
||||
oldest_ancester_time, current_time, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
kUnknownFileChecksumFuncName, kDisableUserTimestamp,
|
||||
kDisableUserTimestamp);
|
||||
|
||||
// If incoming sequence number is higher, update local sequence number.
|
||||
if (file_metadata.largest_seqno > versions_->LastSequence()) {
|
||||
|
||||
@@ -1519,9 +1519,11 @@ void InternalStats::DumpCFMapStats(
|
||||
}
|
||||
}
|
||||
// Cumulative summary
|
||||
double w_amp = (compaction_stats_sum->bytes_written +
|
||||
compaction_stats_sum->bytes_written_blob) /
|
||||
static_cast<double>(curr_ingest + 1);
|
||||
double w_amp = (0 == curr_ingest)
|
||||
? 0.0
|
||||
: (compaction_stats_sum->bytes_written +
|
||||
compaction_stats_sum->bytes_written_blob) /
|
||||
static_cast<double>(curr_ingest);
|
||||
// Stats summary across levels
|
||||
std::map<LevelStatType, double> sum_stats;
|
||||
PrepareLevelStats(&sum_stats, total_files, total_files_being_compacted,
|
||||
|
||||
+8
-8
@@ -56,7 +56,7 @@ using ProtectionInfoKVOS64 = ProtectionInfoKVOS<uint64_t>;
|
||||
template <typename T>
|
||||
class ProtectionInfo {
|
||||
public:
|
||||
ProtectionInfo<T>() = default;
|
||||
ProtectionInfo() = default;
|
||||
|
||||
Status GetStatus() const;
|
||||
ProtectionInfoKVO<T> ProtectKVO(const Slice& key, const Slice& value,
|
||||
@@ -83,7 +83,7 @@ class ProtectionInfo {
|
||||
static const uint64_t kSeedS = 0x77A00858DDD37F21;
|
||||
static const uint64_t kSeedC = 0x4A2AB5CBD26F542C;
|
||||
|
||||
ProtectionInfo<T>(T val) : val_(val) {
|
||||
ProtectionInfo(T val) : val_(val) {
|
||||
static_assert(sizeof(ProtectionInfo<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class ProtectionInfo {
|
||||
template <typename T>
|
||||
class ProtectionInfoKVO {
|
||||
public:
|
||||
ProtectionInfoKVO<T>() = default;
|
||||
ProtectionInfoKVO() = default;
|
||||
|
||||
ProtectionInfo<T> StripKVO(const Slice& key, const Slice& value,
|
||||
ValueType op_type) const;
|
||||
@@ -117,7 +117,7 @@ class ProtectionInfoKVO {
|
||||
friend class ProtectionInfoKVOS<T>;
|
||||
friend class ProtectionInfoKVOC<T>;
|
||||
|
||||
explicit ProtectionInfoKVO<T>(T val) : info_(val) {
|
||||
explicit ProtectionInfoKVO(T val) : info_(val) {
|
||||
static_assert(sizeof(ProtectionInfoKVO<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class ProtectionInfoKVO {
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOC {
|
||||
public:
|
||||
ProtectionInfoKVOC<T>() = default;
|
||||
ProtectionInfoKVOC() = default;
|
||||
|
||||
ProtectionInfoKVO<T> StripC(ColumnFamilyId column_family_id) const;
|
||||
|
||||
@@ -155,7 +155,7 @@ class ProtectionInfoKVOC {
|
||||
private:
|
||||
friend class ProtectionInfoKVO<T>;
|
||||
|
||||
explicit ProtectionInfoKVOC<T>(T val) : kvo_(val) {
|
||||
explicit ProtectionInfoKVOC(T val) : kvo_(val) {
|
||||
static_assert(sizeof(ProtectionInfoKVOC<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ class ProtectionInfoKVOC {
|
||||
template <typename T>
|
||||
class ProtectionInfoKVOS {
|
||||
public:
|
||||
ProtectionInfoKVOS<T>() = default;
|
||||
ProtectionInfoKVOS() = default;
|
||||
|
||||
ProtectionInfoKVO<T> StripS(SequenceNumber sequence_number) const;
|
||||
|
||||
@@ -193,7 +193,7 @@ class ProtectionInfoKVOS {
|
||||
private:
|
||||
friend class ProtectionInfoKVO<T>;
|
||||
|
||||
explicit ProtectionInfoKVOS<T>(T val) : kvo_(val) {
|
||||
explicit ProtectionInfoKVOS(T val) : kvo_(val) {
|
||||
static_assert(sizeof(ProtectionInfoKVOS<T>) == sizeof(T), "");
|
||||
}
|
||||
|
||||
|
||||
@@ -377,6 +377,7 @@ TEST_F(EventListenerTest, MultiCF) {
|
||||
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
ASSERT_EQ(listener->flushed_dbs_.size(), i);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
@@ -771,6 +772,7 @@ class TableFileCreationListener : public EventListener {
|
||||
} else {
|
||||
if (idx >= 0) {
|
||||
failure_[idx]++;
|
||||
last_failure_ = info.status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -778,6 +780,7 @@ class TableFileCreationListener : public EventListener {
|
||||
int started_[2];
|
||||
int finished_[2];
|
||||
int failure_[2];
|
||||
Status last_failure_;
|
||||
};
|
||||
|
||||
TEST_F(EventListenerTest, TableFileCreationListenersTest) {
|
||||
@@ -800,6 +803,7 @@ TEST_F(EventListenerTest, TableFileCreationListenersTest) {
|
||||
test_env->SetStatus(Status::NotSupported("not supported"));
|
||||
ASSERT_NOK(Flush());
|
||||
listener->CheckAndResetCounters(1, 1, 1, 0, 0, 0);
|
||||
ASSERT_TRUE(listener->last_failure_.IsNotSupported());
|
||||
test_env->SetStatus(Status::OK());
|
||||
|
||||
Reopen(options);
|
||||
@@ -816,6 +820,14 @@ TEST_F(EventListenerTest, TableFileCreationListenersTest) {
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
listener->CheckAndResetCounters(0, 0, 0, 1, 1, 0);
|
||||
|
||||
// Verify that an empty table file that is immediately deleted gives Aborted
|
||||
// status to listener.
|
||||
ASSERT_OK(Put("baz", "z"));
|
||||
ASSERT_OK(SingleDelete("baz"));
|
||||
ASSERT_OK(Flush());
|
||||
listener->CheckAndResetCounters(1, 1, 1, 0, 0, 0);
|
||||
ASSERT_TRUE(listener->last_failure_.IsAborted());
|
||||
|
||||
ASSERT_OK(Put("foo", "aaa3"));
|
||||
ASSERT_OK(Put("bar", "bbb3"));
|
||||
ASSERT_OK(Flush());
|
||||
@@ -824,6 +836,7 @@ TEST_F(EventListenerTest, TableFileCreationListenersTest) {
|
||||
dbfull()->CompactRange(CompactRangeOptions(), &kRangeStart, &kRangeEnd));
|
||||
ASSERT_NOK(dbfull()->TEST_WaitForCompact());
|
||||
listener->CheckAndResetCounters(1, 1, 0, 1, 1, 1);
|
||||
ASSERT_TRUE(listener->last_failure_.IsNotSupported());
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
+31
-4
@@ -8,6 +8,9 @@
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_fetcher.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/prefetch_buffer_collection.h"
|
||||
#include "db/compaction/compaction_iteration_stats.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "monitoring/perf_context_imp.h"
|
||||
#include "monitoring/statistics.h"
|
||||
@@ -121,7 +124,9 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
const SequenceNumber stop_before,
|
||||
const bool at_bottom,
|
||||
const bool allow_data_in_errors,
|
||||
Version* version) {
|
||||
const BlobFetcher* blob_fetcher,
|
||||
PrefetchBufferCollection* prefetch_buffers,
|
||||
CompactionIterationStats* c_iter_stats) {
|
||||
// Get a copy of the internal key, before it's invalidated by iter->Next()
|
||||
// Also maintain the list of merge operands seen.
|
||||
assert(HasOperator());
|
||||
@@ -212,13 +217,35 @@ Status MergeHelper::MergeUntil(InternalIterator* iter,
|
||||
!range_del_agg->ShouldDelete(
|
||||
ikey, RangeDelPositioningMode::kForwardTraversal))) {
|
||||
if (ikey.type == kTypeBlobIndex) {
|
||||
assert(version);
|
||||
BlobFetcher blob_fetcher(version, ReadOptions());
|
||||
s = blob_fetcher.FetchBlob(ikey.user_key, val, &blob_value);
|
||||
BlobIndex blob_index;
|
||||
|
||||
s = blob_index.DecodeFrom(val);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
FilePrefetchBuffer* prefetch_buffer =
|
||||
prefetch_buffers ? prefetch_buffers->GetOrCreatePrefetchBuffer(
|
||||
blob_index.file_number())
|
||||
: nullptr;
|
||||
|
||||
uint64_t bytes_read = 0;
|
||||
|
||||
assert(blob_fetcher);
|
||||
|
||||
s = blob_fetcher->FetchBlob(ikey.user_key, blob_index,
|
||||
prefetch_buffer, &blob_value,
|
||||
&bytes_read);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
val_ptr = &blob_value;
|
||||
|
||||
if (c_iter_stats) {
|
||||
++c_iter_stats->num_blobs_read;
|
||||
c_iter_stats->total_blob_bytes_read += bytes_read;
|
||||
}
|
||||
} else {
|
||||
val_ptr = &val;
|
||||
}
|
||||
|
||||
+13
-6
@@ -26,7 +26,9 @@ class Logger;
|
||||
class MergeOperator;
|
||||
class Statistics;
|
||||
class SystemClock;
|
||||
class Version;
|
||||
class BlobFetcher;
|
||||
class PrefetchBufferCollection;
|
||||
struct CompactionIterationStats;
|
||||
|
||||
class MergeHelper {
|
||||
public:
|
||||
@@ -70,6 +72,10 @@ class MergeHelper {
|
||||
// we could reach the start of the history of this user key.
|
||||
// allow_data_in_errors: (IN) if true, data details will be displayed in
|
||||
// error/log messages.
|
||||
// blob_fetcher: (IN) blob fetcher object for the compaction's input version.
|
||||
// prefetch_buffers: (IN/OUT) a collection of blob file prefetch buffers
|
||||
// used for compaction readahead.
|
||||
// c_iter_stats: (OUT) compaction iteration statistics.
|
||||
//
|
||||
// Returns one of the following statuses:
|
||||
// - OK: Entries were successfully merged.
|
||||
@@ -82,11 +88,12 @@ class MergeHelper {
|
||||
//
|
||||
// REQUIRED: The first key in the input is not corrupted.
|
||||
Status MergeUntil(InternalIterator* iter,
|
||||
CompactionRangeDelAggregator* range_del_agg = nullptr,
|
||||
const SequenceNumber stop_before = 0,
|
||||
const bool at_bottom = false,
|
||||
const bool allow_data_in_errors = false,
|
||||
Version* version = nullptr);
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
const SequenceNumber stop_before, const bool at_bottom,
|
||||
const bool allow_data_in_errors,
|
||||
const BlobFetcher* blob_fetcher,
|
||||
PrefetchBufferCollection* prefetch_buffers,
|
||||
CompactionIterationStats* c_iter_stats);
|
||||
|
||||
// Filters a merge operand using the compaction filter specified
|
||||
// in the constructor. Returns the decision that the filter made.
|
||||
|
||||
@@ -32,8 +32,10 @@ class MergeHelperTest : public testing::Test {
|
||||
merge_helper_.reset(new MergeHelper(env_, icmp_.user_comparator(),
|
||||
merge_op_.get(), filter_.get(), nullptr,
|
||||
false, latest_snapshot));
|
||||
return merge_helper_->MergeUntil(iter_.get(), nullptr /* range_del_agg */,
|
||||
stop_before, at_bottom);
|
||||
return merge_helper_->MergeUntil(
|
||||
iter_.get(), nullptr /* range_del_agg */, stop_before, at_bottom,
|
||||
false /* allow_data_in_errors */, nullptr /* blob_fetcher */,
|
||||
nullptr /* prefetch_buffers */, nullptr /* c_iter_stats */);
|
||||
}
|
||||
|
||||
void AddKeyVal(const std::string& user_key, const SequenceNumber& seq,
|
||||
|
||||
@@ -308,14 +308,6 @@ TEST_F(ObsoleteFilesTest, BlobFiles) {
|
||||
|
||||
} // 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);
|
||||
|
||||
+18
-22
@@ -266,24 +266,22 @@ class TestPlainTableReader : public PlainTableReader {
|
||||
const EnvOptions& env_options, const InternalKeyComparator& icomparator,
|
||||
EncodingType encoding_type, uint64_t file_size, int bloom_bits_per_key,
|
||||
double hash_table_ratio, size_t index_sparseness,
|
||||
const TableProperties* table_properties,
|
||||
std::unique_ptr<TableProperties>&& props,
|
||||
std::unique_ptr<RandomAccessFileReader>&& file,
|
||||
const ImmutableOptions& ioptions, const SliceTransform* prefix_extractor,
|
||||
bool* expect_bloom_not_match, bool store_index_in_file,
|
||||
uint32_t column_family_id, const std::string& column_family_name)
|
||||
: PlainTableReader(ioptions, std::move(file), env_options, icomparator,
|
||||
encoding_type, file_size, table_properties,
|
||||
encoding_type, file_size, props.get(),
|
||||
prefix_extractor),
|
||||
expect_bloom_not_match_(expect_bloom_not_match) {
|
||||
Status s = MmapDataIfNeeded();
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
s = PopulateIndex(const_cast<TableProperties*>(table_properties),
|
||||
bloom_bits_per_key, hash_table_ratio, index_sparseness,
|
||||
2 * 1024 * 1024);
|
||||
s = PopulateIndex(props.get(), bloom_bits_per_key, hash_table_ratio,
|
||||
index_sparseness, 2 * 1024 * 1024);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
TableProperties* props = const_cast<TableProperties*>(table_properties);
|
||||
EXPECT_EQ(column_family_id, static_cast<uint32_t>(props->column_family_id));
|
||||
EXPECT_EQ(column_family_name, props->column_family_name);
|
||||
if (store_index_in_file) {
|
||||
@@ -297,7 +295,7 @@ class TestPlainTableReader : public PlainTableReader {
|
||||
EXPECT_TRUE(num_blocks_ptr != props->user_collected_properties.end());
|
||||
}
|
||||
}
|
||||
table_properties_.reset(props);
|
||||
table_properties_ = std::move(props);
|
||||
}
|
||||
|
||||
~TestPlainTableReader() override {}
|
||||
@@ -337,26 +335,24 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
std::unique_ptr<RandomAccessFileReader>&& file, uint64_t file_size,
|
||||
std::unique_ptr<TableReader>* table,
|
||||
bool /*prefetch_index_and_filter_in_cache*/) const override {
|
||||
TableProperties* props = nullptr;
|
||||
auto s =
|
||||
ReadTableProperties(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions, &props,
|
||||
true /* compression_type_missing */);
|
||||
std::unique_ptr<TableProperties> props;
|
||||
auto s = ReadTableProperties(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions, &props);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
if (store_index_in_file_) {
|
||||
BlockHandle bloom_block_handle;
|
||||
s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions,
|
||||
BloomBlockBuilder::kBloomBlock, &bloom_block_handle,
|
||||
/* compression_type_missing */ true);
|
||||
s = FindMetaBlockInFile(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions,
|
||||
BloomBlockBuilder::kBloomBlock,
|
||||
&bloom_block_handle);
|
||||
EXPECT_TRUE(s.ok());
|
||||
|
||||
BlockHandle index_block_handle;
|
||||
s = FindMetaBlock(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions,
|
||||
PlainTableIndexBuilder::kPlainTableIndexBlock,
|
||||
&index_block_handle, /* compression_type_missing */ true);
|
||||
s = FindMetaBlockInFile(file.get(), file_size, kPlainTableMagicNumber,
|
||||
table_reader_options.ioptions,
|
||||
PlainTableIndexBuilder::kPlainTableIndexBlock,
|
||||
&index_block_handle);
|
||||
EXPECT_TRUE(s.ok());
|
||||
}
|
||||
|
||||
@@ -370,8 +366,8 @@ class TestPlainTableFactory : public PlainTableFactory {
|
||||
std::unique_ptr<PlainTableReader> new_reader(new TestPlainTableReader(
|
||||
table_reader_options.env_options,
|
||||
table_reader_options.internal_comparator, encoding_type, file_size,
|
||||
bloom_bits_per_key_, hash_table_ratio_, index_sparseness_, props,
|
||||
std::move(file), table_reader_options.ioptions,
|
||||
bloom_bits_per_key_, hash_table_ratio_, index_sparseness_,
|
||||
std::move(props), std::move(file), table_reader_options.ioptions,
|
||||
table_reader_options.prefix_extractor, expect_bloom_not_match_,
|
||||
store_index_in_file_, column_family_id_, column_family_name_));
|
||||
|
||||
|
||||
+2
-1
@@ -635,7 +635,8 @@ class Repairer {
|
||||
table->meta.fd.largest_seqno, table->meta.marked_for_compaction,
|
||||
table->meta.oldest_blob_file_number,
|
||||
table->meta.oldest_ancester_time, table->meta.file_creation_time,
|
||||
table->meta.file_checksum, table->meta.file_checksum_func_name);
|
||||
table->meta.file_checksum, table->meta.file_checksum_func_name,
|
||||
table->meta.min_timestamp, table->meta.max_timestamp);
|
||||
}
|
||||
assert(next_file_number_ > 0);
|
||||
vset_.MarkFileNumberUsed(next_file_number_ - 1);
|
||||
|
||||
@@ -655,6 +655,16 @@ size_t TableCache::GetMemoryUsageByTableReader(
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool TableCache::HasEntry(Cache* cache, uint64_t file_number) {
|
||||
Cache::Handle* handle = cache->Lookup(GetSliceForFileNumber(&file_number));
|
||||
if (handle) {
|
||||
cache->Release(handle);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void TableCache::Evict(Cache* cache, uint64_t file_number) {
|
||||
cache->Erase(GetSliceForFileNumber(&file_number));
|
||||
}
|
||||
|
||||
@@ -125,6 +125,9 @@ class TableCache {
|
||||
// Evict any entry for the specified file number
|
||||
static void Evict(Cache* cache, uint64_t file_number);
|
||||
|
||||
// Query whether specified file number is currently in cache
|
||||
static bool HasEntry(Cache* cache, uint64_t file_number);
|
||||
|
||||
// Clean table handle and erase it from the table cache
|
||||
// Used in DB close, or the file is not live anymore.
|
||||
void EraseHandle(const FileDescriptor& fd, Cache::Handle* handle);
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
// This file defines a collection of statistics collectors.
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/table_properties.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "rocksdb/table_properties.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// Base class for internal table properties collector.
|
||||
@@ -108,4 +110,66 @@ class UserKeyTablePropertiesCollectorFactory
|
||||
std::shared_ptr<TablePropertiesCollectorFactory> user_collector_factory_;
|
||||
};
|
||||
|
||||
// When rocksdb creates a newtable, it will encode all "user keys" into
|
||||
// "internal keys". This class collects min/max timestamp from the encoded
|
||||
// internal key when Add() is invoked.
|
||||
//
|
||||
// @param cmp the user comparator to compare the timestamps in internal key.
|
||||
class TimestampTablePropertiesCollector : public IntTblPropCollector {
|
||||
public:
|
||||
explicit TimestampTablePropertiesCollector(const Comparator* cmp)
|
||||
: cmp_(cmp),
|
||||
timestamp_min_(kDisableUserTimestamp),
|
||||
timestamp_max_(kDisableUserTimestamp) {}
|
||||
|
||||
Status InternalAdd(const Slice& key, const Slice& /* value */,
|
||||
uint64_t /* file_size */) override {
|
||||
auto user_key = ExtractUserKey(key);
|
||||
assert(cmp_ && cmp_->timestamp_size() > 0);
|
||||
if (user_key.size() < cmp_->timestamp_size()) {
|
||||
return Status::Corruption(
|
||||
"User key size mismatch when comparing to timestamp size.");
|
||||
}
|
||||
auto timestamp_in_key =
|
||||
ExtractTimestampFromUserKey(user_key, cmp_->timestamp_size());
|
||||
if (timestamp_max_ == kDisableUserTimestamp ||
|
||||
cmp_->CompareTimestamp(timestamp_in_key, timestamp_max_) > 0) {
|
||||
timestamp_max_.assign(timestamp_in_key.data(), timestamp_in_key.size());
|
||||
}
|
||||
if (timestamp_min_ == kDisableUserTimestamp ||
|
||||
cmp_->CompareTimestamp(timestamp_min_, timestamp_in_key) > 0) {
|
||||
timestamp_min_.assign(timestamp_in_key.data(), timestamp_in_key.size());
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void BlockAdd(uint64_t /* block_raw_bytes */,
|
||||
uint64_t /* block_compressed_bytes_fast */,
|
||||
uint64_t /* block_compressed_bytes_slow */) override {
|
||||
return;
|
||||
}
|
||||
|
||||
Status Finish(UserCollectedProperties* properties) override {
|
||||
assert(timestamp_min_.size() == timestamp_max_.size() &&
|
||||
timestamp_max_.size() == cmp_->timestamp_size());
|
||||
properties->insert({"rocksdb.timestamp_min", timestamp_min_});
|
||||
properties->insert({"rocksdb.timestamp_max", timestamp_max_});
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
const char* Name() const override {
|
||||
return "TimestampTablePropertiesCollector";
|
||||
}
|
||||
|
||||
UserCollectedProperties GetReadableProperties() const override {
|
||||
return {{"rocksdb.timestamp_min", Slice(timestamp_min_).ToString(true)},
|
||||
{"rocksdb.timestamp_max", Slice(timestamp_max_).ToString(true)}};
|
||||
}
|
||||
|
||||
protected:
|
||||
const Comparator* const cmp_;
|
||||
std::string timestamp_min_;
|
||||
std::string timestamp_max_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -291,11 +291,9 @@ void TestCustomizedTablePropertiesCollector(
|
||||
std::unique_ptr<RandomAccessFileReader> fake_file_reader(
|
||||
new RandomAccessFileReader(std::move(source), "test"));
|
||||
|
||||
TableProperties* props;
|
||||
std::unique_ptr<TableProperties> props;
|
||||
Status s = ReadTableProperties(fake_file_reader.get(), fwf->contents().size(),
|
||||
magic_number, ioptions, &props,
|
||||
true /* compression_type_missing */);
|
||||
std::unique_ptr<TableProperties> props_guard(props);
|
||||
magic_number, ioptions, &props);
|
||||
ASSERT_OK(s);
|
||||
|
||||
auto user_collected = props->user_collected_properties;
|
||||
@@ -432,13 +430,11 @@ void TestInternalKeyPropertiesCollector(
|
||||
std::unique_ptr<RandomAccessFileReader> reader(
|
||||
new RandomAccessFileReader(std::move(source), "test"));
|
||||
|
||||
TableProperties* props;
|
||||
Status s =
|
||||
ReadTableProperties(reader.get(), fwf->contents().size(), magic_number,
|
||||
ioptions, &props, true /* compression_type_missing */);
|
||||
std::unique_ptr<TableProperties> props;
|
||||
Status s = ReadTableProperties(reader.get(), fwf->contents().size(),
|
||||
magic_number, ioptions, &props);
|
||||
ASSERT_OK(s);
|
||||
|
||||
std::unique_ptr<TableProperties> props_guard(props);
|
||||
auto user_collected = props->user_collected_properties;
|
||||
uint64_t deleted = GetDeletedKeys(user_collected);
|
||||
ASSERT_EQ(5u, deleted); // deletes + single-deletes
|
||||
|
||||
Loaded 100 of 302 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user