mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-07 11:32:34 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e51db47feb | ||
|
|
c3034fce32 | ||
|
|
2dea3dd258 | ||
|
|
37c4996282 | ||
|
|
53da604580 | ||
|
|
60f5a22cff | ||
|
|
2898067de8 | ||
|
|
238a9c3f68 | ||
|
|
2511b42c7e | ||
|
|
1920121cef | ||
|
|
7c393c3fc6 | ||
|
|
7513e9011b | ||
|
|
bf117c54ef | ||
|
|
c04a86a0e9 | ||
|
|
3455ab0e2b | ||
|
|
39a07c9651 | ||
|
|
a379dae4f7 | ||
|
|
ac37bfded0 | ||
|
|
df5dc73bec | ||
|
|
1e5b631e51 | ||
|
|
0229a88dfe | ||
|
|
42ba60b3ba | ||
|
|
aeb913dd01 | ||
|
|
b678cb1f86 | ||
|
|
206845c057 | ||
|
|
4e4ec16957 | ||
|
|
5ad3227650 | ||
|
|
31193a73a4 | ||
|
|
803a40d412 | ||
|
|
870033291a | ||
|
|
7b9ecd4067 | ||
|
|
e8e911a11c | ||
|
|
a53d6d25e0 | ||
|
|
da90e23998 | ||
|
|
955b80e84f | ||
|
|
0b75b22321 | ||
|
|
bbdc4f2e9a | ||
|
|
c8665611bc | ||
|
|
5afd1e309c | ||
|
|
d1b70b05a6 | ||
|
|
837705ad80 | ||
|
|
bb485e986a | ||
|
|
5dd18a8d8e | ||
|
|
f127d459ad | ||
|
|
ac3f3f3719 | ||
|
|
b1a53db327 | ||
|
|
ed8eb436db | ||
|
|
714ce5041d | ||
|
|
df4197ca6e | ||
|
|
fcd8088333 | ||
|
|
f33611d5e9 | ||
|
|
1ae026c400 | ||
|
|
570248aeff | ||
|
|
9dc887ece0 | ||
|
|
c76778e2bd | ||
|
|
b20737709f | ||
|
|
a95a776d75 | ||
|
|
41d32152ce | ||
|
|
d45b837701 | ||
|
|
ba224b75c7 | ||
|
|
41c4b665f4 | ||
|
|
a0cbb69421 | ||
|
|
93a7389442 | ||
|
|
3503f28982 | ||
|
|
89f66d4484 | ||
|
|
bac399449d | ||
|
|
390c5246d2 | ||
|
|
373e3a154d | ||
|
|
be219089ad | ||
|
|
6f9ed59b1d | ||
|
|
be8199cdb9 | ||
|
|
a904c62d28 | ||
|
|
3d844dff1d | ||
|
|
95d0ee95fa | ||
|
|
66b62a123a | ||
|
|
68d8b28389 | ||
|
|
75741eb0ce | ||
|
|
d44ef2ed4d | ||
|
|
6adc39e1bf | ||
|
|
3f20925dc4 | ||
|
|
3ab0eae860 | ||
|
|
005c046734 | ||
|
|
0a1aed4e71 | ||
|
|
f89423a57a | ||
|
|
b511b042f4 | ||
|
|
cbb3b25915 | ||
|
|
54d73d6429 | ||
|
|
065bea1587 | ||
|
|
a50da404be | ||
|
|
e9b627fd56 | ||
|
|
6f33901899 | ||
|
|
7ed69fbe72 | ||
|
|
19a89267ca | ||
|
|
82a70e1470 | ||
|
|
d96b0127a0 | ||
|
|
8ea0a2c1bd | ||
|
|
e817bc9628 | ||
|
|
5ba1b6e549 | ||
|
|
d53f7ff69a | ||
|
|
865a25101d | ||
|
|
575ea26ec9 | ||
|
|
298edae941 | ||
|
|
d81c2d1e3d | ||
|
|
ecccc63179 | ||
|
|
d5bd0039b9 | ||
|
|
25be1ed66a | ||
|
|
32a4d59477 | ||
|
|
b788e3f497 | ||
|
|
281ac9c89e | ||
|
|
99908db998 | ||
|
|
416dc7bed7 | ||
|
|
3981430f54 | ||
|
|
dcddc1065e | ||
|
|
9047fd7766 | ||
|
|
79407a8a61 | ||
|
|
d5a46c40e5 | ||
|
|
d60ae5b1c7 | ||
|
|
0d0aa578ff | ||
|
|
146263887f | ||
|
|
b3dbeadc34 | ||
|
|
58162835d1 | ||
|
|
a42a342a7a | ||
|
|
6ad0810393 | ||
|
|
3897ce3125 | ||
|
|
d61a449364 | ||
|
|
5a2b4ed671 | ||
|
|
f44e69c64a | ||
|
|
db325a5904 | ||
|
|
2f93a3b809 | ||
|
|
80a59a03a7 | ||
|
|
707f8d168a | ||
|
|
4d5b575563 | ||
|
|
9167ece586 | ||
|
|
799cf37cb1 | ||
|
|
2655477c67 | ||
|
|
eda83eaac0 | ||
|
|
ab718b415f | ||
|
|
d561af487c | ||
|
|
9e701b48e0 | ||
|
|
1c88f66ff8 | ||
|
|
cda7923169 | ||
|
|
b53e3d2adb | ||
|
|
c75ef03e58 | ||
|
|
dfa6b408fe | ||
|
|
886774eabf | ||
|
|
956ce9bde2 | ||
|
|
a607b88240 | ||
|
|
a4405fd981 |
No files matched your search
+77
-34
@@ -49,6 +49,9 @@ commands:
|
||||
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
|
||||
|
||||
pre-steps-macos:
|
||||
steps:
|
||||
@@ -88,6 +91,21 @@ commands:
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libgflags-dev
|
||||
|
||||
install-benchmark:
|
||||
steps:
|
||||
- run: # currently doesn't support ubuntu-1604 which doesn't have libbenchmark package, user can still install by building it youself
|
||||
name: Install benchmark
|
||||
command: |
|
||||
sudo apt-get update -y && sudo apt-get install -y libbenchmark-dev
|
||||
|
||||
upgrade-cmake:
|
||||
steps:
|
||||
- run:
|
||||
name: Upgrade cmake
|
||||
command: |
|
||||
sudo apt remove --purge cmake
|
||||
sudo snap install cmake --classic
|
||||
|
||||
install-gflags-on-macos:
|
||||
steps:
|
||||
- run:
|
||||
@@ -140,12 +158,12 @@ 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) | .circleci/cat_ignore_eagain
|
||||
- run: ulimit -S -n 1048576 && (mkdir build && cd build && cmake -DWITH_GFLAGS=1 .. && make V=1 -j32 && ctest -j10) | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-linux:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -155,7 +173,7 @@ jobs:
|
||||
|
||||
build-linux-mem-env:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -165,7 +183,7 @@ jobs:
|
||||
|
||||
build-linux-encrypted-env:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -175,7 +193,7 @@ jobs:
|
||||
|
||||
build-linux-shared_lib-alt_namespace-status_checked:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -185,7 +203,7 @@ jobs:
|
||||
|
||||
build-linux-release:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
@@ -212,7 +230,7 @@ jobs:
|
||||
|
||||
build-linux-lite:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -222,7 +240,7 @@ jobs:
|
||||
|
||||
build-linux-lite-release:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
@@ -235,7 +253,7 @@ jobs:
|
||||
|
||||
build-linux-clang-no_test_run:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
@@ -245,7 +263,7 @@ jobs:
|
||||
|
||||
build-linux-clang10-asan:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -256,7 +274,7 @@ jobs:
|
||||
|
||||
build-linux-clang10-mini-tsan:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -267,7 +285,7 @@ jobs:
|
||||
|
||||
build-linux-clang10-ubsan:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -278,7 +296,7 @@ jobs:
|
||||
|
||||
build-linux-clang10-clang-analyze:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -288,13 +306,26 @@ jobs:
|
||||
- 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.
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-no_test_run:
|
||||
build-linux-cmake:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
resource_class: large
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: (mkdir build && cd build && cmake -DWITH_GFLAGS=0 .. && make V=1 -j8) | .circleci/cat_ignore_eagain
|
||||
- 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
|
||||
- post-steps
|
||||
|
||||
build-linux-cmake-ubuntu-20:
|
||||
machine:
|
||||
image: ubuntu-2004:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- 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
|
||||
- post-steps
|
||||
|
||||
build-linux-unity:
|
||||
@@ -309,7 +340,7 @@ jobs:
|
||||
|
||||
build-linux-gcc-4_8-no_test_run:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -347,6 +378,17 @@ jobs:
|
||||
- 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
|
||||
- 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.
|
||||
build-linux-microbench:
|
||||
machine:
|
||||
image: ubuntu-2004:202010-01
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-benchmark
|
||||
- run: DEBUG_LEVEL=0 make microbench | .circleci/cat_ignore_eagain
|
||||
- post-steps
|
||||
|
||||
build-windows:
|
||||
executor: windows-2xlarge
|
||||
parameters:
|
||||
@@ -417,7 +459,7 @@ jobs:
|
||||
|
||||
build-linux-java:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
@@ -441,7 +483,7 @@ jobs:
|
||||
|
||||
build-linux-java-static:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: large
|
||||
environment:
|
||||
JAVA_HOME: /usr/lib/jvm/java-1.8.0-openjdk-amd64
|
||||
@@ -512,7 +554,7 @@ jobs:
|
||||
|
||||
build-examples:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -525,7 +567,7 @@ jobs:
|
||||
|
||||
build-cmake-mingw:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -548,7 +590,7 @@ jobs:
|
||||
|
||||
build-linux-non-shm:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
parameters:
|
||||
start_test:
|
||||
@@ -604,6 +646,7 @@ jobs:
|
||||
JAVA_HOME: /usr/lib/jvm/java-8-openjdk-arm64
|
||||
steps:
|
||||
- pre-steps
|
||||
- install-gflags
|
||||
- run:
|
||||
name: "Set Java Environment"
|
||||
command: |
|
||||
@@ -616,7 +659,7 @@ jobs:
|
||||
command: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=0 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=0 -DWITH_GFLAGS=1 -DWITH_BENCHMARK_TOOLS=0 -DWITH_TOOLS=0 -DWITH_CORE_TOOLS=1 ..
|
||||
make -j4
|
||||
- run:
|
||||
name: "Build Java with cmake"
|
||||
@@ -624,13 +667,13 @@ jobs:
|
||||
rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=0 ..
|
||||
cmake -DJNI=1 -DCMAKE_BUILD_TYPE=Release -DWITH_GFLAGS=1 ..
|
||||
make -j4 rocksdb rocksdbjni
|
||||
- post-steps
|
||||
|
||||
build-format-compatible:
|
||||
machine:
|
||||
image: ubuntu-1604:202007-01
|
||||
image: ubuntu-1604:202104-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- pre-steps
|
||||
@@ -650,6 +693,10 @@ workflows:
|
||||
build-linux:
|
||||
jobs:
|
||||
- build-linux
|
||||
build-linux-cmake:
|
||||
jobs:
|
||||
- build-linux-cmake
|
||||
- build-linux-cmake-ubuntu-20
|
||||
build-linux-mem-env:
|
||||
jobs:
|
||||
- build-linux-mem-env
|
||||
@@ -701,12 +748,6 @@ workflows:
|
||||
name: "build-windows-vs2017"
|
||||
vs_year: "2017"
|
||||
cmake_generator: "Visual Studio 15 Win64"
|
||||
# build-windows-vs2015:
|
||||
# jobs:
|
||||
# - build-windows:
|
||||
# name: "build-windows-vs2015"
|
||||
# vs_year: "2015"
|
||||
# cmake_generator: "Visual Studio 14 Win64"
|
||||
build-java:
|
||||
jobs:
|
||||
- build-linux-java
|
||||
@@ -733,7 +774,6 @@ workflows:
|
||||
build-linux-compilers-no_test_run:
|
||||
jobs:
|
||||
- build-linux-clang-no_test_run
|
||||
- build-linux-cmake-no_test_run
|
||||
- build-linux-gcc-4_8-no_test_run
|
||||
- build-linux-gcc-8-no_test_run
|
||||
- build-linux-gcc-9-no_test_run
|
||||
@@ -751,6 +791,9 @@ workflows:
|
||||
build-linux-arm:
|
||||
jobs:
|
||||
- build-linux-arm
|
||||
build-microbench:
|
||||
jobs:
|
||||
- build-linux-microbench
|
||||
nightly:
|
||||
triggers:
|
||||
- schedule:
|
||||
|
||||
+29
-17
@@ -32,10 +32,11 @@
|
||||
# 3. cmake ..
|
||||
# 4. make -j
|
||||
|
||||
cmake_minimum_required(VERSION 3.5.1)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/modules/")
|
||||
include(ReadVersion)
|
||||
include(GoogleTest)
|
||||
get_rocksdb_version(rocksdb_VERSION)
|
||||
project(rocksdb
|
||||
VERSION ${rocksdb_VERSION}
|
||||
@@ -71,6 +72,12 @@ option(WITH_WINDOWS_UTF8_FILENAMES "use UTF8 as characterset for opening files,
|
||||
if (WITH_WINDOWS_UTF8_FILENAMES)
|
||||
add_definitions(-DROCKSDB_WINDOWS_UTF8_FILENAMES)
|
||||
endif()
|
||||
|
||||
if ($ENV{CIRCLECI})
|
||||
message(STATUS "Build for CircieCI env, a few tests may be disabled")
|
||||
add_definitions(-DCIRCLECI)
|
||||
endif()
|
||||
|
||||
# third-party/folly is only validated to work on Linux and Windows for now.
|
||||
# So only turn it on there by default.
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux|Windows")
|
||||
@@ -123,11 +130,11 @@ else()
|
||||
set(GFLAGS_LIB ${GFLAGS_TARGET})
|
||||
else()
|
||||
# Config with GFLAGS_LIBRARIES available since gflags 2.1.0
|
||||
set(GFLAGS_LIB ${GFLAGS_LIBRARIES})
|
||||
set(GFLAGS_LIB ${gflags_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
find_package(gflags REQUIRED)
|
||||
set(GFLAGS_LIB gflags::gflags)
|
||||
set(GFLAGS_LIB gflags::gflags)
|
||||
endif()
|
||||
include_directories(${GFLAGS_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_LIB})
|
||||
@@ -211,7 +218,7 @@ if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi /nologo /EHsc /GS /Gd /GR /GF /fp:precise /Zc:wchar_t /Zc:forScope /errorReport:queue")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FC /d2Zi+ /W4 /wd4127 /wd4800 /wd4996 /wd4351 /wd4100 /wd4204 /wd4324")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wextra -Wall -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wsign-compare -Wshadow -Wno-unused-parameter -Wno-unused-variable -Woverloaded-virtual -Wnon-virtual-dtor -Wno-missing-field-initializers -Wno-strict-aliasing")
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-prototypes")
|
||||
@@ -621,12 +628,14 @@ set(SOURCES
|
||||
cache/lru_cache.cc
|
||||
cache/sharded_cache.cc
|
||||
db/arena_wrapped_db_iter.cc
|
||||
db/blob/blob_fetcher.cc
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_builder.cc
|
||||
db/blob/blob_file_cache.cc
|
||||
db/blob/blob_file_garbage.cc
|
||||
db/blob/blob_file_meta.cc
|
||||
db/blob/blob_file_reader.cc
|
||||
db/blob/blob_garbage_meter.cc
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_sequential_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
@@ -925,13 +934,8 @@ if(WIN32)
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc)
|
||||
if(NOT MINGW)
|
||||
# Mingw only supports std::thread when using
|
||||
# posix threads.
|
||||
list(APPEND SOURCES
|
||||
port/win/win_thread.cc)
|
||||
endif()
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.cc)
|
||||
if(WITH_XPRESS)
|
||||
list(APPEND SOURCES
|
||||
port/win/xpress_win.cc)
|
||||
@@ -1115,17 +1119,20 @@ if(WITH_TESTS)
|
||||
list(APPEND TESTS
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
db/blob/blob_counting_iterator_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
db/blob/blob_file_builder_test.cc
|
||||
db/blob/blob_file_cache_test.cc
|
||||
db/blob/blob_file_garbage_test.cc
|
||||
db/blob/blob_file_reader_test.cc
|
||||
db/blob/blob_garbage_meter_test.cc
|
||||
db/blob/db_blob_basic_test.cc
|
||||
db/blob/db_blob_compaction_test.cc
|
||||
db/blob/db_blob_corruption_test.cc
|
||||
db/blob/db_blob_index_test.cc
|
||||
db/column_family_test.cc
|
||||
db/compact_files_test.cc
|
||||
db/compaction/clipping_iterator_test.cc
|
||||
db/compaction/compaction_job_stats_test.cc
|
||||
db/compaction/compaction_job_test.cc
|
||||
db/compaction/compaction_iterator_test.cc
|
||||
@@ -1321,21 +1328,21 @@ if(WITH_TESTS)
|
||||
|
||||
foreach(sourcefile ${TESTS})
|
||||
get_filename_component(exename ${sourcefile} NAME_WE)
|
||||
add_executable(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} ${sourcefile})
|
||||
set_target_properties(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX}
|
||||
add_executable(${exename}${ARTIFACT_SUFFIX} ${sourcefile})
|
||||
set_target_properties(${exename}${ARTIFACT_SUFFIX}
|
||||
PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD_RELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_MINRELEASE 1
|
||||
EXCLUDE_FROM_DEFAULT_BUILD_RELWITHDEBINFO 1
|
||||
OUTPUT_NAME ${exename}${ARTIFACT_SUFFIX}
|
||||
)
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${THIRDPARTY_LIBS} ${ROCKSDB_LIB})
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} testutillib${ARTIFACT_SUFFIX} testharness gtest ${THIRDPARTY_LIBS} ${ROCKSDB_LIB})
|
||||
if(NOT "${exename}" MATCHES "db_sanity_test")
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX})
|
||||
gtest_discover_tests(${exename} DISCOVERY_TIMEOUT 120)
|
||||
add_dependencies(check ${exename}${ARTIFACT_SUFFIX})
|
||||
endif()
|
||||
if("${exename}" MATCHES "env_librados_test")
|
||||
# env_librados_test.cc uses librados directly
|
||||
target_link_libraries(${CMAKE_PROJECT_NAME}_${exename}${ARTIFACT_SUFFIX} rados)
|
||||
target_link_libraries(${exename}${ARTIFACT_SUFFIX} rados)
|
||||
endif()
|
||||
endforeach(sourcefile ${TESTS})
|
||||
|
||||
@@ -1415,3 +1422,8 @@ option(WITH_EXAMPLES "build with examples" OFF)
|
||||
if(WITH_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
option(WITH_BENCHMARK "build benchmark tests" OFF)
|
||||
if(WITH_BENCHMARK)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/microbench/)
|
||||
endif()
|
||||
+57
-7
@@ -1,16 +1,66 @@
|
||||
# Rocksdb Change Log
|
||||
## 6.21.3 (2021-07-19)
|
||||
## 6.23.4 (2021-08-31)
|
||||
### Bug Fixes
|
||||
* Fixed confusingly duplicated output in LOG for periodic stats ("DUMPING STATS"), including "Compaction Stats" and "File Read Latency Histogram By Level". (Included because the next bug fix built upon this one.)
|
||||
* Fixed block cache entry stat scans not to hold the DB mutex, which was a serious performance bug for tail latencies in TransactionDB and likely elsewhere.
|
||||
* Fix a race in item ref counting in LRUCache when promoting an item from the SecondaryCache.
|
||||
|
||||
## 6.21.2 (2021-06-14)
|
||||
## 6.23.3 (2021-08-09)
|
||||
### Bug Fixes
|
||||
* Fixed related but less common performance bugs in background gathering of block cache entry statistics.
|
||||
* Removed a call to `RenameFile()` on a non-existent info log file ("LOG") when opening a new DB. Such a call was guaranteed to fail though did not impact applications since we swallowed the error. Now we also stopped swallowing errors in renaming "LOG" file.
|
||||
* Fixed a bug affecting the batched `MultiGet` API when used with keys spanning multiple column families and `sorted_input == false`.
|
||||
|
||||
## 6.21.1 (2021-06-08)
|
||||
## 6.23.2 (2021-08-04)
|
||||
### Bug Fixes
|
||||
* Fixed a performance bug in background gathering of block cache entry statistics, that could consume a lot of CPU when there are many column families with a shared block cache.
|
||||
* Fixed a race related to the destruction of `ColumnFamilyData` objects. The earlier logic unlocked the DB mutex before destroying the thread-local `SuperVersion` pointers, which could result in a process crash if another thread managed to get a reference to the `ColumnFamilyData` object.
|
||||
* Fixed an issue where `OnFlushCompleted` was not called for atomic flush.
|
||||
|
||||
## 6.23.1 (2021-07-22)
|
||||
### Bug Fixes
|
||||
* Fix a race condition during multiple DB instances opening.
|
||||
|
||||
## 6.23.0 (2021-07-16)
|
||||
### Behavior Changes
|
||||
* Obsolete keys in the bottommost level that were preserved for a snapshot will now be cleaned upon snapshot release in all cases. This form of compaction (snapshot release triggered compaction) previously had an artificial limitation that multiple tombstones needed to be present.
|
||||
|
||||
### Bug Fixes
|
||||
* Blob file checksums are now printed in hexadecimal format when using the `manifest_dump` `ldb` command.
|
||||
* `GetLiveFilesMetaData()` now populates the `temperature`, `oldest_ancester_time`, and `file_creation_time` fields of its `LiveFileMetaData` results when the information is available. Previously these fields always contained zero indicating unknown.
|
||||
* Fix mismatches of OnCompaction{Begin,Completed} in case of DisableManualCompaction().
|
||||
* Fix continuous logging of an existing background error on every user write
|
||||
* Fix a bug that `Get()` return Status::OK() and an empty value for non-existent key when `read_options.read_tier = kBlockCacheTier`.
|
||||
* Fix a bug that stat in `get_context` didn't accumulate to statistics when query is failed.
|
||||
|
||||
### New Features
|
||||
* ldb has a new feature, `list_live_files_metadata`, that shows the live SST files, as well as their LSM storage level and the column family they belong to.
|
||||
* The new BlobDB implementation now tracks the amount of garbage in each blob file in the MANIFEST.
|
||||
* Integrated BlobDB now supports Merge with base values (Put/Delete etc.).
|
||||
* RemoteCompaction supports sub-compaction, the job_id in the user interface is changed from `int` to `uint64_t` to support sub-compaction id.
|
||||
* Expose statistics option in RemoteCompaction worker.
|
||||
|
||||
### Public API change
|
||||
* Added APIs to the Customizable class to allow developers to create their own Customizable classes. Created the utilities/customizable_util.h file to contain helper methods for developing new Customizable classes.
|
||||
* Change signature of SecondaryCache::Name(). Make SecondaryCache customizable and add SecondaryCache::CreateFromString method.
|
||||
## 6.22.0 (2021-06-18)
|
||||
### Behavior Changes
|
||||
* Added two additional tickers, MEMTABLE_PAYLOAD_BYTES_AT_FLUSH and MEMTABLE_GARBAGE_BYTES_AT_FLUSH. These stats can be used to estimate the ratio of "garbage" (outdated) bytes in the memtable that are discarded at flush time.
|
||||
* Added API comments clarifying safe usage of Disable/EnableManualCompaction and EventListener callbacks for compaction.
|
||||
### Bug Fixes
|
||||
* fs_posix.cc GetFreeSpace() always report disk space available to root even when running as non-root. Linux defaults often have disk mounts with 5 to 10 percent of total space reserved only for root. Out of space could result for non-root users.
|
||||
* Subcompactions are now disabled when user-defined timestamps are used, since the subcompaction boundary picking logic is currently not timestamp-aware, which could lead to incorrect results when different subcompactions process keys that only differ by timestamp.
|
||||
* Fix an issue that `DeleteFilesInRange()` may cause ongoing compaction reports corruption exception, or ASSERT for debug build. There's no actual data loss or corruption that we find.
|
||||
* Fixed confusingly duplicated output in LOG for periodic stats ("DUMPING STATS"), including "Compaction Stats" and "File Read Latency Histogram By Level".
|
||||
* Fixed performance bugs in background gathering of block cache entry statistics, that could consume a lot of CPU when there are many column families with a shared block cache.
|
||||
|
||||
### New Features
|
||||
* Marked the Ribbon filter and optimize_filters_for_memory features as production-ready, each enabling memory savings for Bloom-like filters. Use `NewRibbonFilterPolicy` in place of `NewBloomFilterPolicy` to use Ribbon filters instead of Bloom, or `ribbonfilter` in place of `bloomfilter` in configuration string.
|
||||
* Allow `DBWithTTL` to use `DeleteRange` api just like other DBs. `DeleteRangeCF()` which executes `WriteBatchInternal::DeleteRange()` has been added to the handler in `DBWithTTLImpl::Write()` to implement it.
|
||||
* Add BlockBasedTableOptions.prepopulate_block_cache. If enabled, it prepopulate warm/hot data blocks which are already in memory into block cache at the time of flush. On a flush, the data block that is in memory (in memtables) get flushed to the device. If using Direct IO, additional IO is incurred to read this data back into memory again, which is avoided by enabling this option and it also helps with Distributed FileSystem. More details in include/rocksdb/table.h.
|
||||
* Added a `cancel` field to `CompactRangeOptions`, allowing individual in-process manual range compactions to be cancelled.
|
||||
|
||||
### New Features
|
||||
* Added BlobMetaData to the ColumnFamilyMetaData to return information about blob files
|
||||
|
||||
### Public API change
|
||||
* Added GetAllColumnFamilyMetaData API to retrieve the ColumnFamilyMetaData about all column families.
|
||||
|
||||
## 6.21.0 (2021-05-21)
|
||||
### Bug Fixes
|
||||
|
||||
@@ -344,6 +344,12 @@ ifdef ROCKSDB_VALGRIND_RUN
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_VALGRIND_RUN
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_VALGRIND_RUN
|
||||
endif
|
||||
ifdef ROCKSDB_FULL_VALGRIND_RUN
|
||||
# Some tests are slow when run under valgrind and are only run when
|
||||
# explicitly requested via the ROCKSDB_FULL_VALGRIND_RUN compiler flag.
|
||||
PLATFORM_CCFLAGS += -DROCKSDB_VALGRIND_RUN -DROCKSDB_FULL_VALGRIND_RUN
|
||||
PLATFORM_CXXFLAGS += -DROCKSDB_VALGRIND_RUN -DROCKSDB_FULL_VALGRIND_RUN
|
||||
endif
|
||||
|
||||
ifndef DISABLE_JEMALLOC
|
||||
ifdef JEMALLOC
|
||||
@@ -488,6 +494,7 @@ VALGRIND_ERROR = 2
|
||||
VALGRIND_VER := $(join $(VALGRIND_VER),valgrind)
|
||||
|
||||
VALGRIND_OPTS = --error-exitcode=$(VALGRIND_ERROR) --leak-check=full
|
||||
# Not yet supported: --show-leak-kinds=definite,possible,reachable --errors-for-leak-kinds=definite,possible,reachable
|
||||
|
||||
TEST_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(TEST_LIB_SOURCES) $(MOCK_LIB_SOURCES)) $(GTEST)
|
||||
BENCH_OBJECTS = $(patsubst %.cc, $(OBJ_DIR)/%.o, $(BENCH_LIB_SOURCES))
|
||||
@@ -516,8 +523,10 @@ endif
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
# TODO: finish fixing all tests to pass this check
|
||||
TESTS_FAILING_ASC = \
|
||||
c_test \
|
||||
db_test \
|
||||
db_test2 \
|
||||
env_test \
|
||||
range_locking_test \
|
||||
testutil_test \
|
||||
|
||||
@@ -593,6 +602,8 @@ TEST_LIBS = \
|
||||
# TODO: add back forward_iterator_bench, after making it build in all environemnts.
|
||||
BENCHMARKS = $(patsubst %.cc, %, $(notdir $(BENCH_MAIN_SOURCES)))
|
||||
|
||||
MICROBENCHS = $(patsubst %.cc, %, $(notdir $(MICROBENCH_SOURCES)))
|
||||
|
||||
# if user didn't config LIBNAME, set the default
|
||||
ifeq ($(LIBNAME),)
|
||||
LIBNAME=librocksdb
|
||||
@@ -731,6 +742,9 @@ test_libs: $(TEST_LIBS)
|
||||
|
||||
benchmarks: $(BENCHMARKS)
|
||||
|
||||
microbench: $(MICROBENCHS)
|
||||
for t in $(MICROBENCHS); do echo "===== Running benchmark $$t (`date`)"; ./$$t || exit 1; done;
|
||||
|
||||
dbg: $(LIBRARY) $(BENCHMARKS) tools $(TESTS)
|
||||
|
||||
# creates library and programs
|
||||
@@ -1044,6 +1058,12 @@ ubsan_crash_test_with_best_efforts_recovery: clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
full_valgrind_test:
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
full_valgrind_test_some:
|
||||
ROCKSDB_FULL_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check_some
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
@@ -1162,7 +1182,7 @@ clean-not-downloaded: clean-ext-libraries-bin clean-rocks clean-not-downloaded-r
|
||||
clean-rocks:
|
||||
echo shared=$(ALL_SHARED_LIBS)
|
||||
echo static=$(ALL_STATIC_LIBS)
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(ALL_STATIC_LIBS) $(ALL_SHARED_LIBS)
|
||||
rm -f $(BENCHMARKS) $(TOOLS) $(TESTS) $(PARALLEL_TEST) $(ALL_STATIC_LIBS) $(ALL_SHARED_LIBS) $(MICROBENCHS)
|
||||
rm -rf $(CLEAN_FILES) ios-x86 ios-arm scan_build_report
|
||||
$(FIND) . -name "*.[oda]" -exec rm -f {} \;
|
||||
$(FIND) . -type f -regex ".*\.\(\(gcda\)\|\(gcno\)\)" -exec rm -f {} \;
|
||||
@@ -1825,6 +1845,9 @@ block_cache_trace_analyzer_test: $(OBJ_DIR)/tools/block_cache_analyzer/block_cac
|
||||
defer_test: $(OBJ_DIR)/util/defer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_counting_iterator_test: $(OBJ_DIR)/db/blob/blob_counting_iterator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_file_addition_test: $(OBJ_DIR)/db/blob/blob_file_addition_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1840,6 +1863,9 @@ blob_file_garbage_test: $(OBJ_DIR)/db/blob/blob_file_garbage_test.o $(TEST_LIBRA
|
||||
blob_file_reader_test: $(OBJ_DIR)/db/blob/blob_file_reader_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
blob_garbage_meter_test: $(OBJ_DIR)/db/blob/blob_garbage_meter_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
timer_test: $(OBJ_DIR)/util/timer_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1866,6 +1892,13 @@ db_blob_corruption_test: $(OBJ_DIR)/db/blob/db_blob_corruption_test.o $(TEST_LIB
|
||||
|
||||
db_write_buffer_manager_test: $(OBJ_DIR)/db/db_write_buffer_manager_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
clipping_iterator_test: $(OBJ_DIR)/db/compaction/clipping_iterator_test.o $(TEST_LIBRARY) $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
ribbon_bench: $(OBJ_DIR)/microbench/ribbon_bench.o $(LIBRARY)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
PREFIX ?= /usr/local
|
||||
|
||||
@@ -137,12 +137,14 @@ cpp_library(
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_fetcher.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_builder.cc",
|
||||
"db/blob/blob_file_cache.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_file_reader.cc",
|
||||
"db/blob/blob_garbage_meter.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
@@ -448,12 +450,14 @@ cpp_library(
|
||||
"cache/lru_cache.cc",
|
||||
"cache/sharded_cache.cc",
|
||||
"db/arena_wrapped_db_iter.cc",
|
||||
"db/blob/blob_fetcher.cc",
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_builder.cc",
|
||||
"db/blob/blob_file_cache.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_file_reader.cc",
|
||||
"db/blob/blob_garbage_meter.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_sequential_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
@@ -904,6 +908,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_counting_iterator_test",
|
||||
"db/blob/blob_counting_iterator_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_db_test",
|
||||
"utilities/blob_db/blob_db_test.cc",
|
||||
@@ -946,6 +957,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"blob_garbage_meter_test",
|
||||
"db/blob/blob_garbage_meter_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_based_filter_block_test",
|
||||
"table/block_based/block_based_filter_block_test.cc",
|
||||
@@ -1051,6 +1069,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"clipping_iterator_test",
|
||||
"db/compaction/clipping_iterator_test.cc",
|
||||
"parallel",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"coding_test",
|
||||
"util/coding_test.cc",
|
||||
|
||||
@@ -596,6 +596,16 @@ EOF
|
||||
PLATFORM_CXXFLAGS="$PLATFORM_CXXFLAGS -faligned-new -DHAVE_ALIGNED_NEW"
|
||||
fi
|
||||
fi
|
||||
if ! test $ROCKSDB_DISABLE_BENCHMARK; then
|
||||
# Test whether google benchmark is available
|
||||
$CXX $PLATFORM_CXXFLAGS -x c++ - -o /dev/null -lbenchmark 2>/dev/null <<EOF
|
||||
#include <benchmark/benchmark.h>
|
||||
int main() {}
|
||||
EOF
|
||||
if [ "$?" = 0 ]; then
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lbenchmark"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# TODO(tec): Fix -Wshorten-64-to-32 errors on FreeBSD and enable the warning.
|
||||
|
||||
@@ -106,9 +106,7 @@ NON_SHM="TMPD=/tmp/rocksdb_test_tmp"
|
||||
GCC_481="ROCKSDB_FBCODE_BUILD_WITH_481=1"
|
||||
ASAN="COMPILE_WITH_ASAN=1"
|
||||
CLANG="USE_CLANG=1"
|
||||
# in gcc-5 there are known problems with TSAN like https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71090.
|
||||
# using platform007 gives us gcc-8 or higher which has that bug fixed.
|
||||
TSAN="ROCKSDB_FBCODE_BUILD_WITH_PLATFORM007=1 COMPILE_WITH_TSAN=1"
|
||||
TSAN="COMPILE_WITH_TSAN=1"
|
||||
UBSAN="COMPILE_WITH_UBSAN=1"
|
||||
TSAN_CRASH='CRASH_TEST_EXT_ARGS="--compression_type=zstd --log2_keys_per_lock=22"'
|
||||
NON_TSAN_CRASH="CRASH_TEST_EXT_ARGS=--compression_type=zstd"
|
||||
@@ -1197,6 +1195,40 @@ JAVA_BUILD_TEST_COMMANDS="[
|
||||
}
|
||||
]"
|
||||
|
||||
#
|
||||
# RocksDB fbcode stress/crash test
|
||||
#
|
||||
FBCODE_STRESS_CRASH_TEST_COMMANDS="[
|
||||
{
|
||||
'name':'Rocksdb Fbcode Stress and Crash Test',
|
||||
'oncall':'$ONCALL',
|
||||
'executeLocal': 'true',
|
||||
'timeout': 86400,
|
||||
'steps': [
|
||||
{
|
||||
'name':'Copy RocksDB code to fbcode repo',
|
||||
'shell':'cd internal_repo_rocksdb/repo && git init && git add * && git commit -a -m \"Make internal_repo_rocksdb/repo a git repo\" && cd ../.. && echo Y | python3 rocks/release_script/release_to_fbcode.py -u internal_repo_rocksdb/repo master || $CONTRUN_NAME=db_stress_fbcode $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Build RocksDB fbcode stress tests',
|
||||
'shell':'cd $WORKING_DIR; buck build @mode/dbg rocks/tools:rocks_db_stress || $CONTRUN_NAME=db_stress_fbcode $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
{
|
||||
'name':'Run RocksDB whitebox crash tests',
|
||||
'timeout': 86400,
|
||||
'shell':'cd $WORKING_DIR; mkdir /dev/shm/rocksdb_whitebox_crash_test && TEST_TMPDIR=\$(mktemp -d --tmpdir=/dev/shm/rocksdb_whitebox_crash_test) python3 rocksdb/src/tools/db_crashtest.py --stress_cmd=buck-out/dbg/gen/rocks/tools/rocks_db_stress -secondary_cache_uri=\"$SECONDARY_CACHE_URI\" --env_uri=$ENV_URI -checkpoint_one_in=0 -backup_one_in=0 -cache_size=134217728 -cache_numshardbits=4 -logtostderr=false whitebox || $CONTRUN_NAME=whitebox_crash_test $TASK_CREATION_TOOL',
|
||||
'user':'root',
|
||||
$PARSER
|
||||
},
|
||||
],
|
||||
$REPORT
|
||||
}
|
||||
]"
|
||||
|
||||
|
||||
case $1 in
|
||||
unit)
|
||||
@@ -1334,6 +1366,11 @@ case $1 in
|
||||
java_build)
|
||||
echo $JAVA_BUILD_TEST_COMMANDS
|
||||
;;
|
||||
fbcode_stress_crash)
|
||||
set -f
|
||||
echo $FBCODE_STRESS_CRASH_TEST_COMMANDS
|
||||
set +f
|
||||
;;
|
||||
*)
|
||||
echo "Invalid determinator command"
|
||||
exit 1
|
||||
|
||||
Vendored
+9
@@ -10,6 +10,8 @@
|
||||
#include "rocksdb/cache.h"
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "rocksdb/utilities/customizable_util.h"
|
||||
#include "rocksdb/utilities/options_type.h"
|
||||
#include "util/string_util.h"
|
||||
|
||||
@@ -34,6 +36,13 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status SecondaryCache::CreateFromString(
|
||||
const ConfigOptions& config_options, const std::string& value,
|
||||
std::shared_ptr<SecondaryCache>* result) {
|
||||
return LoadSharedObject<SecondaryCache>(config_options, value, nullptr,
|
||||
result);
|
||||
}
|
||||
|
||||
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
|
||||
Vendored
+3
-4
@@ -13,11 +13,11 @@
|
||||
#include "monitoring/histogram.h"
|
||||
#include "port/port.h"
|
||||
#include "rocksdb/cache.h"
|
||||
#include "rocksdb/convenience.h"
|
||||
#include "rocksdb/db.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/secondary_cache.h"
|
||||
#include "rocksdb/system_clock.h"
|
||||
#include "rocksdb/utilities/object_registry.h"
|
||||
#include "table/block_based/cachable_entry.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/gflags_compat.h"
|
||||
@@ -233,9 +233,8 @@ class CacheBench {
|
||||
LRUCacheOptions opts(FLAGS_cache_size, FLAGS_num_shard_bits, false, 0.5);
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (!FLAGS_secondary_cache_uri.empty()) {
|
||||
Status s =
|
||||
ObjectRegistry::NewInstance()->NewSharedObject<SecondaryCache>(
|
||||
FLAGS_secondary_cache_uri, &secondary_cache);
|
||||
Status s = SecondaryCache::CreateFromString(
|
||||
ConfigOptions(), FLAGS_secondary_cache_uri, &secondary_cache);
|
||||
if (secondary_cache == nullptr) {
|
||||
fprintf(
|
||||
stderr,
|
||||
|
||||
Vendored
+1
-1
@@ -27,7 +27,7 @@ enum class CacheEntryRole {
|
||||
kIndexBlock,
|
||||
// Other kinds of block-based table block
|
||||
kOtherBlock,
|
||||
// WriteBufferManager resevations to account for memtable usage
|
||||
// WriteBufferManager reservations to account for memtable usage
|
||||
kWriteBuffer,
|
||||
// Default bucket, for miscellaneous cache entries. Do not use for
|
||||
// entries that could potentially add up to large usage.
|
||||
|
||||
Vendored
+110
-27
@@ -309,7 +309,8 @@ void LRUCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
|
||||
strict_capacity_limit_ = strict_capacity_limit;
|
||||
}
|
||||
|
||||
Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle) {
|
||||
Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle,
|
||||
bool free_handle_on_fail) {
|
||||
Status s = Status::OK();
|
||||
autovector<LRUHandle*> last_reference_list;
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
@@ -323,14 +324,16 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle) {
|
||||
|
||||
if ((usage_ + total_charge) > capacity_ &&
|
||||
(strict_capacity_limit_ || handle == nullptr)) {
|
||||
e->SetInCache(false);
|
||||
if (handle == nullptr) {
|
||||
// Don't insert the entry but still return ok, as if the entry inserted
|
||||
// into cache and get evicted immediately.
|
||||
e->SetInCache(false);
|
||||
last_reference_list.push_back(e);
|
||||
} else {
|
||||
delete[] reinterpret_cast<char*>(e);
|
||||
*handle = nullptr;
|
||||
if (free_handle_on_fail) {
|
||||
delete[] reinterpret_cast<char*>(e);
|
||||
*handle = nullptr;
|
||||
}
|
||||
s = Status::Incomplete("Insert failed due to LRU cache being full.");
|
||||
}
|
||||
} else {
|
||||
@@ -355,7 +358,10 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle) {
|
||||
if (handle == nullptr) {
|
||||
LRU_Insert(e);
|
||||
} else {
|
||||
e->Ref();
|
||||
// If caller already holds a ref, no need to take one here
|
||||
if (!e->HasRefs()) {
|
||||
e->Ref();
|
||||
}
|
||||
*handle = reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
}
|
||||
@@ -375,6 +381,38 @@ Status LRUCacheShard::InsertItem(LRUHandle* e, Cache::Handle** handle) {
|
||||
return s;
|
||||
}
|
||||
|
||||
void LRUCacheShard::Promote(LRUHandle* e) {
|
||||
SecondaryCacheResultHandle* secondary_handle = e->sec_handle;
|
||||
|
||||
assert(secondary_handle->IsReady());
|
||||
e->SetIncomplete(false);
|
||||
e->SetInCache(true);
|
||||
e->SetPromoted(true);
|
||||
e->value = secondary_handle->Value();
|
||||
e->charge = secondary_handle->Size();
|
||||
delete secondary_handle;
|
||||
|
||||
// This call could fail if the cache is over capacity and
|
||||
// strict_capacity_limit_ is true. In such a case, we don't want
|
||||
// InsertItem() to free the handle, since the item is already in memory
|
||||
// and the caller will most likely just read from disk if we erase it here.
|
||||
if (e->value) {
|
||||
Cache::Handle* handle = reinterpret_cast<Cache::Handle*>(e);
|
||||
Status s = InsertItem(e, &handle, /*free_handle_on_fail=*/false);
|
||||
if (!s.ok()) {
|
||||
// Item is in memory, but not accounted against the cache capacity.
|
||||
// When the handle is released, the item should get deleted
|
||||
assert(!e->InCache());
|
||||
}
|
||||
} else {
|
||||
// Since the secondary cache lookup failed, mark the item as not in cache
|
||||
// Don't charge the cache as its only metadata that'll shortly be released
|
||||
MutexLock l(&mutex_);
|
||||
e->charge = 0;
|
||||
e->SetInCache(false);
|
||||
}
|
||||
}
|
||||
|
||||
Cache::Handle* LRUCacheShard::Lookup(
|
||||
const Slice& key, uint32_t hash,
|
||||
const ShardedCache::CacheItemHelper* helper,
|
||||
@@ -399,47 +437,44 @@ Cache::Handle* LRUCacheShard::Lookup(
|
||||
// mutex if we're going to lookup in the secondary cache
|
||||
// Only support synchronous for now
|
||||
// TODO: Support asynchronous lookup in secondary cache
|
||||
if (!e && secondary_cache_ && helper && helper->saveto_cb && wait) {
|
||||
if (!e && secondary_cache_ && helper && helper->saveto_cb) {
|
||||
// For objects from the secondary cache, we expect the caller to provide
|
||||
// a way to create/delete the primary cache object. The only case where
|
||||
// a deleter would not be required is for dummy entries inserted for
|
||||
// accounting purposes, which we won't demote to the secondary cache
|
||||
// anyway.
|
||||
assert(create_cb && helper->del_cb);
|
||||
std::unique_ptr<SecondaryCacheHandle> secondary_handle =
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle =
|
||||
secondary_cache_->Lookup(key, create_cb, wait);
|
||||
if (secondary_handle != nullptr) {
|
||||
void* value = nullptr;
|
||||
e = reinterpret_cast<LRUHandle*>(
|
||||
new char[sizeof(LRUHandle) - 1 + key.size()]);
|
||||
|
||||
e->flags = 0;
|
||||
e->SetPromoted(true);
|
||||
e->SetSecondaryCacheCompatible(true);
|
||||
e->info_.helper = helper;
|
||||
e->key_length = key.size();
|
||||
e->hash = hash;
|
||||
e->refs = 0;
|
||||
e->next = e->prev = nullptr;
|
||||
e->SetInCache(true);
|
||||
e->SetPriority(priority);
|
||||
memcpy(e->key_data, key.data(), key.size());
|
||||
e->value = nullptr;
|
||||
e->sec_handle = secondary_handle.release();
|
||||
e->Ref();
|
||||
|
||||
value = secondary_handle->Value();
|
||||
e->value = value;
|
||||
e->charge = secondary_handle->Size();
|
||||
|
||||
// This call could nullify e if the cache is over capacity and
|
||||
// strict_capacity_limit_ is true. In such a case, the caller will try
|
||||
// to insert later, which might again fail, but its ok as this should
|
||||
// not be common
|
||||
// Being conservative here since there could be lookups that are
|
||||
// actually ok to fail rather than succeed and bloat up the memory
|
||||
// usage (preloading partitioned index blocks, for example).
|
||||
Status s = InsertItem(e, reinterpret_cast<Cache::Handle**>(&e));
|
||||
if (!s.ok()) {
|
||||
assert(e == nullptr);
|
||||
(*helper->del_cb)(key, value);
|
||||
if (wait) {
|
||||
Promote(e);
|
||||
if (!e->value) {
|
||||
// The secondary cache returned a handle, but the lookup failed
|
||||
e->Unref();
|
||||
e->Free();
|
||||
e = nullptr;
|
||||
}
|
||||
} else {
|
||||
// If wait is false, we always return a handle and let the caller
|
||||
// release the handle after checking for success or failure
|
||||
e->SetIncomplete(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -485,7 +520,12 @@ bool LRUCacheShard::Release(Cache::Handle* handle, bool force_erase) {
|
||||
last_reference = false;
|
||||
}
|
||||
}
|
||||
if (last_reference) {
|
||||
// If it was the last reference, and the entry is either not secondary
|
||||
// cache compatible (i.e a dummy entry for accounting), or is secondary
|
||||
// cache compatible and has a non-null value, then decrement the cache
|
||||
// usage. If value is null in the latter case, taht means the lookup
|
||||
// failed and we didn't charge the cache.
|
||||
if (last_reference && (!e->IsSecondaryCacheCompatible() || e->value)) {
|
||||
size_t total_charge = e->CalcTotalCharge(metadata_charge_policy_);
|
||||
assert(usage_ >= total_charge);
|
||||
usage_ -= total_charge;
|
||||
@@ -530,7 +570,7 @@ Status LRUCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
|
||||
e->SetPriority(priority);
|
||||
memcpy(e->key_data, key.data(), key.size());
|
||||
|
||||
return InsertItem(e, handle);
|
||||
return InsertItem(e, handle, /* free_handle_on_fail */ true);
|
||||
}
|
||||
|
||||
void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
@@ -560,6 +600,18 @@ void LRUCacheShard::Erase(const Slice& key, uint32_t hash) {
|
||||
}
|
||||
}
|
||||
|
||||
bool LRUCacheShard::IsReady(Cache::Handle* handle) {
|
||||
LRUHandle* e = reinterpret_cast<LRUHandle*>(handle);
|
||||
MutexLock l(&mutex_);
|
||||
bool ready = true;
|
||||
if (e->IsPending()) {
|
||||
assert(secondary_cache_);
|
||||
assert(e->sec_handle);
|
||||
ready = e->sec_handle->IsReady();
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
|
||||
size_t LRUCacheShard::GetUsage() const {
|
||||
MutexLock l(&mutex_);
|
||||
return usage_;
|
||||
@@ -600,6 +652,7 @@ LRUCache::LRUCache(size_t capacity, int num_shard_bits,
|
||||
use_adaptive_mutex, metadata_charge_policy,
|
||||
/* max_upper_hash_bits */ 32 - num_shard_bits, secondary_cache);
|
||||
}
|
||||
secondary_cache_ = secondary_cache;
|
||||
}
|
||||
|
||||
LRUCache::~LRUCache() {
|
||||
@@ -665,6 +718,36 @@ double LRUCache::GetHighPriPoolRatio() {
|
||||
return result;
|
||||
}
|
||||
|
||||
void LRUCache::WaitAll(std::vector<Handle*>& handles) {
|
||||
if (secondary_cache_) {
|
||||
std::vector<SecondaryCacheResultHandle*> sec_handles;
|
||||
sec_handles.reserve(handles.size());
|
||||
for (Handle* handle : handles) {
|
||||
if (!handle) {
|
||||
continue;
|
||||
}
|
||||
LRUHandle* lru_handle = reinterpret_cast<LRUHandle*>(handle);
|
||||
if (!lru_handle->IsPending()) {
|
||||
continue;
|
||||
}
|
||||
sec_handles.emplace_back(lru_handle->sec_handle);
|
||||
}
|
||||
secondary_cache_->WaitAll(sec_handles);
|
||||
for (Handle* handle : handles) {
|
||||
if (!handle) {
|
||||
continue;
|
||||
}
|
||||
LRUHandle* lru_handle = reinterpret_cast<LRUHandle*>(handle);
|
||||
if (!lru_handle->IsPending()) {
|
||||
continue;
|
||||
}
|
||||
uint32_t hash = GetHash(handle);
|
||||
LRUCacheShard* shard = static_cast<LRUCacheShard*>(GetShard(Shard(hash)));
|
||||
shard->Promote(lru_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Cache> NewLRUCache(
|
||||
size_t capacity, int num_shard_bits, bool strict_capacity_limit,
|
||||
double high_pri_pool_ratio,
|
||||
|
||||
Vendored
+33
-5
@@ -57,7 +57,12 @@ struct LRUHandle {
|
||||
Cache::DeleterFn deleter;
|
||||
const ShardedCache::CacheItemHelper* helper;
|
||||
} info_;
|
||||
LRUHandle* next_hash;
|
||||
// An entry is not added to the LRUHandleTable until the secondary cache
|
||||
// lookup is complete, so its safe to have this union.
|
||||
union {
|
||||
LRUHandle* next_hash;
|
||||
SecondaryCacheResultHandle* sec_handle;
|
||||
};
|
||||
LRUHandle* next;
|
||||
LRUHandle* prev;
|
||||
size_t charge; // TODO(opt): Only allow uint32_t?
|
||||
@@ -189,7 +194,16 @@ struct LRUHandle {
|
||||
if (!IsSecondaryCacheCompatible() && info_.deleter) {
|
||||
(*info_.deleter)(key(), value);
|
||||
} else if (IsSecondaryCacheCompatible()) {
|
||||
(*info_.helper->del_cb)(key(), value);
|
||||
if (IsPending()) {
|
||||
assert(sec_handle != nullptr);
|
||||
SecondaryCacheResultHandle* tmp_sec_handle = sec_handle;
|
||||
tmp_sec_handle->Wait();
|
||||
value = tmp_sec_handle->Value();
|
||||
delete tmp_sec_handle;
|
||||
}
|
||||
if (value) {
|
||||
(*info_.helper->del_cb)(key(), value);
|
||||
}
|
||||
}
|
||||
delete[] reinterpret_cast<char*>(this);
|
||||
}
|
||||
@@ -314,7 +328,7 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
bool force_erase) override {
|
||||
return Release(handle, force_erase);
|
||||
}
|
||||
virtual bool IsReady(Cache::Handle* /*handle*/) override { return true; }
|
||||
virtual bool IsReady(Cache::Handle* /*handle*/) override;
|
||||
virtual void Wait(Cache::Handle* /*handle*/) override {}
|
||||
virtual bool Ref(Cache::Handle* handle) override;
|
||||
virtual bool Release(Cache::Handle* handle,
|
||||
@@ -347,10 +361,23 @@ class ALIGN_AS(CACHE_LINE_SIZE) LRUCacheShard final : public CacheShard {
|
||||
double GetHighPriPoolRatio();
|
||||
|
||||
private:
|
||||
Status InsertItem(LRUHandle* item, Cache::Handle** handle);
|
||||
friend class LRUCache;
|
||||
// Insert an item into the hash table and, if handle is null, insert into
|
||||
// the LRU list. Older items are evicted as necessary. If the cache is full
|
||||
// and free_handle_on_fail is true, the item is deleted and handle is set to.
|
||||
Status InsertItem(LRUHandle* item, Cache::Handle** handle,
|
||||
bool free_handle_on_fail);
|
||||
Status Insert(const Slice& key, uint32_t hash, void* value, size_t charge,
|
||||
DeleterFn deleter, const Cache::CacheItemHelper* helper,
|
||||
Cache::Handle** handle, Cache::Priority priority);
|
||||
// Promote an item looked up from the secondary cache to the LRU cache. The
|
||||
// item is only inserted into the hash table and not the LRU list, and only
|
||||
// if the cache is not at full capacity, as is the case during Insert. The
|
||||
// caller should hold a reference on the LRUHandle. When the caller releases
|
||||
// the last reference, the item is added to the LRU list.
|
||||
// The item is promoted to the high pri or low pri pool as specified by the
|
||||
// caller in Lookup.
|
||||
void Promote(LRUHandle* e);
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Insert(LRUHandle* e);
|
||||
|
||||
@@ -437,7 +464,7 @@ class LRUCache
|
||||
virtual uint32_t GetHash(Handle* handle) const override;
|
||||
virtual DeleterFn GetDeleter(Handle* handle) const override;
|
||||
virtual void DisownData() override;
|
||||
virtual void WaitAll(std::vector<Handle*>& /*handles*/) override {}
|
||||
virtual void WaitAll(std::vector<Handle*>& handles) override;
|
||||
|
||||
// Retrieves number of elements in LRU, for unit test purpose only
|
||||
size_t TEST_GetLRUSize();
|
||||
@@ -447,6 +474,7 @@ class LRUCache
|
||||
private:
|
||||
LRUCacheShard* shards_ = nullptr;
|
||||
int num_shards_ = 0;
|
||||
std::shared_ptr<SecondaryCache> secondary_cache_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
Vendored
+269
-27
@@ -18,6 +18,7 @@
|
||||
#include "test_util/testharness.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/random.h"
|
||||
#include "utilities/fault_injection_fs.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -203,6 +204,19 @@ TEST_F(LRUCacheTest, EntriesWithPriority) {
|
||||
|
||||
class TestSecondaryCache : public SecondaryCache {
|
||||
public:
|
||||
// Specifies what action to take on a lookup for a particular key
|
||||
enum ResultType {
|
||||
SUCCESS,
|
||||
// Fail lookup immediately
|
||||
FAIL,
|
||||
// Defer the result. It will returned after Wait/WaitAll is called
|
||||
DEFER,
|
||||
// Defer the result and eventually return failure
|
||||
DEFER_AND_FAIL
|
||||
};
|
||||
|
||||
using ResultMap = std::unordered_map<std::string, ResultType>;
|
||||
|
||||
explicit TestSecondaryCache(size_t capacity)
|
||||
: num_inserts_(0), num_lookups_(0), inject_failure_(false) {
|
||||
cache_ = NewLRUCache(capacity, 0, false, 0.5, nullptr,
|
||||
@@ -210,17 +224,22 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
}
|
||||
~TestSecondaryCache() override { cache_.reset(); }
|
||||
|
||||
std::string Name() override { return "TestSecondaryCache"; }
|
||||
const char* Name() const override { return "TestSecondaryCache"; }
|
||||
|
||||
void InjectFailure() { inject_failure_ = true; }
|
||||
|
||||
void ResetInjectFailure() { inject_failure_ = false; }
|
||||
|
||||
void SetDbSessionId(const std::string& db_session_id) {
|
||||
db_session_id_ = db_session_id;
|
||||
}
|
||||
|
||||
Status Insert(const Slice& key, void* value,
|
||||
const Cache::CacheItemHelper* helper) override {
|
||||
if (inject_failure_) {
|
||||
return Status::Corruption("Insertion Data Corrupted");
|
||||
}
|
||||
assert(IsDbSessionIdAsKeyPrefix(key) == true);
|
||||
size_t size;
|
||||
char* buf;
|
||||
Status s;
|
||||
@@ -240,22 +259,37 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<SecondaryCacheHandle> Lookup(
|
||||
std::unique_ptr<SecondaryCacheResultHandle> Lookup(
|
||||
const Slice& key, const Cache::CreateCallback& create_cb,
|
||||
bool /*wait*/) override {
|
||||
std::unique_ptr<SecondaryCacheHandle> secondary_handle;
|
||||
std::string key_str = key.ToString();
|
||||
TEST_SYNC_POINT_CALLBACK("TestSecondaryCache::Lookup", &key_str);
|
||||
|
||||
std::unique_ptr<SecondaryCacheResultHandle> secondary_handle;
|
||||
ResultType type = ResultType::SUCCESS;
|
||||
auto iter = result_map_.find(key.ToString());
|
||||
if (iter != result_map_.end()) {
|
||||
type = iter->second;
|
||||
}
|
||||
if (type == ResultType::FAIL) {
|
||||
return secondary_handle;
|
||||
}
|
||||
|
||||
Cache::Handle* handle = cache_->Lookup(key);
|
||||
num_lookups_++;
|
||||
if (handle) {
|
||||
void* value;
|
||||
size_t charge;
|
||||
char* ptr = (char*)cache_->Value(handle);
|
||||
size_t size = DecodeFixed64(ptr);
|
||||
ptr += sizeof(uint64_t);
|
||||
Status s = create_cb(ptr, size, &value, &charge);
|
||||
void* value = nullptr;
|
||||
size_t charge = 0;
|
||||
Status s;
|
||||
if (type != ResultType::DEFER_AND_FAIL) {
|
||||
char* ptr = (char*)cache_->Value(handle);
|
||||
size_t size = DecodeFixed64(ptr);
|
||||
ptr += sizeof(uint64_t);
|
||||
s = create_cb(ptr, size, &value, &charge);
|
||||
}
|
||||
if (s.ok()) {
|
||||
secondary_handle.reset(
|
||||
new TestSecondaryCacheHandle(cache_.get(), handle, value, charge));
|
||||
secondary_handle.reset(new TestSecondaryCacheResultHandle(
|
||||
cache_.get(), handle, value, charge, type));
|
||||
} else {
|
||||
cache_->Release(handle);
|
||||
}
|
||||
@@ -265,47 +299,92 @@ class TestSecondaryCache : public SecondaryCache {
|
||||
|
||||
void Erase(const Slice& /*key*/) override {}
|
||||
|
||||
void WaitAll(std::vector<SecondaryCacheHandle*> /*handles*/) override {}
|
||||
void WaitAll(std::vector<SecondaryCacheResultHandle*> handles) override {
|
||||
for (SecondaryCacheResultHandle* handle : handles) {
|
||||
TestSecondaryCacheResultHandle* sec_handle =
|
||||
static_cast<TestSecondaryCacheResultHandle*>(handle);
|
||||
sec_handle->SetReady();
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetPrintableOptions() const override { return ""; }
|
||||
|
||||
void SetResultMap(ResultMap&& map) { result_map_ = std::move(map); }
|
||||
|
||||
uint32_t num_inserts() { return num_inserts_; }
|
||||
|
||||
uint32_t num_lookups() { return num_lookups_; }
|
||||
|
||||
private:
|
||||
class TestSecondaryCacheHandle : public SecondaryCacheHandle {
|
||||
public:
|
||||
TestSecondaryCacheHandle(Cache* cache, Cache::Handle* handle, void* value,
|
||||
size_t size)
|
||||
: cache_(cache), handle_(handle), value_(value), size_(size) {}
|
||||
~TestSecondaryCacheHandle() override { cache_->Release(handle_); }
|
||||
bool IsDbSessionIdAsKeyPrefix(const Slice& key) {
|
||||
if (db_session_id_.size() == 0) {
|
||||
return true;
|
||||
}
|
||||
if (key.size() < 20) {
|
||||
return false;
|
||||
}
|
||||
std::string s_key = key.ToString();
|
||||
if (s_key.substr(0, 20) != db_session_id_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsReady() override { return true; }
|
||||
private:
|
||||
class TestSecondaryCacheResultHandle : public SecondaryCacheResultHandle {
|
||||
public:
|
||||
TestSecondaryCacheResultHandle(Cache* cache, Cache::Handle* handle,
|
||||
void* value, size_t size, ResultType type)
|
||||
: cache_(cache),
|
||||
handle_(handle),
|
||||
value_(value),
|
||||
size_(size),
|
||||
is_ready_(true) {
|
||||
if (type != ResultType::SUCCESS) {
|
||||
is_ready_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
~TestSecondaryCacheResultHandle() override { cache_->Release(handle_); }
|
||||
|
||||
bool IsReady() override { return is_ready_; }
|
||||
|
||||
void Wait() override {}
|
||||
|
||||
void* Value() override { return value_; }
|
||||
void* Value() override {
|
||||
assert(is_ready_);
|
||||
return value_;
|
||||
}
|
||||
|
||||
size_t Size() override { return size_; }
|
||||
size_t Size() override { return Value() ? size_ : 0; }
|
||||
|
||||
void SetReady() { is_ready_ = true; }
|
||||
|
||||
private:
|
||||
Cache* cache_;
|
||||
Cache::Handle* handle_;
|
||||
void* value_;
|
||||
size_t size_;
|
||||
bool is_ready_;
|
||||
};
|
||||
|
||||
std::shared_ptr<Cache> cache_;
|
||||
uint32_t num_inserts_;
|
||||
uint32_t num_lookups_;
|
||||
bool inject_failure_;
|
||||
std::string db_session_id_;
|
||||
ResultMap result_map_;
|
||||
};
|
||||
|
||||
class DBSecondaryCacheTest : public DBTestBase {
|
||||
public:
|
||||
DBSecondaryCacheTest()
|
||||
: DBTestBase("/db_secondary_cache_test", /*env_do_fsync=*/true) {}
|
||||
: DBTestBase("/db_secondary_cache_test", /*env_do_fsync=*/true) {
|
||||
fault_fs_.reset(new FaultInjectionTestFS(env_->GetFileSystem()));
|
||||
fault_env_.reset(new CompositeEnvWrapper(env_, fault_fs_));
|
||||
}
|
||||
|
||||
std::shared_ptr<FaultInjectionTestFS> fault_fs_;
|
||||
std::unique_ptr<Env> fault_env_;
|
||||
};
|
||||
|
||||
class LRUSecondaryCacheTest : public LRUCacheTest {
|
||||
@@ -323,6 +402,7 @@ class LRUSecondaryCacheTest : public LRUCacheTest {
|
||||
|
||||
char* Buf() { return buf_.get(); }
|
||||
size_t Size() { return size_; }
|
||||
std::string ToString() { return std::string(Buf(), Size()); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> buf_;
|
||||
@@ -548,14 +628,15 @@ TEST_F(LRUSecondaryCacheTest, FullCapacityTest) {
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
// This lookup should fail, since k1 promotion would have failed due to
|
||||
// the block cache being at capacity
|
||||
// k1 promotion should fail due to the block cache being at capacity,
|
||||
// but the lookup should still succeed
|
||||
Cache::Handle* handle2;
|
||||
handle2 = cache->Lookup("k1", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_EQ(handle2, nullptr);
|
||||
// Since k1 didn't get promoted, k2 should still be in cache
|
||||
ASSERT_NE(handle2, nullptr);
|
||||
// Since k1 didn't get inserted, k2 should still be in cache
|
||||
cache->Release(handle);
|
||||
cache->Release(handle2);
|
||||
handle = cache->Lookup("k2", &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, true);
|
||||
ASSERT_NE(handle, nullptr);
|
||||
@@ -586,11 +667,16 @@ TEST_F(DBSecondaryCacheTest, TestSecondaryCacheCorrectness1) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.env = fault_env_.get();
|
||||
fault_fs_->SetFailGetUniqueId(true);
|
||||
|
||||
// Set the file paranoid check, so after flush, the file will be read
|
||||
// all the blocks will be accessed.
|
||||
options.paranoid_file_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
std::string session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(session_id));
|
||||
secondary_cache->SetDbSessionId(session_id);
|
||||
Random rnd(301);
|
||||
const int N = 6;
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -679,7 +765,12 @@ TEST_F(DBSecondaryCacheTest, TestSecondaryCacheCorrectness2) {
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.paranoid_file_checks = true;
|
||||
options.env = fault_env_.get();
|
||||
fault_fs_->SetFailGetUniqueId(true);
|
||||
DestroyAndReopen(options);
|
||||
std::string session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(session_id));
|
||||
secondary_cache->SetDbSessionId(session_id);
|
||||
Random rnd(301);
|
||||
const int N = 6;
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -767,7 +858,13 @@ TEST_F(DBSecondaryCacheTest, NoSecondaryCacheInsertion) {
|
||||
options.create_if_missing = true;
|
||||
options.paranoid_file_checks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.env = fault_env_.get();
|
||||
fault_fs_->SetFailGetUniqueId(true);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
std::string session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(session_id));
|
||||
secondary_cache->SetDbSessionId(session_id);
|
||||
Random rnd(301);
|
||||
const int N = 6;
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -814,7 +911,12 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheIntensiveTesting) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.env = fault_env_.get();
|
||||
fault_fs_->SetFailGetUniqueId(true);
|
||||
DestroyAndReopen(options);
|
||||
std::string session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(session_id));
|
||||
secondary_cache->SetDbSessionId(session_id);
|
||||
Random rnd(301);
|
||||
const int N = 256;
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -859,7 +961,12 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheFailureTest) {
|
||||
options.create_if_missing = true;
|
||||
options.paranoid_file_checks = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.env = fault_env_.get();
|
||||
fault_fs_->SetFailGetUniqueId(true);
|
||||
DestroyAndReopen(options);
|
||||
std::string session_id;
|
||||
ASSERT_OK(db_->GetDbSessionId(session_id));
|
||||
secondary_cache->SetDbSessionId(session_id);
|
||||
Random rnd(301);
|
||||
const int N = 6;
|
||||
for (int i = 0; i < N; i++) {
|
||||
@@ -932,6 +1039,141 @@ TEST_F(DBSecondaryCacheTest, SecondaryCacheFailureTest) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(LRUSecondaryCacheTest, BasicWaitAllTest) {
|
||||
LRUCacheOptions opts(1024, 2, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache =
|
||||
std::make_shared<TestSecondaryCache>(32 * 1024);
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
const int num_keys = 32;
|
||||
|
||||
Random rnd(301);
|
||||
std::vector<std::string> values;
|
||||
for (int i = 0; i < num_keys; ++i) {
|
||||
std::string str = rnd.RandomString(1020);
|
||||
values.emplace_back(str);
|
||||
TestItem* item = new TestItem(str.data(), str.length());
|
||||
ASSERT_OK(cache->Insert("k" + std::to_string(i), item,
|
||||
&LRUSecondaryCacheTest::helper_, str.length()));
|
||||
}
|
||||
// Force all entries to be evicted to the secondary cache
|
||||
cache->SetCapacity(0);
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 32u);
|
||||
cache->SetCapacity(32 * 1024);
|
||||
|
||||
secondary_cache->SetResultMap(
|
||||
{{"k3", TestSecondaryCache::ResultType::DEFER},
|
||||
{"k4", TestSecondaryCache::ResultType::DEFER_AND_FAIL},
|
||||
{"k5", TestSecondaryCache::ResultType::FAIL}});
|
||||
std::vector<Cache::Handle*> results;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
results.emplace_back(
|
||||
cache->Lookup("k" + std::to_string(i), &LRUSecondaryCacheTest::helper_,
|
||||
test_item_creator, Cache::Priority::LOW, false));
|
||||
}
|
||||
cache->WaitAll(results);
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (i == 4) {
|
||||
ASSERT_EQ(cache->Value(results[i]), nullptr);
|
||||
} else if (i == 5) {
|
||||
ASSERT_EQ(results[i], nullptr);
|
||||
continue;
|
||||
} else {
|
||||
TestItem* item = static_cast<TestItem*>(cache->Value(results[i]));
|
||||
ASSERT_EQ(item->ToString(), values[i]);
|
||||
}
|
||||
cache->Release(results[i]);
|
||||
}
|
||||
|
||||
cache.reset();
|
||||
secondary_cache.reset();
|
||||
}
|
||||
|
||||
// In this test, we have one KV pair per data block. We indirectly determine
|
||||
// the cache key associated with each data block (and thus each KV) by using
|
||||
// a sync point callback in TestSecondaryCache::Lookup. We then control the
|
||||
// lookup result by setting the ResultMap.
|
||||
TEST_F(DBSecondaryCacheTest, TestSecondaryCacheMultiGet) {
|
||||
LRUCacheOptions opts(1 << 20, 0, false, 0.5, nullptr, kDefaultToAdaptiveMutex,
|
||||
kDontChargeCacheMetadata);
|
||||
std::shared_ptr<TestSecondaryCache> secondary_cache(
|
||||
new TestSecondaryCache(2048 * 1024));
|
||||
opts.secondary_cache = secondary_cache;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(opts);
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = cache;
|
||||
table_options.block_size = 4 * 1024;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
Options options = GetDefaultOptions();
|
||||
options.create_if_missing = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.paranoid_file_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
Random rnd(301);
|
||||
const int N = 8;
|
||||
std::vector<std::string> keys;
|
||||
for (int i = 0; i < N; i++) {
|
||||
std::string p_v = rnd.RandomString(4000);
|
||||
keys.emplace_back(p_v);
|
||||
ASSERT_OK(Put(Key(i), p_v));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
// After Flush is successful, RocksDB does the paranoid check for the new
|
||||
// SST file. This will try to lookup all data blocks in the secondary
|
||||
// cache.
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 0u);
|
||||
ASSERT_EQ(secondary_cache->num_lookups(), 8u);
|
||||
|
||||
cache->SetCapacity(0);
|
||||
ASSERT_EQ(secondary_cache->num_inserts(), 8u);
|
||||
cache->SetCapacity(1 << 20);
|
||||
|
||||
std::vector<std::string> cache_keys;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"TestSecondaryCache::Lookup", [&cache_keys](void* key) -> void {
|
||||
cache_keys.emplace_back(*(static_cast<std::string*>(key)));
|
||||
});
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
for (int i = 0; i < N; ++i) {
|
||||
std::string v = Get(Key(i));
|
||||
ASSERT_EQ(4000, v.size());
|
||||
ASSERT_EQ(v, keys[i]);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ASSERT_EQ(secondary_cache->num_lookups(), 16u);
|
||||
cache->SetCapacity(0);
|
||||
cache->SetCapacity(1 << 20);
|
||||
|
||||
ASSERT_EQ(Get(Key(2)), keys[2]);
|
||||
ASSERT_EQ(Get(Key(7)), keys[7]);
|
||||
secondary_cache->SetResultMap(
|
||||
{{cache_keys[3], TestSecondaryCache::ResultType::DEFER},
|
||||
{cache_keys[4], TestSecondaryCache::ResultType::DEFER_AND_FAIL},
|
||||
{cache_keys[5], TestSecondaryCache::ResultType::FAIL}});
|
||||
|
||||
std::vector<std::string> mget_keys(
|
||||
{Key(0), Key(1), Key(2), Key(3), Key(4), Key(5), Key(6), Key(7)});
|
||||
std::vector<PinnableSlice> values(mget_keys.size());
|
||||
std::vector<Status> s(keys.size());
|
||||
std::vector<Slice> key_slices;
|
||||
for (const std::string& key : mget_keys) {
|
||||
key_slices.emplace_back(key);
|
||||
}
|
||||
uint32_t num_lookups = secondary_cache->num_lookups();
|
||||
dbfull()->MultiGet(ReadOptions(), dbfull()->DefaultColumnFamily(),
|
||||
key_slices.size(), key_slices.data(), values.data(),
|
||||
s.data(), false);
|
||||
ASSERT_EQ(secondary_cache->num_lookups(), num_lookups + 5);
|
||||
for (int i = 0; i < N; ++i) {
|
||||
ASSERT_OK(s[i]);
|
||||
ASSERT_EQ(values[i].ToString(), keys[i]);
|
||||
values[i].Reset();
|
||||
}
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Vendored
+2
-1
@@ -115,9 +115,10 @@ class ShardedCache : public Cache {
|
||||
int GetNumShardBits() const;
|
||||
uint32_t GetNumShards() const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
inline uint32_t Shard(uint32_t hash) { return hash & shard_mask_; }
|
||||
|
||||
private:
|
||||
const uint32_t shard_mask_;
|
||||
mutable port::Mutex capacity_mutex_;
|
||||
size_t capacity_;
|
||||
|
||||
@@ -31,16 +31,15 @@ Status ArenaWrappedDBIter::GetProperty(std::string prop_name,
|
||||
}
|
||||
|
||||
void ArenaWrappedDBIter::Init(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iteration,
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
auto mem = arena_.AllocateAligned(sizeof(DBIter));
|
||||
db_iter_ =
|
||||
new (mem) DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
cf_options.user_comparator, /* iter */ nullptr, version,
|
||||
new (mem) DBIter(env, read_options, ioptions, mutable_cf_options,
|
||||
ioptions.user_comparator, /* iter */ nullptr, version,
|
||||
sequence, true, max_sequential_skip_in_iteration,
|
||||
read_callback, db_impl, cfd, expose_blob_index);
|
||||
sv_number_ = version_number;
|
||||
@@ -88,16 +87,15 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
}
|
||||
|
||||
ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index, bool allow_refresh) {
|
||||
ArenaWrappedDBIter* iter = new ArenaWrappedDBIter();
|
||||
iter->Init(env, read_options, cf_options, mutable_cf_options, version,
|
||||
sequence, max_sequential_skip_in_iterations, version_number,
|
||||
read_callback, db_impl, cfd, expose_blob_index, allow_refresh);
|
||||
iter->Init(env, read_options, ioptions, mutable_cf_options, version, sequence,
|
||||
max_sequential_skip_in_iterations, version_number, read_callback,
|
||||
db_impl, cfd, expose_blob_index, allow_refresh);
|
||||
if (db_impl != nullptr && cfd != nullptr && allow_refresh) {
|
||||
iter->StoreRefreshInfo(db_impl, cfd, read_callback, expose_blob_index);
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
Status Refresh() override;
|
||||
|
||||
void Init(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence,
|
||||
uint64_t max_sequential_skip_in_iterations, uint64_t version_number,
|
||||
@@ -105,8 +105,7 @@ class ArenaWrappedDBIter : public Iterator {
|
||||
// `db_impl` and `cfd` are used for reneweal. If left null, renewal will not
|
||||
// be supported.
|
||||
extern ArenaWrappedDBIter* NewArenaWrappedDbIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Version* version,
|
||||
const SequenceNumber& sequence, uint64_t max_sequential_skip_in_iterations,
|
||||
uint64_t version_number, ReadCallback* read_callback,
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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 "db/blob/blob_garbage_meter.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "table/internal_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// An internal iterator that passes each key-value encountered to
|
||||
// BlobGarbageMeter as inflow in order to measure the total number and size of
|
||||
// blobs in the compaction input on a per-blob file basis.
|
||||
class BlobCountingIterator : public InternalIterator {
|
||||
public:
|
||||
BlobCountingIterator(InternalIterator* iter,
|
||||
BlobGarbageMeter* blob_garbage_meter)
|
||||
: iter_(iter), blob_garbage_meter_(blob_garbage_meter) {
|
||||
assert(iter_);
|
||||
assert(blob_garbage_meter_);
|
||||
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
bool Valid() const override { return iter_->Valid() && status_.ok(); }
|
||||
|
||||
void SeekToFirst() override {
|
||||
iter_->SeekToFirst();
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
void SeekToLast() override {
|
||||
iter_->SeekToLast();
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
iter_->Seek(target);
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
iter_->SeekForPrev(target);
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
assert(Valid());
|
||||
|
||||
iter_->Next();
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
bool NextAndGetResult(IterateResult* result) override {
|
||||
assert(Valid());
|
||||
|
||||
const bool res = iter_->NextAndGetResult(result);
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
return res;
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
assert(Valid());
|
||||
|
||||
iter_->Prev();
|
||||
UpdateAndCountBlobIfNeeded();
|
||||
}
|
||||
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return iter_->key();
|
||||
}
|
||||
|
||||
Slice user_key() const override {
|
||||
assert(Valid());
|
||||
return iter_->user_key();
|
||||
}
|
||||
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return iter_->value();
|
||||
}
|
||||
|
||||
Status status() const override { return status_; }
|
||||
|
||||
bool PrepareValue() override {
|
||||
assert(Valid());
|
||||
return iter_->PrepareValue();
|
||||
}
|
||||
|
||||
bool MayBeOutOfLowerBound() override {
|
||||
assert(Valid());
|
||||
return iter_->MayBeOutOfLowerBound();
|
||||
}
|
||||
|
||||
IterBoundCheck UpperBoundCheckResult() override {
|
||||
assert(Valid());
|
||||
return iter_->UpperBoundCheckResult();
|
||||
}
|
||||
|
||||
void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override {
|
||||
iter_->SetPinnedItersMgr(pinned_iters_mgr);
|
||||
}
|
||||
|
||||
bool IsKeyPinned() const override {
|
||||
assert(Valid());
|
||||
return iter_->IsKeyPinned();
|
||||
}
|
||||
|
||||
bool IsValuePinned() const override {
|
||||
assert(Valid());
|
||||
return iter_->IsValuePinned();
|
||||
}
|
||||
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override {
|
||||
return iter_->GetProperty(prop_name, prop);
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateAndCountBlobIfNeeded() {
|
||||
assert(!iter_->Valid() || iter_->status().ok());
|
||||
|
||||
if (!iter_->Valid()) {
|
||||
status_ = iter_->status();
|
||||
return;
|
||||
}
|
||||
|
||||
TEST_SYNC_POINT(
|
||||
"BlobCountingIterator::UpdateAndCountBlobIfNeeded:ProcessInFlow");
|
||||
|
||||
status_ = blob_garbage_meter_->ProcessInFlow(key(), value());
|
||||
}
|
||||
|
||||
InternalIterator* iter_;
|
||||
BlobGarbageMeter* blob_garbage_meter_;
|
||||
Status status_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,325 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_counting_iterator.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_garbage_meter.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void CheckInFlow(const BlobGarbageMeter& blob_garbage_meter,
|
||||
uint64_t blob_file_number, uint64_t count, uint64_t bytes) {
|
||||
const auto& flows = blob_garbage_meter.flows();
|
||||
|
||||
const auto it = flows.find(blob_file_number);
|
||||
if (it == flows.end()) {
|
||||
ASSERT_EQ(count, 0);
|
||||
ASSERT_EQ(bytes, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& in = it->second.GetInFlow();
|
||||
|
||||
ASSERT_EQ(in.GetCount(), count);
|
||||
ASSERT_EQ(in.GetBytes(), bytes);
|
||||
}
|
||||
|
||||
TEST(BlobCountingIteratorTest, CountBlobs) {
|
||||
// Note: the input consists of three key-values: two are blob references to
|
||||
// different blob files, while the third one is a plain value.
|
||||
constexpr char user_key0[] = "key0";
|
||||
constexpr char user_key1[] = "key1";
|
||||
constexpr char user_key2[] = "key2";
|
||||
|
||||
const std::vector<std::string> keys{
|
||||
test::KeyStr(user_key0, 1, kTypeBlobIndex),
|
||||
test::KeyStr(user_key1, 2, kTypeBlobIndex),
|
||||
test::KeyStr(user_key2, 3, kTypeValue)};
|
||||
|
||||
constexpr uint64_t first_blob_file_number = 4;
|
||||
constexpr uint64_t first_offset = 1000;
|
||||
constexpr uint64_t first_size = 2000;
|
||||
|
||||
std::string first_blob_index;
|
||||
BlobIndex::EncodeBlob(&first_blob_index, first_blob_file_number, first_offset,
|
||||
first_size, kNoCompression);
|
||||
|
||||
constexpr uint64_t second_blob_file_number = 6;
|
||||
constexpr uint64_t second_offset = 2000;
|
||||
constexpr uint64_t second_size = 4000;
|
||||
|
||||
std::string second_blob_index;
|
||||
BlobIndex::EncodeBlob(&second_blob_index, second_blob_file_number,
|
||||
second_offset, second_size, kNoCompression);
|
||||
|
||||
const std::vector<std::string> values{first_blob_index, second_blob_index,
|
||||
"raw_value"};
|
||||
|
||||
assert(keys.size() == values.size());
|
||||
|
||||
test::VectorIterator input(keys, values);
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
BlobCountingIterator blob_counter(&input, &blob_garbage_meter);
|
||||
|
||||
constexpr uint64_t first_expected_bytes =
|
||||
first_size +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(user_key0) - 1);
|
||||
constexpr uint64_t second_expected_bytes =
|
||||
second_size +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(user_key1) - 1);
|
||||
|
||||
// Call SeekToFirst and iterate forward
|
||||
blob_counter.SeekToFirst();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[0]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key0);
|
||||
ASSERT_EQ(blob_counter.value(), values[0]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 1,
|
||||
first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 0, 0);
|
||||
|
||||
blob_counter.Next();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[1]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key1);
|
||||
ASSERT_EQ(blob_counter.value(), values[1]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 1,
|
||||
first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 1,
|
||||
second_expected_bytes);
|
||||
|
||||
blob_counter.Next();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[2]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key2);
|
||||
ASSERT_EQ(blob_counter.value(), values[2]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 1,
|
||||
first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 1,
|
||||
second_expected_bytes);
|
||||
|
||||
blob_counter.Next();
|
||||
ASSERT_FALSE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 1,
|
||||
first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 1,
|
||||
second_expected_bytes);
|
||||
|
||||
// Do it again using NextAndGetResult
|
||||
blob_counter.SeekToFirst();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[0]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key0);
|
||||
ASSERT_EQ(blob_counter.value(), values[0]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 2,
|
||||
2 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 1,
|
||||
second_expected_bytes);
|
||||
|
||||
{
|
||||
IterateResult result;
|
||||
ASSERT_TRUE(blob_counter.NextAndGetResult(&result));
|
||||
ASSERT_EQ(result.key, keys[1]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key1);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[1]);
|
||||
ASSERT_EQ(blob_counter.value(), values[1]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 2,
|
||||
2 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 2,
|
||||
2 * second_expected_bytes);
|
||||
}
|
||||
|
||||
{
|
||||
IterateResult result;
|
||||
ASSERT_TRUE(blob_counter.NextAndGetResult(&result));
|
||||
ASSERT_EQ(result.key, keys[2]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key2);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[2]);
|
||||
ASSERT_EQ(blob_counter.value(), values[2]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 2,
|
||||
2 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 2,
|
||||
2 * second_expected_bytes);
|
||||
}
|
||||
|
||||
{
|
||||
IterateResult result;
|
||||
ASSERT_FALSE(blob_counter.NextAndGetResult(&result));
|
||||
ASSERT_FALSE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 2,
|
||||
2 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 2,
|
||||
2 * second_expected_bytes);
|
||||
}
|
||||
|
||||
// Call SeekToLast and iterate backward
|
||||
blob_counter.SeekToLast();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[2]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key2);
|
||||
ASSERT_EQ(blob_counter.value(), values[2]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 2,
|
||||
2 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 2,
|
||||
2 * second_expected_bytes);
|
||||
|
||||
blob_counter.Prev();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[1]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key1);
|
||||
ASSERT_EQ(blob_counter.value(), values[1]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 2,
|
||||
2 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 3,
|
||||
3 * second_expected_bytes);
|
||||
|
||||
blob_counter.Prev();
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[0]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key0);
|
||||
ASSERT_EQ(blob_counter.value(), values[0]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 3,
|
||||
3 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 3,
|
||||
3 * second_expected_bytes);
|
||||
|
||||
blob_counter.Prev();
|
||||
ASSERT_FALSE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 3,
|
||||
3 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 3,
|
||||
3 * second_expected_bytes);
|
||||
|
||||
// Call Seek for all keys (plus one that's greater than all of them)
|
||||
blob_counter.Seek(keys[0]);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[0]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key0);
|
||||
ASSERT_EQ(blob_counter.value(), values[0]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 4,
|
||||
4 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 3,
|
||||
3 * second_expected_bytes);
|
||||
|
||||
blob_counter.Seek(keys[1]);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[1]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key1);
|
||||
ASSERT_EQ(blob_counter.value(), values[1]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 4,
|
||||
4 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 4,
|
||||
4 * second_expected_bytes);
|
||||
|
||||
blob_counter.Seek(keys[2]);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[2]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key2);
|
||||
ASSERT_EQ(blob_counter.value(), values[2]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 4,
|
||||
4 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 4,
|
||||
4 * second_expected_bytes);
|
||||
|
||||
blob_counter.Seek("zzz");
|
||||
ASSERT_FALSE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 4,
|
||||
4 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 4,
|
||||
4 * second_expected_bytes);
|
||||
|
||||
// Call SeekForPrev for all keys (plus one that's less than all of them)
|
||||
blob_counter.SeekForPrev("aaa");
|
||||
ASSERT_FALSE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 4,
|
||||
4 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 4,
|
||||
4 * second_expected_bytes);
|
||||
|
||||
blob_counter.SeekForPrev(keys[0]);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[0]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key0);
|
||||
ASSERT_EQ(blob_counter.value(), values[0]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 5,
|
||||
5 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 4,
|
||||
4 * second_expected_bytes);
|
||||
|
||||
blob_counter.SeekForPrev(keys[1]);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[1]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key1);
|
||||
ASSERT_EQ(blob_counter.value(), values[1]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 5,
|
||||
5 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 5,
|
||||
5 * second_expected_bytes);
|
||||
|
||||
blob_counter.SeekForPrev(keys[2]);
|
||||
ASSERT_TRUE(blob_counter.Valid());
|
||||
ASSERT_OK(blob_counter.status());
|
||||
ASSERT_EQ(blob_counter.key(), keys[2]);
|
||||
ASSERT_EQ(blob_counter.user_key(), user_key2);
|
||||
ASSERT_EQ(blob_counter.value(), values[2]);
|
||||
CheckInFlow(blob_garbage_meter, first_blob_file_number, 5,
|
||||
5 * first_expected_bytes);
|
||||
CheckInFlow(blob_garbage_meter, second_blob_file_number, 5,
|
||||
5 * second_expected_bytes);
|
||||
}
|
||||
|
||||
TEST(BlobCountingIteratorTest, CorruptBlobIndex) {
|
||||
const std::vector<std::string> keys{
|
||||
test::KeyStr("user_key", 1, kTypeBlobIndex)};
|
||||
const std::vector<std::string> values{"i_am_not_a_blob_index"};
|
||||
|
||||
assert(keys.size() == values.size());
|
||||
|
||||
test::VectorIterator input(keys, values);
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
BlobCountingIterator blob_counter(&input, &blob_garbage_meter);
|
||||
|
||||
blob_counter.SeekToFirst();
|
||||
ASSERT_FALSE(blob_counter.Valid());
|
||||
ASSERT_NOK(blob_counter.status());
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_fetcher.h"
|
||||
|
||||
#include "db/version_set.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status BlobFetcher::FetchBlob(const Slice& user_key, const Slice& blob_index,
|
||||
PinnableSlice* blob_value) {
|
||||
Status s;
|
||||
assert(version_);
|
||||
constexpr uint64_t* bytes_read = nullptr;
|
||||
s = version_->GetBlob(read_options_, user_key, blob_index, blob_value,
|
||||
bytes_read);
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/options.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
class Version;
|
||||
|
||||
class BlobFetcher {
|
||||
public:
|
||||
BlobFetcher(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);
|
||||
|
||||
private:
|
||||
Version* version_;
|
||||
ReadOptions read_options_;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -135,7 +135,8 @@ std::ostream& operator<<(std::ostream& os,
|
||||
<< " total_blob_count: " << blob_file_addition.GetTotalBlobCount()
|
||||
<< " total_blob_bytes: " << blob_file_addition.GetTotalBlobBytes()
|
||||
<< " checksum_method: " << blob_file_addition.GetChecksumMethod()
|
||||
<< " checksum_value: " << blob_file_addition.GetChecksumValue();
|
||||
<< " checksum_value: "
|
||||
<< Slice(blob_file_addition.GetChecksumValue()).ToString(/* hex */ true);
|
||||
|
||||
return os;
|
||||
}
|
||||
@@ -146,7 +147,8 @@ JSONWriter& operator<<(JSONWriter& jw,
|
||||
<< "TotalBlobCount" << blob_file_addition.GetTotalBlobCount()
|
||||
<< "TotalBlobBytes" << blob_file_addition.GetTotalBlobBytes()
|
||||
<< "ChecksumMethod" << blob_file_addition.GetChecksumMethod()
|
||||
<< "ChecksumValue" << blob_file_addition.GetChecksumValue();
|
||||
<< "ChecksumValue"
|
||||
<< Slice(blob_file_addition.GetChecksumValue()).ToString(/* hex */ true);
|
||||
|
||||
return jw;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,9 @@ TEST_F(BlobFileAdditionTest, NonEmpty) {
|
||||
constexpr uint64_t total_blob_count = 2;
|
||||
constexpr uint64_t total_blob_bytes = 123456;
|
||||
const std::string checksum_method("SHA1");
|
||||
const std::string checksum_value("bdb7f34a59dfa1592ce7f52e99f98c570c525cbd");
|
||||
const std::string checksum_value(
|
||||
"\xbd\xb7\xf3\x4a\x59\xdf\xa1\x59\x2c\xe7\xf5\x2e\x99\xf9\x8c\x57\x0c\x52"
|
||||
"\x5c\xbd");
|
||||
|
||||
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method,
|
||||
@@ -113,7 +115,9 @@ TEST_F(BlobFileAdditionTest, DecodeErrors) {
|
||||
ASSERT_TRUE(std::strstr(s.getState(), "checksum value"));
|
||||
}
|
||||
|
||||
constexpr char checksum_value[] = "bdb7f34a59dfa1592ce7f52e99f98c570c525cbd";
|
||||
constexpr char checksum_value[] =
|
||||
"\xbd\xb7\xf3\x4a\x59\xdf\xa1\x59\x2c\xe7\xf5\x2e\x99\xf9\x8c\x57\x0c\x52"
|
||||
"\x5c\xbd";
|
||||
PutLengthPrefixedSlice(&str, checksum_value);
|
||||
slice = str;
|
||||
|
||||
@@ -150,7 +154,7 @@ TEST_F(BlobFileAdditionTest, ForwardCompatibleCustomField) {
|
||||
constexpr uint64_t total_blob_count = 9999;
|
||||
constexpr uint64_t total_blob_bytes = 100000000;
|
||||
const std::string checksum_method("CRC32");
|
||||
const std::string checksum_value("3d87ff57");
|
||||
const std::string checksum_value("\x3d\x87\xff\x57");
|
||||
|
||||
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method,
|
||||
@@ -178,7 +182,7 @@ TEST_F(BlobFileAdditionTest, ForwardIncompatibleCustomField) {
|
||||
constexpr uint64_t total_blob_count = 100;
|
||||
constexpr uint64_t total_blob_bytes = 2000000;
|
||||
const std::string checksum_method("CRC32B");
|
||||
const std::string checksum_value("6dbdf23a");
|
||||
const std::string checksum_value("\x6d\xbd\xf2\x3a");
|
||||
|
||||
BlobFileAddition blob_file_addition(blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method,
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
VersionSet* versions, FileSystem* fs,
|
||||
const ImmutableOptions* immutable_cf_options,
|
||||
const ImmutableOptions* immutable_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
@@ -39,14 +39,14 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
std::vector<std::string>* blob_file_paths,
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: BlobFileBuilder([versions]() { return versions->NewFileNumber(); }, fs,
|
||||
immutable_cf_options, mutable_cf_options, file_options,
|
||||
immutable_options, mutable_cf_options, file_options,
|
||||
job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, io_tracer, blob_callback, blob_file_paths,
|
||||
blob_file_additions) {}
|
||||
|
||||
BlobFileBuilder::BlobFileBuilder(
|
||||
std::function<uint64_t()> file_number_generator, FileSystem* fs,
|
||||
const ImmutableOptions* immutable_cf_options,
|
||||
const ImmutableOptions* immutable_options,
|
||||
const MutableCFOptions* mutable_cf_options, const FileOptions* file_options,
|
||||
int job_id, uint32_t column_family_id,
|
||||
const std::string& column_family_name, Env::IOPriority io_priority,
|
||||
@@ -57,7 +57,7 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
std::vector<BlobFileAddition>* blob_file_additions)
|
||||
: file_number_generator_(std::move(file_number_generator)),
|
||||
fs_(fs),
|
||||
immutable_cf_options_(immutable_cf_options),
|
||||
immutable_options_(immutable_options),
|
||||
min_blob_size_(mutable_cf_options->min_blob_size),
|
||||
blob_file_size_(mutable_cf_options->blob_file_size),
|
||||
blob_compression_type_(mutable_cf_options->blob_compression_type),
|
||||
@@ -75,7 +75,7 @@ BlobFileBuilder::BlobFileBuilder(
|
||||
blob_bytes_(0) {
|
||||
assert(file_number_generator_);
|
||||
assert(fs_);
|
||||
assert(immutable_cf_options_);
|
||||
assert(immutable_options_);
|
||||
assert(file_options_);
|
||||
assert(blob_file_paths_);
|
||||
assert(blob_file_paths_->empty());
|
||||
@@ -156,10 +156,10 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
assert(file_number_generator_);
|
||||
const uint64_t blob_file_number = file_number_generator_();
|
||||
|
||||
assert(immutable_cf_options_);
|
||||
assert(!immutable_cf_options_->cf_paths.empty());
|
||||
std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options_->cf_paths.front().path, blob_file_number);
|
||||
assert(immutable_options_);
|
||||
assert(!immutable_options_->cf_paths.empty());
|
||||
std::string blob_file_path =
|
||||
BlobFileName(immutable_options_->cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
|
||||
@@ -184,20 +184,20 @@ Status BlobFileBuilder::OpenBlobFileIfNeeded() {
|
||||
assert(file);
|
||||
file->SetIOPriority(io_priority_);
|
||||
file->SetWriteLifeTimeHint(write_hint_);
|
||||
FileTypeSet tmp_set = immutable_cf_options_->checksum_handoff_file_types;
|
||||
Statistics* const statistics = immutable_cf_options_->stats;
|
||||
FileTypeSet tmp_set = immutable_options_->checksum_handoff_file_types;
|
||||
Statistics* const statistics = immutable_options_->stats;
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_paths_->back(), *file_options_,
|
||||
immutable_cf_options_->clock, io_tracer_, statistics,
|
||||
immutable_cf_options_->listeners,
|
||||
immutable_cf_options_->file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kBlobFile)));
|
||||
immutable_options_->clock, io_tracer_, statistics,
|
||||
immutable_options_->listeners,
|
||||
immutable_options_->file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kBlobFile), false));
|
||||
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
std::unique_ptr<BlobLogWriter> blob_log_writer(new BlobLogWriter(
|
||||
std::move(file_writer), immutable_cf_options_->clock, statistics,
|
||||
blob_file_number, immutable_cf_options_->use_fsync, do_flush));
|
||||
std::move(file_writer), immutable_options_->clock, statistics,
|
||||
blob_file_number, immutable_options_->use_fsync, do_flush));
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
@@ -300,8 +300,8 @@ Status BlobFileBuilder::CloseBlobFile() {
|
||||
std::move(checksum_method),
|
||||
std::move(checksum_value));
|
||||
|
||||
assert(immutable_cf_options_);
|
||||
ROCKS_LOG_INFO(immutable_cf_options_->logger,
|
||||
assert(immutable_options_);
|
||||
ROCKS_LOG_INFO(immutable_options_->logger,
|
||||
"[%s] [JOB %d] Generated blob file #%" PRIu64 ": %" PRIu64
|
||||
" total blobs, %" PRIu64 " total bytes",
|
||||
column_family_name_.c_str(), job_id_, blob_file_number,
|
||||
|
||||
@@ -77,7 +77,7 @@ class BlobFileBuilder {
|
||||
|
||||
std::function<uint64_t()> file_number_generator_;
|
||||
FileSystem* fs_;
|
||||
const ImmutableOptions* immutable_cf_options_;
|
||||
const ImmutableOptions* immutable_options_;
|
||||
uint64_t min_blob_size_;
|
||||
uint64_t blob_file_size_;
|
||||
CompressionType blob_compression_type_;
|
||||
|
||||
@@ -129,7 +129,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
options.enable_blob_files = true;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -141,12 +141,11 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
@@ -178,9 +177,9 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckOneFile) {
|
||||
|
||||
const std::string& blob_file_path = blob_file_paths[0];
|
||||
|
||||
ASSERT_EQ(blob_file_path,
|
||||
BlobFileName(immutable_cf_options.cf_paths.front().path,
|
||||
blob_file_number));
|
||||
ASSERT_EQ(
|
||||
blob_file_path,
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
|
||||
|
||||
ASSERT_EQ(blob_file_additions.size(), 1);
|
||||
|
||||
@@ -214,7 +213,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
options.blob_file_size = value_size;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -226,12 +225,11 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> expected_key_value_pairs(
|
||||
number_of_blobs);
|
||||
@@ -264,7 +262,7 @@ TEST_F(BlobFileBuilderTest, BuildAndCheckMultipleFiles) {
|
||||
const uint64_t blob_file_number = i + 2;
|
||||
|
||||
ASSERT_EQ(blob_file_paths[i],
|
||||
BlobFileName(immutable_cf_options.cf_paths.front().path,
|
||||
BlobFileName(immutable_options.cf_paths.front().path,
|
||||
blob_file_number));
|
||||
|
||||
const auto& blob_file_addition = blob_file_additions[i];
|
||||
@@ -301,7 +299,7 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
options.min_blob_size = 1024;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -313,12 +311,11 @@ TEST_F(BlobFileBuilderTest, InlinedValues) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
for (size_t i = 0; i < number_of_blobs; ++i) {
|
||||
const std::string key = std::to_string(i);
|
||||
@@ -355,7 +352,7 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
options.blob_compression_type = kSnappyCompression;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -367,12 +364,11 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string uncompressed_value(value_size, 'x');
|
||||
@@ -391,9 +387,9 @@ TEST_F(BlobFileBuilderTest, Compression) {
|
||||
|
||||
const std::string& blob_file_path = blob_file_paths[0];
|
||||
|
||||
ASSERT_EQ(blob_file_path,
|
||||
BlobFileName(immutable_cf_options.cf_paths.front().path,
|
||||
blob_file_number));
|
||||
ASSERT_EQ(
|
||||
blob_file_path,
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
|
||||
|
||||
ASSERT_EQ(blob_file_additions.size(), 1);
|
||||
|
||||
@@ -438,7 +434,7 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
options.enable_blob_files = true;
|
||||
options.blob_compression_type = kSnappyCompression;
|
||||
options.env = &mock_env_;
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -450,12 +446,11 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack("CompressData:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
@@ -477,9 +472,9 @@ TEST_F(BlobFileBuilderTest, CompressionError) {
|
||||
constexpr uint64_t blob_file_number = 2;
|
||||
|
||||
ASSERT_EQ(blob_file_paths.size(), 1);
|
||||
ASSERT_EQ(blob_file_paths[0],
|
||||
BlobFileName(immutable_cf_options.cf_paths.front().path,
|
||||
blob_file_number));
|
||||
ASSERT_EQ(
|
||||
blob_file_paths[0],
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
|
||||
|
||||
ASSERT_TRUE(blob_file_additions.empty());
|
||||
}
|
||||
@@ -517,7 +512,7 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
std::make_shared<DummyFileChecksumGenFactory>();
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -529,12 +524,11 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
const std::string key("1");
|
||||
const std::string value("deadbeef");
|
||||
@@ -553,9 +547,9 @@ TEST_F(BlobFileBuilderTest, Checksum) {
|
||||
|
||||
const std::string& blob_file_path = blob_file_paths[0];
|
||||
|
||||
ASSERT_EQ(blob_file_path,
|
||||
BlobFileName(immutable_cf_options.cf_paths.front().path,
|
||||
blob_file_number));
|
||||
ASSERT_EQ(
|
||||
blob_file_path,
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number));
|
||||
|
||||
ASSERT_EQ(blob_file_additions.size(), 1);
|
||||
|
||||
@@ -614,7 +608,7 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
options.blob_file_size = value_size;
|
||||
options.env = &mock_env_;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
MutableCFOptions mutable_cf_options(options);
|
||||
|
||||
constexpr int job_id = 1;
|
||||
@@ -626,12 +620,11 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
std::vector<std::string> blob_file_paths;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
|
||||
BlobFileBuilder builder(TestFileNumberGenerator(), fs_, &immutable_cf_options,
|
||||
&mutable_cf_options, &file_options_, job_id,
|
||||
column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/,
|
||||
nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
BlobFileBuilder builder(
|
||||
TestFileNumberGenerator(), fs_, &immutable_options, &mutable_cf_options,
|
||||
&file_options_, job_id, column_family_id, column_family_name, io_priority,
|
||||
write_hint, nullptr /*IOTracer*/, nullptr /*BlobFileCompletionCallback*/,
|
||||
&blob_file_paths, &blob_file_additions);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
|
||||
Status* const s = static_cast<Status*>(arg);
|
||||
@@ -658,7 +651,7 @@ TEST_P(BlobFileBuilderIOErrorTest, IOError) {
|
||||
|
||||
ASSERT_EQ(blob_file_paths.size(), 1);
|
||||
ASSERT_EQ(blob_file_paths[0],
|
||||
BlobFileName(immutable_cf_options.cf_paths.front().path,
|
||||
BlobFileName(immutable_options.cf_paths.front().path,
|
||||
blob_file_number));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@ BlobFileCache::BlobFileCache(Cache* cache,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
: cache_(cache),
|
||||
mutex_(kNumberOfMutexStripes, kGetSliceNPHash64UnseededFnPtr),
|
||||
immutable_cf_options_(immutable_options),
|
||||
immutable_options_(immutable_options),
|
||||
file_options_(file_options),
|
||||
column_family_id_(column_family_id),
|
||||
blob_file_read_hist_(blob_file_read_hist),
|
||||
io_tracer_(io_tracer) {
|
||||
assert(cache_);
|
||||
assert(immutable_cf_options_);
|
||||
assert(immutable_options_);
|
||||
assert(file_options_);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
assert(immutable_cf_options_);
|
||||
Statistics* const statistics = immutable_cf_options_->stats;
|
||||
assert(immutable_options_);
|
||||
Statistics* const statistics = immutable_options_->stats;
|
||||
|
||||
RecordTick(statistics, NO_FILE_OPENS);
|
||||
|
||||
@@ -73,7 +73,7 @@ Status BlobFileCache::GetBlobFileReader(
|
||||
{
|
||||
assert(file_options_);
|
||||
const Status s = BlobFileReader::Create(
|
||||
*immutable_cf_options_, *file_options_, column_family_id_,
|
||||
*immutable_options_, *file_options_, column_family_id_,
|
||||
blob_file_read_hist_, blob_file_number, io_tracer_, &reader);
|
||||
if (!s.ok()) {
|
||||
RecordTick(statistics, NO_FILE_ERRORS);
|
||||
|
||||
@@ -40,7 +40,7 @@ class BlobFileCache {
|
||||
// Note: mutex_ below is used to guard against multiple threads racing to open
|
||||
// the same file.
|
||||
Striped<port::Mutex, Slice> mutex_;
|
||||
const ImmutableOptions* immutable_cf_options_;
|
||||
const ImmutableOptions* immutable_options_;
|
||||
const FileOptions* file_options_;
|
||||
uint32_t column_family_id_;
|
||||
HistogramImpl* blob_file_read_hist_;
|
||||
|
||||
@@ -29,28 +29,27 @@ namespace {
|
||||
|
||||
// Creates a test blob file with a single blob in it.
|
||||
void WriteBlobFile(uint32_t column_family_id,
|
||||
const ImmutableOptions& immutable_cf_options,
|
||||
const ImmutableOptions& immutable_options,
|
||||
uint64_t blob_file_number) {
|
||||
assert(!immutable_cf_options.cf_paths.empty());
|
||||
assert(!immutable_options.cf_paths.empty());
|
||||
|
||||
const std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options.cf_paths.front().path, blob_file_number);
|
||||
const std::string blob_file_path =
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
ASSERT_OK(NewWritableFile(immutable_cf_options.fs.get(), blob_file_path,
|
||||
&file, FileOptions()));
|
||||
ASSERT_OK(NewWritableFile(immutable_options.fs.get(), blob_file_path, &file,
|
||||
FileOptions()));
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.clock));
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_path, FileOptions(), immutable_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.clock, statistics,
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer), immutable_options.clock,
|
||||
statistics, blob_file_number, use_fsync,
|
||||
do_flush);
|
||||
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
@@ -100,10 +99,10 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
WriteBlobFile(column_family_id, immutable_cf_options, blob_file_number);
|
||||
WriteBlobFile(column_family_id, immutable_options, blob_file_number);
|
||||
|
||||
constexpr size_t capacity = 10;
|
||||
std::shared_ptr<Cache> backing_cache = NewLRUCache(capacity);
|
||||
@@ -111,7 +110,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader) {
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
@@ -145,10 +144,10 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
WriteBlobFile(column_family_id, immutable_cf_options, blob_file_number);
|
||||
WriteBlobFile(column_family_id, immutable_options, blob_file_number);
|
||||
|
||||
constexpr size_t capacity = 10;
|
||||
std::shared_ptr<Cache> backing_cache = NewLRUCache(capacity);
|
||||
@@ -156,7 +155,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_Race) {
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
@@ -199,12 +198,12 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_IOError) {
|
||||
constexpr size_t capacity = 10;
|
||||
std::shared_ptr<Cache> backing_cache = NewLRUCache(capacity);
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
FileOptions file_options;
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
@@ -231,10 +230,10 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
options.enable_blob_files = true;
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
constexpr uint64_t blob_file_number = 123;
|
||||
|
||||
WriteBlobFile(column_family_id, immutable_cf_options, blob_file_number);
|
||||
WriteBlobFile(column_family_id, immutable_options, blob_file_number);
|
||||
|
||||
constexpr size_t capacity = 0;
|
||||
constexpr int num_shard_bits = -1; // determined automatically
|
||||
@@ -245,7 +244,7 @@ TEST_F(BlobFileCacheTest, GetBlobFileReader_CacheFull) {
|
||||
FileOptions file_options;
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_cf_options,
|
||||
BlobFileCache blob_file_cache(backing_cache.get(), &immutable_options,
|
||||
&file_options, column_family_id,
|
||||
blob_file_read_hist, nullptr /*IOTracer*/);
|
||||
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "rocksdb/slice.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
uint64_t SharedBlobFileMetaData::GetBlobFileSize() const {
|
||||
return BlobLogHeader::kSize + total_blob_bytes_ + BlobLogFooter::kSize;
|
||||
}
|
||||
|
||||
std::string SharedBlobFileMetaData::DebugString() const {
|
||||
std::ostringstream oss;
|
||||
@@ -23,7 +29,8 @@ std::ostream& operator<<(std::ostream& os,
|
||||
<< " total_blob_count: " << shared_meta.GetTotalBlobCount()
|
||||
<< " total_blob_bytes: " << shared_meta.GetTotalBlobBytes()
|
||||
<< " checksum_method: " << shared_meta.GetChecksumMethod()
|
||||
<< " checksum_value: " << shared_meta.GetChecksumValue();
|
||||
<< " checksum_value: "
|
||||
<< Slice(shared_meta.GetChecksumValue()).ToString(/* hex */ true);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ class SharedBlobFileMetaData {
|
||||
SharedBlobFileMetaData(SharedBlobFileMetaData&&) = delete;
|
||||
SharedBlobFileMetaData& operator=(SharedBlobFileMetaData&&) = delete;
|
||||
|
||||
uint64_t GetBlobFileSize() const;
|
||||
uint64_t GetBlobFileNumber() const { return blob_file_number_; }
|
||||
uint64_t GetTotalBlobCount() const { return total_blob_count_; }
|
||||
uint64_t GetTotalBlobBytes() const { return total_blob_bytes_; }
|
||||
@@ -112,6 +113,11 @@ class BlobFileMetaData {
|
||||
return shared_meta_;
|
||||
}
|
||||
|
||||
uint64_t GetBlobFileSize() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetBlobFileSize();
|
||||
}
|
||||
|
||||
uint64_t GetBlobFileNumber() const {
|
||||
assert(shared_meta_);
|
||||
return shared_meta_->GetBlobFileNumber();
|
||||
|
||||
+12
-13
@@ -21,10 +21,9 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status BlobFileReader::Create(
|
||||
const ImmutableOptions& immutable_cf_options,
|
||||
const FileOptions& file_options, uint32_t column_family_id,
|
||||
HistogramImpl* blob_file_read_hist, uint64_t blob_file_number,
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const ImmutableOptions& immutable_options, const FileOptions& file_options,
|
||||
uint32_t column_family_id, HistogramImpl* blob_file_read_hist,
|
||||
uint64_t blob_file_number, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
std::unique_ptr<BlobFileReader>* blob_file_reader) {
|
||||
assert(blob_file_reader);
|
||||
assert(!*blob_file_reader);
|
||||
@@ -34,7 +33,7 @@ Status BlobFileReader::Create(
|
||||
|
||||
{
|
||||
const Status s =
|
||||
OpenFile(immutable_cf_options, file_options, blob_file_read_hist,
|
||||
OpenFile(immutable_options, file_options, blob_file_read_hist,
|
||||
blob_file_number, io_tracer, &file_size, &file_reader);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
@@ -67,20 +66,20 @@ Status BlobFileReader::Create(
|
||||
}
|
||||
|
||||
Status BlobFileReader::OpenFile(
|
||||
const ImmutableOptions& immutable_cf_options, const FileOptions& file_opts,
|
||||
const ImmutableOptions& immutable_options, const FileOptions& file_opts,
|
||||
HistogramImpl* blob_file_read_hist, uint64_t blob_file_number,
|
||||
const std::shared_ptr<IOTracer>& io_tracer, uint64_t* file_size,
|
||||
std::unique_ptr<RandomAccessFileReader>* file_reader) {
|
||||
assert(file_size);
|
||||
assert(file_reader);
|
||||
|
||||
const auto& cf_paths = immutable_cf_options.cf_paths;
|
||||
const auto& cf_paths = immutable_options.cf_paths;
|
||||
assert(!cf_paths.empty());
|
||||
|
||||
const std::string blob_file_path =
|
||||
BlobFileName(cf_paths.front().path, blob_file_number);
|
||||
|
||||
FileSystem* const fs = immutable_cf_options.fs.get();
|
||||
FileSystem* const fs = immutable_options.fs.get();
|
||||
assert(fs);
|
||||
|
||||
constexpr IODebugContext* dbg = nullptr;
|
||||
@@ -113,15 +112,15 @@ Status BlobFileReader::OpenFile(
|
||||
|
||||
assert(file);
|
||||
|
||||
if (immutable_cf_options.advise_random_on_open) {
|
||||
if (immutable_options.advise_random_on_open) {
|
||||
file->Hint(FSRandomAccessFile::kRandom);
|
||||
}
|
||||
|
||||
file_reader->reset(new RandomAccessFileReader(
|
||||
std::move(file), blob_file_path, immutable_cf_options.clock, io_tracer,
|
||||
immutable_cf_options.stats, BLOB_DB_BLOB_FILE_READ_MICROS,
|
||||
blob_file_read_hist, immutable_cf_options.rate_limiter.get(),
|
||||
immutable_cf_options.listeners));
|
||||
std::move(file), blob_file_path, immutable_options.clock, io_tracer,
|
||||
immutable_options.stats, BLOB_DB_BLOB_FILE_READ_MICROS,
|
||||
blob_file_read_hist, immutable_options.rate_limiter.get(),
|
||||
immutable_options.listeners));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
@@ -30,35 +30,34 @@ namespace {
|
||||
// Creates a test blob file with a single blob in it. Note: this method
|
||||
// makes it possible to test various corner cases by allowing the caller
|
||||
// to specify the contents of various blob file header/footer fields.
|
||||
void WriteBlobFile(const ImmutableOptions& immutable_cf_options,
|
||||
void WriteBlobFile(const ImmutableOptions& immutable_options,
|
||||
uint32_t column_family_id, bool has_ttl,
|
||||
const ExpirationRange& expiration_range_header,
|
||||
const ExpirationRange& expiration_range_footer,
|
||||
uint64_t blob_file_number, const Slice& key,
|
||||
const Slice& blob, CompressionType compression_type,
|
||||
uint64_t* blob_offset, uint64_t* blob_size) {
|
||||
assert(!immutable_cf_options.cf_paths.empty());
|
||||
assert(!immutable_options.cf_paths.empty());
|
||||
assert(blob_offset);
|
||||
assert(blob_size);
|
||||
|
||||
const std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options.cf_paths.front().path, blob_file_number);
|
||||
const std::string blob_file_path =
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
ASSERT_OK(NewWritableFile(immutable_cf_options.fs.get(), blob_file_path,
|
||||
&file, FileOptions()));
|
||||
ASSERT_OK(NewWritableFile(immutable_options.fs.get(), blob_file_path, &file,
|
||||
FileOptions()));
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.clock));
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), blob_file_path, FileOptions(), immutable_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.clock, statistics,
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer), immutable_options.clock,
|
||||
statistics, blob_file_number, use_fsync,
|
||||
do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, compression_type, has_ttl,
|
||||
expiration_range_header);
|
||||
@@ -122,7 +121,7 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -134,17 +133,17 @@ TEST_F(BlobFileReaderTest, CreateReaderAndGetBlob) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
kNoCompression, &blob_offset, &blob_size);
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob, kNoCompression,
|
||||
&blob_offset, &blob_size);
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
// Make sure the blob can be retrieved with and without checksum verification
|
||||
ReadOptions read_options;
|
||||
@@ -262,7 +261,7 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileReaderTest_Malformed"), 0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr uint64_t blob_file_number = 1;
|
||||
@@ -271,23 +270,23 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
constexpr bool has_ttl = false;
|
||||
constexpr ExpirationRange expiration_range;
|
||||
|
||||
const std::string blob_file_path = BlobFileName(
|
||||
immutable_cf_options.cf_paths.front().path, blob_file_number);
|
||||
const std::string blob_file_path =
|
||||
BlobFileName(immutable_options.cf_paths.front().path, blob_file_number);
|
||||
|
||||
std::unique_ptr<FSWritableFile> file;
|
||||
ASSERT_OK(NewWritableFile(immutable_cf_options.fs.get(), blob_file_path,
|
||||
&file, FileOptions()));
|
||||
ASSERT_OK(NewWritableFile(immutable_options.fs.get(), blob_file_path, &file,
|
||||
FileOptions()));
|
||||
|
||||
std::unique_ptr<WritableFileWriter> file_writer(
|
||||
new WritableFileWriter(std::move(file), blob_file_path, FileOptions(),
|
||||
immutable_cf_options.clock));
|
||||
immutable_options.clock));
|
||||
|
||||
constexpr Statistics* statistics = nullptr;
|
||||
constexpr bool use_fsync = false;
|
||||
constexpr bool do_flush = false;
|
||||
|
||||
BlobLogWriter blob_log_writer(std::move(file_writer),
|
||||
immutable_cf_options.clock, statistics,
|
||||
immutable_options.clock, statistics,
|
||||
blob_file_number, use_fsync, do_flush);
|
||||
|
||||
BlobLogHeader header(column_family_id, kNoCompression, has_ttl,
|
||||
@@ -300,7 +299,7 @@ TEST_F(BlobFileReaderTest, Malformed) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
@@ -314,7 +313,7 @@ TEST_F(BlobFileReaderTest, TTL) {
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileReaderTest_TTL"), 0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = true;
|
||||
@@ -326,15 +325,15 @@ TEST_F(BlobFileReaderTest, TTL) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
kNoCompression, &blob_offset, &blob_size);
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob, kNoCompression,
|
||||
&blob_offset, &blob_size);
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
@@ -350,7 +349,7 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInHeader) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -364,7 +363,7 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInHeader) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl,
|
||||
expiration_range_header, expiration_range_footer,
|
||||
blob_file_number, key, blob, kNoCompression, &blob_offset,
|
||||
&blob_size);
|
||||
@@ -373,7 +372,7 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInHeader) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
@@ -389,7 +388,7 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInFooter) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -403,7 +402,7 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInFooter) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl,
|
||||
expiration_range_header, expiration_range_footer,
|
||||
blob_file_number, key, blob, kNoCompression, &blob_offset,
|
||||
&blob_size);
|
||||
@@ -412,7 +411,7 @@ TEST_F(BlobFileReaderTest, ExpirationRangeInFooter) {
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/,
|
||||
&reader)
|
||||
@@ -428,7 +427,7 @@ TEST_F(BlobFileReaderTest, IncorrectColumnFamily) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -440,9 +439,9 @@ TEST_F(BlobFileReaderTest, IncorrectColumnFamily) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
kNoCompression, &blob_offset, &blob_size);
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob, kNoCompression,
|
||||
&blob_offset, &blob_size);
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
@@ -450,7 +449,7 @@ TEST_F(BlobFileReaderTest, IncorrectColumnFamily) {
|
||||
|
||||
constexpr uint32_t incorrect_column_family_id = 2;
|
||||
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_cf_options, FileOptions(),
|
||||
ASSERT_TRUE(BlobFileReader::Create(immutable_options, FileOptions(),
|
||||
incorrect_column_family_id,
|
||||
blob_file_read_hist, blob_file_number,
|
||||
nullptr /*IOTracer*/, &reader)
|
||||
@@ -464,7 +463,7 @@ TEST_F(BlobFileReaderTest, BlobCRCError) {
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileReaderTest_BlobCRCError"), 0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -476,17 +475,17 @@ TEST_F(BlobFileReaderTest, BlobCRCError) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
kNoCompression, &blob_offset, &blob_size);
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob, kNoCompression,
|
||||
&blob_offset, &blob_size);
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::VerifyBlob:CheckBlobCRC", [](void* arg) {
|
||||
@@ -522,7 +521,7 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
test::PerThreadDBPath(&mock_env_, "BlobFileReaderTest_Compression"), 0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -534,8 +533,8 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob,
|
||||
kSnappyCompression, &blob_offset, &blob_size);
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
@@ -543,8 +542,8 @@ TEST_F(BlobFileReaderTest, Compression) {
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
// Make sure the blob can be retrieved with and without checksum verification
|
||||
ReadOptions read_options;
|
||||
@@ -590,7 +589,7 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -602,8 +601,8 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob,
|
||||
kSnappyCompression, &blob_offset, &blob_size);
|
||||
|
||||
constexpr HistogramImpl* blob_file_read_hist = nullptr;
|
||||
@@ -611,8 +610,8 @@ TEST_F(BlobFileReaderTest, UncompressionError) {
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
ASSERT_OK(BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader));
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobFileReader::UncompressBlobIfNeeded:TamperWithResult", [](void* arg) {
|
||||
@@ -671,7 +670,7 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -683,9 +682,9 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
kNoCompression, &blob_offset, &blob_size);
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob, kNoCompression,
|
||||
&blob_offset, &blob_size);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* /* arg */) {
|
||||
fault_injection_env_.SetFilesystemActive(false,
|
||||
@@ -698,8 +697,8 @@ TEST_P(BlobFileReaderIOErrorTest, IOError) {
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const Status s = BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
|
||||
const bool fail_during_create =
|
||||
(sync_point_ != "BlobFileReader::GetBlob:ReadFromFile");
|
||||
@@ -749,7 +748,7 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
0);
|
||||
options.enable_blob_files = true;
|
||||
|
||||
ImmutableOptions immutable_cf_options(options);
|
||||
ImmutableOptions immutable_options(options);
|
||||
|
||||
constexpr uint32_t column_family_id = 1;
|
||||
constexpr bool has_ttl = false;
|
||||
@@ -761,9 +760,9 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
uint64_t blob_offset = 0;
|
||||
uint64_t blob_size = 0;
|
||||
|
||||
WriteBlobFile(immutable_cf_options, column_family_id, has_ttl,
|
||||
expiration_range, expiration_range, blob_file_number, key, blob,
|
||||
kNoCompression, &blob_offset, &blob_size);
|
||||
WriteBlobFile(immutable_options, column_family_id, has_ttl, expiration_range,
|
||||
expiration_range, blob_file_number, key, blob, kNoCompression,
|
||||
&blob_offset, &blob_size);
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(sync_point_, [](void* arg) {
|
||||
Slice* const slice = static_cast<Slice*>(arg);
|
||||
@@ -780,8 +779,8 @@ TEST_P(BlobFileReaderDecodingErrorTest, DecodingError) {
|
||||
std::unique_ptr<BlobFileReader> reader;
|
||||
|
||||
const Status s = BlobFileReader::Create(
|
||||
immutable_cf_options, FileOptions(), column_family_id,
|
||||
blob_file_read_hist, blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
immutable_options, FileOptions(), column_family_id, blob_file_read_hist,
|
||||
blob_file_number, nullptr /*IOTracer*/, &reader);
|
||||
|
||||
const bool fail_during_create =
|
||||
sync_point_ != "BlobFileReader::GetBlob:TamperWithResult";
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_garbage_meter.h"
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/dbformat.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status BlobGarbageMeter::ProcessInFlow(const Slice& key, const Slice& value) {
|
||||
uint64_t blob_file_number = kInvalidBlobFileNumber;
|
||||
uint64_t bytes = 0;
|
||||
|
||||
const Status s = Parse(key, value, &blob_file_number, &bytes);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (blob_file_number == kInvalidBlobFileNumber) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
flows_[blob_file_number].AddInFlow(bytes);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobGarbageMeter::ProcessOutFlow(const Slice& key, const Slice& value) {
|
||||
uint64_t blob_file_number = kInvalidBlobFileNumber;
|
||||
uint64_t bytes = 0;
|
||||
|
||||
const Status s = Parse(key, value, &blob_file_number, &bytes);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
if (blob_file_number == kInvalidBlobFileNumber) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Note: in order to measure the amount of additional garbage, we only need to
|
||||
// track the outflow for preexisting files, i.e. those that also had inflow.
|
||||
// (Newly written files would only have outflow.)
|
||||
auto it = flows_.find(blob_file_number);
|
||||
if (it == flows_.end()) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
it->second.AddOutFlow(bytes);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status BlobGarbageMeter::Parse(const Slice& key, const Slice& value,
|
||||
uint64_t* blob_file_number, uint64_t* bytes) {
|
||||
assert(blob_file_number);
|
||||
assert(*blob_file_number == kInvalidBlobFileNumber);
|
||||
assert(bytes);
|
||||
assert(*bytes == 0);
|
||||
|
||||
ParsedInternalKey ikey;
|
||||
|
||||
{
|
||||
constexpr bool log_err_key = false;
|
||||
const Status s = ParseInternalKey(key, &ikey, log_err_key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (ikey.type != kTypeBlobIndex) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
BlobIndex blob_index;
|
||||
|
||||
{
|
||||
const Status s = blob_index.DecodeFrom(value);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_index.IsInlined() || blob_index.HasTTL()) {
|
||||
return Status::Corruption("Unexpected TTL/inlined blob index");
|
||||
}
|
||||
|
||||
*blob_file_number = blob_index.file_number();
|
||||
*bytes =
|
||||
blob_index.size() +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(ikey.user_key.size());
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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 <unordered_map>
|
||||
|
||||
#include "db/blob/blob_constants.h"
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
#include "rocksdb/status.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class Slice;
|
||||
|
||||
// A class that can be used to compute the amount of additional garbage
|
||||
// generated by a compaction. It parses the keys and blob references in the
|
||||
// input and output of a compaction, and aggregates the "inflow" and "outflow"
|
||||
// on a per-blob file basis. The amount of additional garbage for any given blob
|
||||
// file can then be computed by subtracting the outflow from the inflow.
|
||||
class BlobGarbageMeter {
|
||||
public:
|
||||
// A class to store the number and total size of blobs on a per-blob file
|
||||
// basis.
|
||||
class BlobStats {
|
||||
public:
|
||||
void Add(uint64_t bytes) {
|
||||
++count_;
|
||||
bytes_ += bytes;
|
||||
}
|
||||
void Add(uint64_t count, uint64_t bytes) {
|
||||
count_ += count;
|
||||
bytes_ += bytes;
|
||||
}
|
||||
|
||||
uint64_t GetCount() const { return count_; }
|
||||
uint64_t GetBytes() const { return bytes_; }
|
||||
|
||||
private:
|
||||
uint64_t count_ = 0;
|
||||
uint64_t bytes_ = 0;
|
||||
};
|
||||
|
||||
// A class to keep track of the "inflow" and the "outflow" and to compute the
|
||||
// amount of additional garbage for a given blob file.
|
||||
class BlobInOutFlow {
|
||||
public:
|
||||
void AddInFlow(uint64_t bytes) {
|
||||
in_flow_.Add(bytes);
|
||||
assert(IsValid());
|
||||
}
|
||||
void AddOutFlow(uint64_t bytes) {
|
||||
out_flow_.Add(bytes);
|
||||
assert(IsValid());
|
||||
}
|
||||
|
||||
const BlobStats& GetInFlow() const { return in_flow_; }
|
||||
const BlobStats& GetOutFlow() const { return out_flow_; }
|
||||
|
||||
bool IsValid() const {
|
||||
return in_flow_.GetCount() >= out_flow_.GetCount() &&
|
||||
in_flow_.GetBytes() >= out_flow_.GetBytes();
|
||||
}
|
||||
bool HasGarbage() const {
|
||||
assert(IsValid());
|
||||
return in_flow_.GetCount() > out_flow_.GetCount();
|
||||
}
|
||||
uint64_t GetGarbageCount() const {
|
||||
assert(IsValid());
|
||||
assert(HasGarbage());
|
||||
return in_flow_.GetCount() - out_flow_.GetCount();
|
||||
}
|
||||
uint64_t GetGarbageBytes() const {
|
||||
assert(IsValid());
|
||||
assert(HasGarbage());
|
||||
return in_flow_.GetBytes() - out_flow_.GetBytes();
|
||||
}
|
||||
|
||||
private:
|
||||
BlobStats in_flow_;
|
||||
BlobStats out_flow_;
|
||||
};
|
||||
|
||||
Status ProcessInFlow(const Slice& key, const Slice& value);
|
||||
Status ProcessOutFlow(const Slice& key, const Slice& value);
|
||||
|
||||
const std::unordered_map<uint64_t, BlobInOutFlow>& flows() const {
|
||||
return flows_;
|
||||
}
|
||||
|
||||
private:
|
||||
static Status Parse(const Slice& key, const Slice& value,
|
||||
uint64_t* blob_file_number, uint64_t* bytes);
|
||||
|
||||
std::unordered_map<uint64_t, BlobInOutFlow> flows_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under both the GPLv2 (found in the
|
||||
// COPYING file in the root directory) and Apache 2.0 License
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_garbage_meter.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "test_util/testharness.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
TEST(BlobGarbageMeterTest, MeasureGarbage) {
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
struct BlobDescriptor {
|
||||
std::string user_key;
|
||||
uint64_t blob_file_number;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
CompressionType compression_type;
|
||||
bool has_in_flow;
|
||||
bool has_out_flow;
|
||||
|
||||
uint64_t GetExpectedBytes() const {
|
||||
return size +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(user_key.size());
|
||||
}
|
||||
};
|
||||
|
||||
// Note: blob file 4 has the same inflow and outflow and hence no additional
|
||||
// garbage. Blob file 5 has less outflow than inflow and thus it does have
|
||||
// additional garbage. Blob file 6 is a newly written file (i.e. no inflow,
|
||||
// only outflow) and is thus not tracked by the meter.
|
||||
std::vector<BlobDescriptor> blobs{
|
||||
{"key", 4, 1234, 555, kLZ4Compression, true, true},
|
||||
{"other_key", 4, 6789, 101010, kLZ4Compression, true, true},
|
||||
{"yet_another_key", 5, 22222, 3456, kLZ4Compression, true, true},
|
||||
{"foo_key", 5, 77777, 8888, kLZ4Compression, true, true},
|
||||
{"bar_key", 5, 999999, 1212, kLZ4Compression, true, false},
|
||||
{"baz_key", 5, 1234567, 890, kLZ4Compression, true, false},
|
||||
{"new_key", 6, 7777, 9999, kNoCompression, false, true}};
|
||||
|
||||
for (const auto& blob : blobs) {
|
||||
constexpr SequenceNumber seq = 123;
|
||||
const InternalKey key(blob.user_key, seq, kTypeBlobIndex);
|
||||
const Slice key_slice = key.Encode();
|
||||
|
||||
std::string value;
|
||||
BlobIndex::EncodeBlob(&value, blob.blob_file_number, blob.offset, blob.size,
|
||||
blob.compression_type);
|
||||
const Slice value_slice(value);
|
||||
|
||||
if (blob.has_in_flow) {
|
||||
ASSERT_OK(blob_garbage_meter.ProcessInFlow(key_slice, value_slice));
|
||||
}
|
||||
if (blob.has_out_flow) {
|
||||
ASSERT_OK(blob_garbage_meter.ProcessOutFlow(key_slice, value_slice));
|
||||
}
|
||||
}
|
||||
|
||||
const auto& flows = blob_garbage_meter.flows();
|
||||
ASSERT_EQ(flows.size(), 2);
|
||||
|
||||
{
|
||||
const auto it = flows.find(4);
|
||||
ASSERT_NE(it, flows.end());
|
||||
|
||||
const auto& flow = it->second;
|
||||
|
||||
constexpr uint64_t expected_count = 2;
|
||||
const uint64_t expected_bytes =
|
||||
blobs[0].GetExpectedBytes() + blobs[1].GetExpectedBytes();
|
||||
|
||||
const auto& in = flow.GetInFlow();
|
||||
ASSERT_EQ(in.GetCount(), expected_count);
|
||||
ASSERT_EQ(in.GetBytes(), expected_bytes);
|
||||
|
||||
const auto& out = flow.GetOutFlow();
|
||||
ASSERT_EQ(out.GetCount(), expected_count);
|
||||
ASSERT_EQ(out.GetBytes(), expected_bytes);
|
||||
|
||||
ASSERT_TRUE(flow.IsValid());
|
||||
ASSERT_FALSE(flow.HasGarbage());
|
||||
}
|
||||
|
||||
{
|
||||
const auto it = flows.find(5);
|
||||
ASSERT_NE(it, flows.end());
|
||||
|
||||
const auto& flow = it->second;
|
||||
|
||||
const auto& in = flow.GetInFlow();
|
||||
|
||||
constexpr uint64_t expected_in_count = 4;
|
||||
const uint64_t expected_in_bytes =
|
||||
blobs[2].GetExpectedBytes() + blobs[3].GetExpectedBytes() +
|
||||
blobs[4].GetExpectedBytes() + blobs[5].GetExpectedBytes();
|
||||
|
||||
ASSERT_EQ(in.GetCount(), expected_in_count);
|
||||
ASSERT_EQ(in.GetBytes(), expected_in_bytes);
|
||||
|
||||
const auto& out = flow.GetOutFlow();
|
||||
|
||||
constexpr uint64_t expected_out_count = 2;
|
||||
const uint64_t expected_out_bytes =
|
||||
blobs[2].GetExpectedBytes() + blobs[3].GetExpectedBytes();
|
||||
|
||||
ASSERT_EQ(out.GetCount(), expected_out_count);
|
||||
ASSERT_EQ(out.GetBytes(), expected_out_bytes);
|
||||
|
||||
ASSERT_TRUE(flow.IsValid());
|
||||
ASSERT_TRUE(flow.HasGarbage());
|
||||
ASSERT_EQ(flow.GetGarbageCount(), expected_in_count - expected_out_count);
|
||||
ASSERT_EQ(flow.GetGarbageBytes(), expected_in_bytes - expected_out_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BlobGarbageMeterTest, PlainValue) {
|
||||
constexpr char user_key[] = "user_key";
|
||||
constexpr SequenceNumber seq = 123;
|
||||
|
||||
const InternalKey key(user_key, seq, kTypeValue);
|
||||
const Slice key_slice = key.Encode();
|
||||
|
||||
constexpr char value[] = "value";
|
||||
const Slice value_slice(value);
|
||||
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
ASSERT_OK(blob_garbage_meter.ProcessInFlow(key_slice, value_slice));
|
||||
ASSERT_OK(blob_garbage_meter.ProcessOutFlow(key_slice, value_slice));
|
||||
ASSERT_TRUE(blob_garbage_meter.flows().empty());
|
||||
}
|
||||
|
||||
TEST(BlobGarbageMeterTest, CorruptInternalKey) {
|
||||
constexpr char corrupt_key[] = "i_am_corrupt";
|
||||
const Slice key_slice(corrupt_key);
|
||||
|
||||
constexpr char value[] = "value";
|
||||
const Slice value_slice(value);
|
||||
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
ASSERT_NOK(blob_garbage_meter.ProcessInFlow(key_slice, value_slice));
|
||||
ASSERT_NOK(blob_garbage_meter.ProcessOutFlow(key_slice, value_slice));
|
||||
}
|
||||
|
||||
TEST(BlobGarbageMeterTest, CorruptBlobIndex) {
|
||||
constexpr char user_key[] = "user_key";
|
||||
constexpr SequenceNumber seq = 123;
|
||||
|
||||
const InternalKey key(user_key, seq, kTypeBlobIndex);
|
||||
const Slice key_slice = key.Encode();
|
||||
|
||||
constexpr char value[] = "i_am_not_a_blob_index";
|
||||
const Slice value_slice(value);
|
||||
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
ASSERT_NOK(blob_garbage_meter.ProcessInFlow(key_slice, value_slice));
|
||||
ASSERT_NOK(blob_garbage_meter.ProcessOutFlow(key_slice, value_slice));
|
||||
}
|
||||
|
||||
TEST(BlobGarbageMeterTest, InlinedTTLBlobIndex) {
|
||||
constexpr char user_key[] = "user_key";
|
||||
constexpr SequenceNumber seq = 123;
|
||||
|
||||
const InternalKey key(user_key, seq, kTypeBlobIndex);
|
||||
const Slice key_slice = key.Encode();
|
||||
|
||||
constexpr uint64_t expiration = 1234567890;
|
||||
constexpr char inlined_value[] = "inlined";
|
||||
|
||||
std::string value;
|
||||
BlobIndex::EncodeInlinedTTL(&value, expiration, inlined_value);
|
||||
|
||||
const Slice value_slice(value);
|
||||
|
||||
BlobGarbageMeter blob_garbage_meter;
|
||||
|
||||
ASSERT_NOK(blob_garbage_meter.ProcessInFlow(key_slice, value_slice));
|
||||
ASSERT_NOK(blob_garbage_meter.ProcessOutFlow(key_slice, value_slice));
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -107,7 +107,8 @@ struct BlobLogRecord {
|
||||
// Note that the offset field of BlobIndex actually points to the blob value
|
||||
// as opposed to the start of the blob record. The following method can
|
||||
// be used to calculate the adjustment needed to read the blob record header.
|
||||
static uint64_t CalculateAdjustmentForRecordHeader(uint64_t key_size) {
|
||||
static constexpr uint64_t CalculateAdjustmentForRecordHeader(
|
||||
uint64_t key_size) {
|
||||
return key_size + kHeaderSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -310,6 +310,63 @@ TEST_F(DBBlobBasicTest, BestEffortsRecovery_MissingNewestBlobFile) {
|
||||
ASSERT_EQ("value" + std::to_string(kNumTableFiles - 2), value);
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, GetMergeBlobWithPut) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("Key1", "v1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("Key1", "v2"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("Key1", "v3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::string value;
|
||||
ASSERT_OK(db_->Get(ReadOptions(), "Key1", &value));
|
||||
ASSERT_EQ(Get("Key1"), "v1,v2,v3");
|
||||
}
|
||||
|
||||
TEST_F(DBBlobBasicTest, MultiGetMergeBlobWithPut) {
|
||||
constexpr size_t num_keys = 3;
|
||||
|
||||
Options options = GetDefaultOptions();
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
ASSERT_OK(Put("Key0", "v0_0"));
|
||||
ASSERT_OK(Put("Key1", "v1_0"));
|
||||
ASSERT_OK(Put("Key2", "v2_0"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("Key0", "v0_1"));
|
||||
ASSERT_OK(Merge("Key1", "v1_1"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("Key0", "v0_2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
std::array<Slice, num_keys> keys{{"Key0", "Key1", "Key2"}};
|
||||
std::array<PinnableSlice, num_keys> values;
|
||||
std::array<Status, num_keys> statuses;
|
||||
|
||||
db_->MultiGet(ReadOptions(), db_->DefaultColumnFamily(), num_keys, &keys[0],
|
||||
&values[0], &statuses[0]);
|
||||
|
||||
ASSERT_OK(statuses[0]);
|
||||
ASSERT_EQ(values[0], "v0_0,v0_1,v0_2");
|
||||
|
||||
ASSERT_OK(statuses[1]);
|
||||
ASSERT_EQ(values[1], "v1_0,v1_1");
|
||||
|
||||
ASSERT_OK(statuses[2]);
|
||||
ASSERT_EQ(values[2], "v2_0");
|
||||
}
|
||||
|
||||
class DBBlobBasicIOErrorTest : public DBBlobBasicTest,
|
||||
public testing::WithParamInterface<std::string> {
|
||||
protected:
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "test_util/sync_point.h"
|
||||
@@ -152,6 +153,31 @@ class AlwaysKeepFilter : public CompactionFilter {
|
||||
return CompactionFilter::Decision::kKeep;
|
||||
}
|
||||
};
|
||||
|
||||
class SkipUntilFilter : public CompactionFilter {
|
||||
public:
|
||||
explicit SkipUntilFilter(std::string skip_until)
|
||||
: skip_until_(std::move(skip_until)) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "rocksdb.compaction.filter.skip.until";
|
||||
}
|
||||
|
||||
CompactionFilter::Decision FilterV2(int /* level */, const Slice& /* key */,
|
||||
ValueType /* value_type */,
|
||||
const Slice& /* existing_value */,
|
||||
std::string* /* new_value */,
|
||||
std::string* skip_until) const override {
|
||||
assert(skip_until);
|
||||
*skip_until = skip_until_;
|
||||
|
||||
return CompactionFilter::Decision::kRemoveAndSkipUntil;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string skip_until_;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class DBBlobBadCompactionFilterTest
|
||||
@@ -254,6 +280,49 @@ TEST_F(DBBlobCompactionTest, BlindWriteFilter) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, SkipUntilFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
std::unique_ptr<CompactionFilter> compaction_filter_guard(
|
||||
new SkipUntilFilter("z"));
|
||||
options.compaction_filter = compaction_filter_guard.get();
|
||||
|
||||
Reopen(options);
|
||||
|
||||
const std::vector<std::string> keys{"a", "b", "c"};
|
||||
const std::vector<std::string> values{"a_value", "b_value", "c_value"};
|
||||
assert(keys.size() == values.size());
|
||||
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
ASSERT_OK(Put(keys[i], values[i]));
|
||||
}
|
||||
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
int process_in_flow_called = 0;
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlobCountingIterator::UpdateAndCountBlobIfNeeded:ProcessInFlow",
|
||||
[&process_in_flow_called](void* /* arg */) { ++process_in_flow_called; });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /* begin */ nullptr,
|
||||
/* end */ nullptr));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
for (const auto& key : keys) {
|
||||
ASSERT_EQ(Get(key), "NOT_FOUND");
|
||||
}
|
||||
|
||||
// Make sure SkipUntil was performed using iteration rather than Seek
|
||||
ASSERT_EQ(process_in_flow_called, keys.size());
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBlobBadCompactionFilterTest, BadDecisionFromCompactionFilter) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
@@ -390,6 +459,138 @@ TEST_F(DBBlobCompactionTest, CompactionFilterReadBlobAndKeep) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, TrackGarbage) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
// First table+blob file pair: 4 blobs with different keys
|
||||
constexpr char first_key[] = "first_key";
|
||||
constexpr char first_value[] = "first_value";
|
||||
constexpr char second_key[] = "second_key";
|
||||
constexpr char second_value[] = "second_value";
|
||||
constexpr char third_key[] = "third_key";
|
||||
constexpr char third_value[] = "third_value";
|
||||
constexpr char fourth_key[] = "fourth_key";
|
||||
constexpr char fourth_value[] = "fourth_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, first_value));
|
||||
ASSERT_OK(Put(second_key, second_value));
|
||||
ASSERT_OK(Put(third_key, third_value));
|
||||
ASSERT_OK(Put(fourth_key, fourth_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Second table+blob file pair: overwrite 2 existing keys
|
||||
constexpr char new_first_value[] = "new_first_value";
|
||||
constexpr char new_second_value[] = "new_second_value";
|
||||
|
||||
ASSERT_OK(Put(first_key, new_first_value));
|
||||
ASSERT_OK(Put(second_key, new_second_value));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
// Compact them together. The first blob file should have 2 garbage blobs
|
||||
// corresponding to the 2 overwritten keys.
|
||||
constexpr Slice* begin = nullptr;
|
||||
constexpr Slice* end = nullptr;
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const current = cfd->current();
|
||||
assert(current);
|
||||
|
||||
const VersionStorageInfo* const storage_info = current->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
const auto& blob_files = storage_info->GetBlobFiles();
|
||||
ASSERT_EQ(blob_files.size(), 2);
|
||||
|
||||
{
|
||||
auto it = blob_files.begin();
|
||||
const auto& meta = it->second;
|
||||
assert(meta);
|
||||
|
||||
constexpr uint64_t first_expected_bytes =
|
||||
sizeof(first_value) - 1 +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(first_key) -
|
||||
1);
|
||||
constexpr uint64_t second_expected_bytes =
|
||||
sizeof(second_value) - 1 +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(second_key) -
|
||||
1);
|
||||
constexpr uint64_t third_expected_bytes =
|
||||
sizeof(third_value) - 1 +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(third_key) -
|
||||
1);
|
||||
constexpr uint64_t fourth_expected_bytes =
|
||||
sizeof(fourth_value) - 1 +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(fourth_key) -
|
||||
1);
|
||||
|
||||
ASSERT_EQ(meta->GetTotalBlobCount(), 4);
|
||||
ASSERT_EQ(meta->GetTotalBlobBytes(),
|
||||
first_expected_bytes + second_expected_bytes +
|
||||
third_expected_bytes + fourth_expected_bytes);
|
||||
ASSERT_EQ(meta->GetGarbageBlobCount(), 2);
|
||||
ASSERT_EQ(meta->GetGarbageBlobBytes(),
|
||||
first_expected_bytes + second_expected_bytes);
|
||||
}
|
||||
|
||||
{
|
||||
auto it = blob_files.rbegin();
|
||||
const auto& meta = it->second;
|
||||
assert(meta);
|
||||
|
||||
constexpr uint64_t new_first_expected_bytes =
|
||||
sizeof(new_first_value) - 1 +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(first_key) -
|
||||
1);
|
||||
constexpr uint64_t new_second_expected_bytes =
|
||||
sizeof(new_second_value) - 1 +
|
||||
BlobLogRecord::CalculateAdjustmentForRecordHeader(sizeof(second_key) -
|
||||
1);
|
||||
|
||||
ASSERT_EQ(meta->GetTotalBlobCount(), 2);
|
||||
ASSERT_EQ(meta->GetTotalBlobBytes(),
|
||||
new_first_expected_bytes + new_second_expected_bytes);
|
||||
ASSERT_EQ(meta->GetGarbageBlobCount(), 0);
|
||||
ASSERT_EQ(meta->GetGarbageBlobBytes(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobCompactionTest, MergeBlobWithBase) {
|
||||
Options options = GetDefaultOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
options.disable_auto_compactions = true;
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("Key1", "v1_1"));
|
||||
ASSERT_OK(Put("Key2", "v2_1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Merge("Key1", "v1_2"));
|
||||
ASSERT_OK(Merge("Key2", "v2_2"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(Merge("Key1", "v1_3"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), /*begin=*/nullptr,
|
||||
/*end=*/nullptr));
|
||||
ASSERT_EQ(Get("Key1"), "v1_1,v1_2,v1_3");
|
||||
ASSERT_EQ(Get("Key2"), "v2_1,v2_2");
|
||||
Close();
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -399,7 +399,7 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
create_normal_iterator);
|
||||
verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
|
||||
create_normal_iterator);
|
||||
verify(11, Status::kNotSupported, "", "", create_normal_iterator);
|
||||
verify(11, Status::kCorruption, "", "", create_normal_iterator);
|
||||
verify(13, Status::kOk,
|
||||
get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
|
||||
get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
|
||||
@@ -418,7 +418,11 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
create_blob_iterator, check_is_blob(false));
|
||||
verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
|
||||
create_blob_iterator, check_is_blob(false));
|
||||
verify(11, Status::kNotSupported, "", "", create_blob_iterator);
|
||||
if (tier <= kImmutableMemtables) {
|
||||
verify(11, Status::kNotSupported, "", "", create_blob_iterator);
|
||||
} else {
|
||||
verify(11, Status::kCorruption, "", "", create_blob_iterator);
|
||||
}
|
||||
verify(13, Status::kOk,
|
||||
get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
|
||||
get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
|
||||
@@ -440,7 +444,11 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
create_blob_iterator, check_is_blob(false));
|
||||
verify(9, Status::kOk, get_value(10, 0), get_value(8, 0),
|
||||
create_blob_iterator, check_is_blob(false));
|
||||
verify(11, Status::kNotSupported, "", "", create_blob_iterator);
|
||||
if (tier <= kImmutableMemtables) {
|
||||
verify(11, Status::kNotSupported, "", "", create_blob_iterator);
|
||||
} else {
|
||||
verify(11, Status::kCorruption, "", "", create_blob_iterator);
|
||||
}
|
||||
verify(13, Status::kOk,
|
||||
get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
|
||||
get_value(13, 2) + "," + get_value(13, 1) + "," + get_value(13, 0),
|
||||
@@ -455,6 +463,106 @@ TEST_F(DBBlobIndexTest, Iterate) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBlobIndexTest, IntegratedBlobIterate) {
|
||||
const std::vector<std::vector<std::string>> data = {
|
||||
/*00*/ {"Put"},
|
||||
/*01*/ {"Put", "Merge", "Merge", "Merge"},
|
||||
/*02*/ {"Put"}};
|
||||
|
||||
auto get_key = [](size_t index) { return ("key" + std::to_string(index)); };
|
||||
|
||||
auto get_value = [&](size_t index, size_t version) {
|
||||
return get_key(index) + "_value" + ToString(version);
|
||||
};
|
||||
|
||||
auto check_iterator = [&](Iterator* iterator, Status expected_status,
|
||||
const Slice& expected_value) {
|
||||
ASSERT_EQ(expected_status, iterator->status());
|
||||
if (expected_status.ok()) {
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_EQ(expected_value, iterator->value());
|
||||
} else {
|
||||
ASSERT_FALSE(iterator->Valid());
|
||||
}
|
||||
};
|
||||
|
||||
auto verify = [&](size_t index, Status expected_status,
|
||||
const Slice& expected_value) {
|
||||
// Seek
|
||||
{
|
||||
Iterator* iterator = db_->NewIterator(ReadOptions());
|
||||
std::unique_ptr<Iterator> iterator_guard(iterator);
|
||||
ASSERT_OK(iterator->status());
|
||||
ASSERT_OK(iterator->Refresh());
|
||||
iterator->Seek(get_key(index));
|
||||
check_iterator(iterator, expected_status, expected_value);
|
||||
}
|
||||
// Next
|
||||
{
|
||||
Iterator* iterator = db_->NewIterator(ReadOptions());
|
||||
std::unique_ptr<Iterator> iterator_guard(iterator);
|
||||
ASSERT_OK(iterator->Refresh());
|
||||
iterator->Seek(get_key(index - 1));
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_OK(iterator->status());
|
||||
iterator->Next();
|
||||
check_iterator(iterator, expected_status, expected_value);
|
||||
}
|
||||
// SeekForPrev
|
||||
{
|
||||
Iterator* iterator = db_->NewIterator(ReadOptions());
|
||||
std::unique_ptr<Iterator> iterator_guard(iterator);
|
||||
ASSERT_OK(iterator->status());
|
||||
ASSERT_OK(iterator->Refresh());
|
||||
iterator->SeekForPrev(get_key(index));
|
||||
check_iterator(iterator, expected_status, expected_value);
|
||||
}
|
||||
// Prev
|
||||
{
|
||||
Iterator* iterator = db_->NewIterator(ReadOptions());
|
||||
std::unique_ptr<Iterator> iterator_guard(iterator);
|
||||
iterator->Seek(get_key(index + 1));
|
||||
ASSERT_TRUE(iterator->Valid());
|
||||
ASSERT_OK(iterator->status());
|
||||
iterator->Prev();
|
||||
check_iterator(iterator, expected_status, expected_value);
|
||||
}
|
||||
};
|
||||
|
||||
Options options = GetTestOptions();
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
// fill data
|
||||
for (size_t i = 0; i < data.size(); i++) {
|
||||
for (size_t j = 0; j < data[i].size(); j++) {
|
||||
std::string key = get_key(i);
|
||||
std::string value = get_value(i, j);
|
||||
if (data[i][j] == "Put") {
|
||||
ASSERT_OK(Put(key, value));
|
||||
ASSERT_OK(Flush());
|
||||
} else if (data[i][j] == "Merge") {
|
||||
ASSERT_OK(Merge(key, value));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string expected_value = get_value(1, 0) + "," + get_value(1, 1) + "," +
|
||||
get_value(1, 2) + "," + get_value(1, 3);
|
||||
Status expected_status;
|
||||
verify(1, expected_status, expected_value);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// Test DBIter::FindValueForCurrentKeyUsingSeek flow.
|
||||
ASSERT_OK(dbfull()->SetOptions(cfh(),
|
||||
{{"max_sequential_skip_in_iterations", "0"}}));
|
||||
verify(1, expected_status, expected_value);
|
||||
#endif // !ROCKSDB_LITE
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
+29
-3
@@ -69,7 +69,8 @@ Status BuildTable(
|
||||
int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, Env::WriteLifeTimeHint write_hint,
|
||||
const std::string* full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback, uint64_t* num_input_entries) {
|
||||
BlobFileCompletionCallback* blob_callback, uint64_t* num_input_entries,
|
||||
uint64_t* memtable_payload_bytes, uint64_t* memtable_garbage_bytes) {
|
||||
assert((tboptions.column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
tboptions.column_family_name.empty());
|
||||
@@ -89,9 +90,12 @@ Status BuildTable(
|
||||
new CompactionRangeDelAggregator(&tboptions.internal_comparator,
|
||||
snapshots));
|
||||
uint64_t num_unfragmented_tombstones = 0;
|
||||
uint64_t total_tombstone_payload_bytes = 0;
|
||||
for (auto& range_del_iter : range_del_iters) {
|
||||
num_unfragmented_tombstones +=
|
||||
range_del_iter->num_unfragmented_tombstones();
|
||||
total_tombstone_payload_bytes +=
|
||||
range_del_iter->total_tombstone_payload_bytes();
|
||||
range_del_agg->AddTombstones(std::move(range_del_iter));
|
||||
}
|
||||
|
||||
@@ -161,7 +165,7 @@ Status BuildTable(
|
||||
std::move(file), fname, file_options, ioptions.clock, io_tracer,
|
||||
ioptions.stats, ioptions.listeners,
|
||||
ioptions.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile)));
|
||||
tmp_set.Contains(FileType::kTableFile), false));
|
||||
|
||||
builder = NewTableBuilder(tboptions, file_writer.get());
|
||||
}
|
||||
@@ -191,7 +195,8 @@ Status BuildTable(
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr,
|
||||
/*preserve_deletes_seqnum=*/0, /*manual_compaction_paused=*/nullptr,
|
||||
db_options.info_log, full_history_ts_low);
|
||||
/*manual_compaction_canceled=*/nullptr, db_options.info_log,
|
||||
full_history_ts_low);
|
||||
|
||||
c_iter.SeekToFirst();
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
@@ -199,6 +204,8 @@ Status BuildTable(
|
||||
const Slice& value = c_iter.value();
|
||||
const ParsedInternalKey& ikey = c_iter.ikey();
|
||||
// Generate a rolling 64-bit hash of the key and values
|
||||
// Note :
|
||||
// Here "key" integrates 'sequence_number'+'kType'+'user key'.
|
||||
s = output_validator.Add(key, value);
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
@@ -253,6 +260,25 @@ Status BuildTable(
|
||||
meta->marked_for_compaction = builder->NeedCompact();
|
||||
assert(meta->fd.GetFileSize() > 0);
|
||||
tp = builder->GetTableProperties(); // refresh now that builder is finished
|
||||
if (memtable_payload_bytes != nullptr &&
|
||||
memtable_garbage_bytes != nullptr) {
|
||||
const CompactionIterationStats& ci_stats = c_iter.iter_stats();
|
||||
uint64_t total_payload_bytes = ci_stats.total_input_raw_key_bytes +
|
||||
ci_stats.total_input_raw_value_bytes +
|
||||
total_tombstone_payload_bytes;
|
||||
uint64_t total_payload_bytes_written =
|
||||
(tp.raw_key_size + tp.raw_value_size);
|
||||
// Prevent underflow, which may still happen at this point
|
||||
// since we only support inserts, deletes, and deleteRanges.
|
||||
if (total_payload_bytes_written <= total_payload_bytes) {
|
||||
*memtable_payload_bytes = total_payload_bytes;
|
||||
*memtable_garbage_bytes =
|
||||
total_payload_bytes - total_payload_bytes_written;
|
||||
} else {
|
||||
*memtable_payload_bytes = 0;
|
||||
*memtable_garbage_bytes = 0;
|
||||
}
|
||||
}
|
||||
if (table_properties) {
|
||||
*table_properties = tp;
|
||||
}
|
||||
|
||||
+3
-1
@@ -66,6 +66,8 @@ extern Status BuildTable(
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const std::string* full_history_ts_low = nullptr,
|
||||
BlobFileCompletionCallback* blob_callback = nullptr,
|
||||
uint64_t* num_input_entries = nullptr);
|
||||
uint64_t* num_input_entries = nullptr,
|
||||
uint64_t* memtable_payload_bytes = nullptr,
|
||||
uint64_t* memtable_garbage_bytes = nullptr);
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -88,6 +88,7 @@ using ROCKSDB_NAMESPACE::NewBloomFilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::NewCompactOnDeletionCollectorFactory;
|
||||
using ROCKSDB_NAMESPACE::NewGenericRateLimiter;
|
||||
using ROCKSDB_NAMESPACE::NewLRUCache;
|
||||
using ROCKSDB_NAMESPACE::NewRibbonFilterPolicy;
|
||||
using ROCKSDB_NAMESPACE::OptimisticTransactionDB;
|
||||
using ROCKSDB_NAMESPACE::OptimisticTransactionOptions;
|
||||
using ROCKSDB_NAMESPACE::Options;
|
||||
@@ -3028,6 +3029,11 @@ unsigned char rocksdb_options_get_advise_random_on_open(
|
||||
return opt->rep.advise_random_on_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_experimental_allow_mempurge(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.experimental_allow_mempurge = v;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_access_hint_on_compaction_start(
|
||||
rocksdb_options_t* opt, int v) {
|
||||
switch(v) {
|
||||
@@ -3877,6 +3883,47 @@ rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_bloom(int bits_per_key) {
|
||||
return rocksdb_filterpolicy_create_bloom_format(bits_per_key, true);
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon_format(
|
||||
int bloom_equivalent_bits_per_key) {
|
||||
// Make a rocksdb_filterpolicy_t, but override all of its methods so
|
||||
// they delegate to a NewRibbonFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
struct Wrapper : public rocksdb_filterpolicy_t {
|
||||
const FilterPolicy* rep_;
|
||||
~Wrapper() override { delete rep_; }
|
||||
const char* Name() const override { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const override {
|
||||
return rep_->CreateFilter(keys, n, dst);
|
||||
}
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override {
|
||||
return rep_->KeyMayMatch(key, filter);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::FilterBitsBuilder* GetBuilderWithContext(
|
||||
const ROCKSDB_NAMESPACE::FilterBuildingContext& context)
|
||||
const override {
|
||||
return rep_->GetBuilderWithContext(context);
|
||||
}
|
||||
ROCKSDB_NAMESPACE::FilterBitsReader* GetFilterBitsReader(
|
||||
const Slice& contents) const override {
|
||||
return rep_->GetFilterBitsReader(contents);
|
||||
}
|
||||
static void DoNothing(void*) {}
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = NewRibbonFilterPolicy(bloom_equivalent_bits_per_key);
|
||||
wrapper->state_ = nullptr;
|
||||
wrapper->delete_filter_ = nullptr;
|
||||
wrapper->destructor_ = &Wrapper::DoNothing;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
rocksdb_filterpolicy_t* rocksdb_filterpolicy_create_ribbon(
|
||||
int bloom_equivalent_bits_per_key) {
|
||||
return rocksdb_filterpolicy_create_ribbon_format(
|
||||
bloom_equivalent_bits_per_key);
|
||||
}
|
||||
|
||||
rocksdb_mergeoperator_t* rocksdb_mergeoperator_create(
|
||||
void* state, void (*destructor)(void*),
|
||||
char* (*full_merge)(void*, const char* key, size_t key_length,
|
||||
|
||||
+8
-3
@@ -1043,7 +1043,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
StartPhase("filter");
|
||||
for (run = 0; run <= 2; run++) {
|
||||
for (run = 0; run <= 3; run++) {
|
||||
// First run uses custom filter
|
||||
// Second run uses old block-based bloom filter
|
||||
// Third run uses full bloom filter
|
||||
@@ -1054,8 +1054,10 @@ int main(int argc, char** argv) {
|
||||
FilterKeyMatch, NULL, FilterName);
|
||||
} else if (run == 1) {
|
||||
policy = rocksdb_filterpolicy_create_bloom(8);
|
||||
} else {
|
||||
} else if (run == 2) {
|
||||
policy = rocksdb_filterpolicy_create_bloom_full(8);
|
||||
} else {
|
||||
policy = rocksdb_filterpolicy_create_ribbon(8);
|
||||
}
|
||||
rocksdb_block_based_options_set_filter_policy(table_options, policy);
|
||||
|
||||
@@ -1121,9 +1123,12 @@ int main(int argc, char** argv) {
|
||||
} else if (run == 1) {
|
||||
// Essentially a fingerprint of the block-based Bloom schema
|
||||
CheckCondition(hits == 241);
|
||||
} else {
|
||||
} else if (run == 2) {
|
||||
// Essentially a fingerprint of full Bloom schema, format_version=5
|
||||
CheckCondition(hits == 188);
|
||||
} else {
|
||||
// Essentially a fingerprint of Ribbon schema
|
||||
CheckCondition(hits == 226);
|
||||
}
|
||||
CheckCondition(
|
||||
(keys_to_query - hits) ==
|
||||
|
||||
+34
-29
@@ -445,6 +445,9 @@ bool SuperVersion::Unref() {
|
||||
|
||||
void SuperVersion::Cleanup() {
|
||||
assert(refs.load(std::memory_order_relaxed) == 0);
|
||||
// Since this SuperVersion object is being deleted,
|
||||
// decrement reference to the immutable MemtableList
|
||||
// this SV object was pointing to.
|
||||
imm->Unref(&to_delete);
|
||||
MemTable* m = mem->Unref();
|
||||
if (m != nullptr) {
|
||||
@@ -454,7 +457,7 @@ void SuperVersion::Cleanup() {
|
||||
to_delete.push_back(m);
|
||||
}
|
||||
current->Unref();
|
||||
cfd->UnrefAndTryDelete(this);
|
||||
cfd->UnrefAndTryDelete();
|
||||
}
|
||||
|
||||
void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
@@ -472,10 +475,10 @@ void SuperVersion::Init(ColumnFamilyData* new_cfd, MemTable* new_mem,
|
||||
|
||||
namespace {
|
||||
void SuperVersionUnrefHandle(void* ptr) {
|
||||
// UnrefHandle is called when a thread exists or a ThreadLocalPtr gets
|
||||
// destroyed. When former happens, the thread shouldn't see kSVInUse.
|
||||
// When latter happens, we are in ~ColumnFamilyData(), no get should happen as
|
||||
// well.
|
||||
// UnrefHandle is called when a thread exits or a ThreadLocalPtr gets
|
||||
// destroyed. When the former happens, the thread shouldn't see kSVInUse.
|
||||
// When the latter happens, only super_version_ holds a reference
|
||||
// to ColumnFamilyData, so no further queries are possible.
|
||||
SuperVersion* sv = static_cast<SuperVersion*>(ptr);
|
||||
bool was_last_ref __attribute__((__unused__));
|
||||
was_last_ref = sv->Unref();
|
||||
@@ -504,7 +507,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
const ColumnFamilyOptions& cf_options, const ImmutableDBOptions& db_options,
|
||||
const FileOptions& file_options, ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_session_id)
|
||||
: id_(id),
|
||||
name_(name),
|
||||
dummy_versions_(_dummy_versions),
|
||||
@@ -562,7 +566,8 @@ ColumnFamilyData::ColumnFamilyData(
|
||||
internal_stats_.reset(
|
||||
new InternalStats(ioptions_.num_levels, ioptions_.clock, this));
|
||||
table_cache_.reset(new TableCache(ioptions_, file_options, _table_cache,
|
||||
block_cache_tracer, io_tracer));
|
||||
block_cache_tracer, io_tracer,
|
||||
db_session_id));
|
||||
blob_file_cache_.reset(
|
||||
new BlobFileCache(_table_cache, ioptions(), soptions(), id_,
|
||||
internal_stats_->GetBlobFileReadHist(), io_tracer));
|
||||
@@ -663,7 +668,7 @@ ColumnFamilyData::~ColumnFamilyData() {
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnFamilyData::UnrefAndTryDelete(SuperVersion* sv_under_cleanup) {
|
||||
bool ColumnFamilyData::UnrefAndTryDelete() {
|
||||
int old_refs = refs_.fetch_sub(1);
|
||||
assert(old_refs > 0);
|
||||
|
||||
@@ -673,22 +678,17 @@ bool ColumnFamilyData::UnrefAndTryDelete(SuperVersion* sv_under_cleanup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If called under SuperVersion::Cleanup, we should not re-enter Cleanup on
|
||||
// the same SuperVersion. (But while installing a new SuperVersion, this
|
||||
// cfd could be referenced only by two SuperVersions.)
|
||||
if (old_refs == 2 && super_version_ != nullptr &&
|
||||
super_version_ != sv_under_cleanup) {
|
||||
if (old_refs == 2 && super_version_ != nullptr) {
|
||||
// Only the super_version_ holds me
|
||||
SuperVersion* sv = super_version_;
|
||||
super_version_ = nullptr;
|
||||
// Release SuperVersion reference kept in ThreadLocalPtr.
|
||||
// This must be done outside of mutex_ since unref handler can lock mutex.
|
||||
sv->db_mutex->Unlock();
|
||||
|
||||
// Release SuperVersion references kept in ThreadLocalPtr.
|
||||
local_sv_.reset();
|
||||
sv->db_mutex->Lock();
|
||||
|
||||
if (sv->Unref()) {
|
||||
// May delete this ColumnFamilyData after calling Cleanup()
|
||||
// Note: sv will delete this ColumnFamilyData during Cleanup()
|
||||
assert(sv->cfd == this);
|
||||
sv->Cleanup();
|
||||
delete sv;
|
||||
return true;
|
||||
@@ -1054,17 +1054,20 @@ uint64_t ColumnFamilyData::GetLiveSstFilesSize() const {
|
||||
}
|
||||
|
||||
MemTable* ColumnFamilyData::ConstructNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq,
|
||||
uint64_t log_number) {
|
||||
return new MemTable(internal_comparator_, ioptions_, mutable_cf_options,
|
||||
write_buffer_manager_, earliest_seq, id_);
|
||||
write_buffer_manager_, earliest_seq, id_, log_number);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::CreateNewMemtable(
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq) {
|
||||
const MutableCFOptions& mutable_cf_options, SequenceNumber earliest_seq,
|
||||
uint64_t log_number) {
|
||||
if (mem_ != nullptr) {
|
||||
delete mem_->Unref();
|
||||
}
|
||||
SetMemtable(ConstructNewMemtable(mutable_cf_options, earliest_seq));
|
||||
SetMemtable(
|
||||
ConstructNewMemtable(mutable_cf_options, earliest_seq, log_number));
|
||||
mem_->Ref();
|
||||
}
|
||||
|
||||
@@ -1253,14 +1256,13 @@ bool ColumnFamilyData::ReturnThreadLocalSuperVersion(SuperVersion* sv) {
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex) {
|
||||
db_mutex->AssertHeld();
|
||||
return InstallSuperVersion(sv_context, db_mutex, mutable_cf_options_);
|
||||
return InstallSuperVersion(sv_context, mutable_cf_options_);
|
||||
}
|
||||
|
||||
void ColumnFamilyData::InstallSuperVersion(
|
||||
SuperVersionContext* sv_context, InstrumentedMutex* db_mutex,
|
||||
SuperVersionContext* sv_context,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
SuperVersion* new_superversion = sv_context->new_superversion.release();
|
||||
new_superversion->db_mutex = db_mutex;
|
||||
new_superversion->mutable_cf_options = mutable_cf_options;
|
||||
new_superversion->Init(this, mem_, imm_.current(), current_);
|
||||
SuperVersion* old_superversion = super_version_;
|
||||
@@ -1451,12 +1453,13 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
WriteBufferManager* _write_buffer_manager,
|
||||
WriteController* _write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer)
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_session_id)
|
||||
: max_column_family_(0),
|
||||
dummy_cfd_(new ColumnFamilyData(
|
||||
ColumnFamilyData::kDummyColumnFamilyDataId, "", nullptr, nullptr,
|
||||
nullptr, ColumnFamilyOptions(), *db_options, file_options, nullptr,
|
||||
block_cache_tracer, io_tracer)),
|
||||
block_cache_tracer, io_tracer, db_session_id)),
|
||||
default_cfd_cache_(nullptr),
|
||||
db_name_(dbname),
|
||||
db_options_(db_options),
|
||||
@@ -1465,7 +1468,8 @@ ColumnFamilySet::ColumnFamilySet(const std::string& dbname,
|
||||
write_buffer_manager_(_write_buffer_manager),
|
||||
write_controller_(_write_controller),
|
||||
block_cache_tracer_(block_cache_tracer),
|
||||
io_tracer_(io_tracer) {
|
||||
io_tracer_(io_tracer),
|
||||
db_session_id_(db_session_id) {
|
||||
// initialize linked list
|
||||
dummy_cfd_->prev_ = dummy_cfd_;
|
||||
dummy_cfd_->next_ = dummy_cfd_;
|
||||
@@ -1531,7 +1535,8 @@ ColumnFamilyData* ColumnFamilySet::CreateColumnFamily(
|
||||
assert(column_families_.find(name) == column_families_.end());
|
||||
ColumnFamilyData* new_cfd = new ColumnFamilyData(
|
||||
id, name, dummy_versions, table_cache_, write_buffer_manager_, options,
|
||||
*db_options_, file_options_, this, block_cache_tracer_, io_tracer_);
|
||||
*db_options_, file_options_, this, block_cache_tracer_, io_tracer_,
|
||||
db_session_id_);
|
||||
column_families_.insert({name, id});
|
||||
column_family_data_.insert({id, new_cfd});
|
||||
max_column_family_ = std::max(max_column_family_, id);
|
||||
|
||||
+10
-9
@@ -208,8 +208,6 @@ struct SuperVersion {
|
||||
uint64_t version_number;
|
||||
WriteStallCondition write_stall_condition;
|
||||
|
||||
InstrumentedMutex* db_mutex;
|
||||
|
||||
// should be called outside the mutex
|
||||
SuperVersion() = default;
|
||||
~SuperVersion();
|
||||
@@ -281,8 +279,7 @@ class ColumnFamilyData {
|
||||
// UnrefAndTryDelete() decreases the reference count and do free if needed,
|
||||
// return true if this is freed else false, UnrefAndTryDelete() can only
|
||||
// be called while holding a DB mutex, or during single-threaded recovery.
|
||||
// sv_under_cleanup is only provided when called from SuperVersion::Cleanup.
|
||||
bool UnrefAndTryDelete(SuperVersion* sv_under_cleanup = nullptr);
|
||||
bool UnrefAndTryDelete();
|
||||
|
||||
// SetDropped() can only be called under following conditions:
|
||||
// 1) Holding a DB mutex,
|
||||
@@ -371,9 +368,10 @@ class ColumnFamilyData {
|
||||
|
||||
// See Memtable constructor for explanation of earliest_seq param.
|
||||
MemTable* ConstructNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
SequenceNumber earliest_seq,
|
||||
uint64_t log_number = 0);
|
||||
void CreateNewMemtable(const MutableCFOptions& mutable_cf_options,
|
||||
SequenceNumber earliest_seq);
|
||||
SequenceNumber earliest_seq, uint64_t log_number = 0);
|
||||
|
||||
TableCache* table_cache() const { return table_cache_.get(); }
|
||||
BlobFileCache* blob_file_cache() const { return blob_file_cache_.get(); }
|
||||
@@ -453,7 +451,6 @@ class ColumnFamilyData {
|
||||
// the clients to allocate SuperVersion outside of mutex.
|
||||
// IMPORTANT: Only call this from DBImpl::InstallSuperVersion()
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
void InstallSuperVersion(SuperVersionContext* sv_context,
|
||||
InstrumentedMutex* db_mutex);
|
||||
@@ -520,6 +517,7 @@ class ColumnFamilyData {
|
||||
}
|
||||
|
||||
ThreadLocalPtr* TEST_GetLocalSV() { return local_sv_.get(); }
|
||||
WriteBufferManager* write_buffer_mgr() { return write_buffer_manager_; }
|
||||
|
||||
private:
|
||||
friend class ColumnFamilySet;
|
||||
@@ -532,7 +530,8 @@ class ColumnFamilyData {
|
||||
const FileOptions& file_options,
|
||||
ColumnFamilySet* column_family_set,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_session_id);
|
||||
|
||||
std::vector<std::string> GetDbPaths() const;
|
||||
|
||||
@@ -668,7 +667,8 @@ class ColumnFamilySet {
|
||||
WriteBufferManager* _write_buffer_manager,
|
||||
WriteController* _write_controller,
|
||||
BlockCacheTracer* const block_cache_tracer,
|
||||
const std::shared_ptr<IOTracer>& io_tracer);
|
||||
const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::string& db_session_id);
|
||||
~ColumnFamilySet();
|
||||
|
||||
ColumnFamilyData* GetDefault() const;
|
||||
@@ -733,6 +733,7 @@ class ColumnFamilySet {
|
||||
WriteController* write_controller_;
|
||||
BlockCacheTracer* const block_cache_tracer_;
|
||||
std::shared_ptr<IOTracer> io_tracer_;
|
||||
std::string db_session_id_;
|
||||
};
|
||||
|
||||
// We use ColumnFamilyMemTablesImpl to provide WriteBatch a way to access
|
||||
|
||||
@@ -56,16 +56,8 @@ class ColumnFamilyTestBase : public testing::Test {
|
||||
public:
|
||||
explicit ColumnFamilyTestBase(uint32_t format) : rnd_(139), format_(format) {
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
const char* test_env_uri = getenv("TEST_ENV_URI");
|
||||
if (test_env_uri) {
|
||||
Env* test_env = nullptr;
|
||||
Status s = Env::LoadEnv(test_env_uri, &test_env, &env_guard_);
|
||||
base_env = test_env;
|
||||
EXPECT_OK(s);
|
||||
EXPECT_NE(Env::Default(), base_env);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
EXPECT_OK(
|
||||
test::CreateEnvFromSystem(ConfigOptions(), &base_env, &env_guard_));
|
||||
EXPECT_NE(nullptr, base_env);
|
||||
env_ = new EnvCounter(base_env);
|
||||
env_->skip_fsync_ = true;
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
// 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 "table/internal_iterator.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// An internal iterator that wraps another one and ensures that any keys
|
||||
// returned are strictly within a range [start, end). If the underlying
|
||||
// iterator has already performed the bounds checking, it relies on that result;
|
||||
// otherwise, it performs the necessary key comparisons itself. Both bounds
|
||||
// are optional.
|
||||
class ClippingIterator : public InternalIterator {
|
||||
public:
|
||||
ClippingIterator(InternalIterator* iter, const Slice* start, const Slice* end,
|
||||
const Comparator* cmp)
|
||||
: iter_(iter), start_(start), end_(end), cmp_(cmp), valid_(false) {
|
||||
assert(iter_);
|
||||
assert(cmp_);
|
||||
assert(!start_ || !end_ || cmp_->Compare(*start_, *end_) <= 0);
|
||||
|
||||
UpdateAndEnforceBounds();
|
||||
}
|
||||
|
||||
bool Valid() const override { return valid_; }
|
||||
|
||||
void SeekToFirst() override {
|
||||
if (start_) {
|
||||
iter_->Seek(*start_);
|
||||
} else {
|
||||
iter_->SeekToFirst();
|
||||
}
|
||||
|
||||
UpdateAndEnforceUpperBound();
|
||||
}
|
||||
|
||||
void SeekToLast() override {
|
||||
if (end_) {
|
||||
iter_->SeekForPrev(*end_);
|
||||
|
||||
// Upper bound is exclusive, so we need a key which is strictly smaller
|
||||
if (iter_->Valid() && cmp_->Compare(iter_->key(), *end_) == 0) {
|
||||
iter_->Prev();
|
||||
}
|
||||
} else {
|
||||
iter_->SeekToLast();
|
||||
}
|
||||
|
||||
UpdateAndEnforceLowerBound();
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
if (start_ && cmp_->Compare(target, *start_) < 0) {
|
||||
iter_->Seek(*start_);
|
||||
UpdateAndEnforceUpperBound();
|
||||
return;
|
||||
}
|
||||
|
||||
if (end_ && cmp_->Compare(target, *end_) >= 0) {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
iter_->Seek(target);
|
||||
UpdateAndEnforceUpperBound();
|
||||
}
|
||||
|
||||
void SeekForPrev(const Slice& target) override {
|
||||
if (start_ && cmp_->Compare(target, *start_) < 0) {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (end_ && cmp_->Compare(target, *end_) >= 0) {
|
||||
iter_->SeekForPrev(*end_);
|
||||
|
||||
// Upper bound is exclusive, so we need a key which is strictly smaller
|
||||
if (iter_->Valid() && cmp_->Compare(iter_->key(), *end_) == 0) {
|
||||
iter_->Prev();
|
||||
}
|
||||
|
||||
UpdateAndEnforceLowerBound();
|
||||
return;
|
||||
}
|
||||
|
||||
iter_->SeekForPrev(target);
|
||||
UpdateAndEnforceLowerBound();
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
assert(valid_);
|
||||
iter_->Next();
|
||||
UpdateAndEnforceUpperBound();
|
||||
}
|
||||
|
||||
bool NextAndGetResult(IterateResult* result) override {
|
||||
assert(valid_);
|
||||
assert(result);
|
||||
|
||||
IterateResult res;
|
||||
valid_ = iter_->NextAndGetResult(&res);
|
||||
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (end_) {
|
||||
EnforceUpperBoundImpl(res.bound_check_result);
|
||||
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
res.bound_check_result = IterBoundCheck::kInbound;
|
||||
*result = res;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
assert(valid_);
|
||||
iter_->Prev();
|
||||
UpdateAndEnforceLowerBound();
|
||||
}
|
||||
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
return iter_->key();
|
||||
}
|
||||
|
||||
Slice user_key() const override {
|
||||
assert(valid_);
|
||||
return iter_->user_key();
|
||||
}
|
||||
|
||||
Slice value() const override {
|
||||
assert(valid_);
|
||||
return iter_->value();
|
||||
}
|
||||
|
||||
Status status() const override { return iter_->status(); }
|
||||
|
||||
bool PrepareValue() override {
|
||||
assert(valid_);
|
||||
|
||||
if (iter_->PrepareValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
assert(!iter_->Valid());
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MayBeOutOfLowerBound() override {
|
||||
assert(valid_);
|
||||
return false;
|
||||
}
|
||||
|
||||
IterBoundCheck UpperBoundCheckResult() override {
|
||||
assert(valid_);
|
||||
return IterBoundCheck::kInbound;
|
||||
}
|
||||
|
||||
void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override {
|
||||
iter_->SetPinnedItersMgr(pinned_iters_mgr);
|
||||
}
|
||||
|
||||
bool IsKeyPinned() const override {
|
||||
assert(valid_);
|
||||
return iter_->IsKeyPinned();
|
||||
}
|
||||
|
||||
bool IsValuePinned() const override {
|
||||
assert(valid_);
|
||||
return iter_->IsValuePinned();
|
||||
}
|
||||
|
||||
Status GetProperty(std::string prop_name, std::string* prop) override {
|
||||
return iter_->GetProperty(prop_name, prop);
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateValid() {
|
||||
assert(!iter_->Valid() || iter_->status().ok());
|
||||
|
||||
valid_ = iter_->Valid();
|
||||
}
|
||||
|
||||
void EnforceUpperBoundImpl(IterBoundCheck bound_check_result) {
|
||||
if (bound_check_result == IterBoundCheck::kInbound) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (bound_check_result == IterBoundCheck::kOutOfBound) {
|
||||
valid_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
assert(bound_check_result == IterBoundCheck::kUnknown);
|
||||
|
||||
if (cmp_->Compare(key(), *end_) >= 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void EnforceUpperBound() {
|
||||
if (!valid_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!end_) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnforceUpperBoundImpl(iter_->UpperBoundCheckResult());
|
||||
}
|
||||
|
||||
void EnforceLowerBound() {
|
||||
if (!valid_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!start_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iter_->MayBeOutOfLowerBound()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmp_->Compare(key(), *start_) < 0) {
|
||||
valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AssertBounds() {
|
||||
assert(!valid_ || !start_ || cmp_->Compare(key(), *start_) >= 0);
|
||||
assert(!valid_ || !end_ || cmp_->Compare(key(), *end_) < 0);
|
||||
}
|
||||
|
||||
void UpdateAndEnforceBounds() {
|
||||
UpdateValid();
|
||||
EnforceUpperBound();
|
||||
EnforceLowerBound();
|
||||
AssertBounds();
|
||||
}
|
||||
|
||||
void UpdateAndEnforceUpperBound() {
|
||||
UpdateValid();
|
||||
EnforceUpperBound();
|
||||
AssertBounds();
|
||||
}
|
||||
|
||||
void UpdateAndEnforceLowerBound() {
|
||||
UpdateValid();
|
||||
EnforceLowerBound();
|
||||
AssertBounds();
|
||||
}
|
||||
|
||||
InternalIterator* iter_;
|
||||
const Slice* start_;
|
||||
const Slice* end_;
|
||||
const Comparator* cmp_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -0,0 +1,256 @@
|
||||
// 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/compaction/clipping_iterator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rocksdb/comparator.h"
|
||||
#include "test_util/testharness.h"
|
||||
#include "test_util/testutil.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
// A vector iterator which does its own bounds checking. This is for testing the
|
||||
// optimizations in the clipping iterator where we bypass the bounds checking if
|
||||
// the input iterator has already performed it.
|
||||
class BoundsCheckingVectorIterator : public test::VectorIterator {
|
||||
public:
|
||||
BoundsCheckingVectorIterator(const std::vector<std::string>& keys,
|
||||
const std::vector<std::string>& values,
|
||||
const Slice* start, const Slice* end,
|
||||
const Comparator* cmp)
|
||||
: VectorIterator(keys, values), start_(start), end_(end), cmp_(cmp) {
|
||||
assert(cmp_);
|
||||
}
|
||||
|
||||
bool NextAndGetResult(IterateResult* result) override {
|
||||
assert(Valid());
|
||||
assert(result);
|
||||
|
||||
Next();
|
||||
|
||||
if (!Valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result->key = key();
|
||||
result->bound_check_result = UpperBoundCheckResult();
|
||||
result->value_prepared = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MayBeOutOfLowerBound() override {
|
||||
assert(Valid());
|
||||
|
||||
if (!start_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cmp_->Compare(key(), *start_) < 0;
|
||||
}
|
||||
|
||||
IterBoundCheck UpperBoundCheckResult() override {
|
||||
assert(Valid());
|
||||
|
||||
if (!end_) {
|
||||
return IterBoundCheck::kInbound;
|
||||
}
|
||||
|
||||
return cmp_->Compare(key(), *end_) >= 0 ? IterBoundCheck::kOutOfBound
|
||||
: IterBoundCheck::kInbound;
|
||||
}
|
||||
|
||||
private:
|
||||
const Slice* start_;
|
||||
const Slice* end_;
|
||||
const Comparator* cmp_;
|
||||
};
|
||||
|
||||
class ClippingIteratorTest
|
||||
: public ::testing::Test,
|
||||
public ::testing::WithParamInterface<std::tuple<bool, size_t, size_t>> {};
|
||||
|
||||
TEST_P(ClippingIteratorTest, Clip) {
|
||||
const std::vector<std::string> keys{"key0", "key1", "key2", "key3", "key4",
|
||||
"key5", "key6", "key7", "key8", "key9"};
|
||||
const std::vector<std::string> values{
|
||||
"unused0", "value1", "value2", "value3", "unused4",
|
||||
"unused5", "unused6", "unused7", "unused8", "unused9"};
|
||||
|
||||
assert(keys.size() == values.size());
|
||||
|
||||
// Note: the input always contains key1, key2, and key3; however, the clipping
|
||||
// window is based on the test parameters: its left edge is a value in the
|
||||
// range [0, 4], and its size is a value in the range [0, 5]
|
||||
const std::vector<std::string> input_keys{keys[1], keys[2], keys[3]};
|
||||
const std::vector<std::string> input_values{values[1], values[2], values[3]};
|
||||
|
||||
const bool use_bounds_checking_vec_it = std::get<0>(GetParam());
|
||||
|
||||
const size_t clip_start_idx = std::get<1>(GetParam());
|
||||
const size_t clip_window_size = std::get<2>(GetParam());
|
||||
const size_t clip_end_idx = clip_start_idx + clip_window_size;
|
||||
|
||||
const Slice start(keys[clip_start_idx]);
|
||||
const Slice end(keys[clip_end_idx]);
|
||||
|
||||
std::unique_ptr<InternalIterator> input(
|
||||
use_bounds_checking_vec_it
|
||||
? new BoundsCheckingVectorIterator(input_keys, input_values, &start,
|
||||
&end, BytewiseComparator())
|
||||
: new test::VectorIterator(input_keys, input_values));
|
||||
|
||||
ClippingIterator clip(input.get(), &start, &end, BytewiseComparator());
|
||||
|
||||
// The range the clipping iterator should return values from. This is
|
||||
// essentially the intersection of the input range [1, 4) and the clipping
|
||||
// window [clip_start_idx, clip_end_idx)
|
||||
const size_t data_start_idx =
|
||||
std::max(clip_start_idx, static_cast<size_t>(1));
|
||||
const size_t data_end_idx = std::min(clip_end_idx, static_cast<size_t>(4));
|
||||
|
||||
// Range is empty; all Seeks should fail
|
||||
if (data_start_idx >= data_end_idx) {
|
||||
clip.SeekToFirst();
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
|
||||
clip.SeekToLast();
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
clip.Seek(keys[i]);
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
|
||||
clip.SeekForPrev(keys[i]);
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Range is non-empty; call SeekToFirst and iterate forward
|
||||
clip.SeekToFirst();
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[data_start_idx]);
|
||||
ASSERT_EQ(clip.value(), values[data_start_idx]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
|
||||
for (size_t i = data_start_idx + 1; i < data_end_idx; ++i) {
|
||||
clip.Next();
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[i]);
|
||||
ASSERT_EQ(clip.value(), values[i]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
}
|
||||
|
||||
clip.Next();
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
|
||||
// Do it again using NextAndGetResult
|
||||
clip.SeekToFirst();
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[data_start_idx]);
|
||||
ASSERT_EQ(clip.value(), values[data_start_idx]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
|
||||
for (size_t i = data_start_idx + 1; i < data_end_idx; ++i) {
|
||||
IterateResult result;
|
||||
ASSERT_TRUE(clip.NextAndGetResult(&result));
|
||||
ASSERT_EQ(result.key, keys[i]);
|
||||
ASSERT_EQ(result.bound_check_result, IterBoundCheck::kInbound);
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[i]);
|
||||
ASSERT_EQ(clip.value(), values[i]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
}
|
||||
|
||||
IterateResult result;
|
||||
ASSERT_FALSE(clip.NextAndGetResult(&result));
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
|
||||
// Call SeekToLast and iterate backward
|
||||
clip.SeekToLast();
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[data_end_idx - 1]);
|
||||
ASSERT_EQ(clip.value(), values[data_end_idx - 1]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
|
||||
for (size_t i = data_end_idx - 2; i >= data_start_idx; --i) {
|
||||
clip.Prev();
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[i]);
|
||||
ASSERT_EQ(clip.value(), values[i]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
}
|
||||
|
||||
clip.Prev();
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
|
||||
// Call Seek/SeekForPrev for all keys; Seek should return the smallest key
|
||||
// which is >= the target; SeekForPrev should return the largest key which is
|
||||
// <= the target
|
||||
for (size_t i = 0; i < keys.size(); ++i) {
|
||||
clip.Seek(keys[i]);
|
||||
|
||||
if (i < data_start_idx) {
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[data_start_idx]);
|
||||
ASSERT_EQ(clip.value(), values[data_start_idx]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
} else if (i < data_end_idx) {
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[i]);
|
||||
ASSERT_EQ(clip.value(), values[i]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
} else {
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
}
|
||||
|
||||
clip.SeekForPrev(keys[i]);
|
||||
|
||||
if (i < data_start_idx) {
|
||||
ASSERT_FALSE(clip.Valid());
|
||||
} else if (i < data_end_idx) {
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[i]);
|
||||
ASSERT_EQ(clip.value(), values[i]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
} else {
|
||||
ASSERT_TRUE(clip.Valid());
|
||||
ASSERT_EQ(clip.key(), keys[data_end_idx - 1]);
|
||||
ASSERT_EQ(clip.value(), values[data_end_idx - 1]);
|
||||
ASSERT_FALSE(clip.MayBeOutOfLowerBound());
|
||||
ASSERT_EQ(clip.UpperBoundCheckResult(), IterBoundCheck::kInbound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ClippingIteratorTest, ClippingIteratorTest,
|
||||
::testing::Combine(
|
||||
::testing::Bool(),
|
||||
::testing::Range(static_cast<size_t>(0), static_cast<size_t>(5)),
|
||||
::testing::Range(static_cast<size_t>(0), static_cast<size_t>(6))));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -237,7 +237,8 @@ Compaction::Compaction(
|
||||
is_full_compaction_(IsFullCompaction(vstorage, inputs_)),
|
||||
is_manual_compaction_(_manual_compaction),
|
||||
is_trivial_move_(false),
|
||||
compaction_reason_(_compaction_reason) {
|
||||
compaction_reason_(_compaction_reason),
|
||||
notify_on_compaction_completion_(false) {
|
||||
MarkFilesBeingCompacted(true);
|
||||
if (is_manual_compaction_) {
|
||||
compaction_reason_ = CompactionReason::kManualCompaction;
|
||||
@@ -563,6 +564,14 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
if (max_subcompactions_ <= 1 || cfd_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: the subcompaction boundary picking logic does not currently guarantee
|
||||
// that all user keys that differ only by timestamp get processed by the same
|
||||
// subcompaction.
|
||||
if (cfd_->user_comparator()->timestamp_size() > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cfd_->ioptions()->compaction_style == kCompactionStyleLevel) {
|
||||
return (start_level_ == 0 || is_manual_compaction_) && output_level_ > 0 &&
|
||||
!IsOutputLevelEmpty();
|
||||
@@ -573,6 +582,29 @@ bool Compaction::ShouldFormSubcompactions() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool Compaction::DoesInputReferenceBlobFiles() const {
|
||||
assert(input_version_);
|
||||
|
||||
const VersionStorageInfo* storage_info = input_version_->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
if (storage_info->GetBlobFiles().empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < inputs_.size(); ++i) {
|
||||
for (const FileMetaData* meta : inputs_[i].files) {
|
||||
assert(meta);
|
||||
|
||||
if (meta->oldest_blob_file_number != kInvalidBlobFileNumber) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t Compaction::MinInputFileOldestAncesterTime() const {
|
||||
uint64_t min_oldest_ancester_time = port::kMaxUint64;
|
||||
for (const auto& level_files : inputs_) {
|
||||
|
||||
@@ -223,9 +223,9 @@ class Compaction {
|
||||
// How many total levels are there?
|
||||
int number_levels() const { return number_levels_; }
|
||||
|
||||
// Return the ImmutableCFOptions that should be used throughout the compaction
|
||||
// Return the ImmutableOptions that should be used throughout the compaction
|
||||
// procedure
|
||||
const ImmutableOptions* immutable_cf_options() const {
|
||||
const ImmutableOptions* immutable_options() const {
|
||||
return &immutable_options_;
|
||||
}
|
||||
|
||||
@@ -266,6 +266,11 @@ class Compaction {
|
||||
// Should this compaction be broken up into smaller ones run in parallel?
|
||||
bool ShouldFormSubcompactions() const;
|
||||
|
||||
// Returns true iff at least one input file references a blob file.
|
||||
//
|
||||
// PRE: input version has been set.
|
||||
bool DoesInputReferenceBlobFiles() const;
|
||||
|
||||
// test function to validate the functionality of IsBottommostLevel()
|
||||
// function -- determines if compaction with inputs and storage is bottommost
|
||||
static bool TEST_IsBottommostLevel(
|
||||
@@ -298,6 +303,16 @@ class Compaction {
|
||||
|
||||
uint64_t MinInputFileOldestAncesterTime() const;
|
||||
|
||||
// Called by DBImpl::NotifyOnCompactionCompleted to make sure number of
|
||||
// compaction begin and compaction completion callbacks match.
|
||||
void SetNotifyOnCompactionCompleted() {
|
||||
notify_on_compaction_completion_ = true;
|
||||
}
|
||||
|
||||
bool ShouldNotifyOnCompactionCompleted() const {
|
||||
return notify_on_compaction_completion_;
|
||||
}
|
||||
|
||||
private:
|
||||
// mark (or clear) all files that are being compacted
|
||||
void MarkFilesBeingCompacted(bool mark_as_compacted);
|
||||
@@ -381,6 +396,10 @@ class Compaction {
|
||||
|
||||
// Reason for compaction
|
||||
CompactionReason compaction_reason_;
|
||||
|
||||
// Notify on compaction completion only if listener was notified on compaction
|
||||
// begin.
|
||||
bool notify_on_compaction_completion_;
|
||||
};
|
||||
|
||||
// Return sum of sizes of all files in `files`.
|
||||
|
||||
@@ -45,6 +45,7 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low)
|
||||
: CompactionIterator(
|
||||
@@ -55,7 +56,8 @@ CompactionIterator::CompactionIterator(
|
||||
std::unique_ptr<CompactionProxy>(
|
||||
compaction ? new RealCompaction(compaction) : nullptr),
|
||||
compaction_filter, shutting_down, preserve_deletes_seqnum,
|
||||
manual_compaction_paused, info_log, full_history_ts_low) {}
|
||||
manual_compaction_paused, manual_compaction_canceled, info_log,
|
||||
full_history_ts_low) {}
|
||||
|
||||
CompactionIterator::CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
@@ -70,12 +72,11 @@ CompactionIterator::CompactionIterator(
|
||||
const std::atomic<bool>* shutting_down,
|
||||
const SequenceNumber preserve_deletes_seqnum,
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::shared_ptr<Logger> info_log,
|
||||
const std::string* full_history_ts_low)
|
||||
: input_(
|
||||
input, cmp,
|
||||
compaction ==
|
||||
nullptr), // Now only need to count number of entries in flush.
|
||||
: input_(input, cmp,
|
||||
!compaction || compaction->DoesInputReferenceBlobFiles()),
|
||||
cmp_(cmp),
|
||||
merge_helper_(merge_helper),
|
||||
snapshots_(snapshots),
|
||||
@@ -91,6 +92,7 @@ CompactionIterator::CompactionIterator(
|
||||
compaction_filter_(compaction_filter),
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
manual_compaction_canceled_(manual_compaction_canceled),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
info_log_(info_log),
|
||||
allow_data_in_errors_(allow_data_in_errors),
|
||||
@@ -752,13 +754,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_);
|
||||
Status s = merge_helper_->MergeUntil(&input_, range_del_agg_,
|
||||
prev_snapshot, bottommost_level_,
|
||||
allow_data_in_errors_, version);
|
||||
merge_out_iter_.SeekToFirst();
|
||||
|
||||
if (!s.ok() && !s.IsMergeInProgress()) {
|
||||
|
||||
@@ -97,6 +97,8 @@ class CompactionIterator {
|
||||
virtual double blob_garbage_collection_age_cutoff() const = 0;
|
||||
|
||||
virtual Version* input_version() const = 0;
|
||||
|
||||
virtual bool DoesInputReferenceBlobFiles() const = 0;
|
||||
};
|
||||
|
||||
class RealCompaction : public CompactionProxy {
|
||||
@@ -104,7 +106,7 @@ class CompactionIterator {
|
||||
explicit RealCompaction(const Compaction* compaction)
|
||||
: compaction_(compaction) {
|
||||
assert(compaction_);
|
||||
assert(compaction_->immutable_cf_options());
|
||||
assert(compaction_->immutable_options());
|
||||
assert(compaction_->mutable_cf_options());
|
||||
}
|
||||
|
||||
@@ -126,11 +128,11 @@ class CompactionIterator {
|
||||
}
|
||||
|
||||
bool allow_ingest_behind() const override {
|
||||
return compaction_->immutable_cf_options()->allow_ingest_behind;
|
||||
return compaction_->immutable_options()->allow_ingest_behind;
|
||||
}
|
||||
|
||||
bool preserve_deletes() const override {
|
||||
return compaction_->immutable_cf_options()->preserve_deletes;
|
||||
return compaction_->immutable_options()->preserve_deletes;
|
||||
}
|
||||
|
||||
bool enable_blob_garbage_collection() const override {
|
||||
@@ -146,44 +148,48 @@ class CompactionIterator {
|
||||
return compaction_->input_version();
|
||||
}
|
||||
|
||||
bool DoesInputReferenceBlobFiles() const override {
|
||||
return compaction_->DoesInputReferenceBlobFiles();
|
||||
}
|
||||
|
||||
private:
|
||||
const Compaction* compaction_;
|
||||
};
|
||||
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder,
|
||||
bool allow_data_in_errors,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
const Compaction* compaction = nullptr,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
// Constructor with custom CompactionProxy, used for tests.
|
||||
CompactionIterator(InternalIterator* input, const Comparator* cmp,
|
||||
MergeHelper* merge_helper, SequenceNumber last_sequence,
|
||||
std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder,
|
||||
bool allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
CompactionIterator(
|
||||
InternalIterator* input, const Comparator* cmp, MergeHelper* merge_helper,
|
||||
SequenceNumber last_sequence, std::vector<SequenceNumber>* snapshots,
|
||||
SequenceNumber earliest_write_conflict_snapshot,
|
||||
const SnapshotChecker* snapshot_checker, Env* env,
|
||||
bool report_detailed_time, bool expect_valid_internal_key,
|
||||
CompactionRangeDelAggregator* range_del_agg,
|
||||
BlobFileBuilder* blob_file_builder, bool allow_data_in_errors,
|
||||
std::unique_ptr<CompactionProxy> compaction,
|
||||
const CompactionFilter* compaction_filter = nullptr,
|
||||
const std::atomic<bool>* shutting_down = nullptr,
|
||||
const SequenceNumber preserve_deletes_seqnum = 0,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::shared_ptr<Logger> info_log = nullptr,
|
||||
const std::string* full_history_ts_low = nullptr);
|
||||
|
||||
~CompactionIterator();
|
||||
|
||||
@@ -303,6 +309,7 @@ class CompactionIterator {
|
||||
const CompactionFilter* compaction_filter_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const std::atomic<bool>* manual_compaction_canceled_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
bool bottommost_level_;
|
||||
bool valid_ = false;
|
||||
@@ -399,8 +406,10 @@ class CompactionIterator {
|
||||
|
||||
bool IsPausingManualCompaction() {
|
||||
// This is a best-effort facility, so memory_order_relaxed is sufficient.
|
||||
return manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0;
|
||||
return (manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0) ||
|
||||
(manual_compaction_canceled_ &&
|
||||
manual_compaction_canceled_->load(std::memory_order_relaxed));
|
||||
}
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -182,6 +182,8 @@ class FakeCompaction : public CompactionIterator::CompactionProxy {
|
||||
|
||||
Version* input_version() const override { return nullptr; }
|
||||
|
||||
bool DoesInputReferenceBlobFiles() const override { return false; }
|
||||
|
||||
bool key_not_exists_beyond_output_level = false;
|
||||
|
||||
bool is_bottommost_level = false;
|
||||
@@ -282,7 +284,8 @@ class CompactionIteratorTest : public testing::TestWithParam<bool> {
|
||||
range_del_agg_.get(), nullptr /* blob_file_builder */,
|
||||
true /*allow_data_in_errors*/, std::move(compaction), filter,
|
||||
&shutting_down_, /*preserve_deletes_seqnum=*/0,
|
||||
/*manual_compaction_paused=*/nullptr, /*info_log=*/nullptr,
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, /*info_log=*/nullptr,
|
||||
full_history_ts_low));
|
||||
}
|
||||
|
||||
|
||||
+143
-60
@@ -20,9 +20,12 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "db/blob/blob_counting_iterator.h"
|
||||
#include "db/blob/blob_file_addition.h"
|
||||
#include "db/blob/blob_file_builder.h"
|
||||
#include "db/blob/blob_garbage_meter.h"
|
||||
#include "db/builder.h"
|
||||
#include "db/compaction/clipping_iterator.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_iter.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -146,6 +149,7 @@ struct CompactionJob::SubcompactionState {
|
||||
// State kept for output being generated
|
||||
std::vector<Output> outputs;
|
||||
std::vector<BlobFileAddition> blob_file_additions;
|
||||
std::unique_ptr<BlobGarbageMeter> blob_garbage_meter;
|
||||
std::unique_ptr<WritableFileWriter> outfile;
|
||||
std::unique_ptr<TableBuilder> builder;
|
||||
|
||||
@@ -176,9 +180,17 @@ struct CompactionJob::SubcompactionState {
|
||||
uint64_t overlapped_bytes = 0;
|
||||
// A flag determine whether the key has been seen in ShouldStopBefore()
|
||||
bool seen_key = false;
|
||||
// sub compaction job id, which is used to identify different sub-compaction
|
||||
// within the same compaction job.
|
||||
const uint32_t sub_job_id;
|
||||
|
||||
SubcompactionState(Compaction* c, Slice* _start, Slice* _end, uint64_t size)
|
||||
: compaction(c), start(_start), end(_end), approx_size(size) {
|
||||
SubcompactionState(Compaction* c, Slice* _start, Slice* _end, uint64_t size,
|
||||
uint32_t _sub_job_id)
|
||||
: compaction(c),
|
||||
start(_start),
|
||||
end(_end),
|
||||
approx_size(size),
|
||||
sub_job_id(_sub_job_id) {
|
||||
assert(compaction != nullptr);
|
||||
}
|
||||
|
||||
@@ -228,6 +240,14 @@ struct CompactionJob::SubcompactionState {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Status ProcessOutFlowIfNeeded(const Slice& key, const Slice& value) {
|
||||
if (!blob_garbage_meter) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
return blob_garbage_meter->ProcessOutFlow(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
// Maintains state for the entire compaction
|
||||
@@ -313,9 +333,10 @@ CompactionJob::CompactionJob(
|
||||
EventLogger* event_logger, bool paranoid_file_checks, bool measure_io_stats,
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused, const std::string& db_id,
|
||||
const std::string& db_session_id, std::string full_history_ts_low,
|
||||
BlobFileCompletionCallback* blob_callback)
|
||||
const std::atomic<int>* manual_compaction_paused,
|
||||
const std::atomic<bool>* manual_compaction_canceled,
|
||||
const std::string& db_id, const std::string& db_session_id,
|
||||
std::string full_history_ts_low, BlobFileCompletionCallback* blob_callback)
|
||||
: compact_(new CompactionState(compaction)),
|
||||
compaction_stats_(compaction->compaction_reason(), 1),
|
||||
db_options_(db_options),
|
||||
@@ -339,6 +360,7 @@ CompactionJob::CompactionJob(
|
||||
versions_(versions),
|
||||
shutting_down_(shutting_down),
|
||||
manual_compaction_paused_(manual_compaction_paused),
|
||||
manual_compaction_canceled_(manual_compaction_canceled),
|
||||
preserve_deletes_seqnum_(preserve_deletes_seqnum),
|
||||
db_directory_(db_directory),
|
||||
blob_output_directory_(blob_output_directory),
|
||||
@@ -435,7 +457,8 @@ void CompactionJob::Prepare() {
|
||||
for (size_t i = 0; i <= boundaries_.size(); i++) {
|
||||
Slice* start = i == 0 ? nullptr : &boundaries_[i - 1];
|
||||
Slice* end = i == boundaries_.size() ? nullptr : &boundaries_[i];
|
||||
compact_->sub_compact_states.emplace_back(c, start, end, sizes_[i]);
|
||||
compact_->sub_compact_states.emplace_back(c, start, end, sizes_[i],
|
||||
static_cast<uint32_t>(i));
|
||||
}
|
||||
RecordInHistogram(stats_, NUM_SUBCOMPACTIONS_SCHEDULED,
|
||||
compact_->sub_compact_states.size());
|
||||
@@ -444,7 +467,8 @@ void CompactionJob::Prepare() {
|
||||
constexpr Slice* end = nullptr;
|
||||
constexpr uint64_t size = 0;
|
||||
|
||||
compact_->sub_compact_states.emplace_back(c, start, end, size);
|
||||
compact_->sub_compact_states.emplace_back(c, start, end, size,
|
||||
/*sub_job_id*/ 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,9 +574,10 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
int base_level = v->storage_info()->base_level();
|
||||
uint64_t max_output_files = static_cast<uint64_t>(std::ceil(
|
||||
sum / min_file_fill_percent /
|
||||
MaxFileSizeForLevel(*(c->mutable_cf_options()), out_lvl,
|
||||
c->immutable_cf_options()->compaction_style, base_level,
|
||||
c->immutable_cf_options()->level_compaction_dynamic_level_bytes)));
|
||||
MaxFileSizeForLevel(
|
||||
*(c->mutable_cf_options()), out_lvl,
|
||||
c->immutable_options()->compaction_style, base_level,
|
||||
c->immutable_options()->level_compaction_dynamic_level_bytes)));
|
||||
uint64_t subcompactions =
|
||||
std::min({static_cast<uint64_t>(ranges.size()),
|
||||
static_cast<uint64_t>(c->max_subcompactions()),
|
||||
@@ -751,7 +776,7 @@ Status CompactionJob::Run() {
|
||||
for (const auto& state : compact_->sub_compact_states) {
|
||||
for (const auto& output : state.outputs) {
|
||||
auto fn =
|
||||
TableFileName(state.compaction->immutable_cf_options()->cf_paths,
|
||||
TableFileName(state.compaction->immutable_options()->cf_paths,
|
||||
output.meta.fd.GetNumber(), output.meta.fd.GetPathId());
|
||||
tp[fn] = output.table_properties;
|
||||
}
|
||||
@@ -958,7 +983,8 @@ void CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
compaction_input.column_family.name.c_str(), job_id_,
|
||||
compaction_input.output_level, input_files_oss.str().c_str());
|
||||
CompactionServiceJobStatus compaction_status =
|
||||
db_options_.compaction_service->Start(compaction_input_binary, job_id_);
|
||||
db_options_.compaction_service->Start(compaction_input_binary,
|
||||
GetCompactionId(sub_compact));
|
||||
if (compaction_status != CompactionServiceJobStatus::kSuccess) {
|
||||
sub_compact->status =
|
||||
Status::Incomplete("CompactionService failed to start compaction job.");
|
||||
@@ -967,7 +993,7 @@ void CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
|
||||
std::string compaction_result_binary;
|
||||
compaction_status = db_options_.compaction_service->WaitForComplete(
|
||||
job_id_, &compaction_result_binary);
|
||||
GetCompactionId(sub_compact), &compaction_result_binary);
|
||||
|
||||
CompactionServiceResult compaction_result;
|
||||
s = CompactionServiceResult::Read(compaction_result_binary,
|
||||
@@ -1014,7 +1040,7 @@ void CompactionJob::ProcessKeyValueCompactionWithCompactionService(
|
||||
for (const auto& file : compaction_result.output_files) {
|
||||
uint64_t file_num = versions_->NewFileNumber();
|
||||
auto src_file = compaction_result.output_path + "/" + file.file_name;
|
||||
auto tgt_file = TableFileName(compaction->immutable_cf_options()->cf_paths,
|
||||
auto tgt_file = TableFileName(compaction->immutable_options()->cf_paths,
|
||||
file_num, compaction->output_path_id());
|
||||
s = fs_->RenameFile(src_file, tgt_file, IOOptions(), nullptr);
|
||||
if (!s.ok()) {
|
||||
@@ -1084,6 +1110,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
|
||||
CompactionRangeDelAggregator range_del_agg(&cfd->internal_comparator(),
|
||||
existing_snapshots_);
|
||||
|
||||
const Slice* const start = sub_compact->start;
|
||||
const Slice* const end = sub_compact->end;
|
||||
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
read_options.fill_cache = false;
|
||||
@@ -1093,11 +1123,51 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
// (b) CompactionFilter::Decision::kRemoveAndSkipUntil.
|
||||
read_options.total_order_seek = true;
|
||||
|
||||
// Note: if we're going to support subcompactions for user-defined timestamps,
|
||||
// the timestamp part will have to be stripped from the bounds here.
|
||||
assert((!start && !end) || cfd->user_comparator()->timestamp_size() == 0);
|
||||
read_options.iterate_lower_bound = start;
|
||||
read_options.iterate_upper_bound = end;
|
||||
|
||||
// Although the v2 aggregator is what the level iterator(s) know about,
|
||||
// the AddTombstones calls will be propagated down to the v1 aggregator.
|
||||
std::unique_ptr<InternalIterator> input(
|
||||
std::unique_ptr<InternalIterator> raw_input(
|
||||
versions_->MakeInputIterator(read_options, sub_compact->compaction,
|
||||
&range_del_agg, file_options_for_read_));
|
||||
InternalIterator* input = raw_input.get();
|
||||
|
||||
IterKey start_ikey;
|
||||
IterKey end_ikey;
|
||||
Slice start_slice;
|
||||
Slice end_slice;
|
||||
|
||||
if (start) {
|
||||
start_ikey.SetInternalKey(*start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
start_slice = start_ikey.GetInternalKey();
|
||||
}
|
||||
if (end) {
|
||||
end_ikey.SetInternalKey(*end, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
end_slice = end_ikey.GetInternalKey();
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> clip;
|
||||
if (start || end) {
|
||||
clip.reset(new ClippingIterator(
|
||||
raw_input.get(), start ? &start_slice : nullptr,
|
||||
end ? &end_slice : nullptr, &cfd->internal_comparator()));
|
||||
input = clip.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<InternalIterator> blob_counter;
|
||||
|
||||
if (sub_compact->compaction->DoesInputReferenceBlobFiles()) {
|
||||
sub_compact->blob_garbage_meter.reset(new BlobGarbageMeter);
|
||||
blob_counter.reset(
|
||||
new BlobCountingIterator(input, sub_compact->blob_garbage_meter.get()));
|
||||
input = blob_counter.get();
|
||||
}
|
||||
|
||||
input->SeekToFirst();
|
||||
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_COMPACTION_PROCESS_KV);
|
||||
@@ -1138,7 +1208,7 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
std::unique_ptr<BlobFileBuilder> blob_file_builder(
|
||||
mutable_cf_options->enable_blob_files
|
||||
? new BlobFileBuilder(versions_, fs_.get(),
|
||||
sub_compact->compaction->immutable_cf_options(),
|
||||
sub_compact->compaction->immutable_options(),
|
||||
mutable_cf_options, &file_options_, job_id_,
|
||||
cfd->GetID(), cfd->GetName(),
|
||||
Env::IOPriority::IO_LOW, write_hint_,
|
||||
@@ -1152,28 +1222,18 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
reinterpret_cast<void*>(
|
||||
const_cast<std::atomic<int>*>(manual_compaction_paused_)));
|
||||
|
||||
Slice* start = sub_compact->start;
|
||||
Slice* end = sub_compact->end;
|
||||
if (start != nullptr) {
|
||||
IterKey start_iter;
|
||||
start_iter.SetInternalKey(*start, kMaxSequenceNumber, kValueTypeForSeek);
|
||||
input->Seek(start_iter.GetInternalKey());
|
||||
} else {
|
||||
input->SeekToFirst();
|
||||
}
|
||||
|
||||
Status status;
|
||||
const std::string* const full_history_ts_low =
|
||||
full_history_ts_low_.empty() ? nullptr : &full_history_ts_low_;
|
||||
sub_compact->c_iter.reset(new CompactionIterator(
|
||||
input.get(), cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
input, cfd->user_comparator(), &merge, versions_->LastSequence(),
|
||||
&existing_snapshots_, earliest_write_conflict_snapshot_,
|
||||
snapshot_checker_, env_, ShouldReportDetailedTime(env_, stats_),
|
||||
/*expect_valid_internal_key=*/true, &range_del_agg,
|
||||
blob_file_builder.get(), db_options_.allow_data_in_errors,
|
||||
sub_compact->compaction, compaction_filter, shutting_down_,
|
||||
preserve_deletes_seqnum_, manual_compaction_paused_, db_options_.info_log,
|
||||
full_history_ts_low));
|
||||
preserve_deletes_seqnum_, manual_compaction_paused_,
|
||||
manual_compaction_canceled_, db_options_.info_log, full_history_ts_low));
|
||||
auto c_iter = sub_compact->c_iter.get();
|
||||
c_iter->SeekToFirst();
|
||||
if (c_iter->Valid() && sub_compact->compaction->output_level() != 0) {
|
||||
@@ -1197,12 +1257,9 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
const Slice& key = c_iter->key();
|
||||
const Slice& value = c_iter->value();
|
||||
|
||||
// If an end key (exclusive) is specified, check if the current key is
|
||||
// >= than it and exit if it is because the iterator is out of its range
|
||||
if (end != nullptr &&
|
||||
cfd->user_comparator()->Compare(c_iter->user_key(), *end) >= 0) {
|
||||
break;
|
||||
}
|
||||
assert(!end ||
|
||||
cfd->user_comparator()->Compare(c_iter->user_key(), *end) < 0);
|
||||
|
||||
if (c_iter_stats.num_input_records % kRecordStatsEvery ==
|
||||
kRecordStatsEvery - 1) {
|
||||
RecordDroppedKeys(c_iter_stats, &sub_compact->compaction_job_stats);
|
||||
@@ -1222,6 +1279,11 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
break;
|
||||
}
|
||||
|
||||
status = sub_compact->ProcessOutFlowIfNeeded(key, value);
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
sub_compact->current_output_file_size =
|
||||
sub_compact->builder->EstimatedFileSize();
|
||||
const ParsedInternalKey& ikey = c_iter->ikey();
|
||||
@@ -1317,8 +1379,10 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
status = Status::ShutdownInProgress("Database shutdown");
|
||||
}
|
||||
if ((status.ok() || status.IsColumnFamilyDropped()) &&
|
||||
(manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0)) {
|
||||
((manual_compaction_paused_ &&
|
||||
manual_compaction_paused_->load(std::memory_order_relaxed) > 0) ||
|
||||
(manual_compaction_canceled_ &&
|
||||
manual_compaction_canceled_->load(std::memory_order_relaxed)))) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
if (status.ok()) {
|
||||
@@ -1387,10 +1451,16 @@ void CompactionJob::ProcessKeyValueCompaction(SubcompactionState* sub_compact) {
|
||||
#endif // ROCKSDB_ASSERT_STATUS_CHECKED
|
||||
|
||||
sub_compact->c_iter.reset();
|
||||
input.reset();
|
||||
blob_counter.reset();
|
||||
clip.reset();
|
||||
raw_input.reset();
|
||||
sub_compact->status = status;
|
||||
}
|
||||
|
||||
uint64_t CompactionJob::GetCompactionId(SubcompactionState* sub_compact) {
|
||||
return (uint64_t)job_id_ << 32 | sub_compact->sub_job_id;
|
||||
}
|
||||
|
||||
void CompactionJob::RecordDroppedKeys(
|
||||
const CompactionIterationStats& c_iter_stats,
|
||||
CompactionJobStats* compaction_job_stats) {
|
||||
@@ -1670,7 +1740,7 @@ Status CompactionJob::FinishCompactionOutputFile(
|
||||
// This happens when the output level is bottom level, at the same time
|
||||
// the sub_compact output nothing.
|
||||
std::string fname =
|
||||
TableFileName(sub_compact->compaction->immutable_cf_options()->cf_paths,
|
||||
TableFileName(sub_compact->compaction->immutable_options()->cf_paths,
|
||||
meta->fd.GetNumber(), meta->fd.GetPathId());
|
||||
|
||||
// TODO(AR) it is not clear if there are any larger implications if
|
||||
@@ -1755,19 +1825,6 @@ Status CompactionJob::InstallCompactionResults(
|
||||
auto* compaction = compact_->compaction;
|
||||
assert(compaction);
|
||||
|
||||
// paranoia: verify that the files that we started with
|
||||
// still exist in the current version and in the same original level.
|
||||
// This ensures that a concurrent compaction did not erroneously
|
||||
// pick the same files to compact_.
|
||||
if (!versions_->VerifyCompactionFileConsistency(compaction)) {
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
|
||||
ROCKS_LOG_ERROR(db_options_.info_log, "[%s] [JOB %d] Compaction %s aborted",
|
||||
compaction->column_family_data()->GetName().c_str(),
|
||||
job_id_, compaction->InputLevelSummary(&inputs_summary));
|
||||
return Status::Corruption("Compaction input files inconsistent");
|
||||
}
|
||||
|
||||
{
|
||||
Compaction::InputLevelSummaryBuffer inputs_summary;
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
@@ -1783,6 +1840,8 @@ Status CompactionJob::InstallCompactionResults(
|
||||
// Add compaction inputs
|
||||
compaction->AddInputDeletions(edit);
|
||||
|
||||
std::unordered_map<uint64_t, BlobGarbageMeter::BlobStats> blob_total_garbage;
|
||||
|
||||
for (const auto& sub_compact : compact_->sub_compact_states) {
|
||||
for (const auto& out : sub_compact.outputs) {
|
||||
edit->AddFile(compaction->output_level(), out.meta);
|
||||
@@ -1791,6 +1850,29 @@ Status CompactionJob::InstallCompactionResults(
|
||||
for (const auto& blob : sub_compact.blob_file_additions) {
|
||||
edit->AddBlobFile(blob);
|
||||
}
|
||||
|
||||
if (sub_compact.blob_garbage_meter) {
|
||||
const auto& flows = sub_compact.blob_garbage_meter->flows();
|
||||
|
||||
for (const auto& pair : flows) {
|
||||
const uint64_t blob_file_number = pair.first;
|
||||
const BlobGarbageMeter::BlobInOutFlow& flow = pair.second;
|
||||
|
||||
assert(flow.IsValid());
|
||||
if (flow.HasGarbage()) {
|
||||
blob_total_garbage[blob_file_number].Add(flow.GetGarbageCount(),
|
||||
flow.GetGarbageBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& pair : blob_total_garbage) {
|
||||
const uint64_t blob_file_number = pair.first;
|
||||
const BlobGarbageMeter::BlobStats& stats = pair.second;
|
||||
|
||||
edit->AddBlobFileGarbage(blob_file_number, stats.GetCount(),
|
||||
stats.GetBytes());
|
||||
}
|
||||
|
||||
return versions_->LogAndApply(compaction->column_family_data(),
|
||||
@@ -1914,12 +1996,12 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
writable_file->SetPreallocationBlockSize(static_cast<size_t>(
|
||||
sub_compact->compaction->OutputFilePreallocationSize()));
|
||||
const auto& listeners =
|
||||
sub_compact->compaction->immutable_cf_options()->listeners;
|
||||
sub_compact->compaction->immutable_options()->listeners;
|
||||
sub_compact->outfile.reset(new WritableFileWriter(
|
||||
std::move(writable_file), fname, file_options_, db_options_.clock,
|
||||
io_tracer_, db_options_.stats, listeners,
|
||||
db_options_.file_checksum_gen_factory.get(),
|
||||
tmp_set.Contains(FileType::kTableFile)));
|
||||
tmp_set.Contains(FileType::kTableFile), false));
|
||||
|
||||
TableBuilderOptions tboptions(
|
||||
*cfd->ioptions(), *(sub_compact->compaction->mutable_cf_options()),
|
||||
@@ -1929,7 +2011,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
cfd->GetName(), sub_compact->compaction->output_level(),
|
||||
bottommost_level_, TableFileCreationReason::kCompaction,
|
||||
oldest_ancester_time, 0 /* oldest_key_time */, current_time, db_id_,
|
||||
db_session_id_, sub_compact->compaction->max_output_file_size());
|
||||
db_session_id_, sub_compact->compaction->max_output_file_size(),
|
||||
file_number);
|
||||
sub_compact->builder.reset(
|
||||
NewTableBuilder(tboptions, sub_compact->outfile.get()));
|
||||
LogFlush(db_options_.info_log);
|
||||
@@ -2095,7 +2178,7 @@ void CompactionJob::LogCompaction() {
|
||||
}
|
||||
|
||||
std::string CompactionJob::GetTableFileName(uint64_t file_number) {
|
||||
return TableFileName(compact_->compaction->immutable_cf_options()->cf_paths,
|
||||
return TableFileName(compact_->compaction->immutable_options()->cf_paths,
|
||||
file_number, compact_->compaction->output_path_id());
|
||||
}
|
||||
|
||||
@@ -2126,7 +2209,7 @@ CompactionServiceCompactionJob::CompactionServiceCompactionJob(
|
||||
compaction->mutable_cf_options()->paranoid_file_checks,
|
||||
compaction->mutable_cf_options()->report_bg_io_stats, dbname,
|
||||
&(compaction_service_result->stats), Env::Priority::USER, io_tracer,
|
||||
nullptr, db_id, db_session_id,
|
||||
nullptr, nullptr, db_id, db_session_id,
|
||||
compaction->column_family_data()->GetFullHistoryTsLow()),
|
||||
output_path_(output_path),
|
||||
compaction_input_(compaction_service_input),
|
||||
@@ -2149,8 +2232,8 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
Slice end = compaction_input_.end;
|
||||
compact_->sub_compact_states.emplace_back(
|
||||
c, compaction_input_.has_begin ? &begin : nullptr,
|
||||
compaction_input_.has_end ? &end : nullptr,
|
||||
compaction_input_.approx_size);
|
||||
compaction_input_.has_end ? &end : nullptr, compaction_input_.approx_size,
|
||||
/*sub_job_id*/ 0);
|
||||
|
||||
log_buffer_->FlushBufferToLog();
|
||||
LogCompaction();
|
||||
@@ -2189,7 +2272,7 @@ Status CompactionServiceCompactionJob::Run() {
|
||||
status = io_s;
|
||||
}
|
||||
if (status.ok()) {
|
||||
// TODO: Add verify_table() and VerifyCompactionFileConsistency()
|
||||
// TODO: Add verify_table()
|
||||
}
|
||||
|
||||
// Finish up all book-keeping to unify the subcompaction results
|
||||
|
||||
@@ -81,6 +81,7 @@ class CompactionJob {
|
||||
const std::string& dbname, CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri, const std::shared_ptr<IOTracer>& io_tracer,
|
||||
const std::atomic<int>* manual_compaction_paused = nullptr,
|
||||
const std::atomic<bool>* manual_compaction_canceled = nullptr,
|
||||
const std::string& db_id = "", const std::string& db_session_id = "",
|
||||
std::string full_history_ts_low = "",
|
||||
BlobFileCompletionCallback* blob_callback = nullptr);
|
||||
@@ -167,7 +168,7 @@ class CompactionJob {
|
||||
void UpdateCompactionInputStatsHelper(
|
||||
int* num_files, uint64_t* bytes_read, int input_level);
|
||||
|
||||
int job_id_;
|
||||
uint32_t job_id_;
|
||||
|
||||
CompactionJobStats* compaction_job_stats_;
|
||||
|
||||
@@ -185,6 +186,7 @@ class CompactionJob {
|
||||
VersionSet* versions_;
|
||||
const std::atomic<bool>* shutting_down_;
|
||||
const std::atomic<int>* manual_compaction_paused_;
|
||||
const std::atomic<bool>* manual_compaction_canceled_;
|
||||
const SequenceNumber preserve_deletes_seqnum_;
|
||||
FSDirectory* db_directory_;
|
||||
FSDirectory* blob_output_directory_;
|
||||
@@ -217,6 +219,8 @@ class CompactionJob {
|
||||
std::string full_history_ts_low_;
|
||||
BlobFileCompletionCallback* blob_callback_;
|
||||
|
||||
uint64_t GetCompactionId(SubcompactionState* sub_compact);
|
||||
|
||||
// Get table file name in where it's outputting to, which should also be in
|
||||
// `output_directory_`.
|
||||
virtual std::string GetTableFileName(uint64_t file_number);
|
||||
|
||||
@@ -82,10 +82,11 @@ class CompactionJobTestBase : public testing::Test {
|
||||
mutable_db_options_(),
|
||||
table_cache_(NewLRUCache(50000, 16)),
|
||||
write_buffer_manager_(db_options_.db_write_buffer_size),
|
||||
versions_(new VersionSet(
|
||||
dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr)),
|
||||
versions_(new VersionSet(dbname_, &db_options_, env_options_,
|
||||
table_cache_.get(), &write_buffer_manager_,
|
||||
&write_controller_,
|
||||
/*block_cache_tracer=*/nullptr,
|
||||
/*io_tracer=*/nullptr, /*db_session_id*/ "")),
|
||||
shutting_down_(false),
|
||||
preserve_deletes_seqnum_(0),
|
||||
mock_table_factory_(new mock::MockTableFactory()),
|
||||
@@ -269,7 +270,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr));
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
/*db_session_id*/ ""));
|
||||
compaction_job_stats_.Reset();
|
||||
ASSERT_OK(SetIdentityFile(env_, dbname_));
|
||||
|
||||
@@ -350,7 +352,8 @@ class CompactionJobTestBase : public testing::Test {
|
||||
earliest_write_conflict_snapshot, snapshot_checker, table_cache_,
|
||||
&event_logger, false, false, dbname_, &compaction_job_stats_,
|
||||
Env::Priority::USER, nullptr /* IOTracer */,
|
||||
/*manual_compaction_paused=*/nullptr, /*db_id=*/"",
|
||||
/*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, /*db_id=*/"",
|
||||
/*db_session_id=*/"", full_history_ts_low_);
|
||||
VerifyInitializationOfCompactionJobStats(compaction_job_stats_);
|
||||
|
||||
|
||||
@@ -12,12 +12,16 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
class MyTestCompactionService : public CompactionService {
|
||||
public:
|
||||
MyTestCompactionService(const std::string& db_path,
|
||||
std::shared_ptr<FileSystem> fs, Options& options)
|
||||
: db_path_(db_path), fs_(fs), options_(options) {}
|
||||
MyTestCompactionService(const std::string& db_path, Options& options,
|
||||
std::shared_ptr<Statistics> statistics = nullptr)
|
||||
: db_path_(db_path), options_(options), statistics_(statistics) {}
|
||||
|
||||
static const char* kClassName() { return "MyTestCompactionService"; }
|
||||
|
||||
const char* Name() const override { return kClassName(); }
|
||||
|
||||
CompactionServiceJobStatus Start(const std::string& compaction_service_input,
|
||||
int job_id) override {
|
||||
uint64_t job_id) override {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
jobs_.emplace(job_id, compaction_service_input);
|
||||
CompactionServiceJobStatus s = CompactionServiceJobStatus::kSuccess;
|
||||
@@ -26,7 +30,7 @@ class MyTestCompactionService : public CompactionService {
|
||||
}
|
||||
|
||||
CompactionServiceJobStatus WaitForComplete(
|
||||
int job_id, std::string* compaction_service_result) override {
|
||||
uint64_t job_id, std::string* compaction_service_result) override {
|
||||
std::string compaction_input;
|
||||
{
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -50,10 +54,11 @@ class MyTestCompactionService : public CompactionService {
|
||||
options_override.prefix_extractor = options_.prefix_extractor;
|
||||
options_override.table_factory = options_.table_factory;
|
||||
options_override.sst_partitioner_factory = options_.sst_partitioner_factory;
|
||||
options_override.statistics = statistics_;
|
||||
|
||||
Status s = DB::OpenAndCompact(db_path_, db_path_ + "/" + ToString(job_id),
|
||||
compaction_input, compaction_service_result,
|
||||
options_override);
|
||||
Status s = DB::OpenAndCompact(
|
||||
db_path_, db_path_ + "/" + ROCKSDB_NAMESPACE::ToString(job_id),
|
||||
compaction_input, compaction_service_result, options_override);
|
||||
TEST_SYNC_POINT_CALLBACK("MyTestCompactionService::WaitForComplete::End",
|
||||
compaction_service_result);
|
||||
compaction_num_.fetch_add(1);
|
||||
@@ -69,10 +74,10 @@ class MyTestCompactionService : public CompactionService {
|
||||
private:
|
||||
InstrumentedMutex mutex_;
|
||||
std::atomic_int compaction_num_{0};
|
||||
std::map<int, std::string> jobs_;
|
||||
std::map<uint64_t, std::string> jobs_;
|
||||
const std::string db_path_;
|
||||
std::shared_ptr<FileSystem> fs_;
|
||||
Options options_;
|
||||
std::shared_ptr<Statistics> statistics_;
|
||||
};
|
||||
|
||||
class CompactionServiceTest : public DBTestBase {
|
||||
@@ -119,8 +124,10 @@ class CompactionServiceTest : public DBTestBase {
|
||||
TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
std::shared_ptr<Statistics> compactor_statistics = CreateDBStatistics();
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
dbname_, options, compactor_statistics);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -154,6 +161,12 @@ TEST_F(CompactionServiceTest, BasicCompactions) {
|
||||
dynamic_cast<MyTestCompactionService*>(options.compaction_service.get());
|
||||
ASSERT_GE(my_cs->GetCompactionNum(), 1);
|
||||
|
||||
// make sure the compaction statistics is only recorded on remote side
|
||||
ASSERT_GE(
|
||||
compactor_statistics->getTickerCount(COMPACTION_KEY_DROP_NEWER_ENTRY), 1);
|
||||
ASSERT_EQ(options.statistics->getTickerCount(COMPACTION_KEY_DROP_NEWER_ENTRY),
|
||||
0);
|
||||
|
||||
// Test failed compaction
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImplSecondary::CompactWithoutInstallation::End", [&](void* status) {
|
||||
@@ -191,8 +204,8 @@ TEST_F(CompactionServiceTest, ManualCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
DestroyAndReopen(options);
|
||||
GenerateTestData();
|
||||
|
||||
@@ -232,8 +245,8 @@ TEST_F(CompactionServiceTest, FailedToStart) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
DestroyAndReopen(options);
|
||||
GenerateTestData();
|
||||
|
||||
@@ -257,8 +270,8 @@ TEST_F(CompactionServiceTest, InvalidResult) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.disable_auto_compactions = true;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
DestroyAndReopen(options);
|
||||
GenerateTestData();
|
||||
|
||||
@@ -278,22 +291,31 @@ TEST_F(CompactionServiceTest, InvalidResult) {
|
||||
ASSERT_FALSE(s.ok());
|
||||
}
|
||||
|
||||
// TODO: support sub-compaction
|
||||
TEST_F(CompactionServiceTest, DISABLED_SubCompaction) {
|
||||
TEST_F(CompactionServiceTest, SubCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.max_subcompactions = 10;
|
||||
options.target_file_size_base = 1 << 10; // 1KB
|
||||
options.disable_auto_compactions = true;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
GenerateTestData();
|
||||
VerifyTestData();
|
||||
|
||||
auto my_cs =
|
||||
dynamic_cast<MyTestCompactionService*>(options.compaction_service.get());
|
||||
int compaction_num_before = my_cs->GetCompactionNum();
|
||||
|
||||
auto cro = CompactRangeOptions();
|
||||
cro.max_subcompactions = 10;
|
||||
db_->CompactRange(cro, nullptr, nullptr);
|
||||
Status s = db_->CompactRange(cro, nullptr, nullptr);
|
||||
ASSERT_OK(s);
|
||||
VerifyTestData();
|
||||
int compaction_num = my_cs->GetCompactionNum() - compaction_num_before;
|
||||
// make sure there's sub-compaction by checking the compaction number
|
||||
ASSERT_GE(compaction_num, 2);
|
||||
}
|
||||
|
||||
class PartialDeleteCompactionFilter : public CompactionFilter {
|
||||
@@ -317,8 +339,8 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
options.env = env_;
|
||||
auto delete_comp_filter = PartialDeleteCompactionFilter();
|
||||
options.compaction_filter = &delete_comp_filter;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -360,8 +382,8 @@ TEST_F(CompactionServiceTest, CompactionFilter) {
|
||||
TEST_F(CompactionServiceTest, Snapshot) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -387,8 +409,8 @@ TEST_F(CompactionServiceTest, ConcurrentCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 100;
|
||||
options.env = env_;
|
||||
options.compaction_service = std::make_shared<MyTestCompactionService>(
|
||||
dbname_, env_->GetFileSystem(), options);
|
||||
options.compaction_service =
|
||||
std::make_shared<MyTestCompactionService>(dbname_, options);
|
||||
options.max_background_jobs = 20;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
@@ -55,14 +55,9 @@ class CorruptionTest : public testing::Test {
|
||||
// bug in recovery code. Keep it 4 for now to make the test passes.
|
||||
tiny_cache_ = NewLRUCache(100, 4);
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
const char* test_env_uri = getenv("TEST_ENV_URI");
|
||||
if (test_env_uri) {
|
||||
Status s = Env::LoadEnv(test_env_uri, &base_env, &env_guard_);
|
||||
EXPECT_OK(s);
|
||||
EXPECT_NE(Env::Default(), base_env);
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
EXPECT_OK(
|
||||
test::CreateEnvFromSystem(ConfigOptions(), &base_env, &env_guard_));
|
||||
EXPECT_NE(base_env, nullptr);
|
||||
env_ = new test::ErrorEnv(base_env);
|
||||
options_.wal_recovery_mode = WALRecoveryMode::kTolerateCorruptedTailRecords;
|
||||
options_.env = env_;
|
||||
|
||||
@@ -1362,6 +1362,28 @@ TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFSnapshot) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(DBMultiGetTestWithParam, MultiGetMultiCFUnsorted) {
|
||||
Options options = CurrentOptions();
|
||||
CreateAndReopenWithCF({"one", "two"}, options);
|
||||
|
||||
ASSERT_OK(Put(1, "foo", "bar"));
|
||||
ASSERT_OK(Put(2, "baz", "xyz"));
|
||||
ASSERT_OK(Put(1, "abc", "def"));
|
||||
|
||||
// Note: keys for the same CF do not form a consecutive range
|
||||
std::vector<int> cfs{1, 2, 1};
|
||||
std::vector<std::string> keys{"foo", "baz", "abc"};
|
||||
std::vector<std::string> values;
|
||||
|
||||
values =
|
||||
MultiGet(cfs, keys, /* snapshot */ nullptr, /* batched */ GetParam());
|
||||
|
||||
ASSERT_EQ(values.size(), 3);
|
||||
ASSERT_EQ(values[0], "bar");
|
||||
ASSERT_EQ(values[1], "xyz");
|
||||
ASSERT_EQ(values[2], "def");
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(DBMultiGetTestWithParam, DBMultiGetTestWithParam,
|
||||
testing::Bool());
|
||||
|
||||
|
||||
+63
-25
@@ -257,12 +257,28 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
|
||||
#ifdef SNAPPY
|
||||
TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
ReadOptions read_options;
|
||||
auto table_options = GetTableOptions();
|
||||
auto options = GetOptions(table_options);
|
||||
options.compression = CompressionType::kSnappyCompression;
|
||||
InitTable(options);
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.no_block_cache = true;
|
||||
table_options.block_cache_compressed = nullptr;
|
||||
table_options.block_size = 1;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(20));
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.compression = CompressionType::kSnappyCompression;
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 0; i < kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
}
|
||||
|
||||
ReadOptions read_options;
|
||||
std::shared_ptr<Cache> compressed_cache = NewLRUCache(1 << 25, 0, false);
|
||||
LRUCacheOptions co;
|
||||
co.capacity = 0;
|
||||
@@ -272,25 +288,23 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(co);
|
||||
table_options.block_cache = cache;
|
||||
table_options.no_block_cache = false;
|
||||
table_options.block_cache_compressed = compressed_cache;
|
||||
table_options.max_auto_readahead_size = 0;
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
Reopen(options);
|
||||
RecordCacheCounters(options);
|
||||
|
||||
std::vector<std::unique_ptr<Iterator>> iterators(kNumBlocks - 1);
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
CheckCacheCounters(options, 1, 0, 1, 0);
|
||||
CheckCompressedCacheCounters(options, 1, 0, 1, 0);
|
||||
iterators[i].reset(iter);
|
||||
}
|
||||
|
||||
size_t usage = cache->GetUsage();
|
||||
ASSERT_LT(0, usage);
|
||||
ASSERT_EQ(0, usage);
|
||||
ASSERT_EQ(usage, cache->GetPinnedUsage());
|
||||
size_t compressed_usage = compressed_cache->GetUsage();
|
||||
ASSERT_LT(0, compressed_usage);
|
||||
@@ -302,24 +316,21 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
cache->SetCapacity(usage);
|
||||
cache->SetStrictCapacityLimit(true);
|
||||
ASSERT_EQ(usage, cache->GetPinnedUsage());
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(kNumBlocks - 1));
|
||||
ASSERT_TRUE(iter->status().IsIncomplete());
|
||||
|
||||
// Load last key block.
|
||||
ASSERT_EQ("Result incomplete: Insert failed due to LRU cache being full.",
|
||||
Get(ToString(kNumBlocks - 1)));
|
||||
// Failure will also record the miss counter.
|
||||
CheckCacheCounters(options, 1, 0, 0, 1);
|
||||
CheckCompressedCacheCounters(options, 1, 0, 1, 0);
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
|
||||
// Clear strict capacity limit flag. This time we shall hit compressed block
|
||||
// cache.
|
||||
// cache and load into block cache.
|
||||
cache->SetStrictCapacityLimit(false);
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(kNumBlocks - 1));
|
||||
ASSERT_OK(iter->status());
|
||||
// Load last key block.
|
||||
ASSERT_EQ(value, Get(ToString(kNumBlocks - 1)));
|
||||
CheckCacheCounters(options, 1, 0, 1, 0);
|
||||
CheckCompressedCacheCounters(options, 0, 1, 0, 0);
|
||||
delete iter;
|
||||
iter = nullptr;
|
||||
}
|
||||
#endif // SNAPPY
|
||||
|
||||
@@ -467,6 +478,33 @@ TEST_F(DBBlockCacheTest, IndexAndFilterBlocksStats) {
|
||||
// filter_bytes_insert);
|
||||
}
|
||||
|
||||
#if (defined OS_LINUX || defined OS_WIN)
|
||||
TEST_F(DBBlockCacheTest, WarmCacheWithDataBlocksDuringFlush) {
|
||||
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);
|
||||
table_options.cache_index_and_filter_blocks = false;
|
||||
table_options.prepopulate_block_cache =
|
||||
BlockBasedTableOptions::PrepopulateBlockCache::kFlushOnly;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
std::string value(kValueSize, 'a');
|
||||
for (size_t i = 1; i <= kNumBlocks; i++) {
|
||||
ASSERT_OK(Put(ToString(i), value));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_ADD));
|
||||
|
||||
ASSERT_EQ(value, Get(ToString(i)));
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_DATA_MISS));
|
||||
ASSERT_EQ(i, options.statistics->getTickerCount(BLOCK_CACHE_DATA_HIT));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// A mock cache wraps LRUCache, and record how many entries have been
|
||||
|
||||
@@ -527,7 +527,7 @@ TEST_P(DBBloomFilterTestWithParam, BloomFilter) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
FormatDef, DBBloomFilterTestDefFormatVersion,
|
||||
::testing::Values(
|
||||
@@ -551,7 +551,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
test::kLatestFormatVersion),
|
||||
std::make_tuple(BFP::kAutoBloom, true, test::kLatestFormatVersion),
|
||||
std::make_tuple(BFP::kAutoBloom, false, test::kLatestFormatVersion)));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_F(DBBloomFilterTest, BloomFilterRate) {
|
||||
while (ChangeFilterOptions()) {
|
||||
|
||||
@@ -46,7 +46,7 @@ class DBTestCompactionFilterWithCompactParam
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
CompactionFilterWithOption, DBTestCompactionFilterWithCompactParam,
|
||||
::testing::Values(DBTestBase::OptionConfig::kDefault,
|
||||
@@ -55,11 +55,11 @@ INSTANTIATE_TEST_CASE_P(
|
||||
DBTestBase::OptionConfig::kLevelSubcompactions,
|
||||
DBTestBase::OptionConfig::kUniversalSubcompactions));
|
||||
#else
|
||||
// Run fewer cases in valgrind
|
||||
// Run fewer cases in non-full valgrind to save time.
|
||||
INSTANTIATE_TEST_CASE_P(CompactionFilterWithOption,
|
||||
DBTestCompactionFilterWithCompactParam,
|
||||
::testing::Values(DBTestBase::OptionConfig::kDefault));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
class KeepFilter : public CompactionFilter {
|
||||
public:
|
||||
|
||||
@@ -301,7 +301,7 @@ const SstFileMetaData* PickFileRandomly(
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
// All the TEST_P tests run once with sub_compactions disabled (i.e.
|
||||
// options.max_subcompactions = 1) and once with it enabled
|
||||
TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
@@ -366,7 +366,7 @@ TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, CompactionsPreserveDeletes) {
|
||||
// For each options type we test following
|
||||
@@ -3600,6 +3600,41 @@ TEST_F(DBCompactionTest, CompactFilesOverlapInL0Bug) {
|
||||
ASSERT_EQ("new_val", Get(Key(0)));
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, DeleteFilesInRangeConflictWithCompaction) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
const Snapshot* snapshot = nullptr;
|
||||
const int kMaxKey = 10;
|
||||
|
||||
for (int i = 0; i < kMaxKey; i++) {
|
||||
ASSERT_OK(Put(Key(i), Key(i)));
|
||||
ASSERT_OK(Delete(Key(i)));
|
||||
if (!snapshot) {
|
||||
snapshot = db_->GetSnapshot();
|
||||
}
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Put(Key(kMaxKey), Key(kMaxKey)));
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// test DeleteFilesInRange() deletes the files already picked for compaction
|
||||
SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"VersionSet::LogAndApply:WriteManifestStart",
|
||||
"BackgroundCallCompaction:0"},
|
||||
{"DBImpl::BackgroundCompaction:Finish",
|
||||
"VersionSet::LogAndApply:WriteManifestDone"}});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// release snapshot which mark bottommost file for compaction
|
||||
db_->ReleaseSnapshot(snapshot);
|
||||
std::string begin_string = Key(0);
|
||||
std::string end_string = Key(kMaxKey + 1);
|
||||
Slice begin(begin_string);
|
||||
Slice end(end_string);
|
||||
ASSERT_OK(DeleteFilesInRange(db_, db_->DefaultColumnFamily(), &begin, &end));
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
|
||||
// bottom-level files may contain deletions due to snapshots protecting the
|
||||
// deleted keys. Once the snapshot is released, we should see files with many
|
||||
@@ -5208,11 +5243,12 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
|
||||
ASSERT_TRUE(num_compactions.load() == 1);
|
||||
|
||||
// split the compaction to 5
|
||||
uint64_t total = (l1_avg_size * 10) + (l2_avg_size * 100);
|
||||
int num_split = 5;
|
||||
opts.max_compaction_bytes = total / num_split;
|
||||
DestroyAndReopen(opts);
|
||||
generate_sst_func();
|
||||
uint64_t total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
|
||||
opts.max_compaction_bytes = total_size / num_split;
|
||||
Reopen(opts);
|
||||
num_compactions.store(0);
|
||||
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
|
||||
ASSERT_TRUE(num_compactions.load() == num_split);
|
||||
@@ -5230,8 +5266,9 @@ TEST_F(DBCompactionTest, ManualCompactionMax) {
|
||||
opts.max_compaction_bytes = 0;
|
||||
DestroyAndReopen(opts);
|
||||
generate_sst_func();
|
||||
total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
|
||||
Status s = db_->SetOptions(
|
||||
{{"max_compaction_bytes", std::to_string(total / num_split)}});
|
||||
{{"max_compaction_bytes", std::to_string(total_size / num_split)}});
|
||||
ASSERT_OK(s);
|
||||
|
||||
num_compactions.store(0);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+96
-37
@@ -252,16 +252,17 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
co.num_shard_bits = immutable_db_options_.table_cache_numshardbits;
|
||||
co.metadata_charge_policy = kDontChargeCacheMetadata;
|
||||
table_cache_ = NewLRUCache(co);
|
||||
SetDbSessionId();
|
||||
assert(!db_session_id_.empty());
|
||||
|
||||
versions_.reset(new VersionSet(dbname_, &immutable_db_options_, file_options_,
|
||||
table_cache_.get(), write_buffer_manager_,
|
||||
&write_controller_, &block_cache_tracer_,
|
||||
io_tracer_));
|
||||
io_tracer_, db_session_id_));
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
|
||||
DumpRocksDBBuildVersion(immutable_db_options_.info_log.get());
|
||||
SetDbSessionId();
|
||||
DumpDBFileSummary(immutable_db_options_, dbname_, db_session_id_);
|
||||
immutable_db_options_.Dump(immutable_db_options_.info_log.get());
|
||||
mutable_db_options_.Dump(immutable_db_options_.info_log.get());
|
||||
@@ -548,12 +549,45 @@ Status DBImpl::CloseHelper() {
|
||||
flush_scheduler_.Clear();
|
||||
trim_history_scheduler_.Clear();
|
||||
|
||||
// For now, simply trigger a manual flush at close time
|
||||
// on all the column families.
|
||||
// TODO(bjlemaire): Check if this is needed. Also, in the
|
||||
// future we can contemplate doing a more fine-grained
|
||||
// flushing by first checking if there is a need for
|
||||
// flushing (but need to implement something
|
||||
// else than imm()->IsFlushPending() because the output
|
||||
// memtables added to imm() dont trigger flushes).
|
||||
if (immutable_db_options_.experimental_allow_mempurge) {
|
||||
Status flush_ret;
|
||||
mutex_.Unlock();
|
||||
for (ColumnFamilyData* cf : *versions_->GetColumnFamilySet()) {
|
||||
if (immutable_db_options_.atomic_flush) {
|
||||
flush_ret = AtomicFlushMemTables({cf}, FlushOptions(),
|
||||
FlushReason::kManualFlush);
|
||||
if (!flush_ret.ok()) {
|
||||
ROCKS_LOG_INFO(
|
||||
immutable_db_options_.info_log,
|
||||
"Atomic flush memtables failed upon closing (mempurge).");
|
||||
}
|
||||
} else {
|
||||
flush_ret =
|
||||
FlushMemTable(cf, FlushOptions(), FlushReason::kManualFlush);
|
||||
if (!flush_ret.ok()) {
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
"Flush memtables failed upon closing (mempurge).");
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_.Lock();
|
||||
}
|
||||
|
||||
while (!flush_queue_.empty()) {
|
||||
const FlushRequest& flush_req = PopFirstFromFlushQueue();
|
||||
for (const auto& iter : flush_req) {
|
||||
iter.first->UnrefAndTryDelete();
|
||||
}
|
||||
}
|
||||
|
||||
while (!compaction_queue_.empty()) {
|
||||
auto cfd = PopFirstFromCompactionQueue();
|
||||
cfd->UnrefAndTryDelete();
|
||||
@@ -1894,6 +1928,16 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
if (tracer_) {
|
||||
// TODO: This mutex should be removed later, to improve performance when
|
||||
// tracing is enabled.
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->MultiGet(column_family, keys).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
SequenceNumber consistent_seqnum;
|
||||
|
||||
std::unordered_map<uint32_t, MultiGetColumnFamilyData> multiget_cf_data(
|
||||
@@ -2205,6 +2249,16 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
if (tracer_) {
|
||||
// TODO: This mutex should be removed later, to improve performance when
|
||||
// tracing is enabled.
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->MultiGet(num_keys, column_families, keys).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
|
||||
autovector<KeyContext, MultiGetContext::MAX_BATCH_SIZE> key_context;
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
@@ -2222,20 +2276,18 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
multiget_cf_data;
|
||||
size_t cf_start = 0;
|
||||
ColumnFamilyHandle* cf = sorted_keys[0]->column_family;
|
||||
|
||||
for (size_t i = 0; i < num_keys; ++i) {
|
||||
KeyContext* key_ctx = sorted_keys[i];
|
||||
if (key_ctx->column_family != cf) {
|
||||
multiget_cf_data.emplace_back(
|
||||
MultiGetColumnFamilyData(cf, cf_start, i - cf_start, nullptr));
|
||||
multiget_cf_data.emplace_back(cf, cf_start, i - cf_start, nullptr);
|
||||
cf_start = i;
|
||||
cf = key_ctx->column_family;
|
||||
}
|
||||
}
|
||||
{
|
||||
// multiget_cf_data.emplace_back(
|
||||
// MultiGetColumnFamilyData(cf, cf_start, num_keys - cf_start, nullptr));
|
||||
multiget_cf_data.emplace_back(cf, cf_start, num_keys - cf_start, nullptr);
|
||||
}
|
||||
|
||||
multiget_cf_data.emplace_back(cf, cf_start, num_keys - cf_start, nullptr);
|
||||
|
||||
std::function<MultiGetColumnFamilyData*(
|
||||
autovector<MultiGetColumnFamilyData,
|
||||
MultiGetContext::MAX_BATCH_SIZE>::iterator&)>
|
||||
@@ -2295,7 +2347,7 @@ struct CompareKeyContext {
|
||||
static_cast<ColumnFamilyHandleImpl*>(lhs->column_family);
|
||||
uint32_t cfd_id1 = cfh->cfd()->GetID();
|
||||
const Comparator* comparator = cfh->cfd()->user_comparator();
|
||||
cfh = static_cast<ColumnFamilyHandleImpl*>(lhs->column_family);
|
||||
cfh = static_cast<ColumnFamilyHandleImpl*>(rhs->column_family);
|
||||
uint32_t cfd_id2 = cfh->cfd()->GetID();
|
||||
|
||||
if (cfd_id1 < cfd_id2) {
|
||||
@@ -2319,39 +2371,24 @@ struct CompareKeyContext {
|
||||
void DBImpl::PrepareMultiGetKeys(
|
||||
size_t num_keys, bool sorted_input,
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE>* sorted_keys) {
|
||||
#ifndef NDEBUG
|
||||
if (sorted_input) {
|
||||
for (size_t index = 0; index < sorted_keys->size(); ++index) {
|
||||
if (index > 0) {
|
||||
KeyContext* lhs = (*sorted_keys)[index - 1];
|
||||
KeyContext* rhs = (*sorted_keys)[index];
|
||||
ColumnFamilyHandleImpl* cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(lhs->column_family);
|
||||
uint32_t cfd_id1 = cfh->cfd()->GetID();
|
||||
const Comparator* comparator = cfh->cfd()->user_comparator();
|
||||
cfh =
|
||||
static_cast_with_check<ColumnFamilyHandleImpl>(lhs->column_family);
|
||||
uint32_t cfd_id2 = cfh->cfd()->GetID();
|
||||
#ifndef NDEBUG
|
||||
CompareKeyContext key_context_less;
|
||||
|
||||
assert(cfd_id1 <= cfd_id2);
|
||||
if (cfd_id1 < cfd_id2) {
|
||||
continue;
|
||||
}
|
||||
for (size_t index = 1; index < sorted_keys->size(); ++index) {
|
||||
const KeyContext* const lhs = (*sorted_keys)[index - 1];
|
||||
const KeyContext* const rhs = (*sorted_keys)[index];
|
||||
|
||||
// Both keys are from the same column family
|
||||
int cmp = comparator->CompareWithoutTimestamp(
|
||||
*(lhs->key), /*a_has_ts=*/false, *(rhs->key), /*b_has_ts=*/false);
|
||||
assert(cmp <= 0);
|
||||
}
|
||||
index++;
|
||||
// lhs should be <= rhs, or in other words, rhs should NOT be < lhs
|
||||
assert(!key_context_less(rhs, lhs));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!sorted_input) {
|
||||
CompareKeyContext sort_comparator;
|
||||
std::sort(sorted_keys->begin(), sorted_keys->begin() + num_keys,
|
||||
sort_comparator);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::sort(sorted_keys->begin(), sorted_keys->begin() + num_keys,
|
||||
CompareKeyContext());
|
||||
}
|
||||
|
||||
void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
@@ -2367,6 +2404,15 @@ void DBImpl::MultiGet(const ReadOptions& read_options,
|
||||
const Slice* keys, PinnableSlice* values,
|
||||
std::string* timestamps, Status* statuses,
|
||||
const bool sorted_input) {
|
||||
if (tracer_) {
|
||||
// TODO: This mutex should be removed later, to improve performance when
|
||||
// tracing is enabled.
|
||||
InstrumentedMutexLock lock(&trace_mutex_);
|
||||
if (tracer_) {
|
||||
// TODO: maybe handle the tracing status?
|
||||
tracer_->MultiGet(num_keys, column_family, keys).PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
autovector<KeyContext, MultiGetContext::MAX_BATCH_SIZE> key_context;
|
||||
autovector<KeyContext*, MultiGetContext::MAX_BATCH_SIZE> sorted_keys;
|
||||
sorted_keys.resize(num_keys);
|
||||
@@ -3704,6 +3750,8 @@ Status DBImpl::DeleteFilesInRanges(ColumnFamilyHandle* column_family,
|
||||
deleted_files.insert(level_file);
|
||||
level_file->being_compacted = true;
|
||||
}
|
||||
vstorage->ComputeCompactionScore(*cfd->ioptions(),
|
||||
*cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
}
|
||||
if (edit.GetDeletedFiles().empty()) {
|
||||
@@ -3767,6 +3815,17 @@ void DBImpl::GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ReturnAndCleanupSuperVersion(cfd, sv);
|
||||
}
|
||||
|
||||
void DBImpl::GetAllColumnFamilyMetaData(
|
||||
std::vector<ColumnFamilyMetaData>* metadata) {
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
for (auto cfd : *(versions_->GetColumnFamilySet())) {
|
||||
{
|
||||
metadata->emplace_back();
|
||||
cfd->current()->GetColumnFamilyMetaData(&metadata->back());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status DBImpl::CheckConsistency() {
|
||||
|
||||
+20
-19
@@ -20,6 +20,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "db/column_family.h"
|
||||
#include "db/compaction/compaction_iterator.h"
|
||||
#include "db/compaction/compaction_job.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/error_handler.h"
|
||||
@@ -53,6 +54,7 @@
|
||||
#include "rocksdb/trace_reader_writer.h"
|
||||
#include "rocksdb/transaction_log.h"
|
||||
#include "rocksdb/write_buffer_manager.h"
|
||||
#include "table/merging_iterator.h"
|
||||
#include "table/scoped_arena_iterator.h"
|
||||
#include "util/autovector.h"
|
||||
#include "util/hash.h"
|
||||
@@ -400,12 +402,13 @@ class DBImpl : public DB {
|
||||
FileChecksumList* checksum_list) override;
|
||||
|
||||
// Obtains the meta data of the specified column family of the DB.
|
||||
// Status::NotFound() will be returned if the current DB does not have
|
||||
// any column family match the specified name.
|
||||
// TODO(yhchiang): output parameter is placed in the end in this codebase.
|
||||
virtual void GetColumnFamilyMetaData(ColumnFamilyHandle* column_family,
|
||||
ColumnFamilyMetaData* metadata) override;
|
||||
|
||||
void GetAllColumnFamilyMetaData(
|
||||
std::vector<ColumnFamilyMetaData>* metadata) override;
|
||||
|
||||
Status SuggestCompactRange(ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end) override;
|
||||
|
||||
@@ -962,7 +965,7 @@ class DBImpl : public DB {
|
||||
|
||||
// Return the maximum overlapping data (in bytes) at next level for any
|
||||
// file at a level >= 1.
|
||||
int64_t TEST_MaxNextLevelOverlappingBytes(
|
||||
uint64_t TEST_MaxNextLevelOverlappingBytes(
|
||||
ColumnFamilyHandle* column_family = nullptr);
|
||||
|
||||
// Return the current manifest file no.
|
||||
@@ -974,8 +977,10 @@ class DBImpl : public DB {
|
||||
// get total level0 file size. Only for testing.
|
||||
uint64_t TEST_GetLevel0TotalSize();
|
||||
|
||||
void TEST_GetFilesMetaData(ColumnFamilyHandle* column_family,
|
||||
std::vector<std::vector<FileMetaData>>* metadata);
|
||||
void TEST_GetFilesMetaData(
|
||||
ColumnFamilyHandle* column_family,
|
||||
std::vector<std::vector<FileMetaData>>* metadata,
|
||||
std::vector<std::shared_ptr<BlobFileMetaData>>* blob_metadata = nullptr);
|
||||
|
||||
void TEST_LockMutex();
|
||||
|
||||
@@ -1184,11 +1189,6 @@ class DBImpl : public DB {
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Slice* begin, const Slice* end);
|
||||
|
||||
Status GetApproximateSizesInternal(const SizeApproximationOptions& options,
|
||||
ColumnFamilyHandle* column_family,
|
||||
const Range* range, int n,
|
||||
uint64_t* sizes);
|
||||
|
||||
// The following two functions can only be called when:
|
||||
// 1. WriteThread::Writer::EnterUnbatched() is used.
|
||||
// 2. db_mutex is NOT held
|
||||
@@ -1454,15 +1454,16 @@ class DBImpl : public DB {
|
||||
uint32_t output_path_id;
|
||||
Status status;
|
||||
bool done;
|
||||
bool in_progress; // compaction request being processed?
|
||||
bool incomplete; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin; // nullptr means beginning of key range
|
||||
const InternalKey* end; // nullptr means end of key range
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
bool in_progress; // compaction request being processed?
|
||||
bool incomplete; // only part of requested range compacted
|
||||
bool exclusive; // current behavior of only one manual
|
||||
bool disallow_trivial_move; // Force actual compaction to run
|
||||
const InternalKey* begin; // nullptr means beginning of key range
|
||||
const InternalKey* end; // nullptr means end of key range
|
||||
InternalKey* manual_end; // how far we are compacting
|
||||
InternalKey tmp_storage; // Used to keep track of compaction progress
|
||||
InternalKey tmp_storage1; // Used to keep track of compaction progress
|
||||
std::atomic<bool>* canceled; // Compaction canceled by the user?
|
||||
};
|
||||
struct PrepickedCompaction {
|
||||
// background compaction takes ownership of `compaction`.
|
||||
|
||||
@@ -151,7 +151,6 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
assert(cfd);
|
||||
assert(cfd->imm()->NumNotFlushed() != 0);
|
||||
assert(cfd->imm()->IsFlushPending());
|
||||
|
||||
FlushJob flush_job(
|
||||
dbname_, cfd, immutable_db_options_, mutable_cf_options,
|
||||
port::kMaxUint64 /* memtable_id */, file_options_for_compaction_,
|
||||
@@ -591,6 +590,8 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
autovector<const autovector<MemTable*>*> mems_list;
|
||||
autovector<const MutableCFOptions*> mutable_cf_options_list;
|
||||
autovector<FileMetaData*> tmp_file_meta;
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
for (int i = 0; i != num_cfs; ++i) {
|
||||
const auto& mems = jobs[i]->GetMemTables();
|
||||
if (!cfds[i]->IsDropped() && !mems.empty()) {
|
||||
@@ -598,13 +599,18 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
mems_list.emplace_back(&mems);
|
||||
mutable_cf_options_list.emplace_back(&all_mutable_cf_options[i]);
|
||||
tmp_file_meta.emplace_back(&file_meta[i]);
|
||||
#ifndef ROCKSDB_LITE
|
||||
committed_flush_jobs_info.emplace_back(
|
||||
jobs[i]->GetCommittedFlushJobsInfo());
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
}
|
||||
|
||||
s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, tmp_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), &logs_with_prep_tracker_, &mutex_, tmp_file_meta,
|
||||
&job_context->memtables_to_free, directories_.GetDbDir(), log_buffer);
|
||||
committed_flush_jobs_info, &job_context->memtables_to_free,
|
||||
directories_.GetDbDir(), log_buffer);
|
||||
}
|
||||
|
||||
if (s.ok()) {
|
||||
@@ -807,6 +813,10 @@ Status DBImpl::CompactRange(const CompactRangeOptions& options,
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
|
||||
if (options.canceled && options.canceled->load(std::memory_order_acquire)) {
|
||||
return Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
|
||||
const Comparator* const ucmp = column_family->GetComparator();
|
||||
assert(ucmp);
|
||||
size_t ts_sz = ucmp->timestamp_size();
|
||||
@@ -1253,7 +1263,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, io_tracer_,
|
||||
&manual_compaction_paused_, db_id_, db_session_id_,
|
||||
&manual_compaction_paused_, nullptr, db_id_, db_session_id_,
|
||||
c->column_family_data()->GetFullHistoryTsLow());
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
@@ -1328,7 +1338,7 @@ Status DBImpl::CompactFilesImpl(
|
||||
if (output_file_names != nullptr) {
|
||||
for (const auto& newf : c->edit()->GetNewFiles()) {
|
||||
(*output_file_names)
|
||||
.push_back(TableFileName(c->immutable_cf_options()->cf_paths,
|
||||
.push_back(TableFileName(c->immutable_options()->cf_paths,
|
||||
newf.second.fd.GetNumber(),
|
||||
newf.second.fd.GetPathId()));
|
||||
}
|
||||
@@ -1391,6 +1401,8 @@ void DBImpl::NotifyOnCompactionBegin(ColumnFamilyData* cfd, Compaction* c,
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
c->SetNotifyOnCompactionCompleted();
|
||||
Version* current = cfd->current();
|
||||
current->Ref();
|
||||
// release lock while notifying events
|
||||
@@ -1426,10 +1438,11 @@ void DBImpl::NotifyOnCompactionCompleted(
|
||||
if (shutting_down_.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
if (c->is_manual_compaction() &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
|
||||
if (c->ShouldNotifyOnCompactionCompleted() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
Version* current = cfd->current();
|
||||
current->Ref();
|
||||
// release lock while notifying events
|
||||
@@ -1654,6 +1667,7 @@ Status DBImpl::RunManualCompaction(
|
||||
manual.incomplete = false;
|
||||
manual.exclusive = exclusive;
|
||||
manual.disallow_trivial_move = disallow_trivial_move;
|
||||
manual.canceled = compact_range_options.canceled;
|
||||
// For universal compaction, we enforce every manual compaction to compact
|
||||
// all files.
|
||||
if (begin == nullptr ||
|
||||
@@ -2390,6 +2404,17 @@ void DBImpl::SchedulePendingFlush(const FlushRequest& flush_req,
|
||||
assert(flush_req.size() == 1);
|
||||
ColumnFamilyData* cfd = flush_req[0].first;
|
||||
assert(cfd);
|
||||
// Note: SchedulePendingFlush is always preceded
|
||||
// with an imm()->FlushRequested() call. However,
|
||||
// we want to make this code snipper more resilient to
|
||||
// future changes. Therefore, we add the following if
|
||||
// statement - note that calling it twice (or more)
|
||||
// doesn't break anything.
|
||||
if (immutable_db_options_.experimental_allow_mempurge) {
|
||||
// If imm() contains silent memtables,
|
||||
// requesting a flush will mark the imm_needed as true.
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
if (!cfd->queued_for_flush() && cfd->imm()->IsFlushPending()) {
|
||||
cfd->Ref();
|
||||
cfd->set_queued_for_flush(true);
|
||||
@@ -2531,6 +2556,11 @@ Status DBImpl::BackgroundFlush(bool* made_progress, JobContext* job_context,
|
||||
|
||||
for (const auto& iter : flush_req) {
|
||||
ColumnFamilyData* cfd = iter.first;
|
||||
if (immutable_db_options_.experimental_allow_mempurge) {
|
||||
// If imm() contains silent memtables,
|
||||
// requesting a flush will mark the imm_needed as true.
|
||||
cfd->imm()->FlushRequested();
|
||||
}
|
||||
if (cfd->IsDropped() || !cfd->imm()->IsFlushPending()) {
|
||||
// can't flush this CF, try next one
|
||||
column_families_not_to_flush.push_back(cfd);
|
||||
@@ -2819,6 +2849,9 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
} else if (is_manual &&
|
||||
manual_compaction_paused_.load(std::memory_order_acquire) > 0) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
} else if (is_manual && manual_compaction->canceled &&
|
||||
manual_compaction->canceled->load(std::memory_order_acquire)) {
|
||||
status = Status::Incomplete(Status::SubCode::kManualCompactionPaused);
|
||||
}
|
||||
} else {
|
||||
status = error_handler_.GetBGError();
|
||||
@@ -2946,7 +2979,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data()
|
||||
->current()
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(*(c->immutable_cf_options()),
|
||||
->ComputeCompactionScore(*(c->immutable_options()),
|
||||
*(c->mutable_cf_options()));
|
||||
AddToCompactionQueue(cfd);
|
||||
++unscheduled_compactions_;
|
||||
@@ -3140,7 +3173,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri, io_tracer_,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr, db_id_,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr,
|
||||
is_manual ? manual_compaction->canceled : nullptr, db_id_,
|
||||
db_session_id_, c->column_family_data()->GetFullHistoryTsLow());
|
||||
compaction_job.Prepare();
|
||||
|
||||
@@ -3220,7 +3254,7 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
c->column_family_data()
|
||||
->current()
|
||||
->storage_info()
|
||||
->ComputeCompactionScore(*(c->immutable_cf_options()),
|
||||
->ComputeCompactionScore(*(c->immutable_options()),
|
||||
*(c->mutable_cf_options()));
|
||||
if (!cfd->queued_for_compaction()) {
|
||||
AddToCompactionQueue(cfd);
|
||||
@@ -3385,7 +3419,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
for (const auto fmd : *c->inputs(i)) {
|
||||
const FileDescriptor& desc = fmd->fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
auto fn = TableFileName(c->immutable_cf_options()->cf_paths, file_number,
|
||||
auto fn = TableFileName(c->immutable_options()->cf_paths, file_number,
|
||||
desc.GetPathId());
|
||||
compaction_job_info->input_files.push_back(fn);
|
||||
compaction_job_info->input_file_infos.push_back(CompactionFileInfo{
|
||||
@@ -3404,7 +3438,7 @@ void DBImpl::BuildCompactionJobInfo(
|
||||
const FileDescriptor& desc = meta.fd;
|
||||
const uint64_t file_number = desc.GetNumber();
|
||||
compaction_job_info->output_files.push_back(TableFileName(
|
||||
c->immutable_cf_options()->cf_paths, file_number, desc.GetPathId()));
|
||||
c->immutable_options()->cf_paths, file_number, desc.GetPathId()));
|
||||
compaction_job_info->output_file_infos.push_back(CompactionFileInfo{
|
||||
newf.first, file_number, meta.oldest_blob_file_number});
|
||||
}
|
||||
@@ -3440,7 +3474,7 @@ void DBImpl::InstallSuperVersionAndScheduleWork(
|
||||
if (UNLIKELY(sv_context->new_superversion == nullptr)) {
|
||||
sv_context->NewSuperVersion();
|
||||
}
|
||||
cfd->InstallSuperVersion(sv_context, &mutex_, mutable_cf_options);
|
||||
cfd->InstallSuperVersion(sv_context, mutable_cf_options);
|
||||
|
||||
// There may be a small data race here. The snapshot tricking bottommost
|
||||
// compaction may already be released here. But assuming there will always be
|
||||
|
||||
@@ -43,7 +43,7 @@ bool DBImpl::TEST_WALBufferIsEmpty(bool lock) {
|
||||
return res;
|
||||
}
|
||||
|
||||
int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
uint64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
ColumnFamilyHandle* column_family) {
|
||||
ColumnFamilyData* cfd;
|
||||
if (column_family == nullptr) {
|
||||
@@ -58,7 +58,8 @@ int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes(
|
||||
|
||||
void DBImpl::TEST_GetFilesMetaData(
|
||||
ColumnFamilyHandle* column_family,
|
||||
std::vector<std::vector<FileMetaData>>* metadata) {
|
||||
std::vector<std::vector<FileMetaData>>* metadata,
|
||||
std::vector<std::shared_ptr<BlobFileMetaData>>* blob_metadata) {
|
||||
auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
InstrumentedMutexLock l(&mutex_);
|
||||
@@ -72,6 +73,12 @@ void DBImpl::TEST_GetFilesMetaData(
|
||||
(*metadata)[level].push_back(*f);
|
||||
}
|
||||
}
|
||||
if (blob_metadata != nullptr) {
|
||||
blob_metadata->clear();
|
||||
for (const auto& blob : cfd->current()->storage_info()->GetBlobFiles()) {
|
||||
blob_metadata->push_back(blob.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t DBImpl::TEST_Current_Manifest_FileNo() {
|
||||
|
||||
@@ -300,7 +300,8 @@ Status DBImpl::NewDB(std::vector<std::string>* new_filenames) {
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(file), manifest, file_options, immutable_db_options_.clock,
|
||||
io_tracer_, nullptr /* stats */, immutable_db_options_.listeners,
|
||||
nullptr, tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
nullptr, tmp_set.Contains(FileType::kDescriptorFile),
|
||||
tmp_set.Contains(FileType::kDescriptorFile)));
|
||||
log::Writer log(std::move(file_writer), 0, false);
|
||||
std::string record;
|
||||
new_db.EncodeTo(&record);
|
||||
@@ -629,7 +630,7 @@ Status DBImpl::Recover(
|
||||
// Clear memtables if recovery failed
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
cfd->CreateNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
kMaxSequenceNumber);
|
||||
kMaxSequenceNumber, cfd->GetLogNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1065,7 +1066,7 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
flushed = true;
|
||||
|
||||
cfd->CreateNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
*next_sequence);
|
||||
*next_sequence, cfd->GetLogNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1203,7 +1204,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
flushed = true;
|
||||
|
||||
cfd->CreateNewMemtable(*cfd->GetLatestMutableCFOptions(),
|
||||
versions_->LastSequence());
|
||||
versions_->LastSequence(),
|
||||
cfd->GetLogNumber());
|
||||
}
|
||||
data_seen = true;
|
||||
}
|
||||
@@ -1259,7 +1261,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& wal_numbers,
|
||||
// If there's no data in the WAL, or we flushed all the data, still
|
||||
// truncate the log file. If the process goes into a crash loop before
|
||||
// the file is deleted, the preallocated space will never get freed.
|
||||
GetLogSizeAndMaybeTruncate(wal_numbers.back(), true, nullptr)
|
||||
const bool truncate = !read_only;
|
||||
GetLogSizeAndMaybeTruncate(wal_numbers.back(), truncate, nullptr)
|
||||
.PermitUncheckedError();
|
||||
}
|
||||
}
|
||||
@@ -1292,7 +1295,7 @@ Status DBImpl::GetLogSizeAndMaybeTruncate(uint64_t wal_number, bool truncate,
|
||||
truncate_status = last_log->Close(IOOptions(), nullptr);
|
||||
}
|
||||
// Not a critical error if fail to truncate.
|
||||
if (!truncate_status.ok()) {
|
||||
if (!truncate_status.ok() && !truncate_status.IsNotSupported()) {
|
||||
ROCKS_LOG_WARN(immutable_db_options_.info_log,
|
||||
"Failed to truncate log #%" PRIu64 ": %s", wal_number,
|
||||
truncate_status.ToString().c_str());
|
||||
@@ -1401,7 +1404,7 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
0 /* level */, false /* is_bottommost */,
|
||||
TableFileCreationReason::kRecovery, current_time,
|
||||
0 /* oldest_key_time */, 0 /* file_creation_time */, db_id_,
|
||||
db_session_id_, 0 /* target_file_size */);
|
||||
db_session_id_, 0 /* target_file_size */, meta.fd.GetNumber());
|
||||
s = BuildTable(
|
||||
dbname_, versions_.get(), immutable_db_options_, tboptions,
|
||||
file_options_for_compaction_, cfd->table_cache(), iter.get(),
|
||||
@@ -1539,7 +1542,8 @@ IOStatus DBImpl::CreateWAL(uint64_t log_file_num, uint64_t recycle_log_number,
|
||||
std::unique_ptr<WritableFileWriter> file_writer(new WritableFileWriter(
|
||||
std::move(lfile), log_fname, opt_file_options,
|
||||
immutable_db_options_.clock, io_tracer_, nullptr /* stats */, listeners,
|
||||
nullptr, tmp_set.Contains(FileType::kWalFile)));
|
||||
nullptr, tmp_set.Contains(FileType::kWalFile),
|
||||
tmp_set.Contains(FileType::kWalFile)));
|
||||
*new_log = new log::Writer(std::move(file_writer), log_file_num,
|
||||
immutable_db_options_.recycle_log_file_num > 0,
|
||||
immutable_db_options_.manual_wal_flush);
|
||||
|
||||
@@ -253,8 +253,8 @@ Status DBImplSecondary::RecoverLogFiles(
|
||||
curr_log_num != log_number)) {
|
||||
const MutableCFOptions mutable_cf_options =
|
||||
*cfd->GetLatestMutableCFOptions();
|
||||
MemTable* new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq_of_batch);
|
||||
MemTable* new_mem = cfd->ConstructNewMemtable(
|
||||
mutable_cf_options, seq_of_batch, log_number);
|
||||
cfd->mem()->SetNextLogNumber(log_number);
|
||||
cfd->imm()->Add(cfd->mem(), &job_context->memtables_to_free);
|
||||
new_mem->Ref();
|
||||
@@ -767,6 +767,7 @@ Status DB::OpenAndCompact(
|
||||
compaction_input.db_options.env = override_options.env;
|
||||
compaction_input.db_options.file_checksum_gen_factory =
|
||||
override_options.file_checksum_gen_factory;
|
||||
compaction_input.db_options.statistics = override_options.statistics;
|
||||
compaction_input.column_family.options.comparator =
|
||||
override_options.comparator;
|
||||
compaction_input.column_family.options.merge_operator =
|
||||
|
||||
@@ -278,6 +278,16 @@ class DBImplSecondary : public DBImpl {
|
||||
return AddColumnFamilyId(column_family_id);
|
||||
}
|
||||
|
||||
Status MarkBeginPrepare(bool) override { return Status::OK(); }
|
||||
|
||||
Status MarkEndPrepare(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkRollback(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkCommit(const Slice&) override { return Status::OK(); }
|
||||
|
||||
Status MarkNoop(bool) override { return Status::OK(); }
|
||||
|
||||
const std::unordered_set<uint32_t>& column_families() const {
|
||||
return column_family_ids_;
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
write_thread_.EnterAsBatchGroupLeader(&w, &write_group);
|
||||
|
||||
IOStatus io_s;
|
||||
Status pre_release_cb_status;
|
||||
if (status.ok()) {
|
||||
// Rules for when we can update the memtable concurrently
|
||||
// 1. supported by memtable
|
||||
@@ -361,7 +362,7 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
writer->sequence, disable_memtable, writer->log_used, index++,
|
||||
pre_release_callback_cnt);
|
||||
if (!ws.ok()) {
|
||||
status = ws;
|
||||
status = pre_release_cb_status = ws;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -414,10 +415,13 @@ Status DBImpl::WriteImpl(const WriteOptions& write_options,
|
||||
|
||||
if (!w.CallbackFailed()) {
|
||||
if (!io_s.ok()) {
|
||||
assert(pre_release_cb_status.ok());
|
||||
IOStatusCheck(io_s);
|
||||
} else {
|
||||
WriteStatusCheck(status);
|
||||
WriteStatusCheck(pre_release_cb_status);
|
||||
}
|
||||
} else {
|
||||
assert(io_s.ok() && pre_release_cb_status.ok());
|
||||
}
|
||||
|
||||
if (need_log_sync) {
|
||||
@@ -1801,7 +1805,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
}
|
||||
if (s.ok()) {
|
||||
SequenceNumber seq = versions_->LastSequence();
|
||||
new_mem = cfd->ConstructNewMemtable(mutable_cf_options, seq);
|
||||
new_mem =
|
||||
cfd->ConstructNewMemtable(mutable_cf_options, seq, new_log_number);
|
||||
context->superversion_context.NewSuperVersion();
|
||||
}
|
||||
ROCKS_LOG_INFO(immutable_db_options_.info_log,
|
||||
@@ -1911,6 +1916,8 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
for (auto cf : empty_cfs) {
|
||||
if (cf->IsEmpty()) {
|
||||
cf->SetLogNumber(logfile_number_);
|
||||
// MEMPURGE: No need to change this, because new adds
|
||||
// should still receive new sequence numbers.
|
||||
cf->mem()->SetCreationSeq(versions_->LastSequence());
|
||||
} // cf may become non-empty.
|
||||
}
|
||||
@@ -1933,11 +1940,13 @@ Status DBImpl::SwitchMemtable(ColumnFamilyData* cfd, WriteContext* context) {
|
||||
}
|
||||
|
||||
cfd->mem()->SetNextLogNumber(logfile_number_);
|
||||
assert(new_mem != nullptr);
|
||||
cfd->imm()->Add(cfd->mem(), &context->memtables_to_free_);
|
||||
new_mem->Ref();
|
||||
cfd->SetMemtable(new_mem);
|
||||
InstallSuperVersionAndScheduleWork(cfd, &context->superversion_context,
|
||||
mutable_cf_options);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
mutex_.Unlock();
|
||||
// Notify client that memtable is sealed, now that we have successfully
|
||||
|
||||
+94
-52
@@ -36,7 +36,7 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* cmp, InternalIterator* iter,
|
||||
const Version* version, SequenceNumber s, bool arena_mode,
|
||||
@@ -45,15 +45,15 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index)
|
||||
: prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
|
||||
env_(_env),
|
||||
clock_(cf_options.clock),
|
||||
logger_(cf_options.logger),
|
||||
clock_(ioptions.clock),
|
||||
logger_(ioptions.logger),
|
||||
user_comparator_(cmp),
|
||||
merge_operator_(cf_options.merge_operator.get()),
|
||||
merge_operator_(ioptions.merge_operator.get()),
|
||||
iter_(iter),
|
||||
version_(version),
|
||||
read_callback_(read_callback),
|
||||
sequence_(s),
|
||||
statistics_(cf_options.stats),
|
||||
statistics_(ioptions.stats),
|
||||
max_skip_(max_sequential_skip_in_iterations),
|
||||
max_skippable_internal_keys_(read_options.max_skippable_internal_keys),
|
||||
num_internal_keys_skipped_(0),
|
||||
@@ -75,7 +75,7 @@ DBIter::DBIter(Env* _env, const ReadOptions& read_options,
|
||||
expose_blob_index_(expose_blob_index),
|
||||
is_blob_(false),
|
||||
arena_mode_(arena_mode),
|
||||
range_del_agg_(&cf_options.internal_comparator, s),
|
||||
range_del_agg_(&ioptions.internal_comparator, s),
|
||||
db_impl_(db_impl),
|
||||
cfd_(cfd),
|
||||
start_seqnum_(read_options.iter_start_seqnum),
|
||||
@@ -576,12 +576,8 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
const Slice val = iter_.value();
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, ikey.user_key, &val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, clock_, &pinned_value_, true);
|
||||
Status s = Merge(&val, ikey.user_key);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return false;
|
||||
}
|
||||
// iter_ is positioned after put
|
||||
@@ -598,9 +594,31 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else if (kTypeBlobIndex == ikey.type) {
|
||||
status_ = Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
// hit a put, merge the put value with operands and store the
|
||||
// final result in saved_value_. We are done!
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
const Slice blob_value = value();
|
||||
Status s = Merge(&blob_value, ikey.user_key);
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
is_blob_ = false;
|
||||
// iter_ is positioned after put
|
||||
iter_.Next();
|
||||
if (!iter_.status().ok()) {
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = Status::Corruption(
|
||||
@@ -619,16 +637,10 @@ bool DBIter::MergeValuesNewToOld() {
|
||||
// a deletion marker.
|
||||
// feed null as the existing value to the merge operator, such that
|
||||
// client can differentiate this scenario and do things accordingly.
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, clock_,
|
||||
&pinned_value_, true);
|
||||
Status s = Merge(nullptr, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return false;
|
||||
}
|
||||
|
||||
assert(status_.ok());
|
||||
return true;
|
||||
}
|
||||
@@ -931,21 +943,36 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
if (last_not_merge_type == kTypeDeletion ||
|
||||
last_not_merge_type == kTypeSingleDeletion ||
|
||||
last_not_merge_type == kTypeRangeDeletion) {
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
clock_, &pinned_value_, true);
|
||||
s = Merge(nullptr, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (last_not_merge_type == kTypeBlobIndex) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
const Slice blob_value = value();
|
||||
s = Merge(&blob_value, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
is_blob_ = false;
|
||||
return true;
|
||||
} else {
|
||||
assert(last_not_merge_type == kTypeValue);
|
||||
s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), &pinned_value_,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
clock_, &pinned_value_, true);
|
||||
s = Merge(&pinned_value_, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case kTypeValue:
|
||||
@@ -955,7 +982,6 @@ bool DBIter::FindValueForCurrentKey() {
|
||||
if (!SetBlobValueIfNeeded(saved_key_.GetUserKey(), pinned_value_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
valid_ = false;
|
||||
@@ -1095,25 +1121,33 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
|
||||
if (ikey.type == kTypeValue) {
|
||||
const Slice val = iter_.value();
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), &val,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_,
|
||||
clock_, &pinned_value_, true);
|
||||
Status s = Merge(&val, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
return true;
|
||||
} else if (ikey.type == kTypeMerge) {
|
||||
merge_context_.PushOperand(
|
||||
iter_.value(), iter_.iter()->IsValuePinned() /* operand_pinned */);
|
||||
PERF_COUNTER_ADD(internal_merge_count, 1);
|
||||
} else if (ikey.type == kTypeBlobIndex) {
|
||||
status_ = Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
if (expose_blob_index_) {
|
||||
status_ =
|
||||
Status::NotSupported("BlobDB does not support merge operator.");
|
||||
valid_ = false;
|
||||
return false;
|
||||
}
|
||||
if (!SetBlobValueIfNeeded(ikey.user_key, iter_.value())) {
|
||||
return false;
|
||||
}
|
||||
valid_ = true;
|
||||
const Slice blob_value = value();
|
||||
Status s = Merge(&blob_value, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
return false;
|
||||
}
|
||||
is_blob_ = false;
|
||||
return true;
|
||||
} else {
|
||||
valid_ = false;
|
||||
status_ = Status::Corruption(
|
||||
@@ -1123,13 +1157,8 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
}
|
||||
}
|
||||
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, saved_key_.GetUserKey(), nullptr,
|
||||
merge_context_.GetOperands(), &saved_value_, logger_, statistics_, clock_,
|
||||
&pinned_value_, true);
|
||||
Status s = Merge(nullptr, saved_key_.GetUserKey());
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1152,6 +1181,19 @@ bool DBIter::FindValueForCurrentKeyUsingSeek() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Status DBIter::Merge(const Slice* val, const Slice& user_key) {
|
||||
Status s = MergeHelper::TimedFullMerge(
|
||||
merge_operator_, user_key, val, merge_context_.GetOperands(),
|
||||
&saved_value_, logger_, statistics_, clock_, &pinned_value_, true);
|
||||
if (!s.ok()) {
|
||||
valid_ = false;
|
||||
status_ = s;
|
||||
return s;
|
||||
}
|
||||
valid_ = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Move backwards until the key smaller than saved_key_.
|
||||
// Changes valid_ only if return value is false.
|
||||
bool DBIter::FindUserKeyBeforeSavedKey() {
|
||||
@@ -1536,7 +1578,7 @@ void DBIter::SeekToLast() {
|
||||
}
|
||||
|
||||
Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator,
|
||||
InternalIterator* internal_iter, const Version* version,
|
||||
@@ -1545,7 +1587,7 @@ Iterator* NewDBIterator(Env* env, const ReadOptions& read_options,
|
||||
ReadCallback* read_callback, DBImpl* db_impl,
|
||||
ColumnFamilyData* cfd, bool expose_blob_index) {
|
||||
DBIter* db_iter =
|
||||
new DBIter(env, read_options, cf_options, mutable_cf_options,
|
||||
new DBIter(env, read_options, ioptions, mutable_cf_options,
|
||||
user_key_comparator, internal_iter, version, sequence, false,
|
||||
max_sequential_skip_in_iterations, read_callback, db_impl, cfd,
|
||||
expose_blob_index);
|
||||
|
||||
+4
-3
@@ -114,7 +114,7 @@ class DBIter final : public Iterator {
|
||||
};
|
||||
|
||||
DBIter(Env* _env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options, const Comparator* cmp,
|
||||
InternalIterator* iter, const Version* version, SequenceNumber s,
|
||||
bool arena_mode, uint64_t max_sequential_skip_in_iterations,
|
||||
@@ -299,6 +299,8 @@ class DBIter final : public Iterator {
|
||||
// index when using the integrated BlobDB implementation.
|
||||
bool SetBlobValueIfNeeded(const Slice& user_key, const Slice& blob_index);
|
||||
|
||||
Status Merge(const Slice* val, const Slice& user_key);
|
||||
|
||||
const SliceTransform* prefix_extractor_;
|
||||
Env* const env_;
|
||||
SystemClock* clock_;
|
||||
@@ -383,8 +385,7 @@ class DBIter final : public Iterator {
|
||||
// "*internal_iter") that were live at the specified `sequence` number
|
||||
// into appropriate user keys.
|
||||
extern Iterator* NewDBIterator(
|
||||
Env* env, const ReadOptions& read_options,
|
||||
const ImmutableOptions& cf_options,
|
||||
Env* env, const ReadOptions& read_options, const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
const Comparator* user_key_comparator, InternalIterator* internal_iter,
|
||||
const Version* version, const SequenceNumber& sequence,
|
||||
|
||||
+62
-62
@@ -237,7 +237,7 @@ class DBIteratorTest : public testing::Test {
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
Options options;
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
{
|
||||
TestIterator* internal_iter = new TestIterator(BytewiseComparator());
|
||||
@@ -252,7 +252,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -286,7 +286,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -314,7 +314,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -348,7 +348,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -385,7 +385,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -417,7 +417,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -456,7 +456,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -483,7 +483,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -508,7 +508,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -545,7 +545,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
ro.iterate_upper_bound = &prefix;
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -576,7 +576,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -621,7 +621,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -654,7 +654,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
ReadOptions ro;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -677,7 +677,7 @@ TEST_F(DBIteratorTest, DBIteratorPrevNext) {
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
Options options;
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
ReadOptions ro;
|
||||
|
||||
@@ -686,7 +686,7 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -700,7 +700,7 @@ TEST_F(DBIteratorTest, DBIteratorEmpty) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -757,7 +757,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
|
||||
{
|
||||
@@ -772,7 +772,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -810,7 +810,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -841,7 +841,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 202 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -876,7 +876,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddPut("c", "200");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -896,7 +896,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->AddPut("c", "200");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 200 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -934,7 +934,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -971,7 +971,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, i + 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1004,7 +1004,7 @@ TEST_F(DBIteratorTest, DBIteratorUseSkip) {
|
||||
|
||||
TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
Options options;
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
ReadOptions ro;
|
||||
|
||||
@@ -1023,7 +1023,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 0;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1071,7 +1071,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1117,7 +1117,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1157,7 +1157,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1194,7 +1194,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1226,7 +1226,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1265,7 +1265,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = 2;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1304,7 +1304,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1359,7 +1359,7 @@ TEST_F(DBIteratorTest, DBIteratorSkipInternalKeys) {
|
||||
options.max_sequential_skip_in_iterations = 1000;
|
||||
ro.max_skippable_internal_keys = i;
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 * i + 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1500,7 +1500,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
|
||||
{
|
||||
@@ -1515,7 +1515,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1539,7 +1539,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1563,7 +1563,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1587,7 +1587,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1611,7 +1611,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1635,7 +1635,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1659,7 +1659,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1681,7 +1681,7 @@ TEST_F(DBIteratorTest, DBIterator5) {
|
||||
internal_iter->AddPut("b", "val_b");
|
||||
internal_iter->Finish();
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 10 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1698,7 +1698,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
|
||||
{
|
||||
@@ -1713,7 +1713,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1737,7 +1737,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 1 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1761,7 +1761,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1785,7 +1785,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 3 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1805,7 +1805,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1829,7 +1829,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1853,7 +1853,7 @@ TEST_F(DBIteratorTest, DBIterator6) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1870,7 +1870,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
ReadOptions ro;
|
||||
Options options;
|
||||
options.merge_operator = MergeOperators::CreateFromStringId("stringappend");
|
||||
ImmutableOptions cf_options = ImmutableOptions(options);
|
||||
ImmutableOptions ioptions = ImmutableOptions(options);
|
||||
MutableCFOptions mutable_cf_options = MutableCFOptions(options);
|
||||
|
||||
{
|
||||
@@ -1897,7 +1897,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 0 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1933,7 +1933,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 2 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -1975,7 +1975,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 4 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -2017,7 +2017,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 5 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -2064,7 +2064,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 6 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -2112,7 +2112,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 7 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -2154,7 +2154,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 9 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -2202,7 +2202,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 13 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
@@ -2251,7 +2251,7 @@ TEST_F(DBIteratorTest, DBIterator7) {
|
||||
internal_iter->Finish();
|
||||
|
||||
std::unique_ptr<Iterator> db_iter(NewDBIterator(
|
||||
env_, ro, cf_options, mutable_cf_options, BytewiseComparator(),
|
||||
env_, ro, ioptions, mutable_cf_options, BytewiseComparator(),
|
||||
internal_iter, nullptr /* version */, 14 /* sequence */,
|
||||
options.max_sequential_skip_in_iterations,
|
||||
nullptr /* read_callback */));
|
||||
|
||||
@@ -1536,9 +1536,11 @@ class DBIteratorTestForPinnedData : public DBIteratorTest {
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedNormal) {
|
||||
PinnedDataIteratorRandomized(TestConfig::NORMAL);
|
||||
}
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_P(DBIteratorTestForPinnedData, PinnedDataIteratorRandomizedCLoseAndOpen) {
|
||||
PinnedDataIteratorRandomized(TestConfig::CLOSE_AND_OPEN);
|
||||
|
||||
+111
-21
@@ -19,6 +19,28 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
class LimitedStringAppendMergeOp : public StringAppendTESTOperator {
|
||||
public:
|
||||
LimitedStringAppendMergeOp(int limit, char delim)
|
||||
: StringAppendTESTOperator(delim), limit_(limit) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DBMergeOperatorTest::LimitedStringAppendMergeOp";
|
||||
}
|
||||
|
||||
bool ShouldMerge(const std::vector<Slice>& operands) const override {
|
||||
if (operands.size() > 0 && limit_ > 0 && operands.size() >= limit_) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class DBMergeOperandTest : public DBTestBase {
|
||||
public:
|
||||
DBMergeOperandTest()
|
||||
@@ -26,26 +48,6 @@ class DBMergeOperandTest : public DBTestBase {
|
||||
};
|
||||
|
||||
TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
class LimitedStringAppendMergeOp : public StringAppendTESTOperator {
|
||||
public:
|
||||
LimitedStringAppendMergeOp(int limit, char delim)
|
||||
: StringAppendTESTOperator(delim), limit_(limit) {}
|
||||
|
||||
const char* Name() const override {
|
||||
return "DBMergeOperatorTest::LimitedStringAppendMergeOp";
|
||||
}
|
||||
|
||||
bool ShouldMerge(const std::vector<Slice>& operands) const override {
|
||||
if (operands.size() > 0 && limit_ > 0 && operands.size() >= limit_) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t limit_ = 0;
|
||||
};
|
||||
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
// Use only the latest two merge operands.
|
||||
@@ -214,7 +216,8 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_EQ(values[2], "dc");
|
||||
ASSERT_EQ(values[3], "ed");
|
||||
|
||||
// First 3 k5 values are in SST and next 4 k5 values are in Immutable Memtable
|
||||
// First 3 k5 values are in SST and next 4 k5 values are in Immutable
|
||||
// Memtable
|
||||
ASSERT_OK(Merge("k5", "who"));
|
||||
ASSERT_OK(Merge("k5", "am"));
|
||||
ASSERT_OK(Merge("k5", "i"));
|
||||
@@ -232,6 +235,93 @@ TEST_F(DBMergeOperandTest, GetMergeOperandsBasic) {
|
||||
ASSERT_EQ(values[2], "am");
|
||||
}
|
||||
|
||||
TEST_F(DBMergeOperandTest, BlobDBGetMergeOperandsBasic) {
|
||||
Options options;
|
||||
options.create_if_missing = true;
|
||||
options.enable_blob_files = true;
|
||||
options.min_blob_size = 0;
|
||||
// Use only the latest two merge operands.
|
||||
options.merge_operator = std::make_shared<LimitedStringAppendMergeOp>(2, ',');
|
||||
options.env = env_;
|
||||
Reopen(options);
|
||||
int num_records = 4;
|
||||
int number_of_operands = 0;
|
||||
std::vector<PinnableSlice> values(num_records);
|
||||
GetMergeOperandsOptions merge_operands_info;
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
// All k1 values are in memtable.
|
||||
ASSERT_OK(Put("k1", "x"));
|
||||
ASSERT_OK(Merge("k1", "b"));
|
||||
ASSERT_OK(Merge("k1", "c"));
|
||||
ASSERT_OK(Merge("k1", "d"));
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k1", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "x");
|
||||
ASSERT_EQ(values[1], "b");
|
||||
ASSERT_EQ(values[2], "c");
|
||||
ASSERT_EQ(values[3], "d");
|
||||
|
||||
// expected_max_number_of_operands is less than number of merge operands so
|
||||
// status should be Incomplete.
|
||||
merge_operands_info.expected_max_number_of_operands = num_records - 1;
|
||||
Status status = db_->GetMergeOperands(
|
||||
ReadOptions(), db_->DefaultColumnFamily(), "k1", values.data(),
|
||||
&merge_operands_info, &number_of_operands);
|
||||
ASSERT_EQ(status.IsIncomplete(), true);
|
||||
merge_operands_info.expected_max_number_of_operands = num_records;
|
||||
|
||||
// All k2 values are flushed to L0 into a single file.
|
||||
ASSERT_OK(Put("k2", "q"));
|
||||
ASSERT_OK(Merge("k2", "w"));
|
||||
ASSERT_OK(Merge("k2", "e"));
|
||||
ASSERT_OK(Merge("k2", "r"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k2", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "q,w,e,r");
|
||||
|
||||
// Do some compaction that will make the following tests more predictable
|
||||
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
||||
|
||||
// All k3 values are flushed and are in different files.
|
||||
ASSERT_OK(Put("k3", "ab"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "bc"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "cd"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(Merge("k3", "de"));
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k3", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "ab");
|
||||
ASSERT_EQ(values[1], "bc");
|
||||
ASSERT_EQ(values[2], "cd");
|
||||
ASSERT_EQ(values[3], "de");
|
||||
|
||||
// All K4 values are in different levels
|
||||
ASSERT_OK(Put("k4", "ba"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(4);
|
||||
ASSERT_OK(Merge("k4", "cb"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(3);
|
||||
ASSERT_OK(Merge("k4", "dc"));
|
||||
ASSERT_OK(Flush());
|
||||
MoveFilesToLevel(1);
|
||||
ASSERT_OK(Merge("k4", "ed"));
|
||||
ASSERT_OK(db_->GetMergeOperands(ReadOptions(), db_->DefaultColumnFamily(),
|
||||
"k4", values.data(), &merge_operands_info,
|
||||
&number_of_operands));
|
||||
ASSERT_EQ(values[0], "ba");
|
||||
ASSERT_EQ(values[1], "cb");
|
||||
ASSERT_EQ(values[2], "dc");
|
||||
ASSERT_EQ(values[3], "ed");
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "db/db_impl/db_impl_secondary.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/utilities/transaction_db.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "utilities/fault_injection_env.h"
|
||||
|
||||
@@ -1115,6 +1116,39 @@ TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
Status s = db_secondary_->TryCatchUpWithPrimary();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, OpenWithTransactionDB) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
// Destroy the DB to recreate as a TransactionDB.
|
||||
Close();
|
||||
Destroy(options, true);
|
||||
|
||||
// Create a TransactionDB.
|
||||
TransactionDB* txn_db = nullptr;
|
||||
TransactionDBOptions txn_db_opts;
|
||||
ASSERT_OK(TransactionDB::Open(options, txn_db_opts, dbname_, &txn_db));
|
||||
ASSERT_NE(txn_db, nullptr);
|
||||
db_ = txn_db;
|
||||
|
||||
std::vector<std::string> cfs = {"new_CF"};
|
||||
CreateColumnFamilies(cfs, options);
|
||||
ASSERT_EQ(handles_.size(), 1);
|
||||
|
||||
WriteOptions wopts;
|
||||
TransactionOptions txn_opts;
|
||||
Transaction* txn1 = txn_db->BeginTransaction(wopts, txn_opts, nullptr);
|
||||
ASSERT_NE(txn1, nullptr);
|
||||
ASSERT_OK(txn1->Put(handles_[0], "k1", "v1"));
|
||||
ASSERT_OK(txn1->Commit());
|
||||
delete txn1;
|
||||
|
||||
options = CurrentOptions();
|
||||
options.max_open_files = -1;
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
+130
-33
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "cache/lru_cache.h"
|
||||
#include "db/blob/blob_index.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "db/db_impl/db_impl.h"
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/dbformat.h"
|
||||
@@ -1027,10 +1028,10 @@ TEST_F(DBTest, FailMoreDbPaths) {
|
||||
}
|
||||
|
||||
void CheckColumnFamilyMeta(
|
||||
const ColumnFamilyMetaData& cf_meta,
|
||||
const ColumnFamilyMetaData& cf_meta, const std::string& cf_name,
|
||||
const std::vector<std::vector<FileMetaData>>& files_by_level,
|
||||
uint64_t start_time, uint64_t end_time) {
|
||||
ASSERT_EQ(cf_meta.name, kDefaultColumnFamilyName);
|
||||
ASSERT_EQ(cf_meta.name, cf_name);
|
||||
ASSERT_EQ(cf_meta.levels.size(), files_by_level.size());
|
||||
|
||||
uint64_t cf_size = 0;
|
||||
@@ -1124,6 +1125,53 @@ void CheckLiveFilesMeta(
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
void AddBlobFile(const ColumnFamilyHandle* cfh, uint64_t blob_file_number,
|
||||
uint64_t total_blob_count, uint64_t total_blob_bytes,
|
||||
const std::string& checksum_method,
|
||||
const std::string& checksum_value,
|
||||
uint64_t garbage_blob_count = 0,
|
||||
uint64_t garbage_blob_bytes = 0) {
|
||||
ColumnFamilyData* cfd =
|
||||
(static_cast<const ColumnFamilyHandleImpl*>(cfh))->cfd();
|
||||
assert(cfd);
|
||||
|
||||
Version* const version = cfd->current();
|
||||
assert(version);
|
||||
|
||||
VersionStorageInfo* const storage_info = version->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
// Add a live blob file.
|
||||
|
||||
auto shared_meta = SharedBlobFileMetaData::Create(
|
||||
blob_file_number, total_blob_count, total_blob_bytes, checksum_method,
|
||||
checksum_value);
|
||||
|
||||
auto meta = BlobFileMetaData::Create(std::move(shared_meta),
|
||||
BlobFileMetaData::LinkedSsts(),
|
||||
garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
storage_info->AddBlobFile(std::move(meta));
|
||||
}
|
||||
|
||||
static void CheckBlobMetaData(
|
||||
const BlobMetaData& bmd, uint64_t blob_file_number,
|
||||
uint64_t total_blob_count, uint64_t total_blob_bytes,
|
||||
const std::string& checksum_method, const std::string& checksum_value,
|
||||
uint64_t garbage_blob_count = 0, uint64_t garbage_blob_bytes = 0) {
|
||||
ASSERT_EQ(bmd.blob_file_number, blob_file_number);
|
||||
ASSERT_EQ(bmd.blob_file_name, BlobFileName("", blob_file_number));
|
||||
ASSERT_EQ(bmd.blob_file_size,
|
||||
total_blob_bytes + BlobLogHeader::kSize + BlobLogFooter::kSize);
|
||||
|
||||
ASSERT_EQ(bmd.total_blob_count, total_blob_count);
|
||||
ASSERT_EQ(bmd.total_blob_bytes, total_blob_bytes);
|
||||
ASSERT_EQ(bmd.garbage_blob_count, garbage_blob_count);
|
||||
ASSERT_EQ(bmd.garbage_blob_bytes, garbage_blob_bytes);
|
||||
ASSERT_EQ(bmd.checksum_method, checksum_method);
|
||||
ASSERT_EQ(bmd.checksum_value, checksum_value);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, MetaDataTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
@@ -1164,13 +1212,71 @@ TEST_F(DBTest, MetaDataTest) {
|
||||
|
||||
ColumnFamilyMetaData cf_meta;
|
||||
db_->GetColumnFamilyMetaData(&cf_meta);
|
||||
CheckColumnFamilyMeta(cf_meta, files_by_level, start_time, end_time);
|
||||
|
||||
CheckColumnFamilyMeta(cf_meta, kDefaultColumnFamilyName, files_by_level,
|
||||
start_time, end_time);
|
||||
std::vector<LiveFileMetaData> live_file_meta;
|
||||
db_->GetLiveFilesMetaData(&live_file_meta);
|
||||
CheckLiveFilesMeta(live_file_meta, files_by_level);
|
||||
}
|
||||
|
||||
TEST_F(DBTest, AllMetaDataTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
options.disable_auto_compactions = true;
|
||||
DestroyAndReopen(options);
|
||||
CreateAndReopenWithCF({"pikachu"}, options);
|
||||
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "\x3d\x87\xff\x57";
|
||||
|
||||
int64_t temp_time = 0;
|
||||
options.env->GetCurrentTime(&temp_time).PermitUncheckedError();
|
||||
uint64_t start_time = static_cast<uint64_t>(temp_time);
|
||||
|
||||
Random rnd(301);
|
||||
dbfull()->TEST_LockMutex();
|
||||
for (int cf = 0; cf < 2; cf++) {
|
||||
AddBlobFile(handles_[cf], blob_file_number * (cf + 1),
|
||||
total_blob_count * (cf + 1), total_blob_bytes * (cf + 1),
|
||||
checksum_method, checksum_value);
|
||||
}
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
std::vector<ColumnFamilyMetaData> all_meta;
|
||||
db_->GetAllColumnFamilyMetaData(&all_meta);
|
||||
|
||||
std::vector<std::vector<FileMetaData>> default_files_by_level;
|
||||
std::vector<std::vector<FileMetaData>> pikachu_files_by_level;
|
||||
dbfull()->TEST_GetFilesMetaData(handles_[0], &default_files_by_level);
|
||||
dbfull()->TEST_GetFilesMetaData(handles_[1], &pikachu_files_by_level);
|
||||
|
||||
options.env->GetCurrentTime(&temp_time).PermitUncheckedError();
|
||||
uint64_t end_time = static_cast<uint64_t>(temp_time);
|
||||
|
||||
ASSERT_EQ(all_meta.size(), 2);
|
||||
for (int cf = 0; cf < 2; cf++) {
|
||||
const auto& cfmd = all_meta[cf];
|
||||
if (cf == 0) {
|
||||
CheckColumnFamilyMeta(cfmd, "default", default_files_by_level, start_time,
|
||||
end_time);
|
||||
} else {
|
||||
CheckColumnFamilyMeta(cfmd, "pikachu", pikachu_files_by_level, start_time,
|
||||
end_time);
|
||||
}
|
||||
ASSERT_EQ(cfmd.blob_files.size(), 1U);
|
||||
const auto& bmd = cfmd.blob_files[0];
|
||||
ASSERT_EQ(cfmd.blob_file_count, 1U);
|
||||
ASSERT_EQ(cfmd.blob_file_size, bmd.blob_file_size);
|
||||
ASSERT_EQ(NormalizePath(bmd.blob_file_path), NormalizePath(dbname_));
|
||||
CheckBlobMetaData(bmd, blob_file_number * (cf + 1),
|
||||
total_blob_count * (cf + 1), total_blob_bytes * (cf + 1),
|
||||
checksum_method, checksum_value);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void MinLevelHelper(DBTest* self, Options& options) {
|
||||
Random rnd(301);
|
||||
@@ -2344,41 +2450,19 @@ TEST_F(DBTest, GetLiveBlobFiles) {
|
||||
Options options = CurrentOptions();
|
||||
options.stats_dump_period_sec = 0;
|
||||
|
||||
Reopen(options);
|
||||
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
Version* const version = cfd->current();
|
||||
assert(version);
|
||||
|
||||
VersionStorageInfo* const storage_info = version->storage_info();
|
||||
assert(storage_info);
|
||||
|
||||
// Add a live blob file.
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "3d87ff57";
|
||||
|
||||
auto shared_meta = SharedBlobFileMetaData::Create(
|
||||
blob_file_number, total_blob_count, total_blob_bytes, checksum_method,
|
||||
checksum_value);
|
||||
|
||||
constexpr char checksum_value[] = "\x3d\x87\xff\x57";
|
||||
constexpr uint64_t garbage_blob_count = 0;
|
||||
constexpr uint64_t garbage_blob_bytes = 0;
|
||||
|
||||
auto meta = BlobFileMetaData::Create(std::move(shared_meta),
|
||||
BlobFileMetaData::LinkedSsts(),
|
||||
garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
storage_info->AddBlobFile(std::move(meta));
|
||||
Reopen(options);
|
||||
|
||||
AddBlobFile(db_->DefaultColumnFamily(), blob_file_number, total_blob_count,
|
||||
total_blob_bytes, checksum_method, checksum_value,
|
||||
garbage_blob_count, garbage_blob_bytes);
|
||||
// Make sure it appears in the results returned by GetLiveFiles.
|
||||
uint64_t manifest_size = 0;
|
||||
std::vector<std::string> files;
|
||||
@@ -2386,6 +2470,19 @@ TEST_F(DBTest, GetLiveBlobFiles) {
|
||||
|
||||
ASSERT_FALSE(files.empty());
|
||||
ASSERT_EQ(files[0], BlobFileName("", blob_file_number));
|
||||
|
||||
ColumnFamilyMetaData cfmd;
|
||||
|
||||
db_->GetColumnFamilyMetaData(&cfmd);
|
||||
ASSERT_EQ(cfmd.blob_files.size(), 1);
|
||||
const BlobMetaData& bmd = cfmd.blob_files[0];
|
||||
|
||||
CheckBlobMetaData(bmd, blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value, garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
ASSERT_EQ(NormalizePath(bmd.blob_file_path), NormalizePath(dbname_));
|
||||
ASSERT_EQ(cfmd.blob_file_count, 1U);
|
||||
ASSERT_EQ(cfmd.blob_file_size, bmd.blob_file_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3114,7 +3211,7 @@ class ModelDB : public DB {
|
||||
std::string name_ = "";
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
static std::string RandomKey(Random* rnd, int minimum = 0) {
|
||||
int len;
|
||||
do {
|
||||
@@ -3269,7 +3366,7 @@ TEST_P(DBTestRandomized, Randomized) {
|
||||
if (model_snap != nullptr) model.ReleaseSnapshot(model_snap);
|
||||
if (db_snap != nullptr) db_->ReleaseSnapshot(db_snap);
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_F(DBTest, BlockBasedTablePrefixIndexTest) {
|
||||
// create a DB with block prefix index
|
||||
|
||||
+345
@@ -9,6 +9,7 @@
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/read_callback.h"
|
||||
@@ -3201,6 +3202,350 @@ TEST_F(DBTest2, PausingManualCompaction4) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CancelManualCompaction1) {
|
||||
CompactRangeOptions compact_options;
|
||||
auto canceledPtr =
|
||||
std::unique_ptr<std::atomic<bool>>(new std::atomic<bool>{true});
|
||||
compact_options.canceled = canceledPtr.get();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
|
||||
Random rnd(301);
|
||||
auto generate_files = [&]() {
|
||||
for (int i = 0; i < options.num_levels; i++) {
|
||||
for (int j = 0; j < options.num_levels - i + 1; j++) {
|
||||
for (int k = 0; k < 1000; k++) {
|
||||
ASSERT_OK(Put(Key(k + j * 1000), rnd.RandomString(50)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
for (int l = 1; l < options.num_levels - i; l++) {
|
||||
MoveFilesToLevel(l);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DestroyAndReopen(options);
|
||||
generate_files();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
int run_manual_compactions = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1",
|
||||
[&](void* /*arg*/) { run_manual_compactions++; });
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
// Setup a callback to disable compactions after a couple of levels are
|
||||
// compacted
|
||||
int compactions_run = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RunManualCompaction()::1",
|
||||
[&](void* /*arg*/) { ++compactions_run; });
|
||||
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
// Since compactions are disabled, we shouldn't start compacting.
|
||||
// E.g. we should call the compaction function exactly one time.
|
||||
ASSERT_EQ(compactions_run, 0);
|
||||
ASSERT_EQ(run_manual_compactions, 0);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
compactions_run = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"DBImpl::RunManualCompaction()::1");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RunManualCompaction()::1", [&](void* /*arg*/) {
|
||||
++compactions_run;
|
||||
// After 3 compactions disable
|
||||
if (compactions_run == 3) {
|
||||
compact_options.canceled->store(true, std::memory_order_release);
|
||||
}
|
||||
});
|
||||
|
||||
compact_options.canceled->store(false, std::memory_order_release);
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_EQ(compactions_run, 3);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"DBImpl::RunManualCompaction()::1");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1");
|
||||
|
||||
// Compactions should work again if we re-enable them..
|
||||
compact_options.canceled->store(false, std::memory_order_relaxed);
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CancelManualCompaction2) {
|
||||
CompactRangeOptions compact_options;
|
||||
auto canceledPtr =
|
||||
std::unique_ptr<std::atomic<bool>>(new std::atomic<bool>{true});
|
||||
compact_options.canceled = canceledPtr.get();
|
||||
compact_options.max_subcompactions = 1;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.num_levels = 7;
|
||||
|
||||
Random rnd(301);
|
||||
auto generate_files = [&]() {
|
||||
for (int i = 0; i < options.num_levels; i++) {
|
||||
for (int j = 0; j < options.num_levels - i + 1; j++) {
|
||||
for (int k = 0; k < 1000; k++) {
|
||||
ASSERT_OK(Put(Key(k + j * 1000), rnd.RandomString(50)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
for (int l = 1; l < options.num_levels - i; l++) {
|
||||
MoveFilesToLevel(l);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DestroyAndReopen(options);
|
||||
generate_files();
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
int compactions_run = 0;
|
||||
std::atomic<int> kv_compactions{0};
|
||||
int compactions_stopped_at = 0;
|
||||
int kv_compactions_stopped_at = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RunManualCompaction()::1", [&](void* /*arg*/) {
|
||||
++compactions_run;
|
||||
// After 3 compactions disable
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator:ProcessKV", [&](void* /*arg*/) {
|
||||
int kv_compactions_run =
|
||||
kv_compactions.fetch_add(1, std::memory_order_release);
|
||||
if (kv_compactions_run == 5) {
|
||||
compact_options.canceled->store(true, std::memory_order_release);
|
||||
kv_compactions_stopped_at = kv_compactions_run;
|
||||
compactions_stopped_at = compactions_run;
|
||||
}
|
||||
});
|
||||
|
||||
compact_options.canceled->store(false, std::memory_order_release);
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
// NOTE: as we set compact_options.max_subcompacitons = 1, and store true to
|
||||
// the canceled variable from the single compacting thread (via callback),
|
||||
// this value is deterministically kv_compactions_stopped_at + 1.
|
||||
ASSERT_EQ(kv_compactions, kv_compactions_stopped_at + 1);
|
||||
ASSERT_EQ(compactions_run, compactions_stopped_at);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionIterator::ProcessKV");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"DBImpl::RunManualCompaction()::1");
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionJob::Run():PausingManualCompaction:1");
|
||||
|
||||
// Compactions should work again if we re-enable them..
|
||||
compact_options.canceled->store(false, std::memory_order_relaxed);
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
#ifndef ROCKSDB_LITE
|
||||
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
class CancelCompactionListener : public EventListener {
|
||||
public:
|
||||
CancelCompactionListener()
|
||||
: num_compaction_started_(0), num_compaction_ended_(0) {}
|
||||
|
||||
void OnCompactionBegin(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.cf_name, "default");
|
||||
ASSERT_EQ(ci.base_input_level, 0);
|
||||
num_compaction_started_++;
|
||||
}
|
||||
|
||||
void OnCompactionCompleted(DB* /*db*/, const CompactionJobInfo& ci) override {
|
||||
ASSERT_EQ(ci.cf_name, "default");
|
||||
ASSERT_EQ(ci.base_input_level, 0);
|
||||
ASSERT_EQ(ci.status.code(), code_);
|
||||
ASSERT_EQ(ci.status.subcode(), subcode_);
|
||||
num_compaction_ended_++;
|
||||
}
|
||||
|
||||
std::atomic<size_t> num_compaction_started_;
|
||||
std::atomic<size_t> num_compaction_ended_;
|
||||
Status::Code code_;
|
||||
Status::SubCode subcode_;
|
||||
};
|
||||
|
||||
TEST_F(DBTest2, CancelManualCompactionWithListener) {
|
||||
CompactRangeOptions compact_options;
|
||||
auto canceledPtr =
|
||||
std::unique_ptr<std::atomic<bool>>(new std::atomic<bool>{true});
|
||||
compact_options.canceled = canceledPtr.get();
|
||||
compact_options.max_subcompactions = 1;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
CancelCompactionListener* listener = new CancelCompactionListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
ASSERT_OK(Put(Key(i + j * 10), rnd.RandomString(50)));
|
||||
}
|
||||
Flush();
|
||||
}
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionIterator:ProcessKV", [&](void* /*arg*/) {
|
||||
compact_options.canceled->store(true, std::memory_order_release);
|
||||
});
|
||||
|
||||
int running_compaction = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::FinishCompactionOutputFile1",
|
||||
[&](void* /*arg*/) { running_compaction++; });
|
||||
|
||||
// Case I: 1 Notify begin compaction, 2 DisableManualCompaction, 3 Compaction
|
||||
// not run, 4 Notify compaction end.
|
||||
listener->code_ = Status::kIncomplete;
|
||||
listener->subcode_ = Status::SubCode::kManualCompactionPaused;
|
||||
|
||||
compact_options.canceled->store(false, std::memory_order_release);
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_GT(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
ASSERT_EQ(running_compaction, 0);
|
||||
|
||||
listener->num_compaction_started_ = 0;
|
||||
listener->num_compaction_ended_ = 0;
|
||||
|
||||
// Case II: 1 DisableManualCompaction, 2 Notify begin compaction (return
|
||||
// without notifying), 3 Notify compaction end (return without notifying).
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_EQ(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
ASSERT_EQ(running_compaction, 0);
|
||||
|
||||
// Case III: 1 Notify begin compaction, 2 Compaction in between
|
||||
// 3. DisableManualCompaction, , 4 Notify compaction end.
|
||||
// compact_options.canceled->store(false, std::memory_order_release);
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearCallBack(
|
||||
"CompactionIterator:ProcessKV");
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run:BeforeVerify", [&](void* /*arg*/) {
|
||||
compact_options.canceled->store(true, std::memory_order_release);
|
||||
});
|
||||
|
||||
listener->code_ = Status::kOk;
|
||||
listener->subcode_ = Status::SubCode::kNone;
|
||||
|
||||
compact_options.canceled->store(false, std::memory_order_release);
|
||||
dbfull()->CompactRange(compact_options, nullptr, nullptr);
|
||||
dbfull()->TEST_WaitForCompact(true);
|
||||
|
||||
ASSERT_GT(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
|
||||
// Compaction job will succeed.
|
||||
ASSERT_GT(running_compaction, 0);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CompactionOnBottomPriorityWithListener) {
|
||||
int num_levels = 3;
|
||||
const int kNumFilesTrigger = 4;
|
||||
|
||||
Options options = CurrentOptions();
|
||||
env_->SetBackgroundThreads(0, Env::Priority::HIGH);
|
||||
env_->SetBackgroundThreads(0, Env::Priority::LOW);
|
||||
env_->SetBackgroundThreads(1, Env::Priority::BOTTOM);
|
||||
options.env = env_;
|
||||
options.compaction_style = kCompactionStyleUniversal;
|
||||
options.num_levels = num_levels;
|
||||
options.write_buffer_size = 100 << 10; // 100KB
|
||||
options.target_file_size_base = 32 << 10; // 32KB
|
||||
options.level0_file_num_compaction_trigger = kNumFilesTrigger;
|
||||
// Trigger compaction if size amplification exceeds 110%
|
||||
options.compaction_options_universal.max_size_amplification_percent = 110;
|
||||
|
||||
CancelCompactionListener* listener = new CancelCompactionListener();
|
||||
options.listeners.emplace_back(listener);
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
int num_bottom_thread_compaction_scheduled = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
|
||||
[&](void* /*arg*/) { num_bottom_thread_compaction_scheduled++; });
|
||||
|
||||
int num_compaction_jobs = 0;
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionJob::Run():End",
|
||||
[&](void* /*arg*/) { num_compaction_jobs++; });
|
||||
|
||||
listener->code_ = Status::kOk;
|
||||
listener->subcode_ = Status::SubCode::kNone;
|
||||
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < 1; ++i) {
|
||||
for (int num = 0; num < kNumFilesTrigger; num++) {
|
||||
int key_idx = 0;
|
||||
GenerateNewFile(&rnd, &key_idx, true /* no_wait */);
|
||||
// use no_wait above because that one waits for flush and compaction. We
|
||||
// don't want to wait for compaction because the full compaction is
|
||||
// intentionally blocked while more files are flushed.
|
||||
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
|
||||
}
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
ASSERT_GT(num_bottom_thread_compaction_scheduled, 0);
|
||||
ASSERT_EQ(num_compaction_jobs, 1);
|
||||
ASSERT_GT(listener->num_compaction_started_, 0);
|
||||
ASSERT_EQ(listener->num_compaction_started_, listener->num_compaction_ended_);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, OptimizeForPointLookup) {
|
||||
Options options = CurrentOptions();
|
||||
Close();
|
||||
|
||||
+9
-13
@@ -59,16 +59,8 @@ SpecialEnv::SpecialEnv(Env* base, bool time_elapse_only_sleep)
|
||||
DBTestBase::DBTestBase(const std::string path, bool env_do_fsync)
|
||||
: mem_env_(nullptr), encrypted_env_(nullptr), option_config_(kDefault) {
|
||||
Env* base_env = Env::Default();
|
||||
#ifndef ROCKSDB_LITE
|
||||
const char* test_env_uri = getenv("TEST_ENV_URI");
|
||||
if (test_env_uri) {
|
||||
Env* test_env = nullptr;
|
||||
Status s = Env::LoadEnv(test_env_uri, &test_env, &env_guard_);
|
||||
base_env = test_env;
|
||||
EXPECT_OK(s);
|
||||
EXPECT_NE(Env::Default(), base_env);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
ConfigOptions config_options;
|
||||
EXPECT_OK(test::CreateEnvFromSystem(config_options, &base_env, &env_guard_));
|
||||
EXPECT_NE(nullptr, base_env);
|
||||
if (getenv("MEM_ENV")) {
|
||||
mem_env_ = new MockEnv(base_env);
|
||||
@@ -76,9 +68,13 @@ DBTestBase::DBTestBase(const std::string path, bool env_do_fsync)
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (getenv("ENCRYPTED_ENV")) {
|
||||
std::shared_ptr<EncryptionProvider> provider;
|
||||
Status s = EncryptionProvider::CreateFromString(
|
||||
ConfigOptions(), std::string("test://") + getenv("ENCRYPTED_ENV"),
|
||||
&provider);
|
||||
std::string provider_id = getenv("ENCRYPTED_ENV");
|
||||
if (provider_id.find("=") == std::string::npos &&
|
||||
!EndsWith(provider_id, "://test")) {
|
||||
provider_id = provider_id + "://test";
|
||||
}
|
||||
EXPECT_OK(EncryptionProvider::CreateFromString(ConfigOptions(), provider_id,
|
||||
&provider));
|
||||
encrypted_env_ = NewEncryptedEnv(mem_env_ ? mem_env_ : base_env, provider);
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
@@ -303,6 +303,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
Status Allocate(uint64_t offset, uint64_t len) override {
|
||||
return base_->Allocate(offset, len);
|
||||
}
|
||||
size_t GetUniqueId(char* id, size_t max_size) const override {
|
||||
return base_->GetUniqueId(id, max_size);
|
||||
}
|
||||
};
|
||||
class ManifestFile : public WritableFile {
|
||||
public:
|
||||
@@ -672,6 +675,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& src, const std::string& dest) override {
|
||||
rename_count_.fetch_add(1);
|
||||
if (rename_error_.load(std::memory_order_acquire)) {
|
||||
return Status::NotSupported("Simulated `RenameFile()` error.");
|
||||
}
|
||||
return target()->RenameFile(src, dest);
|
||||
}
|
||||
|
||||
// Something to return when mocking current time
|
||||
const int64_t maybe_starting_time_;
|
||||
|
||||
@@ -699,6 +710,9 @@ class SpecialEnv : public EnvWrapper {
|
||||
// Force write to log files to fail while this pointer is non-nullptr
|
||||
std::atomic<bool> log_write_error_;
|
||||
|
||||
// Force `RenameFile()` to fail while this pointer is non-nullptr
|
||||
std::atomic<bool> rename_error_{false};
|
||||
|
||||
// Slow down every log write, in micro-seconds.
|
||||
std::atomic<int> log_write_slowdown_;
|
||||
|
||||
@@ -742,6 +756,8 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
std::atomic<int> delete_count_;
|
||||
|
||||
std::atomic<int> rename_count_{0};
|
||||
|
||||
std::atomic<bool> is_wal_sync_thread_safe_{true};
|
||||
|
||||
std::atomic<size_t> compaction_readahead_size_{};
|
||||
|
||||
@@ -635,7 +635,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTargetLevel) {
|
||||
ASSERT_EQ("0,0,0,0,1", FilesPerLevel(0));
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
class DBTestUniversalCompactionMultiLevels
|
||||
: public DBTestUniversalCompactionBase {
|
||||
public:
|
||||
@@ -912,7 +912,7 @@ TEST_P(DBTestUniversalCompactionParallel, PickByFileNumberBug) {
|
||||
INSTANTIATE_TEST_CASE_P(Parallel, DBTestUniversalCompactionParallel,
|
||||
::testing::Combine(::testing::Values(1, 10),
|
||||
::testing::Values(false)));
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionOptions) {
|
||||
Options options = CurrentOptions();
|
||||
@@ -1128,7 +1128,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionCompressRatio2) {
|
||||
ASSERT_LT(TotalSize(), 120000U * 12 * 0.82 + 120000 * 2);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_VALGRIND_RUN
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
// Test that checks trivial move in universal compaction
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionTrivialMoveTest1) {
|
||||
int32_t trivial_move = 0;
|
||||
@@ -1221,7 +1221,7 @@ TEST_P(DBTestUniversalCompaction, UniversalCompactionTrivialMoveTest2) {
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
#endif // ROCKSDB_VALGRIND_RUN
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_P(DBTestUniversalCompaction, UniversalCompactionFourPaths) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
+60
-4
@@ -1262,10 +1262,11 @@ class RecoveryTestHelper {
|
||||
std::unique_ptr<WalManager> wal_manager;
|
||||
WriteController write_controller;
|
||||
|
||||
versions.reset(new VersionSet(
|
||||
test->dbname_, &db_options, file_options, table_cache.get(),
|
||||
&write_buffer_manager, &write_controller,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr));
|
||||
versions.reset(new VersionSet(test->dbname_, &db_options, file_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller,
|
||||
/*block_cache_tracer=*/nullptr,
|
||||
/*io_tracer=*/nullptr, /*db_session_id*/ ""));
|
||||
|
||||
wal_manager.reset(
|
||||
new WalManager(db_options, file_options, /*io_tracer=*/nullptr));
|
||||
@@ -2001,6 +2002,61 @@ TEST_F(DBWALTest, TruncateLastLogAfterRecoverWALEmpty) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBWALTest, ReadOnlyRecoveryNoTruncate) {
|
||||
constexpr size_t kKB = 1024;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.avoid_flush_during_recovery = true;
|
||||
if (mem_env_) {
|
||||
ROCKSDB_GTEST_SKIP("Test requires non-mem environment");
|
||||
return;
|
||||
}
|
||||
if (!IsFallocateSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create DB and close with file truncate disabled
|
||||
std::atomic_bool enable_truncate{false};
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"PosixWritableFile::Close", [&](void* arg) {
|
||||
if (!enable_truncate) {
|
||||
*(reinterpret_cast<size_t*>(arg)) = 0;
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
size_t preallocated_size =
|
||||
dbfull()->TEST_GetWalPreallocateBlockSize(options.write_buffer_size);
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
VectorLogPtr log_files_before;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files_before));
|
||||
ASSERT_EQ(1, log_files_before.size());
|
||||
auto& file_before = log_files_before[0];
|
||||
ASSERT_LT(file_before->SizeFileBytes(), 1 * kKB);
|
||||
// The log file has preallocated space.
|
||||
auto db_size = GetAllocatedFileSize(dbname_ + file_before->PathName());
|
||||
ASSERT_GE(db_size, preallocated_size);
|
||||
Close();
|
||||
|
||||
// enable truncate and open DB as readonly, the file should not be truncated
|
||||
// and DB size is not changed.
|
||||
enable_truncate = true;
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
VectorLogPtr log_files_after;
|
||||
ASSERT_OK(dbfull()->GetSortedWalFiles(log_files_after));
|
||||
ASSERT_EQ(1, log_files_after.size());
|
||||
ASSERT_LT(log_files_after[0]->SizeFileBytes(), 1 * kKB);
|
||||
ASSERT_EQ(log_files_after[0]->PathName(), file_before->PathName());
|
||||
// The preallocated space should NOT be truncated.
|
||||
// the DB size is almost the same.
|
||||
ASSERT_NEAR(GetAllocatedFileSize(dbname_ + file_before->PathName()), db_size,
|
||||
db_size / 100);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
#endif // ROCKSDB_FALLOCATE_PRESENT
|
||||
#endif // ROCKSDB_PLATFORM_POSIX
|
||||
|
||||
|
||||
@@ -1494,6 +1494,7 @@ TEST_F(DBBasicTestWithTimestamp, CompactDeletionWithTimestampMarkerToBottom) {
|
||||
Close();
|
||||
}
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
class DBBasicTestWithTimestampFilterPrefixSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
@@ -1625,6 +1626,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey)));
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
class DataVisibilityTest : public DBBasicTestWithTimestampBase {
|
||||
public:
|
||||
@@ -2215,6 +2217,7 @@ TEST_F(DataVisibilityTest, MultiGetCrossCF) {
|
||||
Close();
|
||||
}
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
class DBBasicTestWithTimestampCompressionSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
@@ -2954,6 +2957,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
BlockBasedTableOptions::IndexType::kHashSearch,
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch,
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey)));
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+7
-4
@@ -418,15 +418,18 @@ const Status& ErrorHandler::SetBGError(const IOStatus& bg_io_err,
|
||||
&bg_err, db_mutex_, &auto_recovery);
|
||||
recover_context_ = context;
|
||||
return bg_error_;
|
||||
} else if (bg_io_err.GetScope() ==
|
||||
IOStatus::IOErrorScope::kIOErrorScopeFile ||
|
||||
bg_io_err.GetRetryable()) {
|
||||
} else if (bg_io_err.subcode() != IOStatus::SubCode::kNoSpace &&
|
||||
(bg_io_err.GetScope() ==
|
||||
IOStatus::IOErrorScope::kIOErrorScopeFile ||
|
||||
bg_io_err.GetRetryable())) {
|
||||
// Second, check if the error is a retryable IO error (file scope IO error
|
||||
// is also treated as retryable IO error in RocksDB write path). if it is
|
||||
// retryable error and its severity is higher than bg_error_, overwrite the
|
||||
// bg_error_ with new error. In current stage, for retryable IO error of
|
||||
// compaction, treat it as soft error. In other cases, treat the retryable
|
||||
// IO error as hard error.
|
||||
// IO error as hard error. Note that, all the NoSpace error should be
|
||||
// handled by the SstFileManager::StartErrorRecovery(). Therefore, no matter
|
||||
// it is retryable or file scope, this logic will be bypassed.
|
||||
bool auto_recovery = false;
|
||||
EventHelpers::NotifyOnBackgroundError(db_options_.listeners, reason,
|
||||
&new_bg_io_err, db_mutex_,
|
||||
|
||||
@@ -193,6 +193,53 @@ TEST_F(DBErrorHandlingFSTest, FLushWriteError) {
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
// All the NoSpace IOError will be handled as the regular BG Error no matter the
|
||||
// retryable flag is set of not. So the auto resume for retryable IO Error will
|
||||
// not be triggered. Also, it is mapped as hard error.
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteNoSpaceError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
Options options = GetDefaultOptions();
|
||||
options.env = fault_env_.get();
|
||||
options.create_if_missing = true;
|
||||
options.listeners.emplace_back(listener);
|
||||
options.max_bgerror_resume_count = 2;
|
||||
options.bgerror_resume_retry_interval = 100000; // 0.1 second
|
||||
options.statistics = CreateDBStatistics();
|
||||
Status s;
|
||||
|
||||
listener->EnableAutoRecovery(false);
|
||||
DestroyAndReopen(options);
|
||||
|
||||
IOStatus error_msg = IOStatus::NoSpace("Retryable IO Error");
|
||||
error_msg.SetRetryable(true);
|
||||
|
||||
ASSERT_OK(Put(Key(1), "val1"));
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"BuildTable:BeforeFinishBuildTable",
|
||||
[&](void*) { fault_fs_->SetFilesystemActive(false, error_msg); });
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
s = Flush();
|
||||
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kHardError);
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
fault_fs_->SetFilesystemActive(true);
|
||||
s = dbfull()->Resume();
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_ERROR_COUNT));
|
||||
ASSERT_EQ(1, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_BG_RETRYABLE_IO_ERROR_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_RETRY_TOTAL_COUNT));
|
||||
ASSERT_EQ(0, options.statistics->getAndResetTickerCount(
|
||||
ERROR_HANDLER_AUTORESUME_SUCCESS_COUNT));
|
||||
Destroy(options);
|
||||
}
|
||||
|
||||
TEST_F(DBErrorHandlingFSTest, FLushWriteRetryableError) {
|
||||
std::shared_ptr<ErrorHandlerFSListener> listener(
|
||||
new ErrorHandlerFSListener());
|
||||
|
||||
@@ -1398,6 +1398,7 @@ TEST_F(ExternalSSTFileTest, IngestNonExistingFile) {
|
||||
ASSERT_EQ(1, num_sst_files);
|
||||
}
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
TEST_F(ExternalSSTFileTest, CompactDuringAddFileRandom) {
|
||||
env_->skip_fsync_ = true;
|
||||
Options options = CurrentOptions();
|
||||
@@ -1455,6 +1456,7 @@ TEST_F(ExternalSSTFileTest, CompactDuringAddFileRandom) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_F(ExternalSSTFileTest, PickedLevelDynamic) {
|
||||
env_->skip_fsync_ = true;
|
||||
@@ -1716,6 +1718,7 @@ TEST_F(ExternalSSTFileTest, WithUnorderedWrite) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
env_->skip_fsync_ = true;
|
||||
Options options = CurrentOptions();
|
||||
@@ -1757,6 +1760,7 @@ TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoRandomized) {
|
||||
VerifyDBFromMap(true_data, &kcnt, false);
|
||||
}
|
||||
}
|
||||
#endif // !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFileWithGlobalSeqnoAssignedLevel) {
|
||||
Options options = CurrentOptions();
|
||||
|
||||
+301
-7
@@ -227,9 +227,33 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
prev_cpu_write_nanos = IOSTATS(cpu_write_nanos);
|
||||
prev_cpu_read_nanos = IOSTATS(cpu_read_nanos);
|
||||
}
|
||||
|
||||
// This will release and re-acquire the mutex.
|
||||
Status s = WriteLevel0Table();
|
||||
Status mempurge_s = Status::NotFound("No MemPurge.");
|
||||
if (db_options_.experimental_allow_mempurge &&
|
||||
(cfd_->GetFlushReason() == FlushReason::kWriteBufferFull) &&
|
||||
(!mems_.empty())) {
|
||||
mempurge_s = MemPurge();
|
||||
if (!mempurge_s.ok()) {
|
||||
// Mempurge is typically aborted when the new_mem output memtable
|
||||
// is filled at more than XX % capacity (currently: 60%).
|
||||
if (mempurge_s.IsAborted()) {
|
||||
ROCKS_LOG_INFO(db_options_.info_log, "Mempurge process aborted: %s\n",
|
||||
mempurge_s.ToString().c_str());
|
||||
} else {
|
||||
// However the mempurge process can also fail for
|
||||
// other reasons (eg: new_mem->Add() fails).
|
||||
ROCKS_LOG_WARN(db_options_.info_log, "Mempurge process failed: %s\n",
|
||||
mempurge_s.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
Status s;
|
||||
if (mempurge_s.ok()) {
|
||||
base_->Unref();
|
||||
s = Status::OK();
|
||||
} else {
|
||||
// This will release and re-acquire the mutex.
|
||||
s = WriteLevel0Table();
|
||||
}
|
||||
|
||||
if (s.ok() && cfd_->IsDropped()) {
|
||||
s = Status::ColumnFamilyDropped("Column family dropped during compaction");
|
||||
@@ -248,7 +272,10 @@ Status FlushJob::Run(LogsWithPrepTracker* prep_tracker,
|
||||
s = cfd_->imm()->TryInstallMemtableFlushResults(
|
||||
cfd_, mutable_cf_options_, mems_, prep_tracker, versions_, db_mutex_,
|
||||
meta_.fd.GetNumber(), &job_context_->memtables_to_free, db_directory_,
|
||||
log_buffer_, &committed_flush_jobs_info_, &tmp_io_s);
|
||||
log_buffer_, &committed_flush_jobs_info_, &tmp_io_s,
|
||||
!(mempurge_s.ok()) /* write_edit : true if no mempurge happened (or if aborted),
|
||||
but 'false' if mempurge successful: no new min log number
|
||||
or new level 0 file path to write to manifest. */);
|
||||
if (!tmp_io_s.ok()) {
|
||||
io_status_ = tmp_io_s;
|
||||
}
|
||||
@@ -306,6 +333,262 @@ void FlushJob::Cancel() {
|
||||
base_->Unref();
|
||||
}
|
||||
|
||||
uint64_t FlushJob::ExtractEarliestLogFileNumber() {
|
||||
uint64_t earliest_logno = 0;
|
||||
for (MemTable* m : mems_) {
|
||||
uint64_t logno = m->GetEarliestLogFileNumber();
|
||||
if (logno > 0 && (earliest_logno == 0 || logno < earliest_logno)) {
|
||||
earliest_logno = logno;
|
||||
}
|
||||
}
|
||||
return earliest_logno;
|
||||
}
|
||||
|
||||
Status FlushJob::MemPurge() {
|
||||
Status s;
|
||||
db_mutex_->AssertHeld();
|
||||
db_mutex_->Unlock();
|
||||
assert(!mems_.empty());
|
||||
|
||||
MemTable* new_mem = nullptr;
|
||||
|
||||
// Create two iterators, one for the memtable data (contains
|
||||
// info from puts + deletes), and one for the memtable
|
||||
// Range Tombstones (from DeleteRanges).
|
||||
ReadOptions ro;
|
||||
ro.total_order_seek = true;
|
||||
Arena arena;
|
||||
std::vector<InternalIterator*> memtables;
|
||||
std::vector<std::unique_ptr<FragmentedRangeTombstoneIterator>>
|
||||
range_del_iters;
|
||||
for (MemTable* m : mems_) {
|
||||
memtables.push_back(m->NewIterator(ro, &arena));
|
||||
auto* range_del_iter = m->NewRangeTombstoneIterator(ro, kMaxSequenceNumber);
|
||||
if (range_del_iter != nullptr) {
|
||||
range_del_iters.emplace_back(range_del_iter);
|
||||
}
|
||||
}
|
||||
|
||||
assert(!memtables.empty());
|
||||
SequenceNumber first_seqno = kMaxSequenceNumber;
|
||||
SequenceNumber earliest_seqno = kMaxSequenceNumber;
|
||||
// Pick first and earliest seqno as min of all first_seqno
|
||||
// and earliest_seqno of the mempurged memtables.
|
||||
for (const auto& mem : mems_) {
|
||||
first_seqno = mem->GetFirstSequenceNumber() < first_seqno
|
||||
? mem->GetFirstSequenceNumber()
|
||||
: first_seqno;
|
||||
earliest_seqno = mem->GetEarliestSequenceNumber() < earliest_seqno
|
||||
? mem->GetEarliestSequenceNumber()
|
||||
: earliest_seqno;
|
||||
}
|
||||
|
||||
ScopedArenaIterator iter(
|
||||
NewMergingIterator(&(cfd_->internal_comparator()), memtables.data(),
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
|
||||
uint64_t earliest_logno = ExtractEarliestLogFileNumber();
|
||||
|
||||
auto* ioptions = cfd_->ioptions();
|
||||
|
||||
// Place iterator at the First (meaning most recent) key node.
|
||||
iter->SeekToFirst();
|
||||
|
||||
std::unique_ptr<CompactionRangeDelAggregator> range_del_agg(
|
||||
new CompactionRangeDelAggregator(&(cfd_->internal_comparator()),
|
||||
existing_snapshots_));
|
||||
for (auto& rd_iter : range_del_iters) {
|
||||
range_del_agg->AddTombstones(std::move(rd_iter));
|
||||
}
|
||||
|
||||
// If there is valid data in the memtable,
|
||||
// or at least range tombstones, copy over the info
|
||||
// to the new memtable.
|
||||
if (iter->Valid() || !range_del_agg->IsEmpty()) {
|
||||
// Arbitrary heuristic: maxSize is 60% cpacity.
|
||||
size_t maxSize = ((mutable_cf_options_.write_buffer_size + 6U) / 10U);
|
||||
std::unique_ptr<CompactionFilter> compaction_filter;
|
||||
if (ioptions->compaction_filter_factory != nullptr &&
|
||||
ioptions->compaction_filter_factory->ShouldFilterTableFileCreation(
|
||||
TableFileCreationReason::kFlush)) {
|
||||
CompactionFilter::Context ctx;
|
||||
ctx.is_full_compaction = false;
|
||||
ctx.is_manual_compaction = false;
|
||||
ctx.column_family_id = cfd_->GetID();
|
||||
ctx.reason = TableFileCreationReason::kFlush;
|
||||
compaction_filter =
|
||||
ioptions->compaction_filter_factory->CreateCompactionFilter(ctx);
|
||||
if (compaction_filter != nullptr &&
|
||||
!compaction_filter->IgnoreSnapshots()) {
|
||||
s = Status::NotSupported(
|
||||
"CompactionFilter::IgnoreSnapshots() = false is not supported "
|
||||
"anymore.");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
new_mem = new MemTable((cfd_->internal_comparator()), *(cfd_->ioptions()),
|
||||
mutable_cf_options_, cfd_->write_buffer_mgr(),
|
||||
earliest_seqno, cfd_->GetID(), earliest_logno);
|
||||
assert(new_mem != nullptr);
|
||||
|
||||
Env* env = db_options_.env;
|
||||
assert(env);
|
||||
MergeHelper merge(
|
||||
env, (cfd_->internal_comparator()).user_comparator(),
|
||||
(ioptions->merge_operator).get(), compaction_filter.get(),
|
||||
ioptions->logger, true /* internal key corruption is not ok */,
|
||||
existing_snapshots_.empty() ? 0 : existing_snapshots_.back(),
|
||||
snapshot_checker_);
|
||||
CompactionIterator c_iter(
|
||||
iter.get(), (cfd_->internal_comparator()).user_comparator(), &merge,
|
||||
kMaxSequenceNumber, &existing_snapshots_,
|
||||
earliest_write_conflict_snapshot_, snapshot_checker_, env,
|
||||
ShouldReportDetailedTime(env, ioptions->stats),
|
||||
true /* internal key corruption is not ok */, range_del_agg.get(),
|
||||
nullptr, ioptions->allow_data_in_errors,
|
||||
/*compaction=*/nullptr, compaction_filter.get(),
|
||||
/*shutting_down=*/nullptr,
|
||||
/*preserve_deletes_seqnum=*/0, /*manual_compaction_paused=*/nullptr,
|
||||
/*manual_compaction_canceled=*/nullptr, ioptions->info_log,
|
||||
&(cfd_->GetFullHistoryTsLow()));
|
||||
|
||||
// Set earliest sequence number in the new memtable
|
||||
// to be equal to the earliest sequence number of the
|
||||
// memtable being flushed (See later if there is a need
|
||||
// to update this number!).
|
||||
new_mem->SetEarliestSequenceNumber(earliest_seqno);
|
||||
// Likewise for first seq number.
|
||||
new_mem->SetFirstSequenceNumber(first_seqno);
|
||||
SequenceNumber new_first_seqno = kMaxSequenceNumber;
|
||||
|
||||
c_iter.SeekToFirst();
|
||||
|
||||
// Key transfer
|
||||
for (; c_iter.Valid(); c_iter.Next()) {
|
||||
const ParsedInternalKey ikey = c_iter.ikey();
|
||||
const Slice value = c_iter.value();
|
||||
new_first_seqno =
|
||||
ikey.sequence < new_first_seqno ? ikey.sequence : new_first_seqno;
|
||||
|
||||
// Should we update "OldestKeyTime" ???? -> timestamp appear
|
||||
// to still be an "experimental" feature.
|
||||
s = new_mem->Add(
|
||||
ikey.sequence, ikey.type, ikey.user_key, value,
|
||||
nullptr, // KV protection info set as nullptr since it
|
||||
// should only be useful for the first add to
|
||||
// the original memtable.
|
||||
false, // : allow concurrent_memtable_writes_
|
||||
// Not seen as necessary for now.
|
||||
nullptr, // get_post_process_info(m) must be nullptr
|
||||
// when concurrent_memtable_writes is switched off.
|
||||
nullptr); // hint, only used when concurrent_memtable_writes_
|
||||
// is switched on.
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If new_mem has size greater than maxSize,
|
||||
// then rollback to regular flush operation,
|
||||
// and destroy new_mem.
|
||||
if (new_mem->ApproximateMemoryUsage() > maxSize) {
|
||||
s = Status::Aborted("Mempurge filled more than one memtable.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check status and propagate
|
||||
// potential error status from c_iter
|
||||
if (!s.ok()) {
|
||||
c_iter.status().PermitUncheckedError();
|
||||
} else if (!c_iter.status().ok()) {
|
||||
s = c_iter.status();
|
||||
}
|
||||
|
||||
// Range tombstone transfer.
|
||||
if (s.ok()) {
|
||||
auto range_del_it = range_del_agg->NewIterator();
|
||||
for (range_del_it->SeekToFirst(); range_del_it->Valid();
|
||||
range_del_it->Next()) {
|
||||
auto tombstone = range_del_it->Tombstone();
|
||||
new_first_seqno =
|
||||
tombstone.seq_ < new_first_seqno ? tombstone.seq_ : new_first_seqno;
|
||||
s = new_mem->Add(
|
||||
tombstone.seq_, // Sequence number
|
||||
kTypeRangeDeletion, // KV type
|
||||
tombstone.start_key_, // Key is start key.
|
||||
tombstone.end_key_, // Value is end key.
|
||||
nullptr, // KV protection info set as nullptr since it
|
||||
// should only be useful for the first add to
|
||||
// the original memtable.
|
||||
false, // : allow concurrent_memtable_writes_
|
||||
// Not seen as necessary for now.
|
||||
nullptr, // get_post_process_info(m) must be nullptr
|
||||
// when concurrent_memtable_writes is switched off.
|
||||
nullptr); // hint, only used when concurrent_memtable_writes_
|
||||
// is switched on.
|
||||
|
||||
if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If new_mem has size greater than maxSize,
|
||||
// then rollback to regular flush operation,
|
||||
// and destroy new_mem.
|
||||
if (new_mem->ApproximateMemoryUsage() > maxSize) {
|
||||
s = Status::Aborted(Slice("Mempurge filled more than one memtable."));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If everything happened smoothly and new_mem contains valid data,
|
||||
// decide if it is flushed to storage or kept in the imm()
|
||||
// memtable list (memory).
|
||||
if (s.ok() && (new_first_seqno != kMaxSequenceNumber)) {
|
||||
// Rectify the first sequence number, which (unlike the earliest seq
|
||||
// number) needs to be present in the new memtable.
|
||||
new_mem->SetFirstSequenceNumber(new_first_seqno);
|
||||
|
||||
// The new_mem is added to the list of immutable memtables
|
||||
// only if it filled at less than 60% capacity (arbitrary heuristic).
|
||||
if (new_mem->ApproximateMemoryUsage() < maxSize) {
|
||||
db_mutex_->Lock();
|
||||
cfd_->imm()->Add(new_mem,
|
||||
&job_context_->memtables_to_free,
|
||||
false /* -> trigger_flush=false:
|
||||
* adding this memtable
|
||||
* will not trigger a flush.
|
||||
*/);
|
||||
new_mem->Ref();
|
||||
db_mutex_->Unlock();
|
||||
} else {
|
||||
s = Status::Aborted(Slice("Mempurge filled more than one memtable."));
|
||||
if (new_mem) {
|
||||
job_context_->memtables_to_free.push_back(new_mem);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In this case, the newly allocated new_mem is empty.
|
||||
assert(new_mem != nullptr);
|
||||
job_context_->memtables_to_free.push_back(new_mem);
|
||||
}
|
||||
}
|
||||
|
||||
// Reacquire the mutex for WriteLevel0 function.
|
||||
db_mutex_->Lock();
|
||||
|
||||
// If mempurge successful, don't write input tables to level0,
|
||||
// but write any full output table to level0.
|
||||
if (s.ok()) {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:MemPurgeSuccessful");
|
||||
} else {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:MemPurgeUnsuccessful");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Status FlushJob::WriteLevel0Table() {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_FLUSH_WRITE_L0);
|
||||
@@ -362,7 +645,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
|
||||
{
|
||||
ScopedArenaIterator iter(
|
||||
NewMergingIterator(&cfd_->internal_comparator(), &memtables[0],
|
||||
NewMergingIterator(&cfd_->internal_comparator(), memtables.data(),
|
||||
static_cast<int>(memtables.size()), &arena));
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"[%s] [JOB %d] Level-0 flush table #%" PRIu64 ": started",
|
||||
@@ -403,6 +686,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
: meta_.oldest_ancester_time;
|
||||
|
||||
uint64_t num_input_entries = 0;
|
||||
uint64_t memtable_payload_bytes = 0;
|
||||
uint64_t memtable_garbage_bytes = 0;
|
||||
IOStatus io_s;
|
||||
const std::string* const full_history_ts_low =
|
||||
(full_history_ts_low_.empty()) ? nullptr : &full_history_ts_low_;
|
||||
@@ -412,7 +697,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
mutable_cf_options_.compression_opts, cfd_->GetID(), cfd_->GetName(),
|
||||
0 /* level */, false /* is_bottommost */,
|
||||
TableFileCreationReason::kFlush, creation_time, oldest_key_time,
|
||||
current_time, db_id_, db_session_id_, 0 /* target_file_size */);
|
||||
current_time, db_id_, db_session_id_, 0 /* target_file_size */,
|
||||
meta_.fd.GetNumber());
|
||||
s = BuildTable(
|
||||
dbname_, versions_, db_options_, tboptions, file_options_,
|
||||
cfd_->table_cache(), iter.get(), std::move(range_del_iters), &meta_,
|
||||
@@ -421,7 +707,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
&io_s, io_tracer_, event_logger_, job_context_->job_id, Env::IO_HIGH,
|
||||
&table_properties_, write_hint, full_history_ts_low, blob_callback_,
|
||||
&num_input_entries);
|
||||
&num_input_entries, &memtable_payload_bytes, &memtable_garbage_bytes);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
@@ -436,6 +722,13 @@ Status FlushJob::WriteLevel0Table() {
|
||||
s = Status::Corruption(msg);
|
||||
}
|
||||
}
|
||||
if (tboptions.reason == TableFileCreationReason::kFlush) {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:Flush");
|
||||
RecordTick(stats_, MEMTABLE_PAYLOAD_BYTES_AT_FLUSH,
|
||||
memtable_payload_bytes);
|
||||
RecordTick(stats_, MEMTABLE_GARBAGE_BYTES_AT_FLUSH,
|
||||
memtable_garbage_bytes);
|
||||
}
|
||||
LogFlush(db_options_.info_log);
|
||||
}
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
@@ -460,6 +753,7 @@ Status FlushJob::WriteLevel0Table() {
|
||||
const bool has_output = meta_.fd.GetFileSize() > 0;
|
||||
|
||||
if (s.ok() && has_output) {
|
||||
TEST_SYNC_POINT("DBImpl::FlushJob:SSTFileCreated");
|
||||
// if we have more than 1 background thread, then we cannot
|
||||
// insert files directly into higher levels because some other
|
||||
// threads could be concurrently producing compacted files for
|
||||
|
||||
@@ -101,6 +101,29 @@ class FlushJob {
|
||||
void ReportFlushInputSize(const autovector<MemTable*>& mems);
|
||||
void RecordFlushIOStats();
|
||||
Status WriteLevel0Table();
|
||||
|
||||
// Memtable Garbage Collection algorithm: a MemPurge takes the list
|
||||
// of immutable memtables and filters out (or "purge") the outdated bytes
|
||||
// out of it. The output (the filtered bytes, or "useful payload") is
|
||||
// then transfered into a new memtable. If this memtable is filled, then
|
||||
// the mempurge is aborted and rerouted to a regular flush process. Else,
|
||||
// depending on the heuristics, placed onto the immutable memtable list.
|
||||
// The addition to the imm list will not trigger a flush operation. The
|
||||
// flush of the imm list will instead be triggered once the mutable memtable
|
||||
// is added to the imm list.
|
||||
// This process is typically intended for workloads with heavy overwrites
|
||||
// when we want to avoid SSD writes (and reads) as much as possible.
|
||||
// "MemPurge" is an experimental feature still at a very early stage
|
||||
// of development. At the moment it is only compatible with the Get, Put,
|
||||
// Delete operations as well as Iterators and CompactionFilters.
|
||||
// For this early version, "MemPurge" is called by setting the
|
||||
// options.experimental_allow_mempurge flag as "true". When this is
|
||||
// the case, ALL automatic flush operations (kWRiteBufferManagerFull) will
|
||||
// first go through the MemPurge process. herefore, we strongly
|
||||
// recommend all users not to set this flag as true given that the MemPurge
|
||||
// process has not matured yet.
|
||||
Status MemPurge();
|
||||
uint64_t ExtractEarliestLogFileNumber();
|
||||
#ifndef ROCKSDB_LITE
|
||||
std::unique_ptr<FlushJobInfo> GetFlushJobInfo() const;
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
+11
-3
@@ -127,7 +127,8 @@ class FlushJobTestBase : public testing::Test {
|
||||
versions_.reset(
|
||||
new VersionSet(dbname_, &db_options_, env_options_, table_cache_.get(),
|
||||
&write_buffer_manager_, &write_controller_,
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr));
|
||||
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr,
|
||||
/*db_session_id*/ ""));
|
||||
EXPECT_OK(versions_->Recover(column_families, false));
|
||||
}
|
||||
|
||||
@@ -417,12 +418,19 @@ TEST_F(FlushJobTest, FlushMemtablesMultipleColumnFamilies) {
|
||||
for (auto cfd : all_cfds) {
|
||||
mutable_cf_options_list.push_back(cfd->GetLatestMutableCFOptions());
|
||||
}
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
#ifndef ROCKSDB_LITE
|
||||
for (auto& job : flush_jobs) {
|
||||
committed_flush_jobs_info.push_back(job->GetCommittedFlushJobsInfo());
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
Status s = InstallMemtableAtomicFlushResults(
|
||||
nullptr /* imm_lists */, all_cfds, mutable_cf_options_list, mems_list,
|
||||
versions_.get(), nullptr /* prep_tracker */, &mutex_, file_meta_ptrs,
|
||||
&job_context.memtables_to_free, nullptr /* db_directory */,
|
||||
nullptr /* log_buffer */);
|
||||
committed_flush_jobs_info, &job_context.memtables_to_free,
|
||||
nullptr /* db_directory */, nullptr /* log_buffer */);
|
||||
ASSERT_OK(s);
|
||||
|
||||
mutex_.Unlock();
|
||||
|
||||
@@ -1267,7 +1267,7 @@ void InternalStats::DumpDBStats(std::string* value) {
|
||||
snprintf(
|
||||
buf, sizeof(buf),
|
||||
"Interval WAL: %s writes, %s syncs, "
|
||||
"%.2f writes per sync, written: %.2f MB, %.2f MB/s\n",
|
||||
"%.2f writes per sync, written: %.2f GB, %.2f MB/s\n",
|
||||
NumberToHumanString(interval_write_with_wal).c_str(),
|
||||
NumberToHumanString(interval_wal_synced).c_str(),
|
||||
interval_write_with_wal / static_cast<double>(interval_wal_synced + 1),
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ struct SuperVersion;
|
||||
struct SuperVersionContext {
|
||||
struct WriteStallNotification {
|
||||
WriteStallInfo write_stall_info;
|
||||
const ImmutableOptions* immutable_cf_options;
|
||||
const ImmutableOptions* immutable_options;
|
||||
};
|
||||
|
||||
autovector<SuperVersion*> superversions_to_free;
|
||||
@@ -66,7 +66,7 @@ struct SuperVersionContext {
|
||||
notif.write_stall_info.cf_name = name;
|
||||
notif.write_stall_info.condition.prev = old_cond;
|
||||
notif.write_stall_info.condition.cur = new_cond;
|
||||
notif.immutable_cf_options = ioptions;
|
||||
notif.immutable_options = ioptions;
|
||||
write_stall_notifications.push_back(notif);
|
||||
#else
|
||||
(void)old_cond;
|
||||
@@ -80,7 +80,7 @@ struct SuperVersionContext {
|
||||
#if !defined(ROCKSDB_LITE) && !defined(ROCKSDB_DISABLE_STALL_NOTIFICATION)
|
||||
// notify listeners on changed write stall conditions
|
||||
for (auto& notif : write_stall_notifications) {
|
||||
for (auto& listener : notif.immutable_cf_options->listeners) {
|
||||
for (auto& listener : notif.immutable_options->listeners) {
|
||||
listener->OnStallConditionsChanged(notif.write_stall_info);
|
||||
}
|
||||
}
|
||||
|
||||
+30
-24
@@ -356,32 +356,38 @@ TEST_F(EventListenerTest, MultiCF) {
|
||||
#ifdef ROCKSDB_USING_THREAD_STATUS
|
||||
options.enable_thread_tracking = true;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
options.table_properties_collector_factories.push_back(
|
||||
std::make_shared<TestPropertiesCollectorFactory>());
|
||||
std::vector<std::string> cf_names = {
|
||||
"pikachu", "ilya", "muromec", "dobrynia",
|
||||
"nikitich", "alyosha", "popovich"};
|
||||
CreateAndReopenWithCF(cf_names, options);
|
||||
for (auto atomic_flush : {false, true}) {
|
||||
options.atomic_flush = atomic_flush;
|
||||
options.create_if_missing = true;
|
||||
DestroyAndReopen(options);
|
||||
TestFlushListener* listener = new TestFlushListener(options.env, this);
|
||||
options.listeners.emplace_back(listener);
|
||||
options.table_properties_collector_factories.push_back(
|
||||
std::make_shared<TestPropertiesCollectorFactory>());
|
||||
std::vector<std::string> cf_names = {"pikachu", "ilya", "muromec",
|
||||
"dobrynia", "nikitich", "alyosha",
|
||||
"popovich"};
|
||||
CreateAndReopenWithCF(cf_names, options);
|
||||
|
||||
ASSERT_OK(Put(1, "pikachu", std::string(90000, 'p')));
|
||||
ASSERT_OK(Put(2, "ilya", std::string(90000, 'i')));
|
||||
ASSERT_OK(Put(3, "muromec", std::string(90000, 'm')));
|
||||
ASSERT_OK(Put(4, "dobrynia", std::string(90000, 'd')));
|
||||
ASSERT_OK(Put(5, "nikitich", std::string(90000, 'n')));
|
||||
ASSERT_OK(Put(6, "alyosha", std::string(90000, 'a')));
|
||||
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_EQ(listener->flushed_dbs_.size(), i);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
ASSERT_OK(Put(1, "pikachu", std::string(90000, 'p')));
|
||||
ASSERT_OK(Put(2, "ilya", std::string(90000, 'i')));
|
||||
ASSERT_OK(Put(3, "muromec", std::string(90000, 'm')));
|
||||
ASSERT_OK(Put(4, "dobrynia", std::string(90000, 'd')));
|
||||
ASSERT_OK(Put(5, "nikitich", std::string(90000, 'n')));
|
||||
ASSERT_OK(Put(6, "alyosha", std::string(90000, 'a')));
|
||||
ASSERT_OK(Put(7, "popovich", std::string(90000, 'p')));
|
||||
for (int i = 1; i < 8; ++i) {
|
||||
ASSERT_OK(Flush(i));
|
||||
ASSERT_EQ(listener->flushed_dbs_.size(), i);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_.size(), i);
|
||||
}
|
||||
|
||||
// make sure callback functions are called in the right order
|
||||
for (size_t i = 0; i < cf_names.size(); i++) {
|
||||
ASSERT_EQ(listener->flushed_dbs_[i], db_);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_[i], cf_names[i]);
|
||||
// make sure callback functions are called in the right order
|
||||
for (size_t i = 0; i < cf_names.size(); i++) {
|
||||
ASSERT_EQ(listener->flushed_dbs_[i], db_);
|
||||
ASSERT_EQ(listener->flushed_column_family_names_[i], cf_names[i]);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -145,7 +145,8 @@ IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
}
|
||||
|
||||
// Compute the crc of the record type and the payload.
|
||||
crc = crc32c::Extend(crc, ptr, n);
|
||||
uint32_t payload_crc = crc32c::Value(ptr, n);
|
||||
crc = crc32c::Crc32cCombine(crc, payload_crc, n);
|
||||
crc = crc32c::Mask(crc); // Adjust for storage
|
||||
TEST_SYNC_POINT_CALLBACK("LogWriter::EmitPhysicalRecord:BeforeEncodeChecksum",
|
||||
&crc);
|
||||
@@ -154,7 +155,7 @@ IOStatus Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {
|
||||
// Write the header and the payload
|
||||
IOStatus s = dest_->Append(Slice(buf, header_size));
|
||||
if (s.ok()) {
|
||||
s = dest_->Append(Slice(ptr, n));
|
||||
s = dest_->Append(Slice(ptr, n), payload_crc);
|
||||
}
|
||||
block_offset_ += header_size + n;
|
||||
return s;
|
||||
|
||||
+3
-1
@@ -67,7 +67,8 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
SequenceNumber latest_seq, uint32_t column_family_id)
|
||||
SequenceNumber latest_seq, uint32_t column_family_id,
|
||||
uint64_t current_logfile_number)
|
||||
: comparator_(cmp),
|
||||
moptions_(ioptions, mutable_cf_options),
|
||||
refs_(0),
|
||||
@@ -98,6 +99,7 @@ MemTable::MemTable(const InternalKeyComparator& cmp,
|
||||
earliest_seqno_(latest_seq),
|
||||
creation_seq_(latest_seq),
|
||||
mem_next_logfile_number_(0),
|
||||
mem_min_logfile_number_(current_logfile_number),
|
||||
min_prep_log_referenced_(0),
|
||||
locks_(moptions_.inplace_update_support
|
||||
? moptions_.inplace_update_num_locks
|
||||
|
||||
+33
-1
@@ -106,7 +106,8 @@ class MemTable {
|
||||
const ImmutableOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
WriteBufferManager* write_buffer_manager,
|
||||
SequenceNumber earliest_seq, uint32_t column_family_id);
|
||||
SequenceNumber earliest_seq, uint32_t column_family_id,
|
||||
uint64_t current_logfile_number = 0);
|
||||
// No copying allowed
|
||||
MemTable(const MemTable&) = delete;
|
||||
MemTable& operator=(const MemTable&) = delete;
|
||||
@@ -341,6 +342,14 @@ class MemTable {
|
||||
return first_seqno_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Returns the sequence number of the first element that was inserted
|
||||
// into the memtable.
|
||||
// REQUIRES: external synchronization to prevent simultaneous
|
||||
// operations on the same MemTable (unless this Memtable is immutable).
|
||||
void SetFirstSequenceNumber(SequenceNumber first_seqno) {
|
||||
return first_seqno_.store(first_seqno, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Returns the sequence number that is guaranteed to be smaller than or equal
|
||||
// to the sequence number of any key that could be inserted into this
|
||||
// memtable. It can then be assumed that any write with a larger(or equal)
|
||||
@@ -352,6 +361,15 @@ class MemTable {
|
||||
return earliest_seqno_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Sets the sequence number that is guaranteed to be smaller than or equal
|
||||
// to the sequence number of any key that could be inserted into this
|
||||
// memtable. It can then be assumed that any write with a larger(or equal)
|
||||
// sequence number will be present in this memtable or a later memtable.
|
||||
// Used only for MemPurge operation
|
||||
void SetEarliestSequenceNumber(SequenceNumber earliest_seqno) {
|
||||
return earliest_seqno_.store(earliest_seqno, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// DB's latest sequence ID when the memtable is created. This number
|
||||
// may be updated to a more recent one before any key is inserted.
|
||||
SequenceNumber GetCreationSeq() const { return creation_seq_; }
|
||||
@@ -370,6 +388,16 @@ class MemTable {
|
||||
// operations on the same MemTable.
|
||||
void SetNextLogNumber(uint64_t num) { mem_next_logfile_number_ = num; }
|
||||
|
||||
// Set the earliest log file number that (possibly)
|
||||
// contains entries from this memtable.
|
||||
void SetEarliestLogFileNumber(uint64_t logno) {
|
||||
mem_min_logfile_number_ = logno;
|
||||
}
|
||||
|
||||
// Return the earliest log file number that (possibly)
|
||||
// contains entries from this memtable.
|
||||
uint64_t GetEarliestLogFileNumber() { return mem_min_logfile_number_; }
|
||||
|
||||
// if this memtable contains data from a committed
|
||||
// two phase transaction we must take note of the
|
||||
// log which contains that data so we can know
|
||||
@@ -500,6 +528,10 @@ class MemTable {
|
||||
// The log files earlier than this number can be deleted.
|
||||
uint64_t mem_next_logfile_number_;
|
||||
|
||||
// The earliest log containing entries inserted into
|
||||
// this memtable.
|
||||
uint64_t mem_min_logfile_number_;
|
||||
|
||||
// the earliest log containing a prepared section
|
||||
// which has been inserted into this memtable.
|
||||
std::atomic<uint64_t> min_prep_log_referenced_;
|
||||
|
||||
+58
-10
@@ -392,7 +392,7 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
IOStatus* io_s) {
|
||||
IOStatus* io_s, bool write_edits) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
ThreadStatus::STAGE_MEMTABLE_INSTALL_FLUSH_RESULTS);
|
||||
mu->AssertHeld();
|
||||
@@ -476,8 +476,14 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
uint64_t min_wal_number_to_keep = 0;
|
||||
if (vset->db_options()->allow_2pc) {
|
||||
assert(edit_list.size() > 0);
|
||||
// Note that if mempurge is successful, the edit_list will
|
||||
// not be applicable (contains info of new min_log number to keep,
|
||||
// and level 0 file path of SST file created during normal flush,
|
||||
// so both pieces of information are irrelevant after a successful
|
||||
// mempurge operation).
|
||||
min_wal_number_to_keep = PrecomputeMinLogNumberToKeep2PC(
|
||||
vset, *cfd, edit_list, memtables_to_flush, prep_tracker);
|
||||
|
||||
// We piggyback the information of earliest log file to keep in the
|
||||
// manifest entry for the last file flushed.
|
||||
edit_list.back()->SetMinLogNumberToKeep(min_wal_number_to_keep);
|
||||
@@ -502,13 +508,25 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
RemoveMemTablesOrRestoreFlags(status, cfd, batch_count, log_buffer,
|
||||
to_delete, mu);
|
||||
};
|
||||
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, edit_list, mu,
|
||||
db_directory, /*new_descriptor_log=*/false,
|
||||
/*column_family_options=*/nullptr,
|
||||
manifest_write_cb);
|
||||
*io_s = vset->io_status();
|
||||
if (write_edits) {
|
||||
// this can release and reacquire the mutex.
|
||||
s = vset->LogAndApply(cfd, mutable_cf_options, edit_list, mu,
|
||||
db_directory, /*new_descriptor_log=*/false,
|
||||
/*column_family_options=*/nullptr,
|
||||
manifest_write_cb);
|
||||
*io_s = vset->io_status();
|
||||
} else {
|
||||
// If write_edit is false (e.g: successful mempurge),
|
||||
// then remove old memtables, wake up manifest write queue threads,
|
||||
// and don't commit anything to the manifest file.
|
||||
RemoveMemTablesOrRestoreFlags(s, cfd, batch_count, log_buffer,
|
||||
to_delete, mu);
|
||||
// Notify new head of manifest write queue.
|
||||
// wake up all the waiting writers
|
||||
// TODO(bjlemaire): explain full reason needed or investigate more.
|
||||
vset->WakeUpWaitingManifestWriters();
|
||||
*io_s = IOStatus::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
commit_in_progress_ = false;
|
||||
@@ -516,7 +534,8 @@ Status MemTableList::TryInstallMemtableFlushResults(
|
||||
}
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete) {
|
||||
void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete,
|
||||
bool trigger_flush) {
|
||||
assert(static_cast<int>(current_->memlist_.size()) >= num_flush_not_started_);
|
||||
InstallNewVersion();
|
||||
// this method is used to move mutable memtable into an immutable list.
|
||||
@@ -527,7 +546,8 @@ void MemTableList::Add(MemTable* m, autovector<MemTable*>* to_delete) {
|
||||
current_->Add(m, to_delete);
|
||||
m->MarkImmutable();
|
||||
num_flush_not_started_++;
|
||||
if (num_flush_not_started_ == 1) {
|
||||
|
||||
if (num_flush_not_started_ > 0 && trigger_flush) {
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
UpdateCachedValuesFromMemTableListVersion();
|
||||
@@ -674,6 +694,21 @@ void MemTableList::RemoveMemTablesOrRestoreFlags(
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the earliest log that possibly contain entries
|
||||
// from one of the memtables of this memtable_list.
|
||||
uint64_t MemTableList::EarliestLogContainingData() {
|
||||
uint64_t min_log = 0;
|
||||
|
||||
for (auto& m : current_->memlist_) {
|
||||
uint64_t log = m->GetEarliestLogFileNumber();
|
||||
if (log > 0 && (min_log == 0 || log < min_log)) {
|
||||
min_log = log;
|
||||
}
|
||||
}
|
||||
|
||||
return min_log;
|
||||
}
|
||||
|
||||
uint64_t MemTableList::PrecomputeMinLogContainingPrepSection(
|
||||
const std::unordered_set<MemTable*>* memtables_to_flush) {
|
||||
uint64_t min_log = 0;
|
||||
@@ -701,6 +736,8 @@ Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
LogsWithPrepTracker* prep_tracker, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_metas,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer) {
|
||||
AutoThreadOperationStageUpdater stage_updater(
|
||||
@@ -730,6 +767,17 @@ Status InstallMemtableAtomicFlushResults(
|
||||
(*mems_list[k])[i]->SetFlushCompleted(true);
|
||||
(*mems_list[k])[i]->SetFileNumber(file_metas[k]->fd.GetNumber());
|
||||
}
|
||||
#ifndef ROCKSDB_LITE
|
||||
if (committed_flush_jobs_info[k]) {
|
||||
assert(!mems_list[k]->empty());
|
||||
assert((*mems_list[k])[0]);
|
||||
std::unique_ptr<FlushJobInfo> flush_job_info =
|
||||
(*mems_list[k])[0]->ReleaseFlushJobInfo();
|
||||
committed_flush_jobs_info[k]->push_back(std::move(flush_job_info));
|
||||
}
|
||||
#else //! ROCKSDB_LITE
|
||||
(void)committed_flush_jobs_info;
|
||||
#endif // ROCKSDB_LITE
|
||||
}
|
||||
|
||||
Status s;
|
||||
|
||||
+28
-3
@@ -140,6 +140,8 @@ class MemTableListVersion {
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, LogsWithPrepTracker* prep_tracker,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -268,11 +270,15 @@ class MemTableList {
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer,
|
||||
std::list<std::unique_ptr<FlushJobInfo>>* committed_flush_jobs_info,
|
||||
IOStatus* io_s);
|
||||
IOStatus* io_s, bool write_edits = true);
|
||||
|
||||
// New memtables are inserted at the front of the list.
|
||||
// Takes ownership of the referenced held on *m by the caller of Add().
|
||||
void Add(MemTable* m, autovector<MemTable*>* to_delete);
|
||||
// By default, adding memtables will flag that the memtable list needs to be
|
||||
// flushed, but in certain situations, like after a mempurge, we may want to
|
||||
// avoid flushing the memtable list upon addition of a memtable.
|
||||
void Add(MemTable* m, autovector<MemTable*>* to_delete,
|
||||
bool trigger_flush = true);
|
||||
|
||||
// Returns an estimate of the number of bytes of data in use.
|
||||
size_t ApproximateMemoryUsage();
|
||||
@@ -308,7 +314,18 @@ class MemTableList {
|
||||
// non-empty (regardless of the min_write_buffer_number_to_merge
|
||||
// parameter). This flush request will persist until the next time
|
||||
// PickMemtablesToFlush() is called.
|
||||
void FlushRequested() { flush_requested_ = true; }
|
||||
void FlushRequested() {
|
||||
flush_requested_ = true;
|
||||
// If there are some memtables stored in imm() that dont trigger
|
||||
// flush (eg: mempurge output memtable), then update imm_flush_needed.
|
||||
// Note: if race condition and imm_flush_needed is set to true
|
||||
// when there is num_flush_not_started_==0, then there is no
|
||||
// impact whatsoever. Imm_flush_needed is only used in an assert
|
||||
// in IsFlushPending().
|
||||
if (num_flush_not_started_ > 0) {
|
||||
imm_flush_needed.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
||||
|
||||
bool HasFlushRequested() { return flush_requested_; }
|
||||
|
||||
@@ -332,6 +349,10 @@ class MemTableList {
|
||||
|
||||
size_t* current_memory_usage() { return ¤t_memory_usage_; }
|
||||
|
||||
// Returns the earliest log that possibly contain entries
|
||||
// from one of the memtables of this memtable_list.
|
||||
uint64_t EarliestLogContainingData();
|
||||
|
||||
// Returns the min log containing the prep section after memtables listsed in
|
||||
// `memtables_to_flush` are flushed and their status is persisted in manifest.
|
||||
uint64_t PrecomputeMinLogContainingPrepSection(
|
||||
@@ -383,6 +404,8 @@ class MemTableList {
|
||||
const autovector<const autovector<MemTable*>*>& mems_list,
|
||||
VersionSet* vset, LogsWithPrepTracker* prep_tracker,
|
||||
InstrumentedMutex* mu, const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
|
||||
@@ -433,6 +456,8 @@ extern Status InstallMemtableAtomicFlushResults(
|
||||
const autovector<const autovector<MemTable*>*>& mems_list, VersionSet* vset,
|
||||
LogsWithPrepTracker* prep_tracker, InstrumentedMutex* mu,
|
||||
const autovector<FileMetaData*>& file_meta,
|
||||
const autovector<std::list<std::unique_ptr<FlushJobInfo>>*>&
|
||||
committed_flush_jobs_info,
|
||||
autovector<MemTable*>* to_delete, FSDirectory* db_directory,
|
||||
LogBuffer* log_buffer);
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -103,7 +103,7 @@ class MemTableListTest : public testing::Test {
|
||||
VersionSet versions(dbname, &immutable_db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller, /*block_cache_tracer=*/nullptr,
|
||||
/*io_tracer=*/nullptr);
|
||||
/*io_tracer=*/nullptr, /*db_session_id*/ "");
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("one", ColumnFamilyOptions());
|
||||
@@ -153,7 +153,7 @@ class MemTableListTest : public testing::Test {
|
||||
VersionSet versions(dbname, &immutable_db_options, env_options,
|
||||
table_cache.get(), &write_buffer_manager,
|
||||
&write_controller, /*block_cache_tracer=*/nullptr,
|
||||
/*io_tracer=*/nullptr);
|
||||
/*io_tracer=*/nullptr, /*db_session_id*/ "");
|
||||
std::vector<ColumnFamilyDescriptor> cf_descs;
|
||||
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions());
|
||||
cf_descs.emplace_back("one", ColumnFamilyOptions());
|
||||
@@ -182,12 +182,21 @@ class MemTableListTest : public testing::Test {
|
||||
for (auto& meta : file_metas) {
|
||||
file_meta_ptrs.push_back(&meta);
|
||||
}
|
||||
std::vector<std::list<std::unique_ptr<FlushJobInfo>>>
|
||||
committed_flush_jobs_info_storage(cf_ids.size());
|
||||
autovector<std::list<std::unique_ptr<FlushJobInfo>>*>
|
||||
committed_flush_jobs_info;
|
||||
for (int i = 0; i < static_cast<int>(cf_ids.size()); ++i) {
|
||||
committed_flush_jobs_info.push_back(
|
||||
&committed_flush_jobs_info_storage[i]);
|
||||
}
|
||||
|
||||
InstrumentedMutex mutex;
|
||||
InstrumentedMutexLock l(&mutex);
|
||||
return InstallMemtableAtomicFlushResults(
|
||||
&lists, cfds, mutable_cf_options_list, mems_list, &versions,
|
||||
nullptr /* prep_tracker */, &mutex, file_meta_ptrs, to_delete, nullptr,
|
||||
&log_buffer);
|
||||
nullptr /* prep_tracker */, &mutex, file_meta_ptrs,
|
||||
committed_flush_jobs_info, to_delete, nullptr, &log_buffer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loaded 100 of 273 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user