mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 02:52:35 -05:00
Compare commits
144
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9ed290bfa | ||
|
|
9cc25190e1 | ||
|
|
50d6969816 | ||
|
|
83a4dd1a67 | ||
|
|
8efb5cfb6f | ||
|
|
9f21d08660 | ||
|
|
5b2bbacb6f | ||
|
|
1092f19d95 | ||
|
|
d739318ba7 | ||
|
|
096beb787e | ||
|
|
dea4063b13 | ||
|
|
25a0d0ca30 | ||
|
|
d76eed4839 | ||
|
|
88b4210701 | ||
|
|
a607f3efaa | ||
|
|
f4583f7480 | ||
|
|
552fd765b3 | ||
|
|
d6b7b7712f | ||
|
|
569b87e8c7 | ||
|
|
775dc623ad | ||
|
|
312f23c92d | ||
|
|
223b57eeb8 | ||
|
|
94d04529de | ||
|
|
742b452863 | ||
|
|
b7bab48099 | ||
|
|
9bfd46d0d8 | ||
|
|
aa8f1331af | ||
|
|
88db97b06d | ||
|
|
9c24a5cb4d | ||
|
|
f7613e2a9e | ||
|
|
15d9f28da5 | ||
|
|
bacd6edcbe | ||
|
|
7e2ac0c3a0 | ||
|
|
d63f86e506 | ||
|
|
9810f40075 | ||
|
|
af58d92760 | ||
|
|
83833637c1 | ||
|
|
717749f4c0 | ||
|
|
d854abad78 | ||
|
|
722ebba834 | ||
|
|
b3585a11b4 | ||
|
|
fbe2d259cb | ||
|
|
e6be168aa5 | ||
|
|
02db03af8d | ||
|
|
5abda3bb8b | ||
|
|
edf74d1cb1 | ||
|
|
6a8ddd374d | ||
|
|
1fb3593f25 | ||
|
|
f5e649453c | ||
|
|
119b26fac0 | ||
|
|
fb08330f74 | ||
|
|
3020df9df5 | ||
|
|
f8c2e5a608 | ||
|
|
8988f831d8 | ||
|
|
f941adef88 | ||
|
|
23e446a157 | ||
|
|
2e7070b194 | ||
|
|
98b0cbea88 | ||
|
|
1bee0fca05 | ||
|
|
2677bd5967 | ||
|
|
aaece2a98d | ||
|
|
c7432cc3c0 | ||
|
|
a8170d774c | ||
|
|
6cbe9d9762 | ||
|
|
31bd2d790e | ||
|
|
e85cbdb4e8 | ||
|
|
0b45a68c59 | ||
|
|
2f3261831b | ||
|
|
02df00d97b | ||
|
|
055b4d25f8 | ||
|
|
b7c825d5cf | ||
|
|
afa3518839 | ||
|
|
0f85d163e6 | ||
|
|
f005dac2d9 | ||
|
|
0b8c549b3f | ||
|
|
ffe08ffcc2 | ||
|
|
22e5c513c2 | ||
|
|
0a17d95357 | ||
|
|
172adce767 | ||
|
|
43f8a9dcce | ||
|
|
78e291b17e | ||
|
|
9360776cb9 | ||
|
|
2adb7e3768 | ||
|
|
556972e964 | ||
|
|
bfc9737aca | ||
|
|
38f988d3b4 | ||
|
|
14eca6bf04 | ||
|
|
298b00a396 | ||
|
|
8d87e9cea1 | ||
|
|
66942e8158 | ||
|
|
02f59ed669 | ||
|
|
bcb9e41080 | ||
|
|
038e02d8d9 | ||
|
|
0c56fc4d66 | ||
|
|
c5abf78bca | ||
|
|
961c7590d6 | ||
|
|
e3f953a863 | ||
|
|
bcefc59e9f | ||
|
|
9060e6fa79 | ||
|
|
82a82c76e7 | ||
|
|
bd68bfb41b | ||
|
|
b464a85e33 | ||
|
|
b559dba817 | ||
|
|
803a517b48 | ||
|
|
bcd32560dd | ||
|
|
35a25a3fb9 | ||
|
|
826295a5e9 | ||
|
|
292bcf6227 | ||
|
|
38be686160 | ||
|
|
c7aedf1b48 | ||
|
|
eb04bb86c6 | ||
|
|
545e14b53b | ||
|
|
aaafcb80ab | ||
|
|
a1523efcdf | ||
|
|
39b24432d4 | ||
|
|
1551f1011a | ||
|
|
0ac0098705 | ||
|
|
ada700b906 | ||
|
|
b9d65f5aa6 | ||
|
|
d790e6004f | ||
|
|
50ba245f6d | ||
|
|
91b7553293 | ||
|
|
b11a8b1b9a | ||
|
|
06a2dcebea | ||
|
|
50d63a2af0 | ||
|
|
3bea276fc8 | ||
|
|
244797aa4b | ||
|
|
07204837ce | ||
|
|
4a4b8a1344 | ||
|
|
70aaa9ceeb | ||
|
|
c384c08a4f | ||
|
|
3f218074ee | ||
|
|
2e9324718a | ||
|
|
f0e8731b72 | ||
|
|
3a1c29d40e | ||
|
|
a50ea71c00 | ||
|
|
d9cd33516a | ||
|
|
e72e2167fd | ||
|
|
1c84660457 | ||
|
|
12825894a2 | ||
|
|
94265234de | ||
|
|
3730b05dc9 | ||
|
|
f286fb344b | ||
|
|
b27a1448b6 |
No files matched your search
+94
-2
@@ -6,12 +6,78 @@ orbs:
|
||||
executors:
|
||||
windows-2xlarge:
|
||||
machine:
|
||||
image: 'windows-server-2019-vs2019:stable'
|
||||
image: 'windows-server-2019-vs2019:201908-06'
|
||||
resource_class: windows.2xlarge
|
||||
shell: bash.exe
|
||||
|
||||
jobs:
|
||||
build:
|
||||
build-linux:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: pyenv global 3.5.2
|
||||
- run: sudo apt-get update -y
|
||||
- run: sudo apt-get install -y libgflags-dev
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 PRINT_PARALLEL_OUTPUTS=1 make J=32 all check -j32
|
||||
|
||||
build-linux-release:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make release -j32
|
||||
|
||||
build-linux-lite:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: pyenv global 3.5.2
|
||||
- run: SKIP_FORMAT_BUCK_CHECKS=1 PRINT_PARALLEL_OUTPUTS=1 LITE=1 make J=32 all check -j32
|
||||
|
||||
build-linux-lite-release:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: make release -j32
|
||||
|
||||
build-linux-clang-no-test:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: sudo apt-get update -y
|
||||
- run: sudo apt-get install -y clang
|
||||
- run: CC=clang CXX=clang++ USE_CLANG=1 PORTABLE=1 make all -j32
|
||||
|
||||
build-linux-clang10-no-test:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: sudo sh -c 'echo "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" >> /etc/apt/sources.list'
|
||||
- run: sudo sh -c 'echo "deb-src http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main" >> /etc/apt/sources.list'
|
||||
- run: sudo apt-get update -y
|
||||
- run: sudo apt-get install -y clang-10
|
||||
- run: CC=clang-10 CXX=clang++-10 ROCKSDB_DISABLE_ALIGNED_NEW=1 USE_CLANG=1 make all -j32 # aligned new doesn't work for reason we haven't figured out
|
||||
|
||||
build-linux-cmake:
|
||||
machine:
|
||||
image: ubuntu-1604:201903-01
|
||||
resource_class: 2xlarge
|
||||
steps:
|
||||
- checkout # check out the code in the project directory
|
||||
- run: mkdir build && cd build && cmake -DWITH_GFLAGS=0 .. && make -j32
|
||||
|
||||
build-windows:
|
||||
executor: windows-2xlarge
|
||||
|
||||
environment:
|
||||
@@ -54,3 +120,29 @@ jobs:
|
||||
shell: powershell.exe
|
||||
command: |
|
||||
build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_test,db_test2,env_basic_test,env_test,db_merge_operand_test -Concurrency 16
|
||||
|
||||
workflows:
|
||||
build-linux:
|
||||
jobs:
|
||||
- build-linux
|
||||
build-linux-lite:
|
||||
jobs:
|
||||
- build-linux-lite
|
||||
build-linux-release:
|
||||
jobs:
|
||||
- build-linux-release
|
||||
build-linux-lite-release:
|
||||
jobs:
|
||||
- build-linux-lite-release
|
||||
build-linux-clang-no-test:
|
||||
jobs:
|
||||
- build-linux-clang-no-test
|
||||
build-linux-clang10-no-test:
|
||||
jobs:
|
||||
- build-linux-clang10-no-test
|
||||
build-linux-cmake:
|
||||
jobs:
|
||||
- build-linux-cmake
|
||||
build-windows:
|
||||
jobs:
|
||||
- build-windows
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
args: https://raw.githubusercontent.com/llvm-mirror/clang/master/tools/clang-format/clang-format-diff.py
|
||||
|
||||
- name: Check format
|
||||
run: make check-format
|
||||
run: VERBOSE_CHECK=1 make check-format
|
||||
|
||||
- name: Compare buckify output
|
||||
run: make check-buck-targets
|
||||
@@ -85,3 +85,5 @@ buckifier/*.pyc
|
||||
buckifier/__pycache__
|
||||
|
||||
compile_commands.json
|
||||
clang-format-diff.py
|
||||
.py3/
|
||||
+33
-2
@@ -53,6 +53,8 @@ env:
|
||||
- JOB_NAME=cmake-gcc9 # 3-5 minutes
|
||||
- JOB_NAME=cmake-gcc9-c++20 # 3-5 minutes
|
||||
- JOB_NAME=cmake-mingw # 3 minutes
|
||||
- JOB_NAME=make-gcc4.8
|
||||
- JOB_NAME=status_checked
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
@@ -64,6 +66,8 @@ matrix:
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- os: osx
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: osx
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: osx
|
||||
arch: ppc64le
|
||||
- os: osx
|
||||
@@ -71,9 +75,15 @@ matrix:
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-mingw
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=make-gcc4.8
|
||||
- os: linux
|
||||
compiler: clang
|
||||
# Exclude all but most unique cmake variants for pull requests, but build all in branches
|
||||
@@ -197,6 +207,17 @@ matrix:
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=cmake-gcc9-c++20
|
||||
- if: type = pull_request
|
||||
os : osx
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
os : linux
|
||||
arch: arm64
|
||||
env: JOB_NAME=status_checked
|
||||
- if: type = pull_request
|
||||
os: linux
|
||||
arch: ppc64le
|
||||
env: JOB_NAME=status_checked
|
||||
|
||||
install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == osx ]; then
|
||||
@@ -213,6 +234,10 @@ install:
|
||||
- if [ "${JOB_NAME}" == cmake-mingw ]; then
|
||||
sudo apt-get install -y mingw-w64 ;
|
||||
fi
|
||||
- if [ "${JOB_NAME}" == make-gcc4.8 ]; then
|
||||
sudo apt-get install -y g++-4.8 ;
|
||||
CC=gcc-4.8 && CXX=g++-4.8;
|
||||
fi
|
||||
- if [[ "${JOB_NAME}" == cmake* ]] && [ "${TRAVIS_OS_NAME}" == linux ]; then
|
||||
CMAKE_DIST_URL="https://rocksdb-deps.s3-us-west-2.amazonaws.com/cmake/cmake-3.14.5-Linux-$(uname -m).tar.bz2";
|
||||
TAR_OPT="--strip-components=1 -xj";
|
||||
@@ -269,7 +294,7 @@ script:
|
||||
OPT=-DTRAVIS V=1 make rocksdbjava jtest
|
||||
;;
|
||||
lite_build)
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 static_lib tools
|
||||
OPT='-DTRAVIS -DROCKSDB_LITE' V=1 make -j4 all
|
||||
;;
|
||||
examples)
|
||||
OPT=-DTRAVIS V=1 make -j4 static_lib && cd examples && make -j4
|
||||
@@ -285,7 +310,13 @@ script:
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
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 .. && make -j4 && cd .. && rm -rf build && mkdir build && cd build && cmake -DJNI=1 .. -DCMAKE_BUILD_TYPE=Release $OPT && make -j4 rocksdb rocksdbjni
|
||||
;;
|
||||
make-gcc4.8)
|
||||
OPT=-DTRAVIS V=1 SKIP_LINK=1 make -j4 all && [ "Linking broken because libgflags compiled with newer ABI" ]
|
||||
;;
|
||||
status_checked)
|
||||
OPT=-DTRAVIS V=1 ASSERT_STATUS_CHECKED=1 make -j4 check_some
|
||||
;;
|
||||
esac
|
||||
notifications:
|
||||
|
||||
+67
-26
@@ -95,7 +95,7 @@ if(MSVC)
|
||||
option(WITH_XPRESS "build with windows built in compression" OFF)
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/thirdparty.inc)
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD" AND NOT CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
|
||||
# FreeBSD has jemalloc as default malloc
|
||||
# but it does not have all the jemalloc files in include/...
|
||||
set(WITH_JEMALLOC ON)
|
||||
@@ -107,18 +107,32 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# No config file for this
|
||||
if(WITH_GFLAGS)
|
||||
find_package(gflags REQUIRED)
|
||||
# Config with namespace available since gflags 2.2.2
|
||||
option(GFLAGS_USE_TARGET_NAMESPACE "Use gflags import target with namespace." ON)
|
||||
find_package(gflags CONFIG)
|
||||
if(gflags_FOUND)
|
||||
if(TARGET ${GFLAGS_TARGET})
|
||||
# Config with GFLAGS_TARGET available since gflags 2.2.0
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_TARGET})
|
||||
else()
|
||||
# Config with GFLAGS_LIBRARIES available since gflags 2.1.0
|
||||
list(APPEND THIRDPARTY_LIBS ${GFLAGS_LIBRARIES})
|
||||
endif()
|
||||
else()
|
||||
find_package(gflags REQUIRED)
|
||||
list(APPEND THIRDPARTY_LIBS gflags::gflags)
|
||||
endif()
|
||||
add_definitions(-DGFLAGS=1)
|
||||
include_directories(${gflags_INCLUDE_DIR})
|
||||
list(APPEND THIRDPARTY_LIBS gflags::gflags)
|
||||
endif()
|
||||
|
||||
if(WITH_SNAPPY)
|
||||
find_package(snappy REQUIRED)
|
||||
find_package(Snappy CONFIG)
|
||||
if(NOT Snappy_FOUND)
|
||||
find_package(Snappy REQUIRED)
|
||||
endif()
|
||||
add_definitions(-DSNAPPY)
|
||||
list(APPEND THIRDPARTY_LIBS snappy::snappy)
|
||||
list(APPEND THIRDPARTY_LIBS Snappy::snappy)
|
||||
endif()
|
||||
|
||||
if(WITH_ZLIB)
|
||||
@@ -434,6 +448,8 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
add_definitions(-DOS_LINUX)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "SunOS")
|
||||
add_definitions(-DOS_SOLARIS)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "kFreeBSD")
|
||||
add_definitions(-DOS_GNU_KFREEBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
add_definitions(-DOS_FREEBSD)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD")
|
||||
@@ -509,7 +525,6 @@ endif()
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
if(WITH_FOLLY_DISTRIBUTED_MUTEX)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/third-party/folly)
|
||||
endif()
|
||||
@@ -526,6 +541,9 @@ set(SOURCES
|
||||
db/blob/blob_file_addition.cc
|
||||
db/blob/blob_file_garbage.cc
|
||||
db/blob/blob_file_meta.cc
|
||||
db/blob/blob_log_format.cc
|
||||
db/blob/blob_log_reader.cc
|
||||
db/blob/blob_log_writer.cc
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/column_family.cc
|
||||
@@ -717,9 +735,6 @@ set(SOURCES
|
||||
utilities/blob_db/blob_db_impl_filesnapshot.cc
|
||||
utilities/blob_db/blob_dump_tool.cc
|
||||
utilities/blob_db/blob_file.cc
|
||||
utilities/blob_db/blob_log_reader.cc
|
||||
utilities/blob_db/blob_log_writer.cc
|
||||
utilities/blob_db/blob_log_format.cc
|
||||
utilities/cassandra/cassandra_compaction_filter.cc
|
||||
utilities/cassandra/format.cc
|
||||
utilities/cassandra/merge_operator.cc
|
||||
@@ -790,9 +805,13 @@ if(WIN32)
|
||||
port/win/env_win.cc
|
||||
port/win/env_default.cc
|
||||
port/win/port_win.cc
|
||||
port/win/win_logger.cc
|
||||
port/win/win_thread.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()
|
||||
if(WITH_XPRESS)
|
||||
list(APPEND SOURCES
|
||||
port/win/xpress_win.cc)
|
||||
@@ -839,12 +858,12 @@ else()
|
||||
endif()
|
||||
|
||||
add_library(${ROCKSDB_STATIC_LIB} STATIC ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB}
|
||||
target_link_libraries(${ROCKSDB_STATIC_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(ROCKSDB_BUILD_SHARED)
|
||||
add_library(${ROCKSDB_SHARED_LIB} SHARED ${SOURCES})
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB}
|
||||
target_link_libraries(${ROCKSDB_SHARED_LIB} PRIVATE
|
||||
${THIRDPARTY_LIBS} ${SYSTEM_LIBS})
|
||||
|
||||
if(WIN32)
|
||||
@@ -872,6 +891,16 @@ else()
|
||||
endif()
|
||||
|
||||
option(WITH_JNI "build with JNI" OFF)
|
||||
# Tests are excluded from Release builds
|
||||
CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON
|
||||
"CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
|
||||
option(WITH_BENCHMARK_TOOLS "build with benchmarks" ON)
|
||||
option(WITH_CORE_TOOLS "build with ldb and sst_dump" ON)
|
||||
option(WITH_TOOLS "build with tools" ON)
|
||||
|
||||
if(WITH_TESTS OR WITH_BENCHMARK_TOOLS OR WITH_TOOLS OR WITH_JNI OR JNI)
|
||||
include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/third-party/gtest-1.8.1/fused-src)
|
||||
endif()
|
||||
if(WITH_JNI OR JNI)
|
||||
message(STATUS "JNI library is enabled")
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/java)
|
||||
@@ -909,6 +938,8 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
|
||||
install(DIRECTORY include/rocksdb COMPONENT devel DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
|
||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/cmake/modules" COMPONENT devel DESTINATION ${package_config_destination})
|
||||
|
||||
install(
|
||||
TARGETS ${ROCKSDB_STATIC_LIB}
|
||||
EXPORT RocksDBTargets
|
||||
@@ -945,16 +976,19 @@ if(NOT WIN32 OR ROCKSDB_INSTALL_ON_WINDOWS)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Tests are excluded from Release builds
|
||||
CMAKE_DEPENDENT_OPTION(WITH_TESTS "build with tests" ON
|
||||
"CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
|
||||
option(WITH_ALL_TESTS "Build all test, rather than a small subset" ON)
|
||||
|
||||
if(WITH_TESTS)
|
||||
add_subdirectory(third-party/gtest-1.8.1/fused-src/gtest)
|
||||
add_library(testharness STATIC
|
||||
test_util/testharness.cc)
|
||||
target_link_libraries(testharness gtest)
|
||||
|
||||
set(TESTS
|
||||
db/db_basic_test.cc
|
||||
env/env_basic_test.cc
|
||||
)
|
||||
if(WITH_ALL_TESTS)
|
||||
list(APPEND TESTS
|
||||
cache/cache_test.cc
|
||||
cache/lru_cache_test.cc
|
||||
db/blob/blob_file_addition_test.cc
|
||||
@@ -969,7 +1003,6 @@ if(WITH_TESTS)
|
||||
db/comparator_db_test.cc
|
||||
db/corruption_test.cc
|
||||
db/cuckoo_table_db_test.cc
|
||||
db/db_basic_test.cc
|
||||
db/db_with_timestamp_basic_test.cc
|
||||
db/db_block_cache_test.cc
|
||||
db/db_bloom_filter_test.cc
|
||||
@@ -1032,7 +1065,6 @@ if(WITH_TESTS)
|
||||
db/write_batch_test.cc
|
||||
db/write_callback_test.cc
|
||||
db/write_controller_test.cc
|
||||
env/env_basic_test.cc
|
||||
env/env_test.cc
|
||||
env/io_posix_test.cc
|
||||
env/mock_env_test.cc
|
||||
@@ -1053,6 +1085,7 @@ if(WITH_TESTS)
|
||||
options/options_settable_test.cc
|
||||
options/options_test.cc
|
||||
table/block_based/block_based_filter_block_test.cc
|
||||
table/block_based/block_based_table_reader_test.cc
|
||||
table/block_based/block_test.cc
|
||||
table/block_based/data_block_hash_index_test.cc
|
||||
table/block_based/full_filter_block_test.cc
|
||||
@@ -1064,6 +1097,7 @@ if(WITH_TESTS)
|
||||
table/sst_file_reader_test.cc
|
||||
table/table_test.cc
|
||||
table/block_fetcher_test.cc
|
||||
test_util/testutil_test.cc
|
||||
tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc
|
||||
tools/ldb_cmd_test.cc
|
||||
tools/reduce_levels_test.cc
|
||||
@@ -1113,7 +1147,8 @@ if(WITH_TESTS)
|
||||
utilities/transactions/write_unprepared_transaction_test.cc
|
||||
utilities/ttl/ttl_test.cc
|
||||
utilities/write_batch_with_index/write_batch_with_index_test.cc
|
||||
)
|
||||
)
|
||||
endif()
|
||||
if(WITH_LIBRADOS)
|
||||
list(APPEND TESTS utilities/env_librados_test.cc)
|
||||
endif()
|
||||
@@ -1158,6 +1193,10 @@ if(WITH_TESTS)
|
||||
add_test(NAME ${exename} COMMAND ${exename}${ARTIFACT_SUFFIX})
|
||||
add_dependencies(check ${CMAKE_PROJECT_NAME}_${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)
|
||||
endif()
|
||||
endforeach(sourcefile ${TESTS})
|
||||
|
||||
if(WIN32)
|
||||
@@ -1181,7 +1220,6 @@ if(WITH_TESTS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(WITH_BENCHMARK_TOOLS "build with benchmarks" ON)
|
||||
if(WITH_BENCHMARK_TOOLS)
|
||||
add_executable(db_bench
|
||||
tools/db_bench.cc
|
||||
@@ -1220,8 +1258,6 @@ if(WITH_BENCHMARK_TOOLS)
|
||||
${ROCKSDB_LIB})
|
||||
endif()
|
||||
|
||||
option(WITH_CORE_TOOLS "build with ldb and sst_dump" ON)
|
||||
option(WITH_TOOLS "build with tools" ON)
|
||||
if(WITH_CORE_TOOLS OR WITH_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
add_custom_target(core_tools
|
||||
@@ -1233,3 +1269,8 @@ if(WITH_TOOLS)
|
||||
add_custom_target(tools
|
||||
DEPENDS ${tool_deps})
|
||||
endif()
|
||||
|
||||
option(WITH_EXAMPLES "build with examples" OFF)
|
||||
if(WITH_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
+47
-1
@@ -1,11 +1,58 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Public API Change
|
||||
* Encryption file classes now exposed for inheritance in env_encryption.h
|
||||
|
||||
### Behavior Changes
|
||||
* Best-efforts recovery ignores CURRENT file completely. If CURRENT file is missing during recovery, best-efforts recovery still proceeds with MANIFEST file(s).
|
||||
* In best-efforts recovery, an error that is not Corruption or IOError::kNotFound or IOError::kPathNotFound will be overwritten silently. Fix this by checking all non-ok cases and return early.
|
||||
* Compressed block cache was automatically disabled with read-only DBs by mistake. Now it is fixed: compressed block cache will be in effective with read-only DB too.
|
||||
* Fix a bug of wrong iterator result if another thread finishes an update and a DB flush between two statement.
|
||||
|
||||
### Public API Change
|
||||
* `DB::GetDbSessionId(std::string& session_id)` is added. `session_id` stores a unique identifier that gets reset every time the DB is opened. This DB session ID should be unique among all open DB instances on all hosts, and should be unique among re-openings of the same or other DBs. This identifier is recorded in the LOG file on the line starting with "DB Session ID:".
|
||||
|
||||
### New Features
|
||||
* DB identity (`db_id`) and DB session identity (`db_session_id`) are added to table properties and stored in SST files. SST files generated from SstFileWriter and Repairer have DB identity “SST Writer” and “DB Repairer”, respectively. Their DB session IDs are generated in the same way as `DB::GetDbSessionId`. The session ID for SstFileWriter (resp., Repairer) resets every time `SstFileWriter::Open` (resp., `Repairer::Run`) is called.
|
||||
* Added experimental option BlockBasedTableOptions::optimize_filters_for_memory for reducing allocated memory size of Bloom filters (~10% savings with Jemalloc) while preserving the same general accuracy. To have an effect, the option requires format_version=5 and malloc_usable_size. Enabling this option is forward and backward compatible with existing format_version=5.
|
||||
|
||||
### Bug Fixes
|
||||
* Fail recovery and report once hitting a physical log record checksum mismatch, while reading MANIFEST. RocksDB should not continue processing the MANIFEST any further.
|
||||
|
||||
## 6.11 (6/12/2020)
|
||||
### Bug Fixes
|
||||
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
||||
* Fix possible false NotFound status from batched MultiGet using index type kHashSearch.
|
||||
* Fix corruption caused by enabling delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode, along with parallel compactions. The bug can result in two parallel compactions picking the same input files, resulting in the DB resurrecting older and deleted versions of some keys.
|
||||
* Fix a use-after-free bug in best-efforts recovery. column_family_memtables_ needs to point to valid ColumnFamilySet.
|
||||
* Let best-efforts recovery ignore corrupted files during table loading.
|
||||
* Fix corrupt key read from ingested file when iterator direction switches from reverse to forward at a key that is a prefix of another key in the same file. It is only possible in files with a non-zero global seqno.
|
||||
* Fix abnormally large estimate from GetApproximateSizes when a range starts near the end of one SST file and near the beginning of another. Now GetApproximateSizes consistently and fairly includes the size of SST metadata in addition to data blocks, attributing metadata proportionally among the data blocks based on their size.
|
||||
* Fix potential file descriptor leakage in PosixEnv's IsDirectory() and NewRandomAccessFile().
|
||||
* Fix false negative from the VerifyChecksum() API when there is a checksum mismatch in an index partition block in a BlockBasedTable format table file (index_type is kTwoLevelIndexSearch).
|
||||
* Fix sst_dump to return non-zero exit code if the specified file is not a recognized SST file or fails requested checks.
|
||||
* Fix incorrect results from batched MultiGet for duplicate keys, when the duplicate key matches the largest key of an SST file and the value type for the key in the file is a merge value.
|
||||
|
||||
### Public API Change
|
||||
* Flush(..., column_family) may return Status::ColumnFamilyDropped() instead of Status::InvalidArgument() if column_family is dropped while processing the flush request.
|
||||
* BlobDB now explicitly disallows using the default column family's storage directories as blob directory.
|
||||
* DeleteRange now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
* ldb now uses options.force_consistency_checks = true by default and "--disable_consistency_checks" is added to disable it.
|
||||
* DB::OpenForReadOnly no longer creates files or directories if the named DB does not exist, unless create_if_missing is set to true.
|
||||
* The consistency checks that validate LSM state changes (table file additions/deletions during flushes and compactions) are now stricter, more efficient, and no longer optional, i.e. they are performed even if `force_consistency_checks` is `false`.
|
||||
* Disable delete triggered compaction (NewCompactOnDeletionCollectorFactory) in universal compaction mode and num_levels = 1 in order to avoid a corruption bug.
|
||||
* `pin_l0_filter_and_index_blocks_in_cache` no longer applies to L0 files larger than `1.5 * write_buffer_size` to give more predictable memory usage. Such L0 files may exist due to intra-L0 compaction, external file ingestion, or user dynamically changing `write_buffer_size` (note, however, that files that are already pinned will continue being pinned, even after such a dynamic change).
|
||||
* In point-in-time wal recovery mode, fail database recovery in case of IOError while reading the WAL to avoid data loss.
|
||||
* A new method `Env::LowerThreadPoolCPUPriority(Priority, CpuPriority)` is added to `Env` to be able to lower to a specific priority such as `CpuPriority::kIdle`.
|
||||
|
||||
### New Features
|
||||
* sst_dump to add a new --readahead_size argument. Users can specify read size when scanning the data. Sst_dump also tries to prefetch tail part of the SST files so usually some number of I/Os are saved there too.
|
||||
* Generate file checksum in SstFileWriter if Options.file_checksum_gen_factory is set. The checksum and checksum function name are stored in ExternalSstFileInfo after the sst file write is finished.
|
||||
* Add a value_size_soft_limit in read options which limits the cumulative value size of keys read in batches in MultiGet. Once the cumulative value size of found keys exceeds read_options.value_size_soft_limit, all the remaining keys are returned with status Abort without further finding their values. By default the value_size_soft_limit is std::numeric_limits<uint64_t>::max().
|
||||
* Enable SST file ingestion with file checksum information when calling IngestExternalFiles(const std::vector<IngestExternalFileArg>& args). Added files_checksums and files_checksum_func_names to IngestExternalFileArg such that user can ingest the sst files with their file checksum information. Added verify_file_checksum to IngestExternalFileOptions (default is True). To be backward compatible, if DB does not enable file checksum or user does not provide checksum information (vectors of files_checksums and files_checksum_func_names are both empty), verification of file checksum is always sucessful. If DB enables file checksum, DB will always generate the checksum for each ingested SST file during Prepare stage of ingestion and store the checksum in Manifest, unless verify_file_checksum is False and checksum information is provided by the application. In this case, we only verify the checksum function name and directly store the ingested checksum in Manifest. If verify_file_checksum is set to True, DB will verify the ingested checksum and function name with the genrated ones. Any mismatch will fail the ingestion. Note that, if IngestExternalFileOptions::write_global_seqno is True, the seqno will be changed in the ingested file. Therefore, the checksum of the file will be changed. In this case, a new checksum will be generated after the seqno is updated and be stored in the Manifest.
|
||||
|
||||
### Performance Improvements
|
||||
* Eliminate redundant key comparisons during random access in block-based tables.
|
||||
|
||||
## 6.10 (5/2/2020)
|
||||
### Bug Fixes
|
||||
@@ -22,7 +69,6 @@
|
||||
* Add a ConfigOptions argument to the APIs dealing with converting options to and from strings and files. The ConfigOptions is meant to replace some of the options (such as input_strings_escaped and ignore_unknown_options) and allow for more parameters to be passed in the future without changing the function signature.
|
||||
* Add NewFileChecksumGenCrc32cFactory to the file checksum public API, such that the builtin Crc32c based file checksum generator factory can be used by applications.
|
||||
* Add IsDirectory to Env and FS to indicate if a path is a directory.
|
||||
* DeleteRange now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
|
||||
### New Features
|
||||
* Added support for pipelined & parallel compression optimization for `BlockBasedTableBuilder`. This optimization makes block building, block compression and block appending a pipeline, and uses multiple threads to accelerate block compression. Users can set `CompressionOptions::parallel_threads` greater than 1 to enable compression parallelism. This feature is experimental for now.
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
BASH_EXISTS := $(shell which bash)
|
||||
SHELL := $(shell which bash)
|
||||
PYTHON?=$(shell which python)
|
||||
# Default to python3. Some distros like CentOS 8 do not have `python`.
|
||||
PYTHON?=$(shell which python3 || which python || echo python3)
|
||||
export PYTHON
|
||||
|
||||
CLEAN_FILES = # deliberately empty, so we can append below.
|
||||
CFLAGS += ${EXTRA_CFLAGS}
|
||||
@@ -166,6 +168,12 @@ else
|
||||
CXXFLAGS += -fno-rtti
|
||||
endif
|
||||
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
ifeq ($(filter -DROCKSDB_ASSERT_STATUS_CHECKED,$(OPT)),)
|
||||
OPT += -DROCKSDB_ASSERT_STATUS_CHECKED
|
||||
endif
|
||||
endif
|
||||
|
||||
$(warning Warning: Compiling in debug mode. Don't use the resulting binary in production)
|
||||
endif
|
||||
|
||||
@@ -187,12 +195,16 @@ AM_V_CC = $(am__v_CC_$(V))
|
||||
am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_CC_0 = @echo " CC " $@;
|
||||
am__v_CC_1 =
|
||||
CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
AM_V_CCLD = $(am__v_CCLD_$(V))
|
||||
am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
|
||||
ifneq ($(SKIP_LINK), 1)
|
||||
am__v_CCLD_0 = @echo " CCLD " $@;
|
||||
am__v_CCLD_1 =
|
||||
else
|
||||
am__v_CCLD_0 = @echo " !CCLD " $@; true skip
|
||||
am__v_CCLD_1 = true skip
|
||||
endif
|
||||
AM_V_AR = $(am__v_AR_$(V))
|
||||
am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY))
|
||||
am__v_AR_0 = @echo " AR " $@;
|
||||
@@ -204,6 +216,7 @@ LDFLAGS += -lrados
|
||||
endif
|
||||
|
||||
AM_LINK = $(AM_V_CCLD)$(CXX) $^ $(EXEC_LDFLAGS) -o $@ $(LDFLAGS) $(COVERAGEFLAGS)
|
||||
|
||||
# Detect what platform we're building on.
|
||||
# Export some common variables that might have been passed as Make variables
|
||||
# instead of environment variables.
|
||||
@@ -321,7 +334,7 @@ endif
|
||||
|
||||
export GTEST_THROW_ON_FAILURE=1
|
||||
export GTEST_HAS_EXCEPTIONS=1
|
||||
GTEST_DIR = ./third-party/gtest-1.8.1/fused-src
|
||||
GTEST_DIR = third-party/gtest-1.8.1/fused-src
|
||||
# AIX: pre-defined system headers are surrounded by an extern "C" block
|
||||
ifeq ($(PLATFORM), OS_AIX)
|
||||
PLATFORM_CCFLAGS += -I$(GTEST_DIR)
|
||||
@@ -533,6 +546,7 @@ TESTS = \
|
||||
random_access_file_reader_test \
|
||||
file_reader_writer_test \
|
||||
block_based_filter_block_test \
|
||||
block_based_table_reader_test \
|
||||
full_filter_block_test \
|
||||
partitioned_filter_block_test \
|
||||
hash_table_test \
|
||||
@@ -625,10 +639,8 @@ TESTS = \
|
||||
blob_file_garbage_test \
|
||||
timer_test \
|
||||
db_with_timestamp_compaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
testutil_test \
|
||||
io_tracer_test \
|
||||
|
||||
PARALLEL_TEST = \
|
||||
backupable_db_test \
|
||||
@@ -653,10 +665,67 @@ PARALLEL_TEST = \
|
||||
write_prepared_transaction_test \
|
||||
write_unprepared_transaction_test \
|
||||
|
||||
ifeq ($(USE_FOLLY_DISTRIBUTED_MUTEX),1)
|
||||
TESTS += folly_synchronization_distributed_mutex_test
|
||||
PARALLEL_TEST += folly_synchronization_distributed_mutex_test
|
||||
endif
|
||||
|
||||
# options_settable_test doesn't pass with UBSAN as we use hack in the test
|
||||
ifdef COMPILE_WITH_UBSAN
|
||||
TESTS := $(shell echo $(TESTS) | sed 's/\boptions_settable_test\b//g')
|
||||
endif
|
||||
ifdef ASSERT_STATUS_CHECKED
|
||||
# This is a new check for which we will add support incrementally. This
|
||||
# list can be removed once support is fully added.
|
||||
TESTS_PASSING_ASC = \
|
||||
arena_test \
|
||||
autovector_test \
|
||||
blob_file_addition_test \
|
||||
blob_file_garbage_test \
|
||||
bloom_test \
|
||||
cassandra_format_test \
|
||||
cassandra_row_merge_test \
|
||||
cassandra_serialize_test \
|
||||
cleanable_test \
|
||||
coding_test \
|
||||
crc32c_test \
|
||||
dbformat_test \
|
||||
defer_test \
|
||||
dynamic_bloom_test \
|
||||
event_logger_test \
|
||||
file_indexer_test \
|
||||
folly_synchronization_distributed_mutex_test \
|
||||
hash_table_test \
|
||||
hash_test \
|
||||
heap_test \
|
||||
histogram_test \
|
||||
inlineskiplist_test \
|
||||
io_posix_test \
|
||||
iostats_context_test \
|
||||
memkind_kmem_allocator_test \
|
||||
merger_test \
|
||||
mock_env_test \
|
||||
object_registry_test \
|
||||
options_settable_test \
|
||||
options_test \
|
||||
random_test \
|
||||
range_del_aggregator_test \
|
||||
range_tombstone_fragmenter_test \
|
||||
repeatable_thread_test \
|
||||
skiplist_test \
|
||||
slice_test \
|
||||
statistics_test \
|
||||
thread_local_test \
|
||||
timer_queue_test \
|
||||
timer_test \
|
||||
util_merge_operators_test \
|
||||
version_edit_test \
|
||||
work_queue_test \
|
||||
write_controller_test \
|
||||
|
||||
TESTS := $(filter $(TESTS_PASSING_ASC),$(TESTS))
|
||||
PARALLEL_TEST := $(filter $(TESTS_PASSING_ASC),$(PARALLEL_TEST))
|
||||
endif
|
||||
SUBSET := $(TESTS)
|
||||
ifdef ROCKSDBTESTS_START
|
||||
SUBSET := $(shell echo $(SUBSET) | sed 's/^.*$(ROCKSDBTESTS_START)/$(ROCKSDBTESTS_START)/')
|
||||
@@ -776,7 +845,8 @@ endif # PLATFORM_SHARED_EXT
|
||||
dbg rocksdbjavastatic rocksdbjava install install-static install-shared uninstall \
|
||||
analyze tools tools_lib \
|
||||
blackbox_crash_test_with_atomic_flush whitebox_crash_test_with_atomic_flush \
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn
|
||||
blackbox_crash_test_with_txn whitebox_crash_test_with_txn \
|
||||
blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
|
||||
all: $(LIBRARY) $(BENCHMARKS) tools tools_lib test_libs $(TESTS)
|
||||
@@ -908,6 +978,12 @@ J ?= 100%
|
||||
# Use this regexp to select the subset of tests whose names match.
|
||||
tests-regexp = .
|
||||
|
||||
ifeq ($(PRINT_PARALLEL_OUTPUTS), 1)
|
||||
parallel_com = '{}'
|
||||
else
|
||||
parallel_com = '{} >& t/log-{/}'
|
||||
endif
|
||||
|
||||
.PHONY: check_0
|
||||
check_0:
|
||||
$(AM_V_GEN)export TEST_TMPDIR=$(TMPD); \
|
||||
@@ -921,9 +997,12 @@ check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu '{} >& t/log-{/}'
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu $(parallel_com) ; \
|
||||
parallel_retcode=$$? ; \
|
||||
awk '{ if ($$7 != 0 || $$8 != 0) { if ($$7 == "Exitval") { h = $$0; } else { if (!f) print h; print; f = 1 } } } END { if(f) exit 1; }' < LOG ; \
|
||||
if [ $$parallel_retcode -ne 0 ] ; then exit 1 ; fi
|
||||
|
||||
valgrind-blacklist-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized|MySQLStyleTransactionTest.TransactionStressTest
|
||||
valgrind-exclude-regexp = InlineSkipTest.ConcurrentInsert|TransactionStressTest.DeadlockStress|DBCompactionTest.SuggestCompactRangeNoTwoLevel0Compactions|BackupableDBTest.RateLimiting|DBTest.CloseSpeedup|DBTest.ThreadStatusFlush|DBTest.RateLimitingTest|DBTest.EncodeDecompressedBlockSizeTest|FaultInjectionTest.UninstalledCompaction|HarnessTest.Randomized|ExternalSSTFileTest.CompactDuringAddFileRandom|ExternalSSTFileTest.IngestFileWithGlobalSeqnoRandomized|MySQLStyleTransactionTest.TransactionStressTest
|
||||
|
||||
.PHONY: valgrind_check_0
|
||||
valgrind_check_0:
|
||||
@@ -938,7 +1017,7 @@ valgrind_check_0:
|
||||
} \
|
||||
| $(prioritize_long_running_tests) \
|
||||
| grep -E '$(tests-regexp)' \
|
||||
| grep -E -v '$(valgrind-blacklist-regexp)' \
|
||||
| grep -E -v '$(valgrind-exclude-regexp)' \
|
||||
| build_tools/gnu_parallel -j$(J) --plain --joblog=LOG $$eta --gnu \
|
||||
'(if [[ "{}" == "./"* ]] ; then $(DRIVER) {}; else {}; fi) ' \
|
||||
'>& t/valgrind_log-{/}'
|
||||
@@ -955,6 +1034,9 @@ CLEAN_FILES += t LOG $(TMPD)
|
||||
watch-log:
|
||||
$(WATCH) --interval=0 'sort -k7,7nr -k4,4gr LOG|$(quoted_perl_command)'
|
||||
|
||||
dump-log:
|
||||
bash -c '$(quoted_perl_command)' < LOG
|
||||
|
||||
# If J != 1 and GNU parallel is installed, run the tests in parallel,
|
||||
# via the check_0 rule above. Otherwise, run them sequentially.
|
||||
check: all
|
||||
@@ -972,12 +1054,16 @@ check: all
|
||||
ifneq ($(PLATFORM), OS_AIX)
|
||||
$(PYTHON) tools/check_all_python.py
|
||||
ifeq ($(filter -DROCKSDB_LITE,$(OPT)),)
|
||||
ifndef ASSERT_STATUS_CHECKED # not yet working with these tests
|
||||
$(PYTHON) tools/ldb_test.py
|
||||
sh tools/rocksdb_dump_test.sh
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
ifndef SKIP_FORMAT_BUCK_CHECKS
|
||||
$(MAKE) check-format
|
||||
$(MAKE) check-buck-targets
|
||||
endif
|
||||
|
||||
# TODO add ldb_tests
|
||||
check_some: $(SUBSET)
|
||||
@@ -993,6 +1079,8 @@ crash_test_with_atomic_flush: whitebox_crash_test_with_atomic_flush blackbox_cra
|
||||
|
||||
crash_test_with_txn: whitebox_crash_test_with_txn blackbox_crash_test_with_txn
|
||||
|
||||
crash_test_with_best_efforts_recovery: blackbox_crash_test_with_best_efforts_recovery
|
||||
|
||||
blackbox_crash_test: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --simple blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
$(PYTHON) -u tools/db_crashtest.py blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
@@ -1003,6 +1091,9 @@ blackbox_crash_test_with_atomic_flush: db_stress
|
||||
blackbox_crash_test_with_txn: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --txn blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
blackbox_crash_test_with_best_efforts_recovery: db_stress
|
||||
$(PYTHON) -u tools/db_crashtest.py --test_best_efforts_recovery blackbox $(CRASH_TEST_EXT_ARGS)
|
||||
|
||||
ifeq ($(CRASH_TEST_KILL_ODD),)
|
||||
CRASH_TEST_KILL_ODD=888887
|
||||
endif
|
||||
@@ -1041,6 +1132,11 @@ asan_crash_test_with_txn:
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
asan_crash_test_with_best_efforts_recovery:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_ASAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_check:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) check -j32
|
||||
@@ -1061,6 +1157,11 @@ ubsan_crash_test_with_txn:
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_txn
|
||||
$(MAKE) clean
|
||||
|
||||
ubsan_crash_test_with_best_efforts_recovery:
|
||||
$(MAKE) clean
|
||||
COMPILE_WITH_UBSAN=1 $(MAKE) crash_test_with_best_efforts_recovery
|
||||
$(MAKE) clean
|
||||
|
||||
valgrind_test:
|
||||
ROCKSDB_VALGRIND_RUN=1 DISABLE_JEMALLOC=1 $(MAKE) valgrind_check
|
||||
|
||||
@@ -1266,9 +1367,9 @@ db_repl_stress: tools/db_repl_stress.o $(LIBOBJECTS) $(TESTUTIL)
|
||||
|
||||
arena_test: memory/arena_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
|
||||
memkind_kmem_allocator_test: memory/memkind_kmem_allocator_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
$(AM_LINK)
|
||||
|
||||
autovector_test: util/autovector_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
@@ -1554,6 +1655,9 @@ file_reader_writer_test: util/file_reader_writer_test.o $(LIBOBJECTS) $(TESTHARN
|
||||
block_based_filter_block_test: table/block_based/block_based_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
block_based_table_reader_test: table/block_based/block_based_table_reader_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
full_filter_block_test: table/block_based/full_filter_block_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
@@ -1791,6 +1895,11 @@ blob_file_garbage_test: db/blob/blob_file_garbage_test.o $(LIBOBJECTS) $(TESTHAR
|
||||
timer_test: util/timer_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
testutil_test: test_util/testutil_test.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
io_tracer_test: trace_replay/io_tracer_test.o trace_replay/io_tracer.o $(LIBOBJECTS) $(TESTHARNESS)
|
||||
$(AM_LINK)
|
||||
|
||||
#-------------------------------------------------
|
||||
# make install related stuff
|
||||
INSTALL_PATH ?= /usr/local
|
||||
@@ -2199,6 +2308,7 @@ endif
|
||||
# Source files dependencies detection
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# FIXME: nothing checks that entries in MAIN_SOURCES actually exist
|
||||
all_sources = $(LIB_SOURCES) $(MAIN_SOURCES) $(MOCK_LIB_SOURCES) $(TOOL_LIB_SOURCES) $(BENCH_LIB_SOURCES) $(TEST_LIB_SOURCES) $(ANALYZER_LIB_SOURCES) $(STRESS_LIB_SOURCES)
|
||||
DEPFILES = $(all_sources:.cc=.cc.d)
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
## RocksDB: A Persistent Key-Value Store for Flash and RAM Storage
|
||||
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](https://circleci.com/gh/facebook/rocksdb)
|
||||
[](https://travis-ci.org/facebook/rocksdb)
|
||||
[](https://ci.appveyor.com/project/Facebook/rocksdb/branch/master)
|
||||
[](http://140.211.168.68:8080/job/Rocksdb)
|
||||
|
||||
RocksDB is developed and maintained by Facebook Database Engineering Team.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# This file @generated by `python buckifier/buckify_rocksdb.py`
|
||||
# This file @generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -26,7 +26,6 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -109,6 +108,11 @@ ROCKSDB_OS_DEPS += ([(
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
|
||||
cpp_library(
|
||||
name = "rocksdb_lib",
|
||||
srcs = [
|
||||
@@ -120,6 +124,9 @@ cpp_library(
|
||||
"db/blob/blob_file_addition.cc",
|
||||
"db/blob/blob_file_garbage.cc",
|
||||
"db/blob/blob_file_meta.cc",
|
||||
"db/blob/blob_log_format.cc",
|
||||
"db/blob/blob_log_reader.cc",
|
||||
"db/blob/blob_log_writer.cc",
|
||||
"db/builder.cc",
|
||||
"db/c.cc",
|
||||
"db/column_family.cc",
|
||||
@@ -287,6 +294,7 @@ cpp_library(
|
||||
"tools/ldb_tool.cc",
|
||||
"tools/sst_dump_tool.cc",
|
||||
"trace_replay/block_cache_tracer.cc",
|
||||
"trace_replay/io_tracer.cc",
|
||||
"trace_replay/trace_replay.cc",
|
||||
"util/build_version.cc",
|
||||
"util/coding.cc",
|
||||
@@ -314,9 +322,6 @@ cpp_library(
|
||||
"utilities/blob_db/blob_db_impl_filesnapshot.cc",
|
||||
"utilities/blob_db/blob_dump_tool.cc",
|
||||
"utilities/blob_db/blob_file.cc",
|
||||
"utilities/blob_db/blob_log_format.cc",
|
||||
"utilities/blob_db/blob_log_reader.cc",
|
||||
"utilities/blob_db/blob_log_writer.cc",
|
||||
"utilities/cassandra/cassandra_compaction_filter.cc",
|
||||
"utilities/cassandra/format.cc",
|
||||
"utilities/cassandra/merge_operator.cc",
|
||||
@@ -394,7 +399,9 @@ cpp_library(
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_lib"],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
)
|
||||
|
||||
cpp_library(
|
||||
@@ -437,10 +444,7 @@ cpp_library(
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
],
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
|
||||
@@ -515,6 +519,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_based_table_reader_test",
|
||||
"table/block_based/block_based_table_reader_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"block_cache_trace_analyzer_test",
|
||||
"tools/block_cache_analyzer/block_cache_trace_analyzer_test.cc",
|
||||
@@ -1145,6 +1156,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"io_tracer_test",
|
||||
"trace_replay/io_tracer_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"iostats_context_test",
|
||||
"monitoring/iostats_context_test.cc",
|
||||
@@ -1460,6 +1478,13 @@ ROCKS_TESTS = [
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"testutil_test",
|
||||
"test_util/testutil_test.cc",
|
||||
"serial",
|
||||
[],
|
||||
[],
|
||||
],
|
||||
[
|
||||
"thread_list_test",
|
||||
"util/thread_list_test.cc",
|
||||
@@ -1613,18 +1638,17 @@ ROCKS_TESTS = [
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
[
|
||||
test_binary(
|
||||
extra_compiler_flags = extra_compiler_flags,
|
||||
extra_deps = extra_deps,
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_os_deps = ROCKSDB_OS_DEPS,
|
||||
rocksdb_os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
cpp_unittest(
|
||||
name = test_name,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
)
|
||||
for test_name, test_cc, parallelism, extra_deps, extra_compiler_flags in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
|
||||
+2
-2
@@ -68,7 +68,7 @@ before_build:
|
||||
- md %APPVEYOR_BUILD_FOLDER%\build
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\build
|
||||
- if DEFINED CMAKE_PLATEFORM_NAME (set "PLATEFORM_OPT=-A %CMAKE_PLATEFORM_NAME%")
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" %PLATEFORM_OPT% %CMAKE_OPT% -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1
|
||||
- cmake .. -G "%CMAKE_GENERATOR%" %PLATEFORM_OPT% %CMAKE_OPT% -DCMAKE_BUILD_TYPE=Debug -DOPTDBG=1 -DPORTABLE=1 -DSNAPPY=1 -DLZ4=1 -DZSTD=1 -DXPRESS=1 -DJNI=1 -DWITH_ALL_TESTS=0
|
||||
- cd ..
|
||||
|
||||
build:
|
||||
@@ -79,7 +79,7 @@ build:
|
||||
test:
|
||||
|
||||
test_script:
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,db_with_timestamp_basic_test,db_test2,db_test,env_basic_test,env_test,db_merge_operand_test -Concurrency 8
|
||||
- ps: build_tools\run_ci_db_test.ps1 -SuiteRun db_basic_test,env_basic_test -Concurrency 8
|
||||
|
||||
on_failure:
|
||||
- cmd: 7z a build-failed.zip %APPVEYOR_BUILD_FOLDER%\build\ && appveyor PushArtifact build-failed.zip
|
||||
|
||||
@@ -20,10 +20,10 @@ from util import ColorString
|
||||
# User can pass extra dependencies as a JSON object via command line, and this
|
||||
# script can include these dependencies in the generate TARGETS file.
|
||||
# Usage:
|
||||
# $python buckifier/buckify_rocksdb.py
|
||||
# $python3 buckifier/buckify_rocksdb.py
|
||||
# (This generates a TARGET file without user-specified dependency for unit
|
||||
# tests.)
|
||||
# $python buckifier/buckify_rocksdb.py \
|
||||
# $python3 buckifier/buckify_rocksdb.py \
|
||||
# '{"fake": { \
|
||||
# "extra_deps": [":test_dep", "//fakes/module:mock1"], \
|
||||
# "extra_compiler_flags": ["-DROCKSDB_LITE", "-Os"], \
|
||||
@@ -108,7 +108,6 @@ def get_tests(repo_path):
|
||||
|
||||
return tests
|
||||
|
||||
|
||||
# Parse extra dependencies passed by user from command line
|
||||
def get_dependencies():
|
||||
deps_map = {
|
||||
@@ -147,6 +146,7 @@ def generate_targets(repo_path, deps_map):
|
||||
return False
|
||||
|
||||
TARGETS = TARGETSBuilder("%s/TARGETS" % repo_path)
|
||||
|
||||
# rocksdb_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_lib",
|
||||
@@ -159,7 +159,10 @@ def generate_targets(repo_path, deps_map):
|
||||
src_mk.get("TEST_LIB_SOURCES", []) +
|
||||
src_mk.get("EXP_LIB_SOURCES", []) +
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", []),
|
||||
[":rocksdb_lib"])
|
||||
[":rocksdb_lib"],
|
||||
extra_external_deps=""" + [
|
||||
("googletest", None, "gtest"),
|
||||
]""")
|
||||
# rocksdb_tools_lib
|
||||
TARGETS.add_library(
|
||||
"rocksdb_tools_lib",
|
||||
@@ -168,14 +171,13 @@ def generate_targets(repo_path, deps_map):
|
||||
["test_util/testutil.cc"],
|
||||
[":rocksdb_lib"])
|
||||
# rocksdb_stress_lib
|
||||
TARGETS.add_library(
|
||||
TARGETS.add_rocksdb_library(
|
||||
"rocksdb_stress_lib",
|
||||
src_mk.get("ANALYZER_LIB_SOURCES", [])
|
||||
+ src_mk.get('STRESS_LIB_SOURCES', [])
|
||||
+ ["test_util/testutil.cc"],
|
||||
[":rocksdb_lib", ":rocksdb_test_lib"])
|
||||
+ ["test_util/testutil.cc"])
|
||||
|
||||
print("Extra dependencies:\n{0}".format(str(deps_map)))
|
||||
print("Extra dependencies:\n{0}".format(json.dumps(deps_map)))
|
||||
# test for every test we found in the Makefile
|
||||
for target_alias, deps in deps_map.items():
|
||||
for test in sorted(tests):
|
||||
@@ -196,8 +198,8 @@ def generate_targets(repo_path, deps_map):
|
||||
test_target_name,
|
||||
match_src[0],
|
||||
is_parallel,
|
||||
deps['extra_deps'],
|
||||
deps['extra_compiler_flags'])
|
||||
json.dumps(deps['extra_deps']),
|
||||
json.dumps(deps['extra_compiler_flags']))
|
||||
|
||||
if test in _EXPORTED_TEST_LIBS:
|
||||
test_library = "%s_lib" % test_target_name
|
||||
@@ -220,6 +222,7 @@ def get_rocksdb_path():
|
||||
|
||||
return rocksdb_path
|
||||
|
||||
|
||||
def exit_with_error(msg):
|
||||
print(ColorString.error(msg))
|
||||
sys.exit(1)
|
||||
|
||||
@@ -15,7 +15,7 @@ echo Backup original TARGETS file.
|
||||
|
||||
cp TARGETS TARGETS.bkp
|
||||
|
||||
python buckifier/buckify_rocksdb.py
|
||||
${PYTHON:-python3} buckifier/buckify_rocksdb.py
|
||||
|
||||
TGT_DIFF=`git diff TARGETS | head -n 1`
|
||||
|
||||
@@ -24,8 +24,9 @@ then
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 0
|
||||
else
|
||||
echo "Please run 'python buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Please run '${PYTHON:-python3} buckifier/buckify_rocksdb.py' to update TARGETS file."
|
||||
echo "Do not manually update TARGETS file."
|
||||
${PYTHON:-python3} --version
|
||||
mv TARGETS.bkp TARGETS
|
||||
exit 1
|
||||
fi
|
||||
@@ -28,7 +28,8 @@ class TARGETSBuilder(object):
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.targets_file = open(path, 'w')
|
||||
self.targets_file.write(targets_cfg.rocksdb_target_header)
|
||||
header = targets_cfg.rocksdb_target_header_template
|
||||
self.targets_file.write(header)
|
||||
self.total_lib = 0
|
||||
self.total_bin = 0
|
||||
self.total_test = 0
|
||||
@@ -37,17 +38,35 @@ class TARGETSBuilder(object):
|
||||
def __del__(self):
|
||||
self.targets_file.close()
|
||||
|
||||
def add_library(self, name, srcs, deps=None, headers=None):
|
||||
def add_library(self, name, srcs, deps=None, headers=None,
|
||||
extra_external_deps=""):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers,
|
||||
deps=pretty_list(deps)))
|
||||
deps=pretty_list(deps),
|
||||
extra_external_deps=extra_external_deps))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_rocksdb_library(self, name, srcs, headers=None):
|
||||
headers_attr_prefix = ""
|
||||
if headers is None:
|
||||
headers_attr_prefix = "auto_"
|
||||
headers = "AutoHeaders.RECURSIVE_GLOB"
|
||||
else:
|
||||
headers = "[" + pretty_list(headers) + "]"
|
||||
self.targets_file.write(targets_cfg.rocksdb_library_template.format(
|
||||
name=name,
|
||||
srcs=pretty_list(srcs),
|
||||
headers_attr_prefix=headers_attr_prefix,
|
||||
headers=headers))
|
||||
self.total_lib = self.total_lib + 1
|
||||
|
||||
def add_binary(self, name, srcs, deps=None):
|
||||
|
||||
+33
-14
@@ -4,7 +4,8 @@ from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
rocksdb_target_header = """# This file \100generated by `python buckifier/buckify_rocksdb.py`
|
||||
rocksdb_target_header_template = \
|
||||
"""# This file \100generated by `python3 buckifier/buckify_rocksdb.py`
|
||||
# --> DO NOT EDIT MANUALLY <--
|
||||
# This file is a Facebook-specific integration for buck builds, so can
|
||||
# only be validated by Facebook employees.
|
||||
@@ -32,7 +33,6 @@ ROCKSDB_EXTERNAL_DEPS = [
|
||||
("lz4", None, "lz4"),
|
||||
("zstd", None),
|
||||
("tbb", None),
|
||||
("googletest", None, "gtest"),
|
||||
]
|
||||
|
||||
ROCKSDB_OS_DEPS = [
|
||||
@@ -114,6 +114,11 @@ ROCKSDB_OS_DEPS += ([(
|
||||
"linux",
|
||||
["third-party//jemalloc:headers"],
|
||||
)] if sanitizer == "" else [])
|
||||
|
||||
ROCKSDB_LIB_DEPS = [
|
||||
":rocksdb_lib",
|
||||
":rocksdb_test_lib",
|
||||
] if not is_opt_mode else [":rocksdb_lib"]
|
||||
"""
|
||||
|
||||
|
||||
@@ -128,6 +133,21 @@ cpp_library(
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [{deps}],
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS{extra_external_deps},
|
||||
)
|
||||
"""
|
||||
|
||||
rocksdb_library_template = """
|
||||
cpp_library(
|
||||
name = "{name}",
|
||||
srcs = [{srcs}],
|
||||
{headers_attr_prefix}headers = {headers},
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
os_deps = ROCKSDB_OS_DEPS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = ROCKSDB_LIB_DEPS,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
)
|
||||
"""
|
||||
@@ -162,18 +182,17 @@ ROCKS_TESTS = [
|
||||
# Do not build the tests in opt mode, since SyncPoint and other test code
|
||||
# will not be included.
|
||||
[
|
||||
test_binary(
|
||||
extra_compiler_flags = extra_compiler_flags,
|
||||
extra_deps = extra_deps,
|
||||
parallelism = parallelism,
|
||||
rocksdb_arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
rocksdb_compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
rocksdb_external_deps = ROCKSDB_EXTERNAL_DEPS,
|
||||
rocksdb_os_deps = ROCKSDB_OS_DEPS,
|
||||
rocksdb_os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
rocksdb_preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
test_cc = test_cc,
|
||||
test_name = test_name,
|
||||
cpp_unittest(
|
||||
name = test_name,
|
||||
srcs = [test_cc],
|
||||
arch_preprocessor_flags = ROCKSDB_ARCH_PREPROCESSOR_FLAGS,
|
||||
os_preprocessor_flags = ROCKSDB_OS_PREPROCESSOR_FLAGS,
|
||||
compiler_flags = ROCKSDB_COMPILER_FLAGS,
|
||||
preprocessor_flags = ROCKSDB_PREPROCESSOR_FLAGS,
|
||||
deps = [":rocksdb_test_lib"] + extra_deps,
|
||||
external_deps = ROCKSDB_EXTERNAL_DEPS + [
|
||||
("googletest", None, "gtest"),
|
||||
],
|
||||
)
|
||||
for test_name, test_cc, parallelism, extra_deps, extra_compiler_flags in ROCKS_TESTS
|
||||
if not is_opt_mode
|
||||
|
||||
@@ -192,6 +192,17 @@ EOF
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread"
|
||||
# PORT_FILES=port/freebsd/freebsd_specific.cc
|
||||
;;
|
||||
GNU/kFreeBSD)
|
||||
PLATFORM=OS_GNU_KFREEBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -DOS_GNU_KFREEBSD"
|
||||
if [ -z "$USE_CLANG" ]; then
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp"
|
||||
else
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -latomic"
|
||||
fi
|
||||
PLATFORM_LDFLAGS="$PLATFORM_LDFLAGS -lpthread -lrt"
|
||||
# PORT_FILES=port/gnu_kfreebsd/gnu_kfreebsd_specific.cc
|
||||
;;
|
||||
NetBSD)
|
||||
PLATFORM=OS_NETBSD
|
||||
COMMON_FLAGS="$COMMON_FLAGS -fno-builtin-memcmp -D_REENTRANT -DOS_NETBSD"
|
||||
|
||||
+68
-40
@@ -26,53 +26,77 @@ while getopts ':ch' OPTION; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z $CLANG_FORMAT_DIFF ]
|
||||
then
|
||||
CLANG_FORMAT_DIFF="clang-format-diff.py"
|
||||
fi
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
|
||||
# Check clang-format-diff.py
|
||||
if ! which $CLANG_FORMAT_DIFF &> /dev/null
|
||||
then
|
||||
if [ ! -f ./clang-format-diff.py ]
|
||||
then
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
if [ "$CLANG_FORMAT_DIFF" ]; then
|
||||
echo "Note: CLANG_FORMAT_DIFF='$CLANG_FORMAT_DIFF'"
|
||||
# Dry run to confirm dependencies like argparse
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
if [ -x ./clang-format-diff.py ]
|
||||
then
|
||||
PATH=$PATH:.
|
||||
exit 128
|
||||
fi
|
||||
else
|
||||
# First try directly executing the possibilities
|
||||
if clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=clang-format-diff.py
|
||||
elif $REPO_ROOT/clang-format-diff.py --help &> /dev/null < /dev/null; then
|
||||
CLANG_FORMAT_DIFF=$REPO_ROOT/clang-format-diff.py
|
||||
else
|
||||
# This probably means we need to directly invoke the interpreter.
|
||||
# But first find clang-format-diff.py
|
||||
if [ -f "$REPO_ROOT/clang-format-diff.py" ]; then
|
||||
CFD_PATH="$REPO_ROOT/clang-format-diff.py"
|
||||
elif which clang-format-diff.py &> /dev/null; then
|
||||
CFD_PATH="$(which clang-format-diff.py)"
|
||||
else
|
||||
CLANG_FORMAT_DIFF="python ./clang-format-diff.py"
|
||||
echo "You didn't have clang-format-diff.py and/or clang-format available in your computer!"
|
||||
echo "You can download clang-format-diff.py by running: "
|
||||
echo " curl --location http://goo.gl/iUW1u2 -o ${CLANG_FORMAT_DIFF}"
|
||||
echo "You can download clang-format by running:"
|
||||
echo " brew install clang-format"
|
||||
echo " Or"
|
||||
echo " apt install clang-format"
|
||||
echo " This might work too:"
|
||||
echo " yum install git-clang-format"
|
||||
echo "Then, move both files (i.e. ${CLANG_FORMAT_DIFF} and clang-format) to some directory within PATH=${PATH}"
|
||||
echo "and make sure ${CLANG_FORMAT_DIFF} is executable."
|
||||
exit 128
|
||||
fi
|
||||
# Check argparse pre-req on interpreter, or it will fail
|
||||
if echo import argparse | ${PYTHON:-python3}; then
|
||||
true # Good
|
||||
else
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
# Unfortunately, some machines have a Python2 clang-format-diff.py
|
||||
# installed but only a Python3 interpreter installed. Rather than trying
|
||||
# different Python versions that might be installed, we can try migrating
|
||||
# the code to Python3 if it looks like Python2
|
||||
if grep -q "print '" "$CFD_PATH" && \
|
||||
${PYTHON:-python3} --version | grep -q 'ython 3'; then
|
||||
if [ ! -f "$REPO_ROOT/.py3/clang-format-diff.py" ]; then
|
||||
echo "Migrating $CFD_PATH to Python3 in a hidden file"
|
||||
mkdir -p "$REPO_ROOT/.py3"
|
||||
${PYTHON:-python3} -m lib2to3 -w -n -o "$REPO_ROOT/.py3" "$CFD_PATH" > /dev/null || exit 128
|
||||
fi
|
||||
CFD_PATH="$REPO_ROOT/.py3/clang-format-diff.py"
|
||||
fi
|
||||
CLANG_FORMAT_DIFF="${PYTHON:-python3} $CFD_PATH"
|
||||
# This had better work after all those checks
|
||||
if $CLANG_FORMAT_DIFF --help >/dev/null < /dev/null; then
|
||||
true #Good
|
||||
else
|
||||
exit 128
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check argparse, a library that clang-format-diff.py requires.
|
||||
python 2>/dev/null << EOF
|
||||
import argparse
|
||||
EOF
|
||||
|
||||
if [ "$?" != 0 ]
|
||||
then
|
||||
echo "To run clang-format-diff.py, we'll need the library "argparse" to be"
|
||||
echo "installed. You can try either of the follow ways to install it:"
|
||||
echo " 1. Manually download argparse: https://pypi.python.org/pypi/argparse"
|
||||
echo " 2. easy_install argparse (if you have easy_install)"
|
||||
echo " 3. pip install argparse (if you have pip)"
|
||||
exit 129
|
||||
fi
|
||||
|
||||
# TODO(kailiu) following work is not complete since we still need to figure
|
||||
# out how to add the modified files done pre-commit hook to git's commit index.
|
||||
#
|
||||
@@ -124,6 +148,10 @@ then
|
||||
elif [ $CHECK_ONLY ]
|
||||
then
|
||||
echo "Your change has unformatted code. Please run make format!"
|
||||
if [ $VERBOSE_CHECK ]; then
|
||||
clang-format --version
|
||||
echo "$diffs"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Vendored
+30
-5
@@ -14,7 +14,30 @@
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
Status Cache::CreateFromString(const ConfigOptions& /*opts*/,
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
lru_cache_options_type_info = {
|
||||
{"capacity",
|
||||
{offsetof(struct LRUCacheOptions, capacity), OptionType::kSizeT,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, capacity)}},
|
||||
{"num_shard_bits",
|
||||
{offsetof(struct LRUCacheOptions, num_shard_bits), OptionType::kInt,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, num_shard_bits)}},
|
||||
{"strict_capacity_limit",
|
||||
{offsetof(struct LRUCacheOptions, strict_capacity_limit),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, strict_capacity_limit)}},
|
||||
{"high_pri_pool_ratio",
|
||||
{offsetof(struct LRUCacheOptions, high_pri_pool_ratio),
|
||||
OptionType::kDouble, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kMutable,
|
||||
offsetof(struct LRUCacheOptions, high_pri_pool_ratio)}}};
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
Status Cache::CreateFromString(const ConfigOptions& config_options,
|
||||
const std::string& value,
|
||||
std::shared_ptr<Cache>* result) {
|
||||
Status status;
|
||||
@@ -24,12 +47,14 @@ Status Cache::CreateFromString(const ConfigOptions& /*opts*/,
|
||||
} else {
|
||||
#ifndef ROCKSDB_LITE
|
||||
LRUCacheOptions cache_opts;
|
||||
if (!ParseOptionHelper(reinterpret_cast<char*>(&cache_opts),
|
||||
OptionType::kLRUCacheOptions, value)) {
|
||||
status = Status::InvalidArgument("Invalid cache options");
|
||||
status = OptionTypeInfo::ParseStruct(
|
||||
config_options, "", &lru_cache_options_type_info, "", value,
|
||||
reinterpret_cast<char*>(&cache_opts));
|
||||
if (status.ok()) {
|
||||
cache = NewLRUCache(cache_opts);
|
||||
}
|
||||
cache = NewLRUCache(cache_opts);
|
||||
#else
|
||||
(void)config_options;
|
||||
status = Status::NotSupported("Cannot load cache in LITE mode ", value);
|
||||
#endif //! ROCKSDB_LITE
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -182,7 +182,7 @@ struct CacheHandle {
|
||||
void (*deleter)(const Slice&, void* value);
|
||||
|
||||
// Flags and counters associated with the cache handle:
|
||||
// lowest bit: n-cache bit
|
||||
// lowest bit: in-cache bit
|
||||
// second lowest bit: usage bit
|
||||
// the rest bits: reference count
|
||||
// The handle is unused when flags equals to 0. The thread decreases the count
|
||||
|
||||
@@ -1,3 +1,54 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
set(GFLAGS_USE_TARGET_NAMESPACE @GFLAGS_USE_TARGET_NAMESPACE@)
|
||||
|
||||
if(@WITH_JEMALLOC@)
|
||||
find_dependency(JeMalloc)
|
||||
endif()
|
||||
|
||||
if(@WITH_GFLAGS@)
|
||||
find_dependency(gflags CONFIG)
|
||||
if(NOT gflags_FOUND)
|
||||
find_dependency(gflags)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(@WITH_SNAPPY@)
|
||||
find_dependency(Snappy CONFIG)
|
||||
if(NOT Snappy_FOUND)
|
||||
find_dependency(Snappy)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(@WITH_ZLIB@)
|
||||
find_dependency(ZLIB)
|
||||
endif()
|
||||
|
||||
if(@WITH_BZ2@)
|
||||
find_dependency(BZip2)
|
||||
endif()
|
||||
|
||||
if(@WITH_LZ4@)
|
||||
find_dependency(lz4)
|
||||
endif()
|
||||
|
||||
if(@WITH_ZSTD@)
|
||||
find_dependency(zstd)
|
||||
endif()
|
||||
|
||||
if(@WITH_NUMA@)
|
||||
find_dependency(NUMA)
|
||||
endif()
|
||||
|
||||
if(@WITH_TBB@)
|
||||
find_dependency(TBB)
|
||||
endif()
|
||||
|
||||
find_dependency(Threads)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/RocksDBTargets.cmake")
|
||||
check_required_components(RocksDB)
|
||||
@@ -0,0 +1,29 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# Snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# Snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# Snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(Snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(Snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Snappy DEFAULT_MSG Snappy_LIBRARIES Snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
Snappy_LIBRARIES
|
||||
Snappy_INCLUDE_DIRS)
|
||||
|
||||
if(Snappy_FOUND AND NOT (TARGET Snappy::snappy))
|
||||
add_library (Snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(Snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${Snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${Snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -1,8 +1,8 @@
|
||||
# - Find gflags library
|
||||
# Find the gflags includes and library
|
||||
#
|
||||
# gflags_INCLUDE_DIR - where to find gflags.h.
|
||||
# gflags_LIBRARIES - List of libraries when using gflags.
|
||||
# GFLAGS_INCLUDE_DIR - where to find gflags.h.
|
||||
# GFLAGS_LIBRARIES - List of libraries when using gflags.
|
||||
# gflags_FOUND - True if gflags found.
|
||||
|
||||
find_path(GFLAGS_INCLUDE_DIR
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# - Find Snappy
|
||||
# Find the snappy compression library and includes
|
||||
#
|
||||
# snappy_INCLUDE_DIRS - where to find snappy.h, etc.
|
||||
# snappy_LIBRARIES - List of libraries when using snappy.
|
||||
# snappy_FOUND - True if snappy found.
|
||||
|
||||
find_path(snappy_INCLUDE_DIRS
|
||||
NAMES snappy.h
|
||||
HINTS ${snappy_ROOT_DIR}/include)
|
||||
|
||||
find_library(snappy_LIBRARIES
|
||||
NAMES snappy
|
||||
HINTS ${snappy_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(snappy DEFAULT_MSG snappy_LIBRARIES snappy_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
snappy_LIBRARIES
|
||||
snappy_INCLUDE_DIRS)
|
||||
|
||||
if(snappy_FOUND AND NOT (TARGET snappy::snappy))
|
||||
add_library (snappy::snappy UNKNOWN IMPORTED)
|
||||
set_target_properties(snappy::snappy
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${snappy_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${snappy_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -12,7 +12,7 @@ fi
|
||||
ROOT=".."
|
||||
# Fetch right version of gcov
|
||||
if [ -d /mnt/gvfs/third-party -a -z "$CXX" ]; then
|
||||
source $ROOT/build_tools/fbcode_config.sh
|
||||
source $ROOT/build_tools/fbcode_config_platform007.sh
|
||||
GCOV=$GCC_BASE/bin/gcov
|
||||
else
|
||||
GCOV=$(which gcov)
|
||||
|
||||
@@ -56,8 +56,9 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
// TODO(yiwu): For last_seq_same_as_publish_seq_==false, this is not the
|
||||
// correct behavior. Will be corrected automatically when we take a snapshot
|
||||
// here for the case of WritePreparedTxnDB.
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
uint64_t cur_sv_number = cfd_->GetSuperVersionNumber();
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:1");
|
||||
TEST_SYNC_POINT("ArenaWrappedDBIter::Refresh:2");
|
||||
if (sv_number_ != cur_sv_number) {
|
||||
Env* env = db_iter_->env();
|
||||
db_iter_->~DBIter();
|
||||
@@ -65,6 +66,7 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
new (&arena_) Arena();
|
||||
|
||||
SuperVersion* sv = cfd_->GetReferencedSuperVersion(db_impl_);
|
||||
SequenceNumber latest_seq = db_impl_->GetLatestSequenceNumber();
|
||||
if (read_callback_) {
|
||||
read_callback_->Refresh(latest_seq);
|
||||
}
|
||||
@@ -78,7 +80,7 @@ Status ArenaWrappedDBIter::Refresh() {
|
||||
latest_seq, /* allow_unprepared_value */ true);
|
||||
SetIterUnderDBIter(internal_iter);
|
||||
} else {
|
||||
db_iter_->set_sequence(latest_seq);
|
||||
db_iter_->set_sequence(db_impl_->GetLatestSequenceNumber());
|
||||
db_iter_->set_valid(false);
|
||||
}
|
||||
return Status::OK();
|
||||
|
||||
@@ -38,8 +38,15 @@ std::string BlobFileMetaData::DebugString() const {
|
||||
std::ostream& operator<<(std::ostream& os, const BlobFileMetaData& meta) {
|
||||
const auto& shared_meta = meta.GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
os << (*shared_meta);
|
||||
|
||||
os << (*shared_meta) << " garbage_blob_count: " << meta.GetGarbageBlobCount()
|
||||
os << " linked_ssts: {";
|
||||
for (uint64_t file_number : meta.GetLinkedSsts()) {
|
||||
os << ' ' << file_number;
|
||||
}
|
||||
os << " }";
|
||||
|
||||
os << " garbage_blob_count: " << meta.GetGarbageBlobCount()
|
||||
<< " garbage_blob_bytes: " << meta.GetGarbageBlobBytes();
|
||||
|
||||
return os;
|
||||
|
||||
@@ -5,12 +5,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "rocksdb/rocksdb_namespace.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -90,11 +91,15 @@ std::ostream& operator<<(std::ostream& os,
|
||||
|
||||
class BlobFileMetaData {
|
||||
public:
|
||||
using LinkedSsts = std::unordered_set<uint64_t>;
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData> Create(
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta,
|
||||
uint64_t garbage_blob_count, uint64_t garbage_blob_bytes) {
|
||||
return std::shared_ptr<BlobFileMetaData>(new BlobFileMetaData(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes));
|
||||
LinkedSsts linked_ssts, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes) {
|
||||
return std::shared_ptr<BlobFileMetaData>(
|
||||
new BlobFileMetaData(std::move(shared_meta), std::move(linked_ssts),
|
||||
garbage_blob_count, garbage_blob_bytes));
|
||||
}
|
||||
|
||||
BlobFileMetaData(const BlobFileMetaData&) = delete;
|
||||
@@ -128,6 +133,8 @@ class BlobFileMetaData {
|
||||
return shared_meta_->GetChecksumValue();
|
||||
}
|
||||
|
||||
const LinkedSsts& GetLinkedSsts() const { return linked_ssts_; }
|
||||
|
||||
uint64_t GetGarbageBlobCount() const { return garbage_blob_count_; }
|
||||
uint64_t GetGarbageBlobBytes() const { return garbage_blob_bytes_; }
|
||||
|
||||
@@ -135,8 +142,10 @@ class BlobFileMetaData {
|
||||
|
||||
private:
|
||||
BlobFileMetaData(std::shared_ptr<SharedBlobFileMetaData> shared_meta,
|
||||
uint64_t garbage_blob_count, uint64_t garbage_blob_bytes)
|
||||
LinkedSsts linked_ssts, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes)
|
||||
: shared_meta_(std::move(shared_meta)),
|
||||
linked_ssts_(std::move(linked_ssts)),
|
||||
garbage_blob_count_(garbage_blob_count),
|
||||
garbage_blob_bytes_(garbage_blob_bytes) {
|
||||
assert(shared_meta_);
|
||||
@@ -145,6 +154,7 @@ class BlobFileMetaData {
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
|
||||
LinkedSsts linked_ssts_;
|
||||
uint64_t garbage_blob_count_;
|
||||
uint64_t garbage_blob_bytes_;
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
#include "db/blob/blob_log_format.h"
|
||||
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -23,7 +22,6 @@ namespace blob_db {
|
||||
|
||||
constexpr uint32_t kMagicNumber = 2395959; // 0x00248f37
|
||||
constexpr uint32_t kVersion1 = 1;
|
||||
constexpr uint64_t kNoExpiration = std::numeric_limits<uint64_t>::max();
|
||||
|
||||
using ExpirationRange = std::pair<uint64_t, uint64_t>;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "utilities/blob_db/blob_log_reader.h"
|
||||
#include "db/blob/blob_log_reader.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/random_access_file_reader.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include "utilities/blob_db/blob_log_writer.h"
|
||||
#include "db/blob/blob_log_writer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "file/writable_file_writer.h"
|
||||
#include "monitoring/statistics.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
namespace blob_db {
|
||||
@@ -10,12 +10,12 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "db/blob/blob_log_format.h"
|
||||
#include "rocksdb/env.h"
|
||||
#include "rocksdb/slice.h"
|
||||
#include "rocksdb/statistics.h"
|
||||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/types.h"
|
||||
#include "utilities/blob_db/blob_log_format.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
+9
-5
@@ -51,7 +51,8 @@ TableBuilder* NewTableBuilder(
|
||||
uint64_t sample_for_compression, const CompressionOptions& compression_opts,
|
||||
int level, const bool skip_filters, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, const uint64_t target_file_size,
|
||||
const uint64_t file_creation_time) {
|
||||
const uint64_t file_creation_time, const std::string& db_id,
|
||||
const std::string& db_session_id) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -61,7 +62,7 @@ TableBuilder* NewTableBuilder(
|
||||
sample_for_compression, compression_opts,
|
||||
skip_filters, column_family_name, level,
|
||||
creation_time, oldest_key_time, target_file_size,
|
||||
file_creation_time),
|
||||
file_creation_time, db_id, db_session_id),
|
||||
column_family_id, file);
|
||||
}
|
||||
|
||||
@@ -85,7 +86,8 @@ Status BuildTable(
|
||||
EventLogger* event_logger, int job_id, const Env::IOPriority io_priority,
|
||||
TableProperties* table_properties, int level, const uint64_t creation_time,
|
||||
const uint64_t oldest_key_time, Env::WriteLifeTimeHint write_hint,
|
||||
const uint64_t file_creation_time) {
|
||||
const uint64_t file_creation_time, const std::string& db_id,
|
||||
const std::string& db_session_id) {
|
||||
assert((column_family_id ==
|
||||
TablePropertiesCollectorFactory::Context::kUnknownColumnFamily) ==
|
||||
column_family_name.empty());
|
||||
@@ -142,7 +144,7 @@ Status BuildTable(
|
||||
column_family_name, file_writer.get(), compression,
|
||||
sample_for_compression, compression_opts_for_flush, level,
|
||||
false /* skip_filters */, creation_time, oldest_key_time,
|
||||
0 /*target_file_size*/, file_creation_time);
|
||||
0 /*target_file_size*/, file_creation_time, db_id, db_session_id);
|
||||
}
|
||||
|
||||
MergeHelper merge(env, internal_comparator.user_comparator(),
|
||||
@@ -239,7 +241,9 @@ Status BuildTable(
|
||||
(internal_stats == nullptr) ? nullptr
|
||||
: internal_stats->GetFileReadHist(0),
|
||||
TableReaderCaller::kFlush, /*arena=*/nullptr,
|
||||
/*skip_filter=*/false, level, /*smallest_compaction_key=*/nullptr,
|
||||
/*skip_filter=*/false, level,
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key*/ nullptr,
|
||||
/*allow_unprepared_value*/ false));
|
||||
s = it->status();
|
||||
|
||||
+4
-2
@@ -51,7 +51,8 @@ TableBuilder* NewTableBuilder(
|
||||
const CompressionOptions& compression_opts, int level,
|
||||
const bool skip_filters = false, const uint64_t creation_time = 0,
|
||||
const uint64_t oldest_key_time = 0, const uint64_t target_file_size = 0,
|
||||
const uint64_t file_creation_time = 0);
|
||||
const uint64_t file_creation_time = 0, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
// Build a Table file from the contents of *iter. The generated file
|
||||
// will be named according to number specified in meta. On success, the rest of
|
||||
@@ -83,6 +84,7 @@ extern Status BuildTable(
|
||||
TableProperties* table_properties = nullptr, int level = -1,
|
||||
const uint64_t creation_time = 0, const uint64_t oldest_key_time = 0,
|
||||
Env::WriteLifeTimeHint write_hint = Env::WLTH_NOT_SET,
|
||||
const uint64_t file_creation_time = 0);
|
||||
const uint64_t file_creation_time = 0, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -1466,6 +1466,11 @@ void rocksdb_writebatch_delete(
|
||||
b->rep.Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_singledelete(rocksdb_writebatch_t* b, const char* key,
|
||||
size_t klen) {
|
||||
b->rep.SingleDelete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_delete_cf(
|
||||
rocksdb_writebatch_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1473,6 +1478,12 @@ void rocksdb_writebatch_delete_cf(
|
||||
b->rep.Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_singledelete_cf(
|
||||
rocksdb_writebatch_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep.SingleDelete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_deletev(
|
||||
rocksdb_writebatch_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
@@ -1723,6 +1734,11 @@ void rocksdb_writebatch_wi_delete(
|
||||
b->rep->Delete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_singledelete(rocksdb_writebatch_wi_t* b,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->SingleDelete(Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_delete_cf(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
rocksdb_column_family_handle_t* column_family,
|
||||
@@ -1730,6 +1746,12 @@ void rocksdb_writebatch_wi_delete_cf(
|
||||
b->rep->Delete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_singledelete_cf(
|
||||
rocksdb_writebatch_wi_t* b, rocksdb_column_family_handle_t* column_family,
|
||||
const char* key, size_t klen) {
|
||||
b->rep->SingleDelete(column_family->rep, Slice(key, klen));
|
||||
}
|
||||
|
||||
void rocksdb_writebatch_wi_deletev(
|
||||
rocksdb_writebatch_wi_t* b,
|
||||
int num_keys, const char* const* keys_list,
|
||||
@@ -2154,6 +2176,10 @@ void rocksdb_options_destroy(rocksdb_options_t* options) {
|
||||
delete options;
|
||||
}
|
||||
|
||||
rocksdb_options_t* rocksdb_options_create_copy(rocksdb_options_t* options) {
|
||||
return new rocksdb_options_t(*options);
|
||||
}
|
||||
|
||||
void rocksdb_options_increase_parallelism(
|
||||
rocksdb_options_t* opt, int total_threads) {
|
||||
opt->rep.IncreaseParallelism(total_threads);
|
||||
@@ -2179,6 +2205,10 @@ void rocksdb_options_set_allow_ingest_behind(
|
||||
opt->rep.allow_ingest_behind = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_allow_ingest_behind(rocksdb_options_t* opt) {
|
||||
return opt->rep.allow_ingest_behind;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compaction_filter(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_compactionfilter_t* filter) {
|
||||
@@ -2196,6 +2226,10 @@ void rocksdb_options_compaction_readahead_size(
|
||||
opt->rep.compaction_readahead_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_compaction_readahead_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.compaction_readahead_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_comparator(
|
||||
rocksdb_options_t* opt,
|
||||
rocksdb_comparator_t* cmp) {
|
||||
@@ -2208,27 +2242,43 @@ void rocksdb_options_set_merge_operator(
|
||||
opt->rep.merge_operator = std::shared_ptr<MergeOperator>(merge_operator);
|
||||
}
|
||||
|
||||
|
||||
void rocksdb_options_set_create_if_missing(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_if_missing = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_create_if_missing(rocksdb_options_t* opt) {
|
||||
return opt->rep.create_if_missing;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_create_missing_column_families(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.create_missing_column_families = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_create_missing_column_families(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.create_missing_column_families;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_error_if_exists(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.error_if_exists = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_error_if_exists(rocksdb_options_t* opt) {
|
||||
return opt->rep.error_if_exists;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_paranoid_checks(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.paranoid_checks = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_paranoid_checks(rocksdb_options_t* opt) {
|
||||
return opt->rep.paranoid_checks;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_paths(rocksdb_options_t* opt,
|
||||
const rocksdb_dbpath_t** dbpath_values,
|
||||
size_t num_paths) {
|
||||
@@ -2254,52 +2304,98 @@ void rocksdb_options_set_info_log_level(
|
||||
opt->rep.info_log_level = static_cast<InfoLogLevel>(v);
|
||||
}
|
||||
|
||||
int rocksdb_options_get_info_log_level(rocksdb_options_t* opt) {
|
||||
return static_cast<int>(opt->rep.info_log_level);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_db_write_buffer_size(rocksdb_options_t* opt,
|
||||
size_t s) {
|
||||
opt->rep.db_write_buffer_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_db_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.db_write_buffer_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_write_buffer_size(rocksdb_options_t* opt, size_t s) {
|
||||
opt->rep.write_buffer_size = s;
|
||||
}
|
||||
|
||||
size_t rocksdb_options_get_write_buffer_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.write_buffer_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_open_files(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_open_files = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_open_files(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_open_files;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_file_opening_threads(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_file_opening_threads = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_file_opening_threads(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_file_opening_threads;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_total_wal_size(rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_total_wal_size = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_max_total_wal_size(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_total_wal_size;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.target_file_size_base = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_target_file_size_base(rocksdb_options_t* opt) {
|
||||
return opt->rep.target_file_size_base;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_target_file_size_multiplier(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.target_file_size_multiplier = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_target_file_size_multiplier(rocksdb_options_t* opt) {
|
||||
return opt->rep.target_file_size_multiplier;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_base(
|
||||
rocksdb_options_t* opt, uint64_t n) {
|
||||
opt->rep.max_bytes_for_level_base = n;
|
||||
}
|
||||
|
||||
uint64_t rocksdb_options_get_max_bytes_for_level_base(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_bytes_for_level_base;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt, unsigned char v) {
|
||||
opt->rep.level_compaction_dynamic_level_bytes = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_level_compaction_dynamic_level_bytes(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.level_compaction_dynamic_level_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_bytes_for_level_multiplier(rocksdb_options_t* opt,
|
||||
double n) {
|
||||
opt->rep.max_bytes_for_level_multiplier = n;
|
||||
}
|
||||
|
||||
double rocksdb_options_get_max_bytes_for_level_multiplier(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_bytes_for_level_multiplier;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_compaction_bytes(rocksdb_options_t* opt,
|
||||
uint64_t n) {
|
||||
opt->rep.max_compaction_bytes = n;
|
||||
@@ -2322,30 +2418,57 @@ void rocksdb_options_set_skip_stats_update_on_db_open(rocksdb_options_t* opt,
|
||||
opt->rep.skip_stats_update_on_db_open = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_skip_stats_update_on_db_open(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.skip_stats_update_on_db_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt, unsigned char val) {
|
||||
opt->rep.skip_checking_sst_file_sizes_on_db_open = val;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.skip_checking_sst_file_sizes_on_db_open;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_num_levels(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.num_levels = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_num_levels(rocksdb_options_t* opt) {
|
||||
return opt->rep.num_levels;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_file_num_compaction_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_file_num_compaction_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_file_num_compaction_trigger(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_file_num_compaction_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_slowdown_writes_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_slowdown_writes_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_slowdown_writes_trigger(rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_slowdown_writes_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_level0_stop_writes_trigger(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.level0_stop_writes_trigger = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_level0_stop_writes_trigger(rocksdb_options_t* opt) {
|
||||
return opt->rep.level0_stop_writes_trigger;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_mem_compaction_level(rocksdb_options_t* /*opt*/,
|
||||
int /*n*/) {}
|
||||
|
||||
@@ -2357,6 +2480,10 @@ void rocksdb_options_set_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression(rocksdb_options_t* opt, int t) {
|
||||
opt->rep.bottommost_compression = static_cast<CompressionType>(t);
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_per_level(rocksdb_options_t* opt,
|
||||
int* level_values,
|
||||
size_t num_levels) {
|
||||
@@ -2371,7 +2498,7 @@ void rocksdb_options_set_bottommost_compression_options(rocksdb_options_t* opt,
|
||||
int w_bits, int level,
|
||||
int strategy,
|
||||
int max_dict_bytes,
|
||||
bool enabled) {
|
||||
unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.window_bits = w_bits;
|
||||
opt->rep.bottommost_compression_opts.level = level;
|
||||
opt->rep.bottommost_compression_opts.strategy = strategy;
|
||||
@@ -2379,6 +2506,13 @@ void rocksdb_options_set_bottommost_compression_options(rocksdb_options_t* opt,
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_bottommost_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes, unsigned char enabled) {
|
||||
opt->rep.bottommost_compression_opts.zstd_max_train_bytes =
|
||||
zstd_max_train_bytes;
|
||||
opt->rep.bottommost_compression_opts.enabled = enabled;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
int level, int strategy,
|
||||
int max_dict_bytes) {
|
||||
@@ -2388,6 +2522,11 @@ void rocksdb_options_set_compression_options(rocksdb_options_t* opt, int w_bits,
|
||||
opt->rep.compression_opts.max_dict_bytes = max_dict_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_compression_options_zstd_max_train_bytes(
|
||||
rocksdb_options_t* opt, int zstd_max_train_bytes) {
|
||||
opt->rep.compression_opts.zstd_max_train_bytes = zstd_max_train_bytes;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_prefix_extractor(
|
||||
rocksdb_options_t* opt, rocksdb_slicetransform_t* prefix_extractor) {
|
||||
opt->rep.prefix_extractor.reset(prefix_extractor);
|
||||
@@ -2527,35 +2666,67 @@ void rocksdb_options_set_max_write_buffer_number(rocksdb_options_t* opt, int n)
|
||||
opt->rep.max_write_buffer_number = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_min_write_buffer_number_to_merge(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.min_write_buffer_number_to_merge = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_min_write_buffer_number_to_merge(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.min_write_buffer_number_to_merge;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_write_buffer_number_to_maintain = n;
|
||||
}
|
||||
|
||||
int rocksdb_options_get_max_write_buffer_number_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_number_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt, int64_t n) {
|
||||
opt->rep.max_write_buffer_size_to_maintain = n;
|
||||
}
|
||||
|
||||
int64_t rocksdb_options_get_max_write_buffer_size_to_maintain(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.max_write_buffer_size_to_maintain;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_enable_pipelined_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.enable_pipelined_write = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_enable_pipelined_write(
|
||||
rocksdb_options_t* opt) {
|
||||
return opt->rep.enable_pipelined_write;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_unordered_write(rocksdb_options_t* opt,
|
||||
unsigned char v) {
|
||||
opt->rep.unordered_write = v;
|
||||
}
|
||||
|
||||
unsigned char rocksdb_options_get_unordered_write(rocksdb_options_t* opt) {
|
||||
return opt->rep.unordered_write;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_subcompactions(rocksdb_options_t* opt,
|
||||
uint32_t n) {
|
||||
opt->rep.max_subcompactions = n;
|
||||
}
|
||||
|
||||
uint32_t rocksdb_options_get_max_subcompactions(rocksdb_options_t* opt) {
|
||||
return opt->rep.max_subcompactions;
|
||||
}
|
||||
|
||||
void rocksdb_options_set_max_background_jobs(rocksdb_options_t* opt, int n) {
|
||||
opt->rep.max_background_jobs = n;
|
||||
}
|
||||
@@ -4456,6 +4627,10 @@ void rocksdb_approximate_memory_usage_destroy(rocksdb_memory_usage_t* usage) {
|
||||
delete usage;
|
||||
}
|
||||
|
||||
void rocksdb_cancel_all_background_work(rocksdb_t* db, unsigned char wait) {
|
||||
CancelAllBackgroundWork(db->rep, wait);
|
||||
}
|
||||
|
||||
} // end extern "C"
|
||||
|
||||
#endif // !ROCKSDB_LITE
|
||||
+291
@@ -1461,6 +1461,294 @@ int main(int argc, char** argv) {
|
||||
rocksdb_cuckoo_options_destroy(cuckoo_options);
|
||||
}
|
||||
|
||||
StartPhase("options");
|
||||
{
|
||||
rocksdb_options_t* o;
|
||||
o = rocksdb_options_create();
|
||||
|
||||
// Set and check options.
|
||||
rocksdb_options_set_allow_ingest_behind(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(o));
|
||||
|
||||
rocksdb_options_compaction_readahead_size(o, 10);
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_create_if_missing(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(o));
|
||||
|
||||
rocksdb_options_set_create_missing_column_families(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_create_missing_column_families(o));
|
||||
|
||||
rocksdb_options_set_error_if_exists(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(o));
|
||||
|
||||
rocksdb_options_set_paranoid_checks(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(o));
|
||||
|
||||
rocksdb_options_set_info_log_level(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(o));
|
||||
|
||||
rocksdb_options_set_write_buffer_size(o, 100);
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_db_write_buffer_size(o, 1000);
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_max_open_files(o, 21);
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(o));
|
||||
|
||||
rocksdb_options_set_max_file_opening_threads(o, 5);
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(o));
|
||||
|
||||
rocksdb_options_set_max_total_wal_size(o, 400);
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(o));
|
||||
|
||||
rocksdb_options_set_num_levels(o, 7);
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(o));
|
||||
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(o, 4);
|
||||
CheckCondition(4 ==
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(o, 6);
|
||||
CheckCondition(6 == rocksdb_options_get_level0_slowdown_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_stop_writes_trigger(o, 8);
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_base(o, 256);
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_multiplier(o, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_base(o, 1024);
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(o));
|
||||
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(o, 1);
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(o, 2.0);
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(o, 1);
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(o, 97);
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(o, 23);
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(o, 64);
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(o, 50000);
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_enable_pipelined_write(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(o));
|
||||
|
||||
rocksdb_options_set_unordered_write(o, 1);
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(o));
|
||||
|
||||
rocksdb_options_set_max_subcompactions(o, 123456);
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(o));
|
||||
|
||||
// Create a copy that should be equal to the original.
|
||||
rocksdb_options_t* copy;
|
||||
copy = rocksdb_options_create_copy(o);
|
||||
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(copy));
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(copy));
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_create_missing_column_families(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(copy));
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(copy));
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(copy));
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(copy));
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(copy));
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(copy));
|
||||
CheckCondition(
|
||||
4 == rocksdb_options_get_level0_file_num_compaction_trigger(copy));
|
||||
CheckCondition(6 ==
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(copy));
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(copy));
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(copy));
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_level_compaction_dynamic_level_bytes(copy));
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(
|
||||
64 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(copy));
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(copy));
|
||||
|
||||
// Copies should be independent.
|
||||
rocksdb_options_set_allow_ingest_behind(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_allow_ingest_behind(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_allow_ingest_behind(o));
|
||||
|
||||
rocksdb_options_compaction_readahead_size(copy, 20);
|
||||
CheckCondition(20 == rocksdb_options_get_compaction_readahead_size(copy));
|
||||
CheckCondition(10 == rocksdb_options_get_compaction_readahead_size(o));
|
||||
|
||||
rocksdb_options_set_create_if_missing(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_create_if_missing(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_if_missing(o));
|
||||
|
||||
rocksdb_options_set_create_missing_column_families(copy, 0);
|
||||
CheckCondition(0 ==
|
||||
rocksdb_options_get_create_missing_column_families(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_create_missing_column_families(o));
|
||||
|
||||
rocksdb_options_set_error_if_exists(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_error_if_exists(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_error_if_exists(o));
|
||||
|
||||
rocksdb_options_set_paranoid_checks(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_paranoid_checks(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_paranoid_checks(o));
|
||||
|
||||
rocksdb_options_set_info_log_level(copy, 2);
|
||||
CheckCondition(2 == rocksdb_options_get_info_log_level(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_info_log_level(o));
|
||||
|
||||
rocksdb_options_set_write_buffer_size(copy, 200);
|
||||
CheckCondition(200 == rocksdb_options_get_write_buffer_size(copy));
|
||||
CheckCondition(100 == rocksdb_options_get_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_db_write_buffer_size(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_db_write_buffer_size(copy));
|
||||
CheckCondition(1000 == rocksdb_options_get_db_write_buffer_size(o));
|
||||
|
||||
rocksdb_options_set_max_open_files(copy, 42);
|
||||
CheckCondition(42 == rocksdb_options_get_max_open_files(copy));
|
||||
CheckCondition(21 == rocksdb_options_get_max_open_files(o));
|
||||
|
||||
rocksdb_options_set_max_file_opening_threads(copy, 3);
|
||||
CheckCondition(3 == rocksdb_options_get_max_file_opening_threads(copy));
|
||||
CheckCondition(5 == rocksdb_options_get_max_file_opening_threads(o));
|
||||
|
||||
rocksdb_options_set_max_total_wal_size(copy, 4000);
|
||||
CheckCondition(4000 == rocksdb_options_get_max_total_wal_size(copy));
|
||||
CheckCondition(400 == rocksdb_options_get_max_total_wal_size(o));
|
||||
|
||||
rocksdb_options_set_num_levels(copy, 6);
|
||||
CheckCondition(6 == rocksdb_options_get_num_levels(copy));
|
||||
CheckCondition(7 == rocksdb_options_get_num_levels(o));
|
||||
|
||||
rocksdb_options_set_level0_file_num_compaction_trigger(copy, 14);
|
||||
CheckCondition(
|
||||
14 == rocksdb_options_get_level0_file_num_compaction_trigger(copy));
|
||||
CheckCondition(4 ==
|
||||
rocksdb_options_get_level0_file_num_compaction_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_slowdown_writes_trigger(copy, 61);
|
||||
CheckCondition(61 ==
|
||||
rocksdb_options_get_level0_slowdown_writes_trigger(copy));
|
||||
CheckCondition(6 == rocksdb_options_get_level0_slowdown_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_level0_stop_writes_trigger(copy, 17);
|
||||
CheckCondition(17 == rocksdb_options_get_level0_stop_writes_trigger(copy));
|
||||
CheckCondition(8 == rocksdb_options_get_level0_stop_writes_trigger(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_base(copy, 128);
|
||||
CheckCondition(128 == rocksdb_options_get_target_file_size_base(copy));
|
||||
CheckCondition(256 == rocksdb_options_get_target_file_size_base(o));
|
||||
|
||||
rocksdb_options_set_target_file_size_multiplier(copy, 13);
|
||||
CheckCondition(13 == rocksdb_options_get_target_file_size_multiplier(copy));
|
||||
CheckCondition(3 == rocksdb_options_get_target_file_size_multiplier(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_base(copy, 900);
|
||||
CheckCondition(900 == rocksdb_options_get_max_bytes_for_level_base(copy));
|
||||
CheckCondition(1024 == rocksdb_options_get_max_bytes_for_level_base(o));
|
||||
|
||||
rocksdb_options_set_level_compaction_dynamic_level_bytes(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_level_compaction_dynamic_level_bytes(copy));
|
||||
CheckCondition(1 ==
|
||||
rocksdb_options_get_level_compaction_dynamic_level_bytes(o));
|
||||
|
||||
rocksdb_options_set_max_bytes_for_level_multiplier(copy, 8.0);
|
||||
CheckCondition(8.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(copy));
|
||||
CheckCondition(2.0 ==
|
||||
rocksdb_options_get_max_bytes_for_level_multiplier(o));
|
||||
|
||||
rocksdb_options_set_skip_stats_update_on_db_open(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_skip_stats_update_on_db_open(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_skip_stats_update_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_skip_checking_sst_file_sizes_on_db_open(copy, 0);
|
||||
CheckCondition(
|
||||
0 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(copy));
|
||||
CheckCondition(
|
||||
1 == rocksdb_options_get_skip_checking_sst_file_sizes_on_db_open(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number(copy, 2000);
|
||||
CheckCondition(2000 == rocksdb_options_get_max_write_buffer_number(copy));
|
||||
CheckCondition(97 == rocksdb_options_get_max_write_buffer_number(o));
|
||||
|
||||
rocksdb_options_set_min_write_buffer_number_to_merge(copy, 146);
|
||||
CheckCondition(146 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(copy));
|
||||
CheckCondition(23 ==
|
||||
rocksdb_options_get_min_write_buffer_number_to_merge(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_number_to_maintain(copy, 128);
|
||||
CheckCondition(
|
||||
128 == rocksdb_options_get_max_write_buffer_number_to_maintain(copy));
|
||||
CheckCondition(64 ==
|
||||
rocksdb_options_get_max_write_buffer_number_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_max_write_buffer_size_to_maintain(copy, 9000);
|
||||
CheckCondition(9000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(copy));
|
||||
CheckCondition(50000 ==
|
||||
rocksdb_options_get_max_write_buffer_size_to_maintain(o));
|
||||
|
||||
rocksdb_options_set_enable_pipelined_write(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_enable_pipelined_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_enable_pipelined_write(o));
|
||||
|
||||
rocksdb_options_set_unordered_write(copy, 0);
|
||||
CheckCondition(0 == rocksdb_options_get_unordered_write(copy));
|
||||
CheckCondition(1 == rocksdb_options_get_unordered_write(o));
|
||||
|
||||
rocksdb_options_set_max_subcompactions(copy, 90001);
|
||||
CheckCondition(90001 == rocksdb_options_get_max_subcompactions(copy));
|
||||
CheckCondition(123456 == rocksdb_options_get_max_subcompactions(o));
|
||||
|
||||
rocksdb_options_destroy(copy);
|
||||
rocksdb_options_destroy(o);
|
||||
}
|
||||
|
||||
StartPhase("iterate_upper_bound");
|
||||
{
|
||||
// Create new empty database
|
||||
@@ -1840,6 +2128,9 @@ int main(int argc, char** argv) {
|
||||
CheckNoError(err);
|
||||
}
|
||||
|
||||
StartPhase("cancel_all_background_work");
|
||||
rocksdb_cancel_all_background_work(db, 1);
|
||||
|
||||
StartPhase("cleanup");
|
||||
rocksdb_close(db);
|
||||
rocksdb_options_destroy(options);
|
||||
|
||||
@@ -305,12 +305,15 @@ CompactionJob::CompactionJob(
|
||||
const SnapshotChecker* snapshot_checker, std::shared_ptr<Cache> table_cache,
|
||||
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::atomic<bool>* manual_compaction_paused)
|
||||
Env::Priority thread_pri, const std::atomic<bool>* manual_compaction_paused,
|
||||
const std::string& db_id, const std::string& db_session_id)
|
||||
: job_id_(job_id),
|
||||
compact_(new CompactionState(compaction)),
|
||||
compaction_job_stats_(compaction_job_stats),
|
||||
compaction_stats_(compaction->compaction_reason(), 1),
|
||||
dbname_(dbname),
|
||||
db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
db_options_(db_options),
|
||||
file_options_(file_options),
|
||||
env_(db_options.env),
|
||||
@@ -542,7 +545,7 @@ void CompactionJob::GenSubcompactionBoundaries() {
|
||||
// Greedily add ranges to the subcompaction until the sum of the ranges'
|
||||
// sizes becomes >= the expected mean size of a subcompaction
|
||||
sum = 0;
|
||||
for (size_t i = 0; i < ranges.size() - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < ranges.size(); i++) {
|
||||
sum += ranges[i].size;
|
||||
if (subcompactions == 1) {
|
||||
// If there's only one left to schedule then it goes to the end so no
|
||||
@@ -654,6 +657,8 @@ Status CompactionJob::Run() {
|
||||
compact_->compaction->output_level()),
|
||||
TableReaderCaller::kCompactionRefill, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, compact_->compaction->output_level(),
|
||||
MaxFileSizeForL0MetaPin(
|
||||
*compact_->compaction->mutable_cf_options()),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
@@ -1552,7 +1557,8 @@ Status CompactionJob::OpenCompactionOutputFile(
|
||||
sub_compact->compaction->output_compression_opts(),
|
||||
sub_compact->compaction->output_level(), skip_filters,
|
||||
oldest_ancester_time, 0 /* oldest_key_time */,
|
||||
sub_compact->compaction->max_output_file_size(), current_time));
|
||||
sub_compact->compaction->max_output_file_size(), current_time, db_id_,
|
||||
db_session_id_));
|
||||
LogFlush(db_options_.info_log);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,9 @@ class CompactionJob {
|
||||
const std::string& dbname,
|
||||
CompactionJobStats* compaction_job_stats,
|
||||
Env::Priority thread_pri,
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr);
|
||||
const std::atomic<bool>* manual_compaction_paused = nullptr,
|
||||
const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
~CompactionJob();
|
||||
|
||||
@@ -152,6 +154,8 @@ class CompactionJob {
|
||||
|
||||
// DBImpl state
|
||||
const std::string& dbname_;
|
||||
const std::string db_id_;
|
||||
const std::string db_session_id_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const FileOptions file_options_;
|
||||
|
||||
|
||||
@@ -1085,6 +1085,8 @@ void CompactionPicker::PickFilesMarkedForCompaction(
|
||||
Random64 rnd(/* seed */ reinterpret_cast<uint64_t>(vstorage));
|
||||
size_t random_file_index = static_cast<size_t>(rnd.Uniform(
|
||||
static_cast<uint64_t>(vstorage->FilesMarkedForCompaction().size())));
|
||||
TEST_SYNC_POINT_CALLBACK("CompactionPicker::PickFilesMarkedForCompaction",
|
||||
&random_file_index);
|
||||
|
||||
if (continuation(vstorage->FilesMarkedForCompaction()[random_file_index])) {
|
||||
// found the compaction!
|
||||
|
||||
@@ -78,8 +78,17 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
}
|
||||
|
||||
// Create a new VersionStorageInfo object so we can add mode files and then
|
||||
// merge it with the existing VersionStorageInfo
|
||||
void AddVersionStorage() {
|
||||
temp_vstorage_.reset(new VersionStorageInfo(
|
||||
&icmp_, ucmp_, options_.num_levels, ioptions_.compaction_style,
|
||||
vstorage_.get(), false));
|
||||
}
|
||||
|
||||
void DeleteVersionStorage() {
|
||||
vstorage_.reset();
|
||||
temp_vstorage_.reset();
|
||||
files_.clear();
|
||||
file_map_.clear();
|
||||
input_files_.clear();
|
||||
@@ -88,18 +97,24 @@ class CompactionPickerTest : public testing::Test {
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0) {
|
||||
assert(level < vstorage_->num_levels());
|
||||
size_t compensated_file_size = 0, bool marked_for_compact = false) {
|
||||
VersionStorageInfo* vstorage;
|
||||
if (temp_vstorage_) {
|
||||
vstorage = temp_vstorage_.get();
|
||||
} else {
|
||||
vstorage = vstorage_.get();
|
||||
}
|
||||
assert(level < vstorage->num_levels());
|
||||
FileMetaData* f = new FileMetaData(
|
||||
file_number, path_id, file_size,
|
||||
InternalKey(smallest, smallest_seq, kTypeValue),
|
||||
InternalKey(largest, largest_seq, kTypeValue), smallest_seq,
|
||||
largest_seq, /* marked_for_compact */ false, kInvalidBlobFileNumber,
|
||||
largest_seq, marked_for_compact, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
f->compensated_file_size =
|
||||
(compensated_file_size != 0) ? compensated_file_size : file_size;
|
||||
vstorage_->AddFile(level, f);
|
||||
vstorage->AddFile(level, f);
|
||||
files_.emplace_back(f);
|
||||
file_map_.insert({file_number, {f, level}});
|
||||
}
|
||||
@@ -122,6 +137,12 @@ class CompactionPickerTest : public testing::Test {
|
||||
}
|
||||
|
||||
void UpdateVersionStorageInfo() {
|
||||
if (temp_vstorage_) {
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr,
|
||||
vstorage_.get(), nullptr);
|
||||
builder.SaveTo(temp_vstorage_.get());
|
||||
vstorage_ = std::move(temp_vstorage_);
|
||||
}
|
||||
vstorage_->CalculateBaseBytes(ioptions_, mutable_cf_options_);
|
||||
vstorage_->UpdateFilesByCompactionPri(ioptions_.compaction_pri);
|
||||
vstorage_->UpdateNumNonEmptyLevels();
|
||||
@@ -132,6 +153,28 @@ class CompactionPickerTest : public testing::Test {
|
||||
vstorage_->ComputeFilesMarkedForCompaction();
|
||||
vstorage_->SetFinalized();
|
||||
}
|
||||
void AddFileToVersionStorage(int level, uint32_t file_number,
|
||||
const char* smallest, const char* largest,
|
||||
uint64_t file_size = 1, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100,
|
||||
SequenceNumber largest_seq = 100,
|
||||
size_t compensated_file_size = 0,
|
||||
bool marked_for_compact = false) {
|
||||
VersionStorageInfo* base_vstorage = vstorage_.release();
|
||||
vstorage_.reset(new VersionStorageInfo(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleUniversal,
|
||||
base_vstorage, false));
|
||||
Add(level, file_number, smallest, largest, file_size, path_id, smallest_seq,
|
||||
largest_seq, compensated_file_size, marked_for_compact);
|
||||
|
||||
VersionBuilder builder(FileOptions(), &ioptions_, nullptr, base_vstorage,
|
||||
nullptr);
|
||||
builder.SaveTo(vstorage_.get());
|
||||
UpdateVersionStorageInfo();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<VersionStorageInfo> temp_vstorage_;
|
||||
};
|
||||
|
||||
TEST_F(CompactionPickerTest, Empty) {
|
||||
@@ -371,8 +414,8 @@ TEST_F(CompactionPickerTest, LevelTriggerDynamic4) {
|
||||
mutable_cf_options_.max_bytes_for_level_multiplier = 10;
|
||||
NewVersionStorage(num_levels, kCompactionStyleLevel);
|
||||
Add(0, 1U, "150", "200");
|
||||
Add(num_levels - 1, 3U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 4U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 2U, "200", "250", 300U);
|
||||
Add(num_levels - 1, 3U, "300", "350", 3000U);
|
||||
Add(num_levels - 1, 4U, "400", "450", 3U);
|
||||
Add(num_levels - 2, 5U, "150", "180", 300U);
|
||||
Add(num_levels - 2, 6U, "181", "350", 500U);
|
||||
@@ -739,7 +782,7 @@ TEST_F(CompactionPickerTest, CompactionPriMinOverlapping2) {
|
||||
Add(2, 8U, "201", "300",
|
||||
60000000U); // Overlaps with file 28, 29, total size 521M
|
||||
|
||||
Add(3, 26U, "100", "110", 261000000U);
|
||||
Add(3, 25U, "100", "110", 261000000U);
|
||||
Add(3, 26U, "150", "170", 261000000U);
|
||||
Add(3, 27U, "171", "179", 260000000U);
|
||||
Add(3, 28U, "191", "220", 260000000U);
|
||||
@@ -1255,7 +1298,7 @@ TEST_F(CompactionPickerTest, EstimateCompactionBytesNeeded1) {
|
||||
// Size ratio L4/L3 is 9.9
|
||||
// After merge from L3, L4 size is 1000900
|
||||
Add(4, 11U, "400", "500", 999900);
|
||||
Add(5, 11U, "400", "500", 8007200);
|
||||
Add(5, 12U, "400", "500", 8007200);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -1568,8 +1611,8 @@ TEST_F(CompactionPickerTest, IsTrivialMoveOn) {
|
||||
|
||||
Add(3, 5U, "120", "130", 7000U);
|
||||
Add(3, 6U, "170", "180", 7000U);
|
||||
Add(3, 5U, "220", "230", 7000U);
|
||||
Add(3, 5U, "270", "280", 7000U);
|
||||
Add(3, 7U, "220", "230", 7000U);
|
||||
Add(3, 8U, "270", "280", 7000U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(level_compaction_picker.PickCompaction(
|
||||
@@ -1733,6 +1776,165 @@ TEST_F(CompactionPickerTest, IntraL0ForEarliestSeqno) {
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a "regular" universal compaction is
|
||||
// scheduled first, followed by a delete triggered compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300);
|
||||
Add(3, 5U, "010", "080", 8 * kFileSize, 0, 200, 251);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a compaction to reduce sorted runs
|
||||
ASSERT_EQ(CompactionReason::kUniversalSortedRunNum,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(0, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
|
||||
AddVersionStorage();
|
||||
// Simulate a flush and mark the file for compaction
|
||||
Add(0, 7U, "150", "200", kFileSize, 0, 551, 600, 0, true);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionFullOverlap2) {
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
|
||||
// This test covers the case where a delete triggered compaction is
|
||||
// scheduled first, followed by a "regular" compaction. The latter
|
||||
// should fail
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
// Mark file number 4 for compaction
|
||||
Add(0, 4U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 5U, "240", "290", 8 * kFileSize, 0, 201, 250);
|
||||
Add(4, 3U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 6U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(0, compaction->start_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
|
||||
AddVersionStorage();
|
||||
Add(0, 1U, "150", "200", kFileSize, 0, 500, 550);
|
||||
Add(0, 2U, "201", "250", 2 * kFileSize, 0, 401, 450);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
}
|
||||
|
||||
TEST_F(CompactionPickerTest, UniversalMarkedCompactionStartOutputOverlap) {
|
||||
// The case where universal periodic compaction can be picked
|
||||
// with some newer files being compacted.
|
||||
const uint64_t kFileSize = 100000;
|
||||
|
||||
ioptions_.compaction_style = kCompactionStyleUniversal;
|
||||
|
||||
bool input_level_overlap = false;
|
||||
bool output_level_overlap = false;
|
||||
// Let's mark 2 files in 2 different levels for compaction. The
|
||||
// compaction picker will randomly pick one, so use the sync point to
|
||||
// ensure a deterministic order. Loop until both cases are covered
|
||||
size_t random_index = 0;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"CompactionPicker::PickFilesMarkedForCompaction", [&](void* arg) {
|
||||
size_t* index = static_cast<size_t*>(arg);
|
||||
*index = random_index;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
while (!input_level_overlap || !output_level_overlap) {
|
||||
// Ensure that the L0 file gets picked first
|
||||
random_index = !input_level_overlap ? 0 : 1;
|
||||
UniversalCompactionPicker universal_compaction_picker(ioptions_, &icmp_);
|
||||
NewVersionStorage(5, kCompactionStyleUniversal);
|
||||
|
||||
Add(0, 1U, "260", "300", 4 * kFileSize, 0, 260, 300, 0, true);
|
||||
Add(3, 2U, "010", "020", 2 * kFileSize, 0, 201, 248);
|
||||
Add(3, 3U, "250", "270", 2 * kFileSize, 0, 202, 249);
|
||||
Add(3, 4U, "290", "310", 2 * kFileSize, 0, 203, 250);
|
||||
Add(3, 5U, "310", "320", 2 * kFileSize, 0, 204, 251, 0, true);
|
||||
Add(4, 6U, "301", "350", 8 * kFileSize, 0, 101, 150);
|
||||
Add(4, 7U, "501", "750", 8 * kFileSize, 0, 101, 150);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
std::unique_ptr<Compaction> compaction(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
|
||||
ASSERT_TRUE(compaction);
|
||||
// Validate that its a delete triggered compaction
|
||||
ASSERT_EQ(CompactionReason::kFilesMarkedForCompaction,
|
||||
compaction->compaction_reason());
|
||||
ASSERT_TRUE(compaction->start_level() == 0 ||
|
||||
compaction->start_level() == 3);
|
||||
if (compaction->start_level() == 0) {
|
||||
// The L0 file was picked. The next compaction will detect an
|
||||
// overlap on its input level
|
||||
input_level_overlap = true;
|
||||
ASSERT_EQ(3, compaction->output_level());
|
||||
ASSERT_EQ(1U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(3U, compaction->num_input_files(1));
|
||||
} else {
|
||||
// The level 3 file was picked. The next compaction will pick
|
||||
// the L0 file and will detect overlap when adding output
|
||||
// level inputs
|
||||
output_level_overlap = true;
|
||||
ASSERT_EQ(4, compaction->output_level());
|
||||
ASSERT_EQ(2U, compaction->num_input_files(0));
|
||||
ASSERT_EQ(1U, compaction->num_input_files(1));
|
||||
}
|
||||
|
||||
vstorage_->ComputeCompactionScore(ioptions_, mutable_cf_options_);
|
||||
// After recomputing the compaction score, only one marked file will remain
|
||||
random_index = 0;
|
||||
std::unique_ptr<Compaction> compaction2(
|
||||
universal_compaction_picker.PickCompaction(
|
||||
cf_name_, mutable_cf_options_, vstorage_.get(), &log_buffer_));
|
||||
ASSERT_FALSE(compaction2);
|
||||
DeleteVersionStorage();
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -120,8 +120,7 @@ class UniversalCompactionBuilder {
|
||||
LogBuffer* log_buffer_;
|
||||
|
||||
static std::vector<SortedRun> CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& ioptions,
|
||||
const MutableCFOptions& mutable_cf_options);
|
||||
const VersionStorageInfo& vstorage);
|
||||
|
||||
// Pick a path ID to place a newly generated file, with its estimated file
|
||||
// size.
|
||||
@@ -325,8 +324,7 @@ void UniversalCompactionBuilder::SortedRun::DumpSizeInfo(
|
||||
|
||||
std::vector<UniversalCompactionBuilder::SortedRun>
|
||||
UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
const VersionStorageInfo& vstorage, const ImmutableCFOptions& /*ioptions*/,
|
||||
const MutableCFOptions& mutable_cf_options) {
|
||||
const VersionStorageInfo& vstorage) {
|
||||
std::vector<UniversalCompactionBuilder::SortedRun> ret;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(0)) {
|
||||
ret.emplace_back(0, f, f->fd.GetFileSize(), f->compensated_file_size,
|
||||
@@ -336,27 +334,16 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
uint64_t total_compensated_size = 0U;
|
||||
uint64_t total_size = 0U;
|
||||
bool being_compacted = false;
|
||||
bool is_first = true;
|
||||
for (FileMetaData* f : vstorage.LevelFiles(level)) {
|
||||
total_compensated_size += f->compensated_file_size;
|
||||
total_size += f->fd.GetFileSize();
|
||||
if (mutable_cf_options.compaction_options_universal.allow_trivial_move ==
|
||||
true) {
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
}
|
||||
} else {
|
||||
// Compaction always includes all files for a non-zero level, so for a
|
||||
// non-zero level, all the files should share the same being_compacted
|
||||
// value.
|
||||
// This assumption is only valid when
|
||||
// mutable_cf_options.compaction_options_universal.allow_trivial_move
|
||||
// is false
|
||||
assert(is_first || f->being_compacted == being_compacted);
|
||||
}
|
||||
if (is_first) {
|
||||
// Size amp, read amp and periodic compactions always include all files
|
||||
// for a non-zero level. However, a delete triggered compaction and
|
||||
// a trivial move might pick a subset of files in a sorted run. So
|
||||
// always check all files in a sorted run and mark the entire run as
|
||||
// being compacted if one or more files are being compacted
|
||||
if (f->being_compacted) {
|
||||
being_compacted = f->being_compacted;
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
if (total_compensated_size > 0) {
|
||||
@@ -372,8 +359,7 @@ UniversalCompactionBuilder::CalculateSortedRuns(
|
||||
Compaction* UniversalCompactionBuilder::PickCompaction() {
|
||||
const int kLevel0 = 0;
|
||||
score_ = vstorage_->CompactionScore(kLevel0);
|
||||
sorted_runs_ =
|
||||
CalculateSortedRuns(*vstorage_, ioptions_, mutable_cf_options_);
|
||||
sorted_runs_ = CalculateSortedRuns(*vstorage_);
|
||||
|
||||
if (sorted_runs_.size() == 0 ||
|
||||
(vstorage_->FilesMarkedForPeriodicCompaction().empty() &&
|
||||
@@ -779,7 +765,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
}
|
||||
|
||||
// Skip files that are already being compacted
|
||||
for (size_t loop = 0; loop < sorted_runs_.size() - 1; loop++) {
|
||||
for (size_t loop = 0; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (!sr->being_compacted) {
|
||||
start_index = loop; // Consider this as the first candidate.
|
||||
@@ -807,7 +793,7 @@ Compaction* UniversalCompactionBuilder::PickCompactionToReduceSizeAmp() {
|
||||
}
|
||||
|
||||
// keep adding up all the remaining files
|
||||
for (size_t loop = start_index; loop < sorted_runs_.size() - 1; loop++) {
|
||||
for (size_t loop = start_index; loop + 1 < sorted_runs_.size(); loop++) {
|
||||
sr = &sorted_runs_[loop];
|
||||
if (sr->being_compacted) {
|
||||
char file_num_buf[kFormatFileNumberBufSize];
|
||||
@@ -855,6 +841,7 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
std::vector<CompactionInputFiles> inputs;
|
||||
|
||||
if (vstorage_->num_levels() == 1) {
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
// This is single level universal. Since we're basically trying to reclaim
|
||||
// space by processing files marked for compaction due to high tombstone
|
||||
// density, let's do the same thing as compaction to reduce size amp which
|
||||
@@ -877,6 +864,11 @@ Compaction* UniversalCompactionBuilder::PickDeleteTriggeredCompaction() {
|
||||
return nullptr;
|
||||
}
|
||||
inputs.push_back(start_level_inputs);
|
||||
#else
|
||||
// Disable due to a known race condition.
|
||||
// TODO: Reenable once the race condition is fixed
|
||||
return nullptr;
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
} else {
|
||||
int start_level;
|
||||
|
||||
|
||||
+2
-1
@@ -61,7 +61,8 @@ Status VerifySstFileChecksum(const Options& options,
|
||||
s = ioptions.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions, options.prefix_extractor.get(), env_options,
|
||||
internal_comparator, false /* skip_filters */,
|
||||
!kImmortal, -1 /* level */),
|
||||
!kImmortal, false /* force_direct_prefetch */,
|
||||
-1 /* level */),
|
||||
std::move(file_reader), file_size, &table_reader,
|
||||
false /* prefetch_index_and_filter_in_cache */);
|
||||
if (!s.ok()) {
|
||||
|
||||
+3
-39
@@ -157,42 +157,6 @@ class CorruptionTest : public testing::Test {
|
||||
ASSERT_GE(max_expected, correct);
|
||||
}
|
||||
|
||||
void CorruptFile(const std::string& fname, int offset, int bytes_to_corrupt) {
|
||||
struct stat sbuf;
|
||||
if (stat(fname.c_str(), &sbuf) != 0) {
|
||||
const char* msg = strerror(errno);
|
||||
FAIL() << fname << ": " << msg;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
// Relative to end of file; make it absolute
|
||||
if (-offset > sbuf.st_size) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = static_cast<int>(sbuf.st_size + offset);
|
||||
}
|
||||
}
|
||||
if (offset > sbuf.st_size) {
|
||||
offset = static_cast<int>(sbuf.st_size);
|
||||
}
|
||||
if (offset + bytes_to_corrupt > sbuf.st_size) {
|
||||
bytes_to_corrupt = static_cast<int>(sbuf.st_size - offset);
|
||||
}
|
||||
|
||||
// Do it
|
||||
std::string contents;
|
||||
Status s = ReadFileToString(Env::Default(), fname, &contents);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
for (int i = 0; i < bytes_to_corrupt; i++) {
|
||||
contents[i + offset] ^= 0x80;
|
||||
}
|
||||
s = WriteStringToFile(Env::Default(), contents, fname);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
Options options;
|
||||
EnvOptions env_options;
|
||||
ASSERT_NOK(VerifySstFileChecksum(options, env_options, fname));
|
||||
}
|
||||
|
||||
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
|
||||
// Pick file to corrupt
|
||||
std::vector<std::string> filenames;
|
||||
@@ -211,7 +175,7 @@ class CorruptionTest : public testing::Test {
|
||||
}
|
||||
ASSERT_TRUE(!fname.empty()) << filetype;
|
||||
|
||||
CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
test::CorruptFile(fname, offset, bytes_to_corrupt);
|
||||
}
|
||||
|
||||
// corrupts exactly one file at level `level`. if no file found at level,
|
||||
@@ -221,7 +185,7 @@ class CorruptionTest : public testing::Test {
|
||||
db_->GetLiveFilesMetaData(&metadata);
|
||||
for (const auto& m : metadata) {
|
||||
if (m.level == level) {
|
||||
CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
test::CorruptFile(dbname_ + "/" + m.name, offset, bytes_to_corrupt);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -556,7 +520,7 @@ TEST_F(CorruptionTest, RangeDeletionCorrupted) {
|
||||
ImmutableCFOptions(options_), kRangeDelBlock, &range_del_handle));
|
||||
|
||||
ASSERT_OK(TryReopen());
|
||||
CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
test::CorruptFile(filename, static_cast<int>(range_del_handle.offset()), 1);
|
||||
ASSERT_TRUE(TryReopen().IsCorruption());
|
||||
}
|
||||
|
||||
|
||||
+421
-11
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/merge_operator.h"
|
||||
#include "rocksdb/perf_context.h"
|
||||
#include "rocksdb/utilities/debug.h"
|
||||
#include "table/block_based/block_based_table_reader.h"
|
||||
@@ -17,6 +18,8 @@
|
||||
#if !defined(ROCKSDB_LITE)
|
||||
#include "test_util/sync_point.h"
|
||||
#endif
|
||||
#include "utilities/merge_operators.h"
|
||||
#include "utilities/merge_operators/string_append/stringappend.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
@@ -38,6 +41,56 @@ TEST_F(DBBasicTest, OpenWhenOpen) {
|
||||
delete db2;
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, UniqueSession) {
|
||||
Options options = CurrentOptions();
|
||||
std::string sid1, sid2, sid3, sid4;
|
||||
|
||||
db_->GetDbSessionId(sid1);
|
||||
Reopen(options);
|
||||
db_->GetDbSessionId(sid2);
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
db_->GetDbSessionId(sid4);
|
||||
Reopen(options);
|
||||
db_->GetDbSessionId(sid3);
|
||||
|
||||
ASSERT_NE(sid1, sid2);
|
||||
ASSERT_NE(sid1, sid3);
|
||||
ASSERT_NE(sid2, sid3);
|
||||
|
||||
ASSERT_EQ(sid2, sid4);
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
db_->GetDbSessionId(sid1);
|
||||
// Test uniqueness between readonly open (sid1) and regular open (sid3)
|
||||
ASSERT_NE(sid1, sid3);
|
||||
Close();
|
||||
ASSERT_OK(ReadOnlyReopen(options));
|
||||
db_->GetDbSessionId(sid2);
|
||||
ASSERT_EQ("v1", Get("foo"));
|
||||
db_->GetDbSessionId(sid3);
|
||||
|
||||
ASSERT_NE(sid1, sid2);
|
||||
|
||||
ASSERT_EQ(sid2, sid3);
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
CreateAndReopenWithCF({"goku"}, options);
|
||||
db_->GetDbSessionId(sid1);
|
||||
ASSERT_OK(Put("bar", "e1"));
|
||||
db_->GetDbSessionId(sid2);
|
||||
ASSERT_EQ("e1", Get("bar"));
|
||||
db_->GetDbSessionId(sid3);
|
||||
ReopenWithColumnFamilies({"default", "goku"}, options);
|
||||
db_->GetDbSessionId(sid4);
|
||||
|
||||
ASSERT_EQ(sid1, sid2);
|
||||
ASSERT_EQ(sid2, sid3);
|
||||
|
||||
ASSERT_NE(sid1, sid4);
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBBasicTest, ReadOnlyDB) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
@@ -1295,14 +1348,18 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleUnsorted) {
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) {
|
||||
TEST_F(DBBasicTest, MultiGetBatchedSortedMultiFile) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
// To expand the power of this test, generate > 1 table file and
|
||||
// mix with memtable
|
||||
ASSERT_OK(Put(1, "k1", "v1"));
|
||||
ASSERT_OK(Put(1, "k2", "v2"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Put(1, "k3", "v3"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k5", "v5"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
@@ -1333,7 +1390,58 @@ TEST_F(DBBasicTest, MultiGetBatchedSimpleSorted) {
|
||||
ASSERT_TRUE(s[5].IsNotFound());
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
} while (ChangeOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedDuplicateKeys) {
|
||||
Options opts = CurrentOptions();
|
||||
opts.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
CreateAndReopenWithCF({"pikachu"}, opts);
|
||||
SetPerfLevel(kEnableCount);
|
||||
// To expand the power of this test, generate > 1 table file and
|
||||
// mix with memtable
|
||||
ASSERT_OK(Merge(1, "k1", "v1"));
|
||||
ASSERT_OK(Merge(1, "k2", "v2"));
|
||||
Flush(1);
|
||||
MoveFilesToLevel(2, 1);
|
||||
ASSERT_OK(Merge(1, "k3", "v3"));
|
||||
ASSERT_OK(Merge(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
MoveFilesToLevel(2, 1);
|
||||
ASSERT_OK(Merge(1, "k4", "v4_2"));
|
||||
ASSERT_OK(Merge(1, "k6", "v6"));
|
||||
Flush(1);
|
||||
MoveFilesToLevel(2, 1);
|
||||
ASSERT_OK(Merge(1, "k7", "v7"));
|
||||
ASSERT_OK(Merge(1, "k8", "v8"));
|
||||
Flush(1);
|
||||
MoveFilesToLevel(2, 1);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
std::vector<Slice> keys({"k8", "k8", "k8", "k4", "k4", "k1", "k3"});
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
db_->MultiGet(ReadOptions(), handles_[1], keys.size(), keys.data(),
|
||||
values.data(), s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
ASSERT_EQ(std::string(values[0].data(), values[0].size()), "v8");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v8");
|
||||
ASSERT_EQ(std::string(values[2].data(), values[2].size()), "v8");
|
||||
ASSERT_EQ(std::string(values[3].data(), values[3].size()), "v4,v4_2");
|
||||
ASSERT_EQ(std::string(values[4].data(), values[4].size()), "v4,v4_2");
|
||||
ASSERT_EQ(std::string(values[5].data(), values[5].size()), "v1");
|
||||
ASSERT_EQ(std::string(values[6].data(), values[6].size()), "v3");
|
||||
ASSERT_EQ(24, (int)get_perf_context()->multiget_read_bytes);
|
||||
|
||||
for (Status& status : s) {
|
||||
ASSERT_OK(status);
|
||||
}
|
||||
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedMultiLevel) {
|
||||
@@ -1497,6 +1605,221 @@ TEST_F(DBBasicTest, MultiGetBatchedMultiLevelMerge) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSizeInMemory) {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
ASSERT_OK(Put(1, "k1", "v_1"));
|
||||
ASSERT_OK(Put(1, "k2", "v_2"));
|
||||
ASSERT_OK(Put(1, "k3", "v_3"));
|
||||
ASSERT_OK(Put(1, "k4", "v_4"));
|
||||
ASSERT_OK(Put(1, "k5", "v_5"));
|
||||
ASSERT_OK(Put(1, "k6", "v_6"));
|
||||
std::vector<Slice> keys = {"k1", "k2", "k3", "k4", "k5", "k6"};
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<Status> s(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
|
||||
get_perf_context()->Reset();
|
||||
ReadOptions ro;
|
||||
ro.value_size_soft_limit = 11;
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
for (unsigned int i = 0; i < 4; i++) {
|
||||
ASSERT_EQ(std::string(values[i].data(), values[i].size()),
|
||||
"v_" + std::to_string(i + 1));
|
||||
}
|
||||
|
||||
for (unsigned int i = 4; i < 6; i++) {
|
||||
ASSERT_TRUE(s[i].IsAborted());
|
||||
}
|
||||
|
||||
ASSERT_EQ(12, (int)get_perf_context()->multiget_read_bytes);
|
||||
SetPerfLevel(kDisable);
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSize) {
|
||||
do {
|
||||
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
|
||||
SetPerfLevel(kEnableCount);
|
||||
|
||||
ASSERT_OK(Put(1, "k6", "v6"));
|
||||
ASSERT_OK(Put(1, "k7", "v7_"));
|
||||
ASSERT_OK(Put(1, "k3", "v3_"));
|
||||
ASSERT_OK(Put(1, "k4", "v4"));
|
||||
Flush(1);
|
||||
ASSERT_OK(Delete(1, "k4"));
|
||||
ASSERT_OK(Put(1, "k11", "v11"));
|
||||
ASSERT_OK(Delete(1, "no_key"));
|
||||
ASSERT_OK(Put(1, "k8", "v8_"));
|
||||
ASSERT_OK(Put(1, "k13", "v13"));
|
||||
ASSERT_OK(Put(1, "k14", "v14"));
|
||||
ASSERT_OK(Put(1, "k15", "v15"));
|
||||
ASSERT_OK(Put(1, "k16", "v16"));
|
||||
ASSERT_OK(Put(1, "k17", "v17"));
|
||||
Flush(1);
|
||||
|
||||
ASSERT_OK(Put(1, "k1", "v1_"));
|
||||
ASSERT_OK(Put(1, "k2", "v2_"));
|
||||
ASSERT_OK(Put(1, "k5", "v5_"));
|
||||
ASSERT_OK(Put(1, "k9", "v9_"));
|
||||
ASSERT_OK(Put(1, "k10", "v10"));
|
||||
ASSERT_OK(Delete(1, "k2"));
|
||||
ASSERT_OK(Delete(1, "k6"));
|
||||
|
||||
get_perf_context()->Reset();
|
||||
|
||||
std::vector<Slice> keys({"k1", "k10", "k11", "k12", "k13", "k14", "k15",
|
||||
"k16", "k17", "k2", "k3", "k4", "k5", "k6", "k7",
|
||||
"k8", "k9", "no_key"});
|
||||
std::vector<PinnableSlice> values(keys.size());
|
||||
std::vector<ColumnFamilyHandle*> cfs(keys.size(), handles_[1]);
|
||||
std::vector<Status> s(keys.size());
|
||||
|
||||
ReadOptions ro;
|
||||
ro.value_size_soft_limit = 20;
|
||||
db_->MultiGet(ro, handles_[1], keys.size(), keys.data(), values.data(),
|
||||
s.data(), false);
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
|
||||
// In memory keys
|
||||
ASSERT_EQ(std::string(values[0].data(), values[0].size()), "v1_");
|
||||
ASSERT_EQ(std::string(values[1].data(), values[1].size()), "v10");
|
||||
ASSERT_TRUE(s[9].IsNotFound()); // k2
|
||||
ASSERT_EQ(std::string(values[12].data(), values[12].size()), "v5_");
|
||||
ASSERT_TRUE(s[13].IsNotFound()); // k6
|
||||
ASSERT_EQ(std::string(values[16].data(), values[16].size()), "v9_");
|
||||
|
||||
// In sst files
|
||||
ASSERT_EQ(std::string(values[2].data(), values[1].size()), "v11");
|
||||
ASSERT_EQ(std::string(values[4].data(), values[4].size()), "v13");
|
||||
ASSERT_EQ(std::string(values[5].data(), values[5].size()), "v14");
|
||||
|
||||
// Remaining aborted after value_size exceeds.
|
||||
ASSERT_TRUE(s[3].IsAborted());
|
||||
ASSERT_TRUE(s[6].IsAborted());
|
||||
ASSERT_TRUE(s[7].IsAborted());
|
||||
ASSERT_TRUE(s[8].IsAborted());
|
||||
ASSERT_TRUE(s[10].IsAborted());
|
||||
ASSERT_TRUE(s[11].IsAborted());
|
||||
ASSERT_TRUE(s[14].IsAborted());
|
||||
ASSERT_TRUE(s[15].IsAborted());
|
||||
ASSERT_TRUE(s[17].IsAborted());
|
||||
|
||||
// 6 kv pairs * 3 bytes per value (i.e. 18)
|
||||
ASSERT_EQ(21, (int)get_perf_context()->multiget_read_bytes);
|
||||
SetPerfLevel(kDisable);
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, MultiGetBatchedValueSizeMultiLevelMerge) {
|
||||
Options options = CurrentOptions();
|
||||
options.disable_auto_compactions = true;
|
||||
options.merge_operator = MergeOperators::CreateStringAppendOperator();
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy.reset(NewBloomFilterPolicy(10, false));
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
Reopen(options);
|
||||
int num_keys = 0;
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
ASSERT_OK(Put("key_" + std::to_string(i), "val_l2_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(2);
|
||||
|
||||
for (int i = 0; i < 64; i += 3) {
|
||||
ASSERT_OK(Merge("key_" + std::to_string(i), "val_l1_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
MoveFilesToLevel(1);
|
||||
|
||||
for (int i = 0; i < 64; i += 5) {
|
||||
ASSERT_OK(Merge("key_" + std::to_string(i), "val_l0_" + std::to_string(i)));
|
||||
num_keys++;
|
||||
if (num_keys == 8) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
}
|
||||
if (num_keys > 0) {
|
||||
Flush();
|
||||
num_keys = 0;
|
||||
}
|
||||
ASSERT_EQ(0, num_keys);
|
||||
|
||||
for (int i = 0; i < 64; i += 9) {
|
||||
ASSERT_OK(
|
||||
Merge("key_" + std::to_string(i), "val_mem_" + std::to_string(i)));
|
||||
}
|
||||
|
||||
std::vector<std::string> keys_str;
|
||||
for (int i = 10; i < 50; ++i) {
|
||||
keys_str.push_back("key_" + std::to_string(i));
|
||||
}
|
||||
|
||||
std::vector<Slice> keys(keys_str.size());
|
||||
for (int i = 0; i < 40; i++) {
|
||||
keys[i] = Slice(keys_str[i]);
|
||||
}
|
||||
|
||||
std::vector<PinnableSlice> values(keys_str.size());
|
||||
std::vector<Status> statuses(keys_str.size());
|
||||
ReadOptions read_options;
|
||||
read_options.verify_checksums = true;
|
||||
read_options.value_size_soft_limit = 380;
|
||||
db_->MultiGet(read_options, dbfull()->DefaultColumnFamily(), keys.size(),
|
||||
keys.data(), values.data(), statuses.data());
|
||||
|
||||
ASSERT_EQ(values.size(), keys.size());
|
||||
|
||||
uint64_t curr_value_size = 0;
|
||||
for (unsigned int j = 0; j < 26; ++j) {
|
||||
int key = j + 10;
|
||||
std::string value;
|
||||
value.append("val_l2_" + std::to_string(key));
|
||||
if (key % 3 == 0) {
|
||||
value.append(",");
|
||||
value.append("val_l1_" + std::to_string(key));
|
||||
}
|
||||
if (key % 5 == 0) {
|
||||
value.append(",");
|
||||
value.append("val_l0_" + std::to_string(key));
|
||||
}
|
||||
if (key % 9 == 0) {
|
||||
value.append(",");
|
||||
value.append("val_mem_" + std::to_string(key));
|
||||
}
|
||||
curr_value_size += value.size();
|
||||
ASSERT_EQ(values[j], value);
|
||||
ASSERT_OK(statuses[j]);
|
||||
}
|
||||
// ASSERT_TRUE(curr_value_size <= read_options.value_size_hard_limit);
|
||||
|
||||
// All remaning keys status is set Status::Abort
|
||||
for (unsigned int j = 26; j < 40; j++) {
|
||||
ASSERT_TRUE(statuses[j].IsAborted());
|
||||
}
|
||||
}
|
||||
|
||||
// Test class for batched MultiGet with prefix extractor
|
||||
// Param bool - If true, use partitioned filters
|
||||
// If false, use full filter block
|
||||
@@ -1681,13 +2004,13 @@ TEST_F(DBBasicTest, GetAllKeyVersions) {
|
||||
ASSERT_EQ(kNumInserts + kNumDeletes + kNumUpdates, key_versions.size());
|
||||
|
||||
// Check non-default column family
|
||||
for (size_t i = 0; i != kNumInserts - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != kNumInserts; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumUpdates - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != kNumUpdates; ++i) {
|
||||
ASSERT_OK(Put(1, std::to_string(i), "value1"));
|
||||
}
|
||||
for (size_t i = 0; i != kNumDeletes - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != kNumDeletes; ++i) {
|
||||
ASSERT_OK(Delete(1, std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(ROCKSDB_NAMESPACE::GetAllKeyVersions(
|
||||
@@ -1836,11 +2159,16 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
ASSERT_OK(Flush(static_cast<int>(cf)));
|
||||
}
|
||||
|
||||
// Delete files
|
||||
// Delete and corrupt files
|
||||
for (size_t i = 0; i < all_cf_names.size(); ++i) {
|
||||
std::vector<std::string>& files = listener->GetFiles(all_cf_names[i]);
|
||||
ASSERT_EQ(3, files.size());
|
||||
for (int j = static_cast<int>(files.size() - 1); j >= static_cast<int>(i);
|
||||
std::string corrupted_data;
|
||||
ASSERT_OK(ReadFileToString(env_, files[files.size() - 1], &corrupted_data));
|
||||
ASSERT_OK(WriteStringToFile(
|
||||
env_, corrupted_data.substr(0, corrupted_data.size() - 2),
|
||||
files[files.size() - 1], /*should_sync=*/true));
|
||||
for (int j = static_cast<int>(files.size() - 2); j >= static_cast<int>(i);
|
||||
--j) {
|
||||
ASSERT_OK(env_->DeleteFile(files[j]));
|
||||
}
|
||||
@@ -1872,6 +2200,32 @@ TEST_F(DBBasicTest, RecoverWithMissingFiles) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryTryMultipleManifests) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
{
|
||||
// Hack by adding a new MANIFEST with high file number
|
||||
std::string garbage(10, '\0');
|
||||
ASSERT_OK(WriteStringToFile(env_, garbage, dbname_ + "/MANIFEST-001000",
|
||||
/*should_sync=*/true));
|
||||
}
|
||||
{
|
||||
// Hack by adding a corrupted SST not referenced by any MANIFEST
|
||||
std::string garbage(10, '\0');
|
||||
ASSERT_OK(WriteStringToFile(env_, garbage, dbname_ + "/001001.sst",
|
||||
/*should_sync=*/true));
|
||||
}
|
||||
|
||||
options.best_efforts_recovery = true;
|
||||
|
||||
Reopen(options);
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
@@ -1895,6 +2249,35 @@ TEST_F(DBBasicTest, RecoverWithNoCurrentFile) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, RecoverWithNoManifest) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
Close();
|
||||
{
|
||||
// Delete all MANIFEST.
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname_, &files));
|
||||
for (const auto& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile;
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
ASSERT_OK(env_->DeleteFile(dbname_ + "/" + file));
|
||||
}
|
||||
}
|
||||
}
|
||||
options.best_efforts_recovery = true;
|
||||
options.create_if_missing = false;
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsInvalidArgument());
|
||||
options.create_if_missing = true;
|
||||
Reopen(options);
|
||||
// Since no MANIFEST exists, best-efforts recovery creates a new, empty db.
|
||||
ASSERT_EQ("NOT_FOUND", Get("foo"));
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
@@ -1935,14 +2318,41 @@ TEST_F(DBBasicTest, SkipWALIfMissingTableFiles) {
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBBasicTest, ManifestChecksumMismatch) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("bar", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LogWriter::EmitPhysicalRecord:BeforeEncodeChecksum", [&](void* arg) {
|
||||
auto* crc = reinterpret_cast<uint32_t*>(arg);
|
||||
*crc = *crc + 1;
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
WriteOptions write_opts;
|
||||
write_opts.disableWAL = true;
|
||||
Status s = db_->Put(write_opts, "foo", "value");
|
||||
ASSERT_OK(s);
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
ASSERT_OK(Put("foo", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
class DBBasicTestMultiGet : public DBTestBase {
|
||||
public:
|
||||
DBBasicTestMultiGet(std::string test_dir, int num_cfs, bool compressed_cache,
|
||||
bool uncompressed_cache, bool compression_enabled,
|
||||
bool fill_cache, uint32_t compression_parallel_threads)
|
||||
bool uncompressed_cache, bool _compression_enabled,
|
||||
bool _fill_cache, uint32_t compression_parallel_threads)
|
||||
: DBTestBase(test_dir) {
|
||||
compression_enabled_ = compression_enabled;
|
||||
fill_cache_ = fill_cache;
|
||||
compression_enabled_ = _compression_enabled;
|
||||
fill_cache_ = _fill_cache;
|
||||
|
||||
if (compressed_cache) {
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
|
||||
@@ -187,7 +187,7 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -209,12 +209,12 @@ TEST_F(DBBlockCacheTest, TestWithoutCompressedBlockCache) {
|
||||
iter = nullptr;
|
||||
|
||||
// Release iterators and access cache again.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iterators[i].reset();
|
||||
CheckCacheCounters(options, 0, 0, 0, 0);
|
||||
}
|
||||
ASSERT_EQ(0, cache->GetPinnedUsage());
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
@@ -243,7 +243,7 @@ TEST_F(DBBlockCacheTest, TestWithCompressedBlockCache) {
|
||||
Iterator* iter = nullptr;
|
||||
|
||||
// Load blocks into cache.
|
||||
for (size_t i = 0; i < kNumBlocks - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < kNumBlocks; i++) {
|
||||
iter = db_->NewIterator(read_options);
|
||||
iter->Seek(ToString(i));
|
||||
ASSERT_OK(iter->status());
|
||||
|
||||
@@ -1063,12 +1063,21 @@ TEST_P(DBBloomFilterTestVaryPrefixAndFormatVer, PartitionedMultiGet) {
|
||||
bbto.partition_filters = true;
|
||||
bbto.index_type = BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch;
|
||||
bbto.whole_key_filtering = !use_prefix_;
|
||||
bbto.metadata_block_size = 128;
|
||||
if (use_prefix_) { // (not related to prefix, just alternating between)
|
||||
// Make sure code appropriately deals with metadata block size setting
|
||||
// that is "too small" (smaller than minimum size for filter builder)
|
||||
bbto.metadata_block_size = 63;
|
||||
} else {
|
||||
// Make sure the test will work even on platforms with large minimum
|
||||
// filter size, due to large cache line size.
|
||||
// (Largest cache line size + 10+% overhead.)
|
||||
bbto.metadata_block_size = 290;
|
||||
}
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
ReadOptions ropts;
|
||||
|
||||
constexpr uint32_t N = 10000;
|
||||
constexpr uint32_t N = 12000;
|
||||
// Add N/2 evens
|
||||
for (uint32_t i = 0; i < N; i += 2) {
|
||||
ASSERT_OK(Put(UKey(i), UKey(i)));
|
||||
|
||||
@@ -3184,6 +3184,15 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
options.level0_file_num_compaction_trigger = 5;
|
||||
options.max_background_compactions = 2;
|
||||
options.max_subcompactions = max_subcompactions_;
|
||||
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
|
||||
options.write_buffer_size = 2 << 20; // 2MB
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache = NewLRUCache(64 << 20); // 64MB
|
||||
table_options.cache_index_and_filter_blocks = true;
|
||||
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kValueSize = 1 << 20;
|
||||
@@ -3214,6 +3223,7 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
ASSERT_OK(Put(Key(i + 1), value));
|
||||
}
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_EQ(i + 1, TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
}
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
@@ -3228,6 +3238,16 @@ TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
ASSERT_GE(level_to_files[0][i].fd.file_size, 1 << 21);
|
||||
}
|
||||
|
||||
// The index/filter in the file produced by intra-L0 should not be pinned.
|
||||
// That means clearing unref'd entries in block cache and re-accessing the
|
||||
// file produced by intra-L0 should bump the index block miss count.
|
||||
uint64_t prev_index_misses =
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS);
|
||||
table_options.block_cache->EraseUnRefEntries();
|
||||
ASSERT_EQ("", Get(Key(0)));
|
||||
ASSERT_EQ(prev_index_misses + 1,
|
||||
TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
|
||||
}
|
||||
|
||||
TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
|
||||
@@ -5163,7 +5183,6 @@ TEST_P(DBCompactionTestWithParam,
|
||||
// Put one key, to make biggest log sequence number in this memtable is bigger
|
||||
// than sst which would be ingested in next step.
|
||||
ASSERT_OK(Put(Key(2), "b"));
|
||||
ASSERT_EQ(10, NumTableFilesAtLevel(0));
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
std::vector<std::vector<FileMetaData>> level_to_files;
|
||||
|
||||
+2
-2
@@ -499,13 +499,13 @@ TEST_P(DBAtomicFlushTest, AtomicFlushTriggeredByMemTableFull) {
|
||||
TEST_SYNC_POINT(
|
||||
"DBAtomicFlushTest::AtomicFlushTriggeredByMemTableFull:BeforeCheck");
|
||||
if (options.atomic_flush) {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_TRUE(cfh->cfd()->mem()->IsEmpty());
|
||||
}
|
||||
} else {
|
||||
for (size_t i = 0; i != num_cfs - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 != num_cfs; ++i) {
|
||||
auto cfh = static_cast<ColumnFamilyHandleImpl*>(handles_[i]);
|
||||
ASSERT_EQ(0, cfh->cfd()->imm()->NumNotFlushed());
|
||||
ASSERT_FALSE(cfh->cfd()->mem()->IsEmpty());
|
||||
|
||||
+66
-13
@@ -249,7 +249,8 @@ DBImpl::DBImpl(const DBOptions& options, const std::string& dbname,
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
|
||||
DumpRocksDBBuildVersion(immutable_db_options_.info_log.get());
|
||||
DumpDBFileSummary(immutable_db_options_, dbname_);
|
||||
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());
|
||||
DumpSupportInfo(immutable_db_options_.info_log.get());
|
||||
@@ -1787,6 +1788,7 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t num_found = 0;
|
||||
size_t keys_read;
|
||||
uint64_t curr_value_size = 0;
|
||||
for (keys_read = 0; keys_read < num_keys; ++keys_read) {
|
||||
merge_context.Clear();
|
||||
Status& s = stat_list[keys_read];
|
||||
@@ -1830,6 +1832,13 @@ std::vector<Status> DBImpl::MultiGet(
|
||||
if (s.ok()) {
|
||||
bytes_read += value->size();
|
||||
num_found++;
|
||||
curr_value_size += value->size();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
while (++keys_read < num_keys) {
|
||||
stat_list[keys_read] = Status::Aborted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_options.deadline.count() &&
|
||||
@@ -2084,11 +2093,11 @@ void DBImpl::MultiGet(const ReadOptions& read_options, const size_t num_keys,
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
assert(s.IsTimedOut());
|
||||
assert(s.IsTimedOut() || s.IsAborted());
|
||||
for (++cf_iter; cf_iter != multiget_cf_data.end(); ++cf_iter) {
|
||||
for (size_t i = cf_iter->start; i < cf_iter->start + cf_iter->num_keys;
|
||||
++i) {
|
||||
*sorted_keys[i]->s = Status::TimedOut();
|
||||
*sorted_keys[i]->s = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2243,7 +2252,7 @@ void DBImpl::MultiGetWithCallback(
|
||||
Status s = MultiGetImpl(read_options, 0, num_keys, sorted_keys,
|
||||
multiget_cf_data[0].super_version, consistent_seqnum,
|
||||
nullptr, nullptr);
|
||||
assert(s.ok() || s.IsTimedOut());
|
||||
assert(s.ok() || s.IsTimedOut() || s.IsAborted());
|
||||
ReturnAndCleanupSuperVersion(multiget_cf_data[0].cfd,
|
||||
multiget_cf_data[0].super_version);
|
||||
}
|
||||
@@ -2271,6 +2280,7 @@ Status DBImpl::MultiGetImpl(
|
||||
// merge_operands will contain the sequence of merges in the latter case.
|
||||
size_t keys_left = num_keys;
|
||||
Status s;
|
||||
uint64_t curr_value_size = 0;
|
||||
while (keys_left) {
|
||||
if (read_options.deadline.count() &&
|
||||
env_->NowMicros() >
|
||||
@@ -2285,6 +2295,7 @@ Status DBImpl::MultiGetImpl(
|
||||
MultiGetContext ctx(sorted_keys, start_key + num_keys - keys_left,
|
||||
batch_size, snapshot, read_options);
|
||||
MultiGetRange range = ctx.GetMultiGetRange();
|
||||
range.AddValueSize(curr_value_size);
|
||||
bool lookup_current = false;
|
||||
|
||||
keys_left -= batch_size;
|
||||
@@ -2315,6 +2326,11 @@ Status DBImpl::MultiGetImpl(
|
||||
super_version->current->MultiGet(read_options, &range, callback,
|
||||
is_blob_index);
|
||||
}
|
||||
curr_value_size = range.GetValueSize();
|
||||
if (curr_value_size > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Post processing (decrement reference counts and record statistics)
|
||||
@@ -2329,11 +2345,11 @@ Status DBImpl::MultiGetImpl(
|
||||
}
|
||||
}
|
||||
if (keys_left) {
|
||||
assert(s.IsTimedOut());
|
||||
assert(s.IsTimedOut() || s.IsAborted());
|
||||
for (size_t i = start_key + num_keys - keys_left; i < start_key + num_keys;
|
||||
++i) {
|
||||
KeyContext* key = (*sorted_keys)[i];
|
||||
*key->s = Status::TimedOut();
|
||||
*key->s = s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2655,7 +2671,8 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
" guaranteed to be preserved, try larger iter_start_seqnum opt."));
|
||||
}
|
||||
auto cfh = reinterpret_cast<ColumnFamilyHandleImpl*>(column_family);
|
||||
auto cfd = cfh->cfd();
|
||||
ColumnFamilyData* cfd = cfh->cfd();
|
||||
assert(cfd != nullptr);
|
||||
ReadCallback* read_callback = nullptr; // No read callback provided.
|
||||
if (read_options.tailing) {
|
||||
#ifdef ROCKSDB_LITE
|
||||
@@ -2676,10 +2693,11 @@ Iterator* DBImpl::NewIterator(const ReadOptions& read_options,
|
||||
// Note: no need to consider the special case of
|
||||
// last_seq_same_as_publish_seq_==false since NewIterator is overridden in
|
||||
// WritePreparedTxnDB
|
||||
auto snapshot = read_options.snapshot != nullptr
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: versions_->LastSequence();
|
||||
result = NewIteratorImpl(read_options, cfd, snapshot, read_callback);
|
||||
result = NewIteratorImpl(read_options, cfd,
|
||||
(read_options.snapshot != nullptr)
|
||||
? read_options.snapshot->GetSequenceNumber()
|
||||
: kMaxSequenceNumber,
|
||||
read_callback);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2692,6 +2710,24 @@ ArenaWrappedDBIter* DBImpl::NewIteratorImpl(const ReadOptions& read_options,
|
||||
bool allow_refresh) {
|
||||
SuperVersion* sv = cfd->GetReferencedSuperVersion(this);
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:1");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:2");
|
||||
|
||||
if (snapshot == kMaxSequenceNumber) {
|
||||
// Note that the snapshot is assigned AFTER referencing the super
|
||||
// version because otherwise a flush happening in between may compact away
|
||||
// data for the snapshot, so the reader would see neither data that was be
|
||||
// visible to the snapshot before compaction nor the newer data inserted
|
||||
// afterwards.
|
||||
// Note that the super version might not contain all the data available
|
||||
// to this snapshot, but in that case it can see all the data in the
|
||||
// super version, which is a valid consistent state after the user
|
||||
// calls NewIterator().
|
||||
snapshot = versions_->LastSequence();
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:3");
|
||||
TEST_SYNC_POINT("DBImpl::NewIterator:4");
|
||||
}
|
||||
|
||||
// Try to generate a DB iterator tree in continuous memory area to be
|
||||
// cache friendly. Here is an example of result:
|
||||
// +-------------------------------+
|
||||
@@ -3586,6 +3622,21 @@ Status DBImpl::GetDbIdentityFromIdentityFile(std::string* identity) const {
|
||||
return s;
|
||||
}
|
||||
|
||||
Status DBImpl::GetDbSessionId(std::string& session_id) const {
|
||||
session_id.assign(db_session_id_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void DBImpl::SetDbSessionId() {
|
||||
// GenerateUniqueId() generates an identifier
|
||||
// that has a negligible probability of being duplicated
|
||||
db_session_id_ = env_->GenerateUniqueId();
|
||||
// Remove the extra '\n' at the end if there is one
|
||||
if (!db_session_id_.empty() && db_session_id_.back() == '\n') {
|
||||
db_session_id_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Default implementation -- returns not supported status
|
||||
Status DB::CreateColumnFamily(const ColumnFamilyOptions& /*cf_options*/,
|
||||
const std::string& /*column_family_name*/,
|
||||
@@ -4165,7 +4216,8 @@ Status DBImpl::IngestExternalFiles(
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[i].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
exec_results[i].second = ingestion_jobs[i].Prepare(
|
||||
args[i].external_files, start_file_number, super_version);
|
||||
args[i].external_files, args[i].files_checksums,
|
||||
args[i].files_checksum_func_names, start_file_number, super_version);
|
||||
exec_results[i].first = true;
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
@@ -4176,7 +4228,8 @@ Status DBImpl::IngestExternalFiles(
|
||||
static_cast<ColumnFamilyHandleImpl*>(args[0].column_family)->cfd();
|
||||
SuperVersion* super_version = cfd->GetReferencedSuperVersion(this);
|
||||
exec_results[0].second = ingestion_jobs[0].Prepare(
|
||||
args[0].external_files, next_file_number, super_version);
|
||||
args[0].external_files, args[0].files_checksums,
|
||||
args[0].files_checksum_func_names, next_file_number, super_version);
|
||||
exec_results[0].first = true;
|
||||
CleanupSuperVersion(super_version);
|
||||
}
|
||||
|
||||
@@ -350,6 +350,8 @@ class DBImpl : public DB {
|
||||
|
||||
virtual Status GetDbIdentityFromIdentityFile(std::string* identity) const;
|
||||
|
||||
virtual Status GetDbSessionId(std::string& session_id) const override;
|
||||
|
||||
ColumnFamilyHandle* DefaultColumnFamily() const override;
|
||||
|
||||
ColumnFamilyHandle* PersistentStatsColumnFamily() const;
|
||||
@@ -477,6 +479,7 @@ class DBImpl : public DB {
|
||||
Status GetImpl(const ReadOptions& options, const Slice& key,
|
||||
GetImplOptions& get_impl_options);
|
||||
|
||||
// If `snapshot` == kMaxSequenceNumber, set a recent one inside the file.
|
||||
ArenaWrappedDBIter* NewIteratorImpl(const ReadOptions& options,
|
||||
ColumnFamilyData* cfd,
|
||||
SequenceNumber snapshot,
|
||||
@@ -980,6 +983,9 @@ class DBImpl : public DB {
|
||||
protected:
|
||||
const std::string dbname_;
|
||||
std::string db_id_;
|
||||
// db_session_id_ is an identifier that gets reset
|
||||
// every time the DB is opened
|
||||
std::string db_session_id_;
|
||||
std::unique_ptr<VersionSet> versions_;
|
||||
// Flag to check whether we allocated and own the info log file
|
||||
bool own_info_log_;
|
||||
@@ -1161,6 +1167,10 @@ class DBImpl : public DB {
|
||||
// bump up the version set's next_file_number_ to be 1 + largest_file_number.
|
||||
Status FinishBestEffortsRecovery();
|
||||
|
||||
// SetDbSessionId() should be called in the constuctor DBImpl()
|
||||
// to ensure that db_session_id_ gets updated every time the DB is opened
|
||||
void SetDbSessionId();
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
friend class ErrorHandler;
|
||||
|
||||
@@ -154,7 +154,8 @@ Status DBImpl::FlushMemTableToOutputFile(
|
||||
GetDataDir(cfd, 0U),
|
||||
GetCompressionFlush(*cfd->ioptions(), mutable_cf_options), stats_,
|
||||
&event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri);
|
||||
true /* sync_output_directory */, true /* write_manifest */, thread_pri,
|
||||
db_id_, db_session_id_);
|
||||
FileMetaData file_meta;
|
||||
|
||||
TEST_SYNC_POINT("DBImpl::FlushMemTableToOutputFile:BeforePickMemtables");
|
||||
@@ -345,7 +346,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
data_dir, GetCompressionFlush(*cfd->ioptions(), mutable_cf_options),
|
||||
stats_, &event_logger_, mutable_cf_options.report_bg_io_stats,
|
||||
false /* sync_output_directory */, false /* write_manifest */,
|
||||
thread_pri));
|
||||
thread_pri, db_id_, db_session_id_));
|
||||
jobs.back()->PickMemTable();
|
||||
}
|
||||
|
||||
@@ -573,7 +574,7 @@ Status DBImpl::AtomicFlushMemTablesToOutputFiles(
|
||||
// Need to undo atomic flush if something went wrong, i.e. s is not OK and
|
||||
// it is not because of CF drop.
|
||||
if (!s.ok() && !s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && io_s.IsColumnFamilyDropped()) {
|
||||
if (!io_s.ok() && !io_s.IsColumnFamilyDropped()) {
|
||||
error_handler_.SetBGError(io_s, BackgroundErrorReason::kFlush);
|
||||
} else {
|
||||
Status new_bg_error = s;
|
||||
@@ -1031,7 +1032,8 @@ Status DBImpl::CompactFilesImpl(
|
||||
snapshot_checker, table_cache_, &event_logger_,
|
||||
c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, Env::Priority::USER, &manual_compaction_paused_);
|
||||
&compaction_job_stats, Env::Priority::USER, &manual_compaction_paused_,
|
||||
db_id_, db_session_id_);
|
||||
|
||||
// Creating a compaction influences the compaction score because the score
|
||||
// takes running compactions into account (by skipping files that are already
|
||||
@@ -2822,7 +2824,8 @@ Status DBImpl::BackgroundCompaction(bool* made_progress,
|
||||
&event_logger_, c->mutable_cf_options()->paranoid_file_checks,
|
||||
c->mutable_cf_options()->report_bg_io_stats, dbname_,
|
||||
&compaction_job_stats, thread_pri,
|
||||
is_manual ? &manual_compaction_paused_ : nullptr);
|
||||
is_manual ? &manual_compaction_paused_ : nullptr, db_id_,
|
||||
db_session_id_);
|
||||
compaction_job.Prepare();
|
||||
|
||||
NotifyOnCompactionBegin(c->column_family_data(), c.get(), status,
|
||||
|
||||
@@ -686,13 +686,15 @@ uint64_t PrecomputeMinLogNumberToKeep(
|
||||
Status DBImpl::FinishBestEffortsRecovery() {
|
||||
mutex_.AssertHeld();
|
||||
std::vector<std::string> paths;
|
||||
paths.push_back(dbname_);
|
||||
paths.push_back(NormalizePath(dbname_ + std::string(1, kFilePathSeparator)));
|
||||
for (const auto& db_path : immutable_db_options_.db_paths) {
|
||||
paths.push_back(db_path.path);
|
||||
paths.push_back(
|
||||
NormalizePath(db_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
for (const auto* cfd : *versions_->GetColumnFamilySet()) {
|
||||
for (const auto& cf_path : cfd->ioptions()->cf_paths) {
|
||||
paths.push_back(cf_path.path);
|
||||
paths.push_back(
|
||||
NormalizePath(cf_path.path + std::string(1, kFilePathSeparator)));
|
||||
}
|
||||
}
|
||||
// Dedup paths
|
||||
@@ -711,7 +713,8 @@ Status DBImpl::FinishBestEffortsRecovery() {
|
||||
if (!ParseFileName(fname, &number, &type)) {
|
||||
continue;
|
||||
}
|
||||
const std::string normalized_fpath = NormalizePath(path + fname);
|
||||
// path ends with '/' or '\\'
|
||||
const std::string normalized_fpath = path + fname;
|
||||
largest_file_number = std::max(largest_file_number, number);
|
||||
if (type == kTableFile && number >= next_file_number &&
|
||||
files_to_delete.find(normalized_fpath) == files_to_delete.end()) {
|
||||
|
||||
+55
-10
@@ -370,7 +370,30 @@ Status DBImpl::Recover(
|
||||
}
|
||||
|
||||
std::string current_fname = CurrentFileName(dbname_);
|
||||
s = env_->FileExists(current_fname);
|
||||
// Path to any MANIFEST file in the db dir. It does not matter which one.
|
||||
// Since best-efforts recovery ignores CURRENT file, existence of a
|
||||
// MANIFEST indicates the recovery to recover existing db. If no MANIFEST
|
||||
// can be found, a new db will be created.
|
||||
std::string manifest_path;
|
||||
if (!immutable_db_options_.best_efforts_recovery) {
|
||||
s = env_->FileExists(current_fname);
|
||||
} else {
|
||||
s = Status::NotFound();
|
||||
std::vector<std::string> files;
|
||||
// No need to check return value
|
||||
env_->GetChildren(dbname_, &files);
|
||||
for (const std::string& file : files) {
|
||||
uint64_t number = 0;
|
||||
FileType type = kLogFile; // initialize
|
||||
if (ParseFileName(file, &number, &type) && type == kDescriptorFile) {
|
||||
// Found MANIFEST (descriptor log), thus best-efforts recovery does
|
||||
// not have to treat the db as empty.
|
||||
s = Status::OK();
|
||||
manifest_path = dbname_ + "/" + file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.IsNotFound()) {
|
||||
if (immutable_db_options_.create_if_missing) {
|
||||
s = NewDB();
|
||||
@@ -398,14 +421,14 @@ Status DBImpl::Recover(
|
||||
FileOptions customized_fs(file_options_);
|
||||
customized_fs.use_direct_reads |=
|
||||
immutable_db_options_.use_direct_io_for_flush_and_compaction;
|
||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
||||
nullptr);
|
||||
const std::string& fname =
|
||||
manifest_path.empty() ? current_fname : manifest_path;
|
||||
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||
if (!s.ok()) {
|
||||
std::string error_str = s.ToString();
|
||||
// Check if unsupported Direct I/O is the root cause
|
||||
customized_fs.use_direct_reads = false;
|
||||
s = fs_->NewRandomAccessFile(current_fname, customized_fs, &idfile,
|
||||
nullptr);
|
||||
s = fs_->NewRandomAccessFile(fname, customized_fs, &idfile, nullptr);
|
||||
if (s.ok()) {
|
||||
return Status::InvalidArgument(
|
||||
"Direct I/O is not supported by the specified DB.");
|
||||
@@ -425,6 +448,9 @@ Status DBImpl::Recover(
|
||||
s = versions_->TryRecover(column_families, read_only, &db_id_,
|
||||
&missing_table_file);
|
||||
if (s.ok()) {
|
||||
// TryRecover may delete previous column_family_set_.
|
||||
column_family_memtables_.reset(
|
||||
new ColumnFamilyMemTablesImpl(versions_->GetColumnFamilySet()));
|
||||
s = FinishBestEffortsRecovery();
|
||||
}
|
||||
}
|
||||
@@ -701,10 +727,10 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Status* status; // nullptr if immutable_db_options_.paranoid_checks==false
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
ROCKS_LOG_WARN(info_log, "%s%s: dropping %d bytes; %s",
|
||||
(this->status == nullptr ? "(ignoring error) " : ""),
|
||||
fname, static_cast<int>(bytes), s.ToString().c_str());
|
||||
if (this->status != nullptr && this->status->ok()) {
|
||||
*this->status = s;
|
||||
(status == nullptr ? "(ignoring error) " : ""), fname,
|
||||
static_cast<int>(bytes), s.ToString().c_str());
|
||||
if (status != nullptr && status->ok()) {
|
||||
*status = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -827,6 +853,8 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
Slice record;
|
||||
WriteBatch batch;
|
||||
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::RecoverLogFiles:BeforeReadWal",
|
||||
/*arg=*/nullptr);
|
||||
while (!stop_replay_by_wal_filter &&
|
||||
reader.ReadRecord(&record, &scratch,
|
||||
immutable_db_options_.wal_recovery_mode) &&
|
||||
@@ -991,6 +1019,16 @@ Status DBImpl::RecoverLogFiles(const std::vector<uint64_t>& log_numbers,
|
||||
status = Status::OK();
|
||||
} else if (immutable_db_options_.wal_recovery_mode ==
|
||||
WALRecoveryMode::kPointInTimeRecovery) {
|
||||
if (status.IsIOError()) {
|
||||
ROCKS_LOG_ERROR(immutable_db_options_.info_log,
|
||||
"IOError during point-in-time reading log #%" PRIu64
|
||||
" seq #%" PRIu64
|
||||
". %s. This likely mean loss of synced WAL, "
|
||||
"thus recovery fails.",
|
||||
log_number, *next_sequence,
|
||||
status.ToString().c_str());
|
||||
return status;
|
||||
}
|
||||
// We should ignore the error but not continue replaying
|
||||
status = Status::OK();
|
||||
stop_replay_for_corruption = true;
|
||||
@@ -1251,7 +1289,8 @@ Status DBImpl::WriteLevel0TableForRecovery(int job_id, ColumnFamilyData* cfd,
|
||||
mutable_cf_options.compression_opts, paranoid_file_checks,
|
||||
cfd->internal_stats(), TableFileCreationReason::kRecovery, &io_s,
|
||||
&event_logger_, job_id, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time, write_hint);
|
||||
-1 /* level */, current_time, 0 /* oldest_key_time */, write_hint,
|
||||
0 /* file_creation_time */, db_id_, db_session_id_);
|
||||
LogFlush(immutable_db_options_.info_log);
|
||||
ROCKS_LOG_DEBUG(immutable_db_options_.info_log,
|
||||
"[%s] [WriteLevel0TableForRecovery]"
|
||||
@@ -1566,6 +1605,12 @@ Status DBImpl::Open(const DBOptions& db_options, const std::string& dbname,
|
||||
auto sfm = static_cast<SstFileManagerImpl*>(
|
||||
impl->immutable_db_options_.sst_file_manager.get());
|
||||
if (s.ok() && sfm) {
|
||||
// Set Statistics ptr for SstFileManager to dump the stats of
|
||||
// DeleteScheduler.
|
||||
sfm->SetStatisticsPtr(impl->immutable_db_options_.statistics);
|
||||
ROCKS_LOG_INFO(impl->immutable_db_options_.info_log,
|
||||
"SstFileManager instance %p", sfm);
|
||||
|
||||
// Notify SstFileManager about all sst files that already exist in
|
||||
// db_paths[0] and cf_paths[0] when the DB is opened.
|
||||
|
||||
|
||||
@@ -128,12 +128,38 @@ Status DBImplReadOnly::NewIterators(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Return OK if dbname exists in the file system
|
||||
// or create_if_missing is false
|
||||
Status OpenForReadOnlyCheckExistence(const DBOptions& db_options,
|
||||
const std::string& dbname) {
|
||||
Status s;
|
||||
if (!db_options.create_if_missing) {
|
||||
// Attempt to read "CURRENT" file
|
||||
const std::shared_ptr<FileSystem>& fs = db_options.env->GetFileSystem();
|
||||
std::string manifest_path;
|
||||
uint64_t manifest_file_number;
|
||||
s = VersionSet::GetCurrentManifestPath(dbname, fs.get(), &manifest_path,
|
||||
&manifest_file_number);
|
||||
if (!s.ok()) {
|
||||
return Status::NotFound(CurrentFileName(dbname), "does not exist");
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
DB** dbptr, bool /*error_if_log_file_exist*/) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
*dbptr = nullptr;
|
||||
|
||||
// Try to first open DB as fully compacted DB
|
||||
Status s;
|
||||
s = CompactedDBImpl::Open(options, dbname, dbptr);
|
||||
if (s.ok()) {
|
||||
return s;
|
||||
@@ -146,7 +172,8 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
|
||||
s = DB::OpenForReadOnly(db_options, dbname, column_families, &handles, dbptr);
|
||||
s = DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
db_options, dbname, column_families, &handles, dbptr);
|
||||
if (s.ok()) {
|
||||
assert(handles.size() == 1);
|
||||
// i can delete the handle since DBImpl is always holding a
|
||||
@@ -161,6 +188,22 @@ Status DB::OpenForReadOnly(
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
// If dbname does not exist in the file system, should not do anything
|
||||
Status s = OpenForReadOnlyCheckExistence(db_options, dbname);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
return DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
db_options, dbname, column_families, handles, dbptr,
|
||||
error_if_log_file_exist);
|
||||
}
|
||||
|
||||
Status DBImplReadOnly::OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist) {
|
||||
*dbptr = nullptr;
|
||||
handles->clear();
|
||||
|
||||
|
||||
@@ -130,6 +130,15 @@ class DBImplReadOnly : public DBImpl {
|
||||
}
|
||||
|
||||
private:
|
||||
// A "helper" function for DB::OpenForReadOnly without column families
|
||||
// to reduce unnecessary I/O
|
||||
// It has the same functionality as DB::OpenForReadOnly with column families
|
||||
// but does not check the existence of dbname in the file system
|
||||
static Status OpenForReadOnlyWithoutCheck(
|
||||
const DBOptions& db_options, const std::string& dbname,
|
||||
const std::vector<ColumnFamilyDescriptor>& column_families,
|
||||
std::vector<ColumnFamilyHandle*>* handles, DB** dbptr,
|
||||
bool error_if_log_file_exist = false);
|
||||
friend class DB;
|
||||
};
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -1837,8 +1837,30 @@ Status DB::Put(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
|
||||
Status DB::Delete(const WriteOptions& opt, ColumnFamilyHandle* column_family,
|
||||
const Slice& key) {
|
||||
WriteBatch batch;
|
||||
batch.Delete(column_family, key);
|
||||
if (nullptr == opt.timestamp) {
|
||||
WriteBatch batch;
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
const Slice* ts = opt.timestamp;
|
||||
assert(ts != nullptr);
|
||||
const size_t ts_sz = ts->size();
|
||||
constexpr size_t kKeyAndValueLenSize = 11;
|
||||
constexpr size_t kWriteBatchOverhead =
|
||||
WriteBatchInternal::kHeader + sizeof(ValueType) + kKeyAndValueLenSize;
|
||||
WriteBatch batch(key.size() + ts_sz + kWriteBatchOverhead, /*max_bytes=*/0,
|
||||
ts_sz);
|
||||
Status s = batch.Delete(column_family, key);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
s = batch.AssignTimestamp(*ts);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
return Write(opt, &batch);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname) {
|
||||
const std::string& dbname,
|
||||
const std::string& session_id) {
|
||||
if (options.info_log == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -32,6 +33,8 @@ void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
std::string file_info, wal_info;
|
||||
|
||||
Header(options.info_log, "DB SUMMARY\n");
|
||||
Header(options.info_log, "DB Session ID: %s\n", session_id.c_str());
|
||||
|
||||
// Get files in dbname dir
|
||||
if (!env->GetChildren(dbname, &files).ok()) {
|
||||
Error(options.info_log,
|
||||
|
||||
+2
-1
@@ -10,5 +10,6 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
void DumpDBFileSummary(const ImmutableDBOptions& options,
|
||||
const std::string& dbname);
|
||||
const std::string& dbname,
|
||||
const std::string& session_id = "");
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
+2
-1
@@ -258,7 +258,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
// prone to bugs causing the same user key with the same sequence number.
|
||||
// Note that with current timestamp implementation, the same user key can
|
||||
// have different timestamps and zero sequence number on the bottommost
|
||||
// level. This will change in the future.
|
||||
// level. This may change in the future.
|
||||
if ((!is_prev_key_seqnum_zero || timestamp_size_ > 0) &&
|
||||
skipping_saved_key &&
|
||||
CompareKeyForSkip(ikey_.user_key, saved_key_.GetUserKey()) <= 0) {
|
||||
@@ -276,6 +276,7 @@ bool DBIter::FindNextUserEntryInternal(bool skipping_saved_key,
|
||||
reseek_done = false;
|
||||
switch (ikey_.type) {
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
// Arrange to skip all upcoming entries for this key since
|
||||
// they are hidden by this deletion.
|
||||
|
||||
@@ -97,7 +97,8 @@ struct StressTestIterator : public InternalIterator {
|
||||
|
||||
bool MaybeFail() {
|
||||
if (rnd->Next() >=
|
||||
std::numeric_limits<uint64_t>::max() * error_probability) {
|
||||
static_cast<double>(std::numeric_limits<uint64_t>::max()) *
|
||||
error_probability) {
|
||||
return false;
|
||||
}
|
||||
if (rnd->Next() % 2) {
|
||||
@@ -114,7 +115,8 @@ struct StressTestIterator : public InternalIterator {
|
||||
|
||||
void MaybeMutate() {
|
||||
if (rnd->Next() >=
|
||||
std::numeric_limits<uint64_t>::max() * mutation_probability) {
|
||||
static_cast<double>(std::numeric_limits<uint64_t>::max()) *
|
||||
mutation_probability) {
|
||||
return;
|
||||
}
|
||||
do {
|
||||
@@ -126,8 +128,9 @@ struct StressTestIterator : public InternalIterator {
|
||||
if (data->hidden.empty()) {
|
||||
hide_probability = 1;
|
||||
}
|
||||
bool do_hide =
|
||||
rnd->Next() < std::numeric_limits<uint64_t>::max() * hide_probability;
|
||||
bool do_hide = rnd->Next() <
|
||||
static_cast<double>(std::numeric_limits<uint64_t>::max()) *
|
||||
hide_probability;
|
||||
if (do_hide) {
|
||||
// Hide a random entry.
|
||||
size_t idx = rnd->Next() % data->entries.size();
|
||||
|
||||
@@ -303,14 +303,14 @@ TEST_F(DBMemTableTest, InsertWithHint) {
|
||||
ASSERT_EQ(hint_bar, rep->last_hint_in());
|
||||
ASSERT_EQ(hint_bar, rep->last_hint_out());
|
||||
ASSERT_EQ(5, rep->num_insert_with_hint());
|
||||
ASSERT_OK(Put("whitelisted", "vvv"));
|
||||
ASSERT_OK(Put("NotInPrefixDomain", "vvv"));
|
||||
ASSERT_EQ(5, rep->num_insert_with_hint());
|
||||
ASSERT_EQ("foo_v1", Get("foo_k1"));
|
||||
ASSERT_EQ("foo_v2", Get("foo_k2"));
|
||||
ASSERT_EQ("foo_v3", Get("foo_k3"));
|
||||
ASSERT_EQ("bar_v1", Get("bar_k1"));
|
||||
ASSERT_EQ("bar_v2", Get("bar_k2"));
|
||||
ASSERT_EQ("vvv", Get("whitelisted"));
|
||||
ASSERT_EQ("vvv", Get("NotInPrefixDomain"));
|
||||
}
|
||||
|
||||
TEST_F(DBMemTableTest, ColumnFamilyId) {
|
||||
|
||||
@@ -39,7 +39,7 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : db_options_type_info) {
|
||||
for (const auto& opt : db_options_type_info) {
|
||||
if (opt.second.IsMutable() && opt.second.ShouldSerialize()) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class DBOptionsTest : public DBTestBase {
|
||||
std::unordered_map<std::string, std::string> options_map;
|
||||
StringToMap(options_str, &options_map);
|
||||
std::unordered_map<std::string, std::string> mutable_map;
|
||||
for (const auto opt : cf_options_type_info) {
|
||||
for (const auto& opt : cf_options_type_info) {
|
||||
if (opt.second.IsMutable() && opt.second.ShouldSerialize()) {
|
||||
mutable_map[opt.first] = options_map[opt.first];
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ TEST_F(DBRangeDelTest, MaxCompactionBytesCutsOutputFiles) {
|
||||
std::vector<std::vector<FileMetaData>> files;
|
||||
dbfull()->TEST_GetFilesMetaData(db_->DefaultColumnFamily(), &files);
|
||||
|
||||
for (size_t i = 0; i < files[1].size() - 1; ++i) {
|
||||
for (size_t i = 0; i + 1 < files[1].size(); ++i) {
|
||||
ASSERT_TRUE(InternalKeyComparator(opts.comparator)
|
||||
.Compare(files[1][i].largest, files[1][i + 1].smallest) <
|
||||
0);
|
||||
|
||||
@@ -376,6 +376,7 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
options.disable_auto_compactions = true;
|
||||
options.env = env_;
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
Status s;
|
||||
@@ -425,6 +426,9 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
}
|
||||
ASSERT_GT(time_spent_deleting, expected_penlty * 0.9);
|
||||
ASSERT_LT(time_spent_deleting, expected_penlty * 1.1);
|
||||
ASSERT_EQ(4, options.statistics->getAndResetTickerCount(FILES_MARKED_TRASH));
|
||||
ASSERT_EQ(
|
||||
0, options.statistics->getAndResetTickerCount(FILES_DELETED_IMMEDIATELY));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
@@ -816,6 +820,12 @@ TEST_F(DBSSTTest, CancellingCompactionsWorks) {
|
||||
ASSERT_EQ(sfm->GetCompactionsReservedSize(), 0);
|
||||
// Make sure the stat is bumped
|
||||
ASSERT_GT(dbfull()->immutable_db_options().statistics.get()->getTickerCount(COMPACTION_CANCELLED), 0);
|
||||
ASSERT_EQ(0,
|
||||
dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
FILES_MARKED_TRASH));
|
||||
ASSERT_EQ(4,
|
||||
dbfull()->immutable_db_options().statistics.get()->getTickerCount(
|
||||
FILES_DELETED_IMMEDIATELY));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
|
||||
@@ -252,6 +252,26 @@ TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTablePropertiesTest, GetDbIdentifiersProperty) {
|
||||
CreateAndReopenWithCF({"goku"}, CurrentOptions());
|
||||
|
||||
for (uint32_t cf = 0; cf < 2; ++cf) {
|
||||
Put(cf, "key", "val");
|
||||
Put(cf, "foo", "bar");
|
||||
Flush(cf);
|
||||
|
||||
TablePropertiesCollection fname_to_props;
|
||||
ASSERT_OK(db_->GetPropertiesOfAllTables(handles_[cf], &fname_to_props));
|
||||
ASSERT_EQ(1U, fname_to_props.size());
|
||||
|
||||
std::string id, sid;
|
||||
db_->GetDbIdentity(id);
|
||||
db_->GetDbSessionId(sid);
|
||||
ASSERT_EQ(id, fname_to_props.begin()->second->db_id);
|
||||
ASSERT_EQ(sid, fname_to_props.begin()->second->db_session_id);
|
||||
}
|
||||
}
|
||||
|
||||
class DeletionTriggeredCompactionTestListener : public EventListener {
|
||||
public:
|
||||
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
|
||||
|
||||
+56
-27
@@ -1489,18 +1489,24 @@ TEST_F(DBTest, ApproximateSizesMemTable) {
|
||||
}
|
||||
|
||||
TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
// Roughly 4 keys per data block, 1000 keys per file,
|
||||
// with filter substantially larger than a data block
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.filter_policy.reset(NewBloomFilterPolicy(16));
|
||||
table_options.block_size = 100;
|
||||
Options options = CurrentOptions();
|
||||
options.write_buffer_size = 1024 * 1024;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.write_buffer_size = 24 * 1024;
|
||||
options.compression = kNoCompression;
|
||||
options.create_if_missing = true;
|
||||
options.target_file_size_base = 1024 * 1024;
|
||||
options.target_file_size_base = 24 * 1024;
|
||||
DestroyAndReopen(options);
|
||||
const auto default_cf = db_->DefaultColumnFamily();
|
||||
|
||||
const int N = 64000;
|
||||
Random rnd(301);
|
||||
for (int i = 0; i < N; i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 24)));
|
||||
}
|
||||
// Flush everything to files
|
||||
Flush();
|
||||
@@ -1509,7 +1515,7 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
|
||||
// Write more keys
|
||||
for (int i = N; i < (N + N / 4); i++) {
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 1024)));
|
||||
ASSERT_OK(Put(Key(i), RandomString(&rnd, 24)));
|
||||
}
|
||||
// Flush everything to files again
|
||||
Flush();
|
||||
@@ -1517,27 +1523,45 @@ TEST_F(DBTest, ApproximateSizesFilesWithErrorMargin) {
|
||||
// Wait for compaction to finish
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
{
|
||||
const std::string start = Key(0);
|
||||
const std::string end = Key(2 * N);
|
||||
const Range r(start, end);
|
||||
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
SizeApproximationOptions size_approx_options;
|
||||
size_approx_options.include_memtabtles = false;
|
||||
size_approx_options.include_files = true;
|
||||
size_approx_options.files_size_error_margin = -1.0; // disabled
|
||||
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
// Get the precise size without any approximation heuristic
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size);
|
||||
ASSERT_NE(size, 0);
|
||||
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
// Get the size with an approximation heuristic
|
||||
uint64_t size2;
|
||||
const double error_margin = 0.2;
|
||||
size_approx_options.files_size_error_margin = error_margin;
|
||||
db_->GetApproximateSizes(size_approx_options, default_cf, &r, 1, &size2);
|
||||
ASSERT_LT(size2, size * (1 + error_margin));
|
||||
ASSERT_GT(size2, size * (1 - error_margin));
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that metadata is not falsely attributed only to the last data in
|
||||
// the file. (In some applications, filters can be large portion of data
|
||||
// size.)
|
||||
// Perform many queries over small range, enough to ensure crossing file
|
||||
// boundary, and make sure we never see a spike for large filter.
|
||||
for (int i = 0; i < 3000; i += 10) {
|
||||
const std::string start = Key(i);
|
||||
const std::string end = Key(i + 11); // overlap by 1 key
|
||||
const Range r(start, end);
|
||||
uint64_t size;
|
||||
db_->GetApproximateSizes(&r, 1, &size);
|
||||
ASSERT_LE(size, 11 * 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetApproximateMemTableStats) {
|
||||
@@ -1675,12 +1699,13 @@ TEST_F(DBTest, ApproximateSizes_MixOfSmallAndLarge) {
|
||||
ASSERT_TRUE(Between(Size("", Key(2), 1), 20000, 21000));
|
||||
ASSERT_TRUE(Between(Size("", Key(3), 1), 120000, 121000));
|
||||
ASSERT_TRUE(Between(Size("", Key(4), 1), 130000, 131000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 231000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 241000));
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540000, 541000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550000, 560000));
|
||||
ASSERT_TRUE(Between(Size("", Key(5), 1), 230000, 232000));
|
||||
ASSERT_TRUE(Between(Size("", Key(6), 1), 240000, 242000));
|
||||
// Ensure some overhead is accounted for, even without including all
|
||||
ASSERT_TRUE(Between(Size("", Key(7), 1), 540500, 545000));
|
||||
ASSERT_TRUE(Between(Size("", Key(8), 1), 550500, 555000));
|
||||
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110000, 111000));
|
||||
ASSERT_TRUE(Between(Size(Key(3), Key(5), 1), 110100, 111000));
|
||||
|
||||
dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1]);
|
||||
}
|
||||
@@ -3006,6 +3031,10 @@ class ModelDB : public DB {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetDbSessionId(std::string& /*session_id*/) const override {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
SequenceNumber GetLatestSequenceNumber() const override { return 0; }
|
||||
|
||||
bool SetPreserveDeletesSequenceNumber(SequenceNumber /*seqnum*/) override {
|
||||
|
||||
+388
-5
@@ -25,6 +25,127 @@ class DBTest2 : public DBTestBase {
|
||||
DBTest2() : DBTestBase("/db_test2") {}
|
||||
};
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
TEST_F(DBTest2, OpenForReadOnly) {
|
||||
DB* db_ptr = nullptr;
|
||||
std::string dbname = test::PerThreadDBPath("db_readonly");
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
// OpenForReadOnly should fail but will create <dbname> in the file system
|
||||
ASSERT_NOK(DB::OpenForReadOnly(options, dbname, &db_ptr));
|
||||
// Since <dbname> is created, we should be able to delete the dir
|
||||
// We first get the list files under <dbname>
|
||||
// There should not be any subdirectories -- this is not checked here
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||
for (auto& f : files) {
|
||||
if (f != "." && f != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
}
|
||||
// <dbname> should be empty now and we should be able to delete it
|
||||
ASSERT_OK(env_->DeleteDir(dbname));
|
||||
options.create_if_missing = false;
|
||||
// OpenForReadOnly should fail since <dbname> was successfully deleted
|
||||
ASSERT_NOK(DB::OpenForReadOnly(options, dbname, &db_ptr));
|
||||
// With create_if_missing false, there should not be a dir in the file system
|
||||
ASSERT_NOK(env_->FileExists(dbname));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, OpenForReadOnlyWithColumnFamilies) {
|
||||
DB* db_ptr = nullptr;
|
||||
std::string dbname = test::PerThreadDBPath("db_readonly");
|
||||
Options options = CurrentOptions();
|
||||
options.create_if_missing = true;
|
||||
|
||||
ColumnFamilyOptions cf_options(options);
|
||||
std::vector<ColumnFamilyDescriptor> column_families;
|
||||
column_families.push_back(
|
||||
ColumnFamilyDescriptor(kDefaultColumnFamilyName, cf_options));
|
||||
column_families.push_back(ColumnFamilyDescriptor("goku", cf_options));
|
||||
std::vector<ColumnFamilyHandle*> handles;
|
||||
// OpenForReadOnly should fail but will create <dbname> in the file system
|
||||
ASSERT_NOK(
|
||||
DB::OpenForReadOnly(options, dbname, column_families, &handles, &db_ptr));
|
||||
// Since <dbname> is created, we should be able to delete the dir
|
||||
// We first get the list files under <dbname>
|
||||
// There should not be any subdirectories -- this is not checked here
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(env_->GetChildren(dbname, &files));
|
||||
for (auto& f : files) {
|
||||
if (f != "." && f != "..") {
|
||||
ASSERT_OK(env_->DeleteFile(dbname + "/" + f));
|
||||
}
|
||||
}
|
||||
// <dbname> should be empty now and we should be able to delete it
|
||||
ASSERT_OK(env_->DeleteDir(dbname));
|
||||
options.create_if_missing = false;
|
||||
// OpenForReadOnly should fail since <dbname> was successfully deleted
|
||||
ASSERT_NOK(
|
||||
DB::OpenForReadOnly(options, dbname, column_families, &handles, &db_ptr));
|
||||
// With create_if_missing false, there should not be a dir in the file system
|
||||
ASSERT_NOK(env_->FileExists(dbname));
|
||||
}
|
||||
|
||||
class TestReadOnlyWithCompressedCache
|
||||
: public DBTestBase,
|
||||
public testing::WithParamInterface<std::tuple<int, bool>> {
|
||||
public:
|
||||
TestReadOnlyWithCompressedCache()
|
||||
: DBTestBase("/test_readonly_with_compressed_cache") {
|
||||
max_open_files_ = std::get<0>(GetParam());
|
||||
use_mmap_ = std::get<1>(GetParam());
|
||||
}
|
||||
int max_open_files_;
|
||||
bool use_mmap_;
|
||||
};
|
||||
|
||||
TEST_P(TestReadOnlyWithCompressedCache, ReadOnlyWithCompressedCache) {
|
||||
if (use_mmap_ && !IsMemoryMappedAccessSupported()) {
|
||||
return;
|
||||
}
|
||||
ASSERT_OK(Put("foo", "bar"));
|
||||
ASSERT_OK(Put("foo2", "barbarbarbarbarbarbarbar"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
DB* db_ptr = nullptr;
|
||||
Options options = CurrentOptions();
|
||||
options.allow_mmap_reads = use_mmap_;
|
||||
options.max_open_files = max_open_files_;
|
||||
options.compression = kSnappyCompression;
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_cache_compressed = NewLRUCache(8 * 1024 * 1024);
|
||||
table_options.no_block_cache = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
options.statistics = CreateDBStatistics();
|
||||
|
||||
ASSERT_OK(DB::OpenForReadOnly(options, dbname_, &db_ptr));
|
||||
|
||||
std::string v;
|
||||
ASSERT_OK(db_ptr->Get(ReadOptions(), "foo", &v));
|
||||
ASSERT_EQ("bar", v);
|
||||
ASSERT_EQ(0, options.statistics->getTickerCount(BLOCK_CACHE_COMPRESSED_HIT));
|
||||
ASSERT_OK(db_ptr->Get(ReadOptions(), "foo", &v));
|
||||
ASSERT_EQ("bar", v);
|
||||
if (Snappy_Supported()) {
|
||||
if (use_mmap_) {
|
||||
ASSERT_EQ(0,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_COMPRESSED_HIT));
|
||||
} else {
|
||||
ASSERT_EQ(1,
|
||||
options.statistics->getTickerCount(BLOCK_CACHE_COMPRESSED_HIT));
|
||||
}
|
||||
}
|
||||
|
||||
delete db_ptr;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(TestReadOnlyWithCompressedCache,
|
||||
TestReadOnlyWithCompressedCache,
|
||||
::testing::Combine(::testing::Values(-1, 100),
|
||||
::testing::Bool()));
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
class PrefixFullBloomWithReverseComparator
|
||||
: public DBTestBase,
|
||||
public ::testing::WithParamInterface<bool> {
|
||||
@@ -1268,6 +1389,152 @@ class CompactionCompressionListener : public EventListener {
|
||||
const Options* db_options_;
|
||||
};
|
||||
|
||||
enum CompressionFailureType {
|
||||
kTestCompressionFail,
|
||||
kTestDecompressionFail,
|
||||
kTestDecompressionCorruption
|
||||
};
|
||||
|
||||
class CompressionFailuresTest
|
||||
: public DBTest2,
|
||||
public testing::WithParamInterface<std::tuple<
|
||||
CompressionFailureType, CompressionType, uint32_t, uint32_t>> {
|
||||
public:
|
||||
CompressionFailuresTest() {
|
||||
std::tie(compression_failure_type_, compression_type_,
|
||||
compression_max_dict_bytes_, compression_parallel_threads_) =
|
||||
GetParam();
|
||||
}
|
||||
|
||||
CompressionFailureType compression_failure_type_ = kTestCompressionFail;
|
||||
CompressionType compression_type_ = kNoCompression;
|
||||
uint32_t compression_max_dict_bytes_ = 0;
|
||||
uint32_t compression_parallel_threads_ = 0;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
DBTest2, CompressionFailuresTest,
|
||||
::testing::Combine(::testing::Values(kTestCompressionFail,
|
||||
kTestDecompressionFail,
|
||||
kTestDecompressionCorruption),
|
||||
::testing::ValuesIn(GetSupportedCompressions()),
|
||||
::testing::Values(0, 10), ::testing::Values(1, 4)));
|
||||
|
||||
TEST_P(CompressionFailuresTest, CompressionFailures) {
|
||||
if (compression_type_ == kNoCompression) {
|
||||
return;
|
||||
}
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
options.max_bytes_for_level_base = 1024;
|
||||
options.max_bytes_for_level_multiplier = 2;
|
||||
options.num_levels = 7;
|
||||
options.max_background_compactions = 1;
|
||||
options.target_file_size_base = 512;
|
||||
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.block_size = 512;
|
||||
table_options.verify_compression = true;
|
||||
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||
|
||||
options.compression = compression_type_;
|
||||
options.compression_opts.parallel_threads = compression_parallel_threads_;
|
||||
options.compression_opts.max_dict_bytes = compression_max_dict_bytes_;
|
||||
options.bottommost_compression_opts.parallel_threads =
|
||||
compression_parallel_threads_;
|
||||
options.bottommost_compression_opts.max_dict_bytes =
|
||||
compression_max_dict_bytes_;
|
||||
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"BlockBasedTableBuilder::CompressBlockInternal:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
bool* ret = static_cast<bool*>(arg);
|
||||
*ret = false;
|
||||
});
|
||||
} else if (compression_failure_type_ == kTestDecompressionFail) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockContentsForCompressionType:TamperWithReturnValue",
|
||||
[](void* arg) {
|
||||
Status* ret = static_cast<Status*>(arg);
|
||||
ASSERT_OK(*ret);
|
||||
*ret = Status::Corruption("kTestDecompressionFail");
|
||||
});
|
||||
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"UncompressBlockContentsForCompressionType:"
|
||||
"TamperWithDecompressionOutput",
|
||||
[](void* arg) {
|
||||
BlockContents* contents = static_cast<BlockContents*>(arg);
|
||||
// Ensure uncompressed data != original data
|
||||
const size_t len = contents->data.size() + 1;
|
||||
std::unique_ptr<char[]> fake_data(new char[len]());
|
||||
*contents = BlockContents(std::move(fake_data), len);
|
||||
});
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> key_value_written;
|
||||
|
||||
const int kKeySize = 5;
|
||||
const int kValUnitSize = 16;
|
||||
const int kValSize = 256;
|
||||
Random rnd(405);
|
||||
|
||||
Status s = Status::OK();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Write 10 random files
|
||||
for (int i = 0; i < 10; i++) {
|
||||
for (int j = 0; j < 5; j++) {
|
||||
std::string key = RandomString(&rnd, kKeySize);
|
||||
// Ensure good compression ratio
|
||||
std::string valueUnit = RandomString(&rnd, kValUnitSize);
|
||||
std::string value;
|
||||
for (int k = 0; k < kValSize; k += kValUnitSize) {
|
||||
value += valueUnit;
|
||||
}
|
||||
s = Put(key, value);
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
key_value_written[key] = value;
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
}
|
||||
s = Flush();
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
s = dbfull()->TEST_WaitForCompact();
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
if (i == 4) {
|
||||
// Make compression fail at the mid of table building
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
}
|
||||
}
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
|
||||
if (compression_failure_type_ == kTestCompressionFail) {
|
||||
// Should be kNoCompression, check content consistency
|
||||
std::unique_ptr<Iterator> db_iter(db_->NewIterator(ReadOptions()));
|
||||
for (db_iter->SeekToFirst(); db_iter->Valid(); db_iter->Next()) {
|
||||
std::string key = db_iter->key().ToString();
|
||||
std::string value = db_iter->value().ToString();
|
||||
ASSERT_NE(key_value_written.find(key), key_value_written.end());
|
||||
ASSERT_EQ(key_value_written[key], value);
|
||||
key_value_written.erase(key);
|
||||
}
|
||||
ASSERT_EQ(0, key_value_written.size());
|
||||
} else if (compression_failure_type_ == kTestDecompressionFail) {
|
||||
ASSERT_EQ(std::string(s.getState()),
|
||||
"Could not decompress: kTestDecompressionFail");
|
||||
} else if (compression_failure_type_ == kTestDecompressionCorruption) {
|
||||
ASSERT_EQ(std::string(s.getState()),
|
||||
"Decompressed block did not match raw block");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, CompressionOptions) {
|
||||
if (!Zlib_Supported() || !Snappy_Supported()) {
|
||||
return;
|
||||
@@ -1801,6 +2068,10 @@ class MockPersistentCache : public PersistentCache {
|
||||
return PersistentCache::StatsType();
|
||||
}
|
||||
|
||||
uint64_t NewId() override {
|
||||
return last_id_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
Status Insert(const Slice& page_key, const char* data,
|
||||
const size_t size) override {
|
||||
MutexLock _(&lock_);
|
||||
@@ -1841,6 +2112,7 @@ class MockPersistentCache : public PersistentCache {
|
||||
const bool is_compressed_ = true;
|
||||
size_t size_ = 0;
|
||||
const size_t max_size_ = 10 * 1024; // 10KiB
|
||||
std::atomic<uint64_t> last_id_{1};
|
||||
};
|
||||
|
||||
#ifdef OS_LINUX
|
||||
@@ -1964,10 +2236,7 @@ TEST_F(DBTest2, TestPerfContextIterCpuTime) {
|
||||
}
|
||||
#endif // OS_LINUX
|
||||
|
||||
// GetUniqueIdFromFile is not implemented on these platforms. Persistent cache
|
||||
// breaks when that function is not implemented and no regular block cache is
|
||||
// provided.
|
||||
#if !defined(OS_SOLARIS) && !defined(OS_WIN)
|
||||
#if !defined OS_SOLARIS
|
||||
TEST_F(DBTest2, PersistentCache) {
|
||||
int num_iter = 80;
|
||||
|
||||
@@ -2031,7 +2300,7 @@ TEST_F(DBTest2, PersistentCache) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !defined(OS_SOLARIS) && !defined(OS_WIN)
|
||||
#endif // !defined OS_SOLARIS
|
||||
|
||||
namespace {
|
||||
void CountSyncPoint() {
|
||||
@@ -2688,6 +2957,94 @@ TEST_F(DBTest2, OptimizeForSmallDB) {
|
||||
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
TEST_F(DBTest2, IterRaceFlush1) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::NewIterator:1", "DBTest2::IterRaceFlush:1"},
|
||||
{"DBTest2::IterRaceFlush:2", "DBImpl::NewIterator:2"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush:2");
|
||||
});
|
||||
|
||||
// iterator is created after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Seek("foo");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("foo", it->key().ToString());
|
||||
}
|
||||
|
||||
t1.join();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, IterRaceFlush2) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"DBImpl::NewIterator:3", "DBTest2::IterRaceFlush2:1"},
|
||||
{"DBTest2::IterRaceFlush2:2", "DBImpl::NewIterator:4"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush2:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::IterRaceFlush2:2");
|
||||
});
|
||||
|
||||
// iterator is created after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Seek("foo");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("foo", it->key().ToString());
|
||||
}
|
||||
|
||||
t1.join();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, IterRefreshRaceFlush) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
|
||||
{{"ArenaWrappedDBIter::Refresh:1", "DBTest2::IterRefreshRaceFlush:1"},
|
||||
{"DBTest2::IterRefreshRaceFlush:2", "ArenaWrappedDBIter::Refresh:2"}});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
ROCKSDB_NAMESPACE::port::Thread t1([&] {
|
||||
TEST_SYNC_POINT("DBTest2::IterRefreshRaceFlush:1");
|
||||
ASSERT_OK(Put("foo", "v2"));
|
||||
Flush();
|
||||
TEST_SYNC_POINT("DBTest2::IterRefreshRaceFlush:2");
|
||||
});
|
||||
|
||||
// iterator is created after the first Put(), so it should see either
|
||||
// "v1" or "v2".
|
||||
{
|
||||
std::unique_ptr<Iterator> it(db_->NewIterator(ReadOptions()));
|
||||
it->Refresh();
|
||||
it->Seek("foo");
|
||||
ASSERT_TRUE(it->Valid());
|
||||
ASSERT_EQ("foo", it->key().ToString());
|
||||
}
|
||||
|
||||
t1.join();
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, GetRaceFlush1) {
|
||||
ASSERT_OK(Put("foo", "v1"));
|
||||
|
||||
@@ -4717,6 +5074,32 @@ TEST_F(DBTest2, AutoPrefixMode1) {
|
||||
}
|
||||
}
|
||||
#endif // ROCKSDB_LITE
|
||||
|
||||
// WAL recovery mode is WALRecoveryMode::kPointInTimeRecovery.
|
||||
TEST_F(DBTest2, PointInTimeRecoveryWithIOErrorWhileReadingWal) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value0"));
|
||||
Close();
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
bool should_inject_error = false;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::RecoverLogFiles:BeforeReadWal",
|
||||
[&](void* /*arg*/) { should_inject_error = true; });
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"LogReader::ReadMore:AfterReadFile", [&](void* arg) {
|
||||
if (should_inject_error) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*reinterpret_cast<Status*>(arg) = Status::IOError("Injected IOError");
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
options.avoid_flush_during_recovery = true;
|
||||
options.wal_recovery_mode = WALRecoveryMode::kPointInTimeRecovery;
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsIOError());
|
||||
}
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
@@ -1953,6 +1953,7 @@ TEST_F(DBTestUniversalCompaction2, BasicL0toL1) {
|
||||
ASSERT_GT(NumTableFilesAtLevel(6), 0);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_SINGLE_LEVEL_DTC)
|
||||
TEST_F(DBTestUniversalCompaction2, SingleLevel) {
|
||||
const int kNumKeys = 3000;
|
||||
const int kWindowSize = 100;
|
||||
@@ -1991,6 +1992,7 @@ TEST_F(DBTestUniversalCompaction2, SingleLevel) {
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_EQ(1, NumTableFilesAtLevel(0));
|
||||
}
|
||||
#endif // ENABLE_SINGLE_LEVEL_DTC
|
||||
|
||||
TEST_F(DBTestUniversalCompaction2, MultipleLevels) {
|
||||
const int kWindowSize = 100;
|
||||
|
||||
@@ -449,6 +449,65 @@ TEST_F(DBBasicTestWithTimestamp, MaxKeysSkipped) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Create two L0, and compact them to a new L1. In this test, L1 is L_bottom.
|
||||
// Two L0s:
|
||||
// f1 f2
|
||||
// <a, 1, kTypeValue> <a, 3, kTypeDeletionWithTimestamp>...<b, 2, kTypeValue>
|
||||
// Since f2.smallest < f1.largest < f2.largest
|
||||
// f1 and f2 will be the inputs of a real compaction instead of trivial move.
|
||||
TEST_F(DBBasicTestWithTimestamp, CompactDeletionWithTimestampMarkerToBottom) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.num_levels = 2;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
DestroyAndReopen(options);
|
||||
WriteOptions write_opts;
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "a", "value0"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Put(write_opts, "b", "value0"));
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
write_opts.timestamp = &ts;
|
||||
ASSERT_OK(db_->Delete(write_opts, "a"));
|
||||
ASSERT_OK(Flush());
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
|
||||
ReadOptions read_opts;
|
||||
ts_str = Timestamp(1, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
std::string value;
|
||||
Status s = db_->Get(read_opts, "a", &value);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("value0", value);
|
||||
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
s = db_->Get(read_opts, "a", &value);
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
|
||||
// Time-travel to the past before deletion
|
||||
ts_str = Timestamp(2, 0);
|
||||
ts = ts_str;
|
||||
read_opts.timestamp = &ts;
|
||||
s = db_->Get(read_opts, "a", &value);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("value0", value);
|
||||
Close();
|
||||
}
|
||||
|
||||
class DBBasicTestWithTimestampCompressionSettings
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
@@ -536,6 +595,104 @@ TEST_P(DBBasicTestWithTimestampCompressionSettings, PutAndGet) {
|
||||
Close();
|
||||
}
|
||||
|
||||
TEST_P(DBBasicTestWithTimestampCompressionSettings, PutDeleteGet) {
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
options.create_if_missing = true;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
const int kNumKeysPerFile = 1024;
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<0>(GetParam());
|
||||
bbto.whole_key_filtering = true;
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
|
||||
const CompressionType comp_type = std::get<1>(GetParam());
|
||||
#if LZ4_VERSION_NUMBER < 10400 // r124+
|
||||
if (comp_type == kLZ4Compression || comp_type == kLZ4HCCompression) {
|
||||
return;
|
||||
}
|
||||
#endif // LZ4_VERSION_NUMBER >= 10400
|
||||
if (!ZSTD_Supported() && comp_type == kZSTD) {
|
||||
return;
|
||||
}
|
||||
if (!Zlib_Supported() && comp_type == kZlibCompression) {
|
||||
return;
|
||||
}
|
||||
|
||||
options.compression = comp_type;
|
||||
options.compression_opts.max_dict_bytes = std::get<2>(GetParam());
|
||||
if (comp_type == kZSTD) {
|
||||
options.compression_opts.zstd_max_train_bytes = std::get<2>(GetParam());
|
||||
}
|
||||
options.compression_opts.parallel_threads = std::get<3>(GetParam());
|
||||
options.target_file_size_base = 1 << 26; // 64MB
|
||||
|
||||
DestroyAndReopen(options);
|
||||
|
||||
const size_t kNumL0Files =
|
||||
static_cast<size_t>(Options().level0_file_num_compaction_trigger);
|
||||
{
|
||||
// Generate enough L0 files with ts=1 to trigger compaction to L1
|
||||
std::string ts_str = Timestamp(1, 0);
|
||||
Slice ts = ts_str;
|
||||
WriteOptions wopts;
|
||||
wopts.timestamp = &ts;
|
||||
for (size_t i = 0; i != kNumL0Files; ++i) {
|
||||
for (int j = 0; j != kNumKeysPerFile; ++j) {
|
||||
ASSERT_OK(db_->Put(wopts, Key1(j), "value" + std::to_string(i)));
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
// Generate another L0 at ts=3
|
||||
ts_str = Timestamp(3, 0);
|
||||
ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
for (int i = 0; i != kNumKeysPerFile; ++i) {
|
||||
std::string key_str = Key1(i);
|
||||
Slice key(key_str);
|
||||
if ((i % 3) == 0) {
|
||||
ASSERT_OK(db_->Delete(wopts, key));
|
||||
} else {
|
||||
ASSERT_OK(db_->Put(wopts, key, "new_value"));
|
||||
}
|
||||
}
|
||||
ASSERT_OK(db_->Flush(FlushOptions()));
|
||||
// Populate memtable at ts=5
|
||||
ts_str = Timestamp(5, 0);
|
||||
ts = ts_str;
|
||||
wopts.timestamp = &ts;
|
||||
for (int i = 0; i != kNumKeysPerFile; ++i) {
|
||||
std::string key_str = Key1(i);
|
||||
Slice key(key_str);
|
||||
if ((i % 3) == 1) {
|
||||
ASSERT_OK(db_->Delete(wopts, key));
|
||||
} else if ((i % 3) == 2) {
|
||||
ASSERT_OK(db_->Put(wopts, key, "new_value_2"));
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::string ts_str = Timestamp(6, 0);
|
||||
Slice ts = ts_str;
|
||||
ReadOptions ropts;
|
||||
ropts.timestamp = &ts;
|
||||
for (uint64_t i = 0; i != static_cast<uint64_t>(kNumKeysPerFile); ++i) {
|
||||
std::string value;
|
||||
Status s = db_->Get(ropts, Key1(i), &value);
|
||||
if ((i % 3) == 2) {
|
||||
ASSERT_OK(s);
|
||||
ASSERT_EQ("new_value_2", value);
|
||||
} else {
|
||||
ASSERT_TRUE(s.IsNotFound());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
// A class which remembers the name of each flushed file.
|
||||
class FlushedFileCollector : public EventListener {
|
||||
@@ -870,15 +1027,10 @@ TEST_P(DBBasicTestWithTimestampPrefixSeek, ForwardIterateWithPrefix) {
|
||||
for (size_t i = 0; i != write_ts_list.size(); ++i) {
|
||||
Slice write_ts = write_ts_list[i];
|
||||
write_opts.timestamp = &write_ts;
|
||||
uint64_t key = kMinKey;
|
||||
do {
|
||||
for (uint64_t key = kMaxKey; key >= kMinKey; --key) {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(i));
|
||||
ASSERT_OK(s);
|
||||
if (key == kMaxKey) {
|
||||
break;
|
||||
}
|
||||
++key;
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
const std::vector<std::string> read_ts_list = {Timestamp(5, 0xffffffff),
|
||||
@@ -962,6 +1114,90 @@ INSTANTIATE_TEST_CASE_P(
|
||||
false))),
|
||||
::testing::Bool()));
|
||||
|
||||
class DBBasicTestWithTsIterTombstones
|
||||
: public DBBasicTestWithTimestampBase,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<std::shared_ptr<const SliceTransform>,
|
||||
std::shared_ptr<const FilterPolicy>, int>> {
|
||||
public:
|
||||
DBBasicTestWithTsIterTombstones()
|
||||
: DBBasicTestWithTimestampBase("/db_basic_ts_iter_tombstones") {}
|
||||
};
|
||||
|
||||
TEST_P(DBBasicTestWithTsIterTombstones, ForwardIterDelete) {
|
||||
constexpr size_t kNumKeysPerFile = 128;
|
||||
Options options = CurrentOptions();
|
||||
options.env = env_;
|
||||
const size_t kTimestampSize = Timestamp(0, 0).size();
|
||||
TestComparator test_cmp(kTimestampSize);
|
||||
options.comparator = &test_cmp;
|
||||
options.prefix_extractor = std::get<0>(GetParam());
|
||||
options.memtable_factory.reset(new SpecialSkipListFactory(kNumKeysPerFile));
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.filter_policy = std::get<1>(GetParam());
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(bbto));
|
||||
options.num_levels = std::get<2>(GetParam());
|
||||
DestroyAndReopen(options);
|
||||
std::vector<std::string> write_ts_strs = {Timestamp(2, 0), Timestamp(4, 0)};
|
||||
constexpr uint64_t kMaxKey = 0xffffffffffffffff;
|
||||
constexpr uint64_t kMinKey = 0xfffffffffffff000;
|
||||
// Insert kMinKey...kMaxKey
|
||||
uint64_t key = kMinKey;
|
||||
WriteOptions write_opts;
|
||||
Slice ts = write_ts_strs[0];
|
||||
write_opts.timestamp = &ts;
|
||||
do {
|
||||
Status s = db_->Put(write_opts, Key1(key), "value" + std::to_string(key));
|
||||
ASSERT_OK(s);
|
||||
if (kMaxKey == key) {
|
||||
break;
|
||||
}
|
||||
++key;
|
||||
} while (true);
|
||||
// Delete them all
|
||||
ts = write_ts_strs[1];
|
||||
write_opts.timestamp = &ts;
|
||||
for (key = kMaxKey; key >= kMinKey; --key) {
|
||||
Status s;
|
||||
if (0 != (key % 2)) {
|
||||
s = db_->Put(write_opts, Key1(key), "value1" + std::to_string(key));
|
||||
} else {
|
||||
s = db_->Delete(write_opts, Key1(key));
|
||||
}
|
||||
ASSERT_OK(s);
|
||||
}
|
||||
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
||||
{
|
||||
std::string read_ts = Timestamp(4, 0);
|
||||
ts = read_ts;
|
||||
ReadOptions read_opts;
|
||||
read_opts.total_order_seek = true;
|
||||
read_opts.timestamp = &ts;
|
||||
std::unique_ptr<Iterator> iter(db_->NewIterator(read_opts));
|
||||
size_t count = 0;
|
||||
key = kMinKey + 1;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next(), ++count, key += 2) {
|
||||
ASSERT_EQ(Key1(key), iter->key());
|
||||
ASSERT_EQ("value1" + std::to_string(key), iter->value());
|
||||
}
|
||||
ASSERT_EQ((kMaxKey - kMinKey + 1) / 2, count);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
Timestamp, DBBasicTestWithTsIterTombstones,
|
||||
::testing::Combine(
|
||||
::testing::Values(
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(7)),
|
||||
std::shared_ptr<const SliceTransform>(NewFixedPrefixTransform(8))),
|
||||
::testing::Values(std::shared_ptr<const FilterPolicy>(nullptr),
|
||||
std::shared_ptr<const FilterPolicy>(
|
||||
NewBloomFilterPolicy(10, false)),
|
||||
std::shared_ptr<const FilterPolicy>(
|
||||
NewBloomFilterPolicy(20, false))),
|
||||
::testing::Values(2, 6)));
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ namespace ROCKSDB_NAMESPACE {
|
||||
// and the value type is embedded as the low 8 bits in the sequence
|
||||
// number in internal keys, we need to use the highest-numbered
|
||||
// ValueType, not the lowest).
|
||||
const ValueType kValueTypeForSeek = kTypeBlobIndex;
|
||||
const ValueType kValueTypeForSeek = kTypeDeletionWithTimestamp;
|
||||
const ValueType kValueTypeForSeekForPrev = kTypeDeletion;
|
||||
|
||||
uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {
|
||||
|
||||
+4
-2
@@ -69,7 +69,8 @@ enum ValueType : unsigned char {
|
||||
// generated by WriteUnprepared write policy is not mistakenly read by
|
||||
// another.
|
||||
kTypeBeginUnprepareXID = 0x13, // WAL only.
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
kTypeDeletionWithTimestamp = 0x14,
|
||||
kMaxValue = 0x7F // Not used for storing records.
|
||||
};
|
||||
|
||||
// Defined in dbformat.cc
|
||||
@@ -79,7 +80,8 @@ extern const ValueType kValueTypeForSeekForPrev;
|
||||
// Checks whether a type is an inline value type
|
||||
// (i.e. a type used in memtable skiplist and sst file datablock).
|
||||
inline bool IsValueType(ValueType t) {
|
||||
return t <= kTypeMerge || t == kTypeSingleDeletion || t == kTypeBlobIndex;
|
||||
return t <= kTypeMerge || t == kTypeSingleDeletion || t == kTypeBlobIndex ||
|
||||
kTypeDeletionWithTimestamp == t;
|
||||
}
|
||||
|
||||
// Checks whether a type is from user operation
|
||||
|
||||
+3
-1
@@ -121,7 +121,9 @@ void EventHelpers::LogAndNotifyTableFileCreationFinished(
|
||||
<< table_properties.compression_options << "creation_time"
|
||||
<< table_properties.creation_time << "oldest_key_time"
|
||||
<< table_properties.oldest_key_time << "file_creation_time"
|
||||
<< table_properties.file_creation_time;
|
||||
<< table_properties.file_creation_time << "db_id"
|
||||
<< table_properties.db_id << "db_session_id"
|
||||
<< table_properties.db_session_id;
|
||||
|
||||
// user collected properties
|
||||
for (const auto& prop : table_properties.readable_properties) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "db/db_test_util.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
@@ -41,6 +42,29 @@ class ExternalSSTFileBasicTest
|
||||
return db_->IngestExternalFile(files, opts);
|
||||
}
|
||||
|
||||
Status AddFileWithFileChecksum(
|
||||
const std::vector<std::string>& files,
|
||||
const std::vector<std::string>& files_checksums,
|
||||
const std::vector<std::string>& files_checksum_func_names,
|
||||
bool verify_file_checksum = true, bool move_files = false,
|
||||
bool skip_snapshot_check = false, bool write_global_seqno = true) {
|
||||
IngestExternalFileOptions opts;
|
||||
opts.move_files = move_files;
|
||||
opts.snapshot_consistency = !skip_snapshot_check;
|
||||
opts.allow_global_seqno = false;
|
||||
opts.allow_blocking_flush = false;
|
||||
opts.write_global_seqno = write_global_seqno;
|
||||
opts.verify_file_checksum = verify_file_checksum;
|
||||
|
||||
IngestExternalFileArg arg;
|
||||
arg.column_family = db_->DefaultColumnFamily();
|
||||
arg.external_files = files;
|
||||
arg.options = opts;
|
||||
arg.files_checksums = files_checksums;
|
||||
arg.files_checksum_func_names = files_checksum_func_names;
|
||||
return db_->IngestExternalFiles({arg});
|
||||
}
|
||||
|
||||
Status GenerateAndAddExternalFile(
|
||||
const Options options, std::vector<int> keys,
|
||||
const std::vector<ValueType>& value_types,
|
||||
@@ -174,6 +198,8 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
ASSERT_EQ(file1_info.num_range_del_entries, 0);
|
||||
ASSERT_EQ(file1_info.smallest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.largest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.file_checksum, kUnknownFileChecksum);
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, kUnknownFileChecksumFuncName);
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Put(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
@@ -192,6 +218,369 @@ TEST_F(ExternalSSTFileBasicTest, Basic) {
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
class ChecksumVerifyHelper {
|
||||
private:
|
||||
Options options_;
|
||||
|
||||
public:
|
||||
ChecksumVerifyHelper(Options& options) : options_(options) {}
|
||||
~ChecksumVerifyHelper() {}
|
||||
|
||||
Status GetSingleFileChecksumAndFuncName(
|
||||
const std::string& file_path, std::string* file_checksum,
|
||||
std::string* file_checksum_func_name) {
|
||||
Status s;
|
||||
EnvOptions soptions;
|
||||
std::unique_ptr<SequentialFile> file_reader;
|
||||
s = options_.env->NewSequentialFile(file_path, &file_reader, soptions);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
std::unique_ptr<char[]> scratch(new char[2048]);
|
||||
Slice result;
|
||||
FileChecksumGenFactory* file_checksum_gen_factory =
|
||||
options_.file_checksum_gen_factory.get();
|
||||
if (file_checksum_gen_factory == nullptr) {
|
||||
*file_checksum = kUnknownFileChecksum;
|
||||
*file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
return Status::OK();
|
||||
} else {
|
||||
FileChecksumGenContext gen_context;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
file_checksum_gen_factory->CreateFileChecksumGenerator(gen_context);
|
||||
*file_checksum_func_name = file_checksum_gen->Name();
|
||||
s = file_reader->Read(2048, &result, scratch.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
while (result.size() != 0) {
|
||||
file_checksum_gen->Update(scratch.get(), result.size());
|
||||
s = file_reader->Read(2048, &result, scratch.get());
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
file_checksum_gen->Finalize();
|
||||
*file_checksum = file_checksum_gen->GetChecksum();
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, BasicWithFileChecksumCrc32c) {
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
ChecksumVerifyHelper checksum_helper(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// Current file size should be 0 after sst_file_writer init and before open a
|
||||
// file.
|
||||
ASSERT_EQ(sst_file_writer.FileSize(), 0);
|
||||
|
||||
// file1.sst (0 => 99)
|
||||
std::string file1 = sst_files_dir_ + "file1.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::string file_checksum, file_checksum_func_name;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file1, &file_checksum, &file_checksum_func_name));
|
||||
|
||||
// Current file size should be non-zero after success write.
|
||||
ASSERT_GT(sst_file_writer.FileSize(), 0);
|
||||
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(0));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(99));
|
||||
ASSERT_EQ(file1_info.num_range_del_entries, 0);
|
||||
ASSERT_EQ(file1_info.smallest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.largest_range_del_key, "");
|
||||
ASSERT_EQ(file1_info.file_checksum, file_checksum);
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name);
|
||||
// sst_file_writer already finished, cannot add this value
|
||||
s = sst_file_writer.Put(Key(100), "bad_val");
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
s = sst_file_writer.DeleteRange(Key(100), Key(200));
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
DestroyAndReopen(options);
|
||||
// Add file using file path
|
||||
s = DeprecatedAddFile({file1});
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(db_->GetLatestSequenceNumber(), 0U);
|
||||
for (int k = 0; k < 100; k++) {
|
||||
ASSERT_EQ(Get(Key(k)), Key(k) + "_val");
|
||||
}
|
||||
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, IngestFileWithFileChecksum) {
|
||||
Options old_options = CurrentOptions();
|
||||
Options options = CurrentOptions();
|
||||
options.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory();
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
ChecksumVerifyHelper checksum_helper(options);
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// file01.sst (1000 => 1099)
|
||||
std::string file1 = sst_files_dir_ + "file01.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file1));
|
||||
for (int k = 1000; k < 1100; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file1_info;
|
||||
Status s = sst_file_writer.Finish(&file1_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file1_info.file_path, file1);
|
||||
ASSERT_EQ(file1_info.num_entries, 100);
|
||||
ASSERT_EQ(file1_info.smallest_key, Key(1000));
|
||||
ASSERT_EQ(file1_info.largest_key, Key(1099));
|
||||
std::string file_checksum1, file_checksum_func_name1;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file1, &file_checksum1, &file_checksum_func_name1));
|
||||
ASSERT_EQ(file1_info.file_checksum, file_checksum1);
|
||||
ASSERT_EQ(file1_info.file_checksum_func_name, file_checksum_func_name1);
|
||||
|
||||
// file02.sst (1100 => 1299)
|
||||
std::string file2 = sst_files_dir_ + "file02.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file2));
|
||||
for (int k = 1100; k < 1300; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val"));
|
||||
}
|
||||
ExternalSstFileInfo file2_info;
|
||||
s = sst_file_writer.Finish(&file2_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file2_info.file_path, file2);
|
||||
ASSERT_EQ(file2_info.num_entries, 200);
|
||||
ASSERT_EQ(file2_info.smallest_key, Key(1100));
|
||||
ASSERT_EQ(file2_info.largest_key, Key(1299));
|
||||
std::string file_checksum2, file_checksum_func_name2;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file2, &file_checksum2, &file_checksum_func_name2));
|
||||
ASSERT_EQ(file2_info.file_checksum, file_checksum2);
|
||||
ASSERT_EQ(file2_info.file_checksum_func_name, file_checksum_func_name2);
|
||||
|
||||
// file03.sst (1300 => 1499)
|
||||
std::string file3 = sst_files_dir_ + "file03.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file3));
|
||||
for (int k = 1300; k < 1500; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file3_info;
|
||||
s = sst_file_writer.Finish(&file3_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file3_info.file_path, file3);
|
||||
ASSERT_EQ(file3_info.num_entries, 200);
|
||||
ASSERT_EQ(file3_info.smallest_key, Key(1300));
|
||||
ASSERT_EQ(file3_info.largest_key, Key(1499));
|
||||
std::string file_checksum3, file_checksum_func_name3;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file3, &file_checksum3, &file_checksum_func_name3));
|
||||
ASSERT_EQ(file3_info.file_checksum, file_checksum3);
|
||||
ASSERT_EQ(file3_info.file_checksum_func_name, file_checksum_func_name3);
|
||||
|
||||
// file04.sst (1500 => 1799)
|
||||
std::string file4 = sst_files_dir_ + "file04.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file4));
|
||||
for (int k = 1500; k < 1800; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file4_info;
|
||||
s = sst_file_writer.Finish(&file4_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file4_info.file_path, file4);
|
||||
ASSERT_EQ(file4_info.num_entries, 300);
|
||||
ASSERT_EQ(file4_info.smallest_key, Key(1500));
|
||||
ASSERT_EQ(file4_info.largest_key, Key(1799));
|
||||
std::string file_checksum4, file_checksum_func_name4;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file4, &file_checksum4, &file_checksum_func_name4));
|
||||
ASSERT_EQ(file4_info.file_checksum, file_checksum4);
|
||||
ASSERT_EQ(file4_info.file_checksum_func_name, file_checksum_func_name4);
|
||||
|
||||
// file05.sst (1800 => 1899)
|
||||
std::string file5 = sst_files_dir_ + "file05.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file5));
|
||||
for (int k = 1800; k < 2000; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file5_info;
|
||||
s = sst_file_writer.Finish(&file5_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file5_info.file_path, file5);
|
||||
ASSERT_EQ(file5_info.num_entries, 200);
|
||||
ASSERT_EQ(file5_info.smallest_key, Key(1800));
|
||||
ASSERT_EQ(file5_info.largest_key, Key(1999));
|
||||
std::string file_checksum5, file_checksum_func_name5;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file5, &file_checksum5, &file_checksum_func_name5));
|
||||
ASSERT_EQ(file5_info.file_checksum, file_checksum5);
|
||||
ASSERT_EQ(file5_info.file_checksum_func_name, file_checksum_func_name5);
|
||||
|
||||
// file06.sst (2000 => 2199)
|
||||
std::string file6 = sst_files_dir_ + "file06.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(file6));
|
||||
for (int k = 2000; k < 2200; k++) {
|
||||
ASSERT_OK(sst_file_writer.Put(Key(k), Key(k) + "_val_overlap"));
|
||||
}
|
||||
ExternalSstFileInfo file6_info;
|
||||
s = sst_file_writer.Finish(&file6_info);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_EQ(file6_info.file_path, file6);
|
||||
ASSERT_EQ(file6_info.num_entries, 200);
|
||||
ASSERT_EQ(file6_info.smallest_key, Key(2000));
|
||||
ASSERT_EQ(file6_info.largest_key, Key(2199));
|
||||
std::string file_checksum6, file_checksum_func_name6;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
file6, &file_checksum6, &file_checksum_func_name6));
|
||||
ASSERT_EQ(file6_info.file_checksum, file_checksum6);
|
||||
ASSERT_EQ(file6_info.file_checksum_func_name, file_checksum_func_name6);
|
||||
|
||||
s = AddFileWithFileChecksum({file1}, {file_checksum1, "xyz"},
|
||||
{file_checksum1}, true, false, false, false);
|
||||
// does not care the checksum input since db does not enable file checksum
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file1));
|
||||
std::vector<LiveFileMetaData> live_files;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files);
|
||||
std::set<std::string> set1;
|
||||
for (auto f : live_files) {
|
||||
set1.insert(f.name);
|
||||
ASSERT_EQ(f.file_checksum, kUnknownFileChecksum);
|
||||
ASSERT_EQ(f.file_checksum_func_name, kUnknownFileChecksumFuncName);
|
||||
}
|
||||
|
||||
// Reopen Db with checksum enabled
|
||||
Reopen(options);
|
||||
// Enable verify_file_checksum option
|
||||
// The checksum vector does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file2}, {file_checksum2, "xyz"},
|
||||
{file_checksum_func_name2}, true, false, false,
|
||||
false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// The checksum name does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file2}, {file_checksum2}, {"xyz"}, true, false,
|
||||
false, false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// The checksum itself does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file2}, {"xyz"}, {file_checksum_func_name2},
|
||||
true, false, false, false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// All matches, ingestion is successful
|
||||
s = AddFileWithFileChecksum({file2}, {file_checksum2},
|
||||
{file_checksum_func_name2}, true, false, false,
|
||||
false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files1;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files1);
|
||||
for (auto f : live_files1) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_EQ(f.file_checksum, file_checksum2);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name2);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_OK(env_->FileExists(file2));
|
||||
|
||||
// Enable verify_file_checksum option
|
||||
// No checksum information is provided, generate it when ingesting
|
||||
std::vector<std::string> checksum, checksum_func;
|
||||
s = AddFileWithFileChecksum({file3}, checksum, checksum_func, true, false,
|
||||
false, false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files2;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files2);
|
||||
for (auto f : live_files2) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_EQ(f.file_checksum, file_checksum3);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name3);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file3));
|
||||
|
||||
// Does not enable verify_file_checksum options
|
||||
// The checksum name does not match, fail the ingestion
|
||||
s = AddFileWithFileChecksum({file4}, {file_checksum4}, {"xyz"}, false, false,
|
||||
false, false);
|
||||
ASSERT_FALSE(s.ok()) << s.ToString();
|
||||
|
||||
// Does not enable verify_file_checksum options
|
||||
// Checksum function name matches, store the checksum being ingested.
|
||||
s = AddFileWithFileChecksum({file4}, {"asd"}, {file_checksum_func_name4},
|
||||
false, false, false, false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files3;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files3);
|
||||
for (auto f : live_files3) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_FALSE(f.file_checksum == file_checksum4);
|
||||
ASSERT_EQ(f.file_checksum, "asd");
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name4);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file4));
|
||||
|
||||
// enable verify_file_checksum options, DB enable checksum, and enable
|
||||
// write_global_seq. So the checksum stored is different from the one
|
||||
// ingested due to the sequence number changes.
|
||||
s = AddFileWithFileChecksum({file5}, {file_checksum5},
|
||||
{file_checksum_func_name5}, true, false, false,
|
||||
true);
|
||||
ASSERT_OK(s);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files4;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files4);
|
||||
for (auto f : live_files4) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
std::string cur_checksum5, cur_checksum_func_name5;
|
||||
ASSERT_OK(checksum_helper.GetSingleFileChecksumAndFuncName(
|
||||
dbname_ + f.name, &cur_checksum5, &cur_checksum_func_name5));
|
||||
ASSERT_EQ(f.file_checksum, cur_checksum5);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name5);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file5));
|
||||
|
||||
// Does not enable verify_file_checksum options and also the ingested file
|
||||
// checksum information is empty. DB will generate and store the checksum
|
||||
// in Manifest.
|
||||
std::vector<std::string> files_c6, files_name6;
|
||||
s = AddFileWithFileChecksum({file6}, files_c6, files_name6, false, false,
|
||||
false, false);
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
std::vector<LiveFileMetaData> live_files6;
|
||||
dbfull()->GetLiveFilesMetaData(&live_files6);
|
||||
for (auto f : live_files6) {
|
||||
if (set1.find(f.name) == set1.end()) {
|
||||
ASSERT_EQ(f.file_checksum, file_checksum6);
|
||||
ASSERT_EQ(f.file_checksum_func_name, file_checksum_func_name6);
|
||||
set1.insert(f.name);
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(s.ok()) << s.ToString();
|
||||
ASSERT_OK(env_->FileExists(file6));
|
||||
}
|
||||
|
||||
TEST_F(ExternalSSTFileBasicTest, NoCopy) {
|
||||
Options options = CurrentOptions();
|
||||
const ImmutableCFOptions ioptions(options);
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
Status ExternalSstFileIngestionJob::Prepare(
|
||||
const std::vector<std::string>& external_files_paths,
|
||||
const std::vector<std::string>& files_checksums,
|
||||
const std::vector<std::string>& files_checksum_func_names,
|
||||
uint64_t next_file_number, SuperVersion* sv) {
|
||||
Status status;
|
||||
|
||||
@@ -68,7 +70,7 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
info2->smallest_internal_key) < 0;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < num_files - 1; i++) {
|
||||
for (size_t i = 0; i + 1 < num_files; i++) {
|
||||
if (sstableKeyCompare(ucmp, sorted_files[i]->largest_internal_key,
|
||||
sorted_files[i + 1]->smallest_internal_key) >= 0) {
|
||||
files_overlap_ = true;
|
||||
@@ -142,6 +144,9 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
break;
|
||||
}
|
||||
f.internal_file_path = path_inside_db;
|
||||
// Initialize the checksum information of ingested files.
|
||||
f.file_checksum = kUnknownFileChecksum;
|
||||
f.file_checksum_func_name = kUnknownFileChecksumFuncName;
|
||||
ingestion_path_ids.insert(f.fd.GetPathId());
|
||||
}
|
||||
|
||||
@@ -160,6 +165,128 @@ Status ExternalSstFileIngestionJob::Prepare(
|
||||
}
|
||||
TEST_SYNC_POINT("ExternalSstFileIngestionJob::AfterSyncDir");
|
||||
|
||||
// Generate and check the sst file checksum. Note that, if
|
||||
// IngestExternalFileOptions::write_global_seqno is true, we will not update
|
||||
// the checksum information in the files_to_ingests_ here, since the file is
|
||||
// upadted with the new global_seqno. After global_seqno is updated, DB will
|
||||
// generate the new checksum and store it in the Manifest. In all other cases
|
||||
// if ingestion_options_.write_global_seqno == true and
|
||||
// verify_file_checksum is false, we only check the checksum function name.
|
||||
if (status.ok() && db_options_.file_checksum_gen_factory != nullptr) {
|
||||
if (ingestion_options_.verify_file_checksum == false &&
|
||||
files_checksums.size() == files_to_ingest_.size() &&
|
||||
files_checksum_func_names.size() == files_to_ingest_.size()) {
|
||||
// Only when verify_file_checksum == false and the checksum for ingested
|
||||
// files are provided, DB will use the provided checksum and does not
|
||||
// generate the checksum for ingested files.
|
||||
need_generate_file_checksum_ = false;
|
||||
} else {
|
||||
need_generate_file_checksum_ = true;
|
||||
}
|
||||
FileChecksumGenContext gen_context;
|
||||
std::unique_ptr<FileChecksumGenerator> file_checksum_gen =
|
||||
db_options_.file_checksum_gen_factory->CreateFileChecksumGenerator(
|
||||
gen_context);
|
||||
std::vector<std::string> generated_checksums;
|
||||
std::vector<std::string> generated_checksum_func_names;
|
||||
// Step 1: generate the checksum for ingested sst file.
|
||||
if (need_generate_file_checksum_) {
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
|
||||
std::string generated_checksum, generated_checksum_func_name;
|
||||
IOStatus io_s = GenerateOneFileChecksum(
|
||||
fs_, files_to_ingest_[i].internal_file_path,
|
||||
db_options_.file_checksum_gen_factory.get(), &generated_checksum,
|
||||
&generated_checksum_func_name,
|
||||
ingestion_options_.verify_checksums_readahead_size,
|
||||
db_options_.allow_mmap_reads);
|
||||
if (!io_s.ok()) {
|
||||
status = io_s;
|
||||
ROCKS_LOG_WARN(db_options_.info_log,
|
||||
"Sst file checksum generation of file: %s failed: %s",
|
||||
files_to_ingest_[i].internal_file_path.c_str(),
|
||||
status.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
if (ingestion_options_.write_global_seqno == false) {
|
||||
files_to_ingest_[i].file_checksum = generated_checksum;
|
||||
files_to_ingest_[i].file_checksum_func_name =
|
||||
generated_checksum_func_name;
|
||||
}
|
||||
generated_checksums.push_back(generated_checksum);
|
||||
generated_checksum_func_names.push_back(generated_checksum_func_name);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: based on the verify_file_checksum and ingested checksum
|
||||
// information, do the verification.
|
||||
if (status.ok()) {
|
||||
if (files_checksums.size() == files_to_ingest_.size() &&
|
||||
files_checksum_func_names.size() == files_to_ingest_.size()) {
|
||||
// Verify the checksum and checksum function name.
|
||||
if (ingestion_options_.verify_file_checksum) {
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
|
||||
if (files_checksum_func_names[i] !=
|
||||
generated_checksum_func_names[i]) {
|
||||
status = Status::InvalidArgument(
|
||||
"Checksum function name does not match with the checksum "
|
||||
"function name of this DB");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Sst file checksum verification of file: %s failed: %s",
|
||||
external_files_paths[i].c_str(), status.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
if (files_checksums[i] != generated_checksums[i]) {
|
||||
status = Status::Corruption(
|
||||
"Ingested checksum does not match with the generated "
|
||||
"checksum");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Sst file checksum verification of file: %s failed: %s",
|
||||
files_to_ingest_[i].internal_file_path.c_str(),
|
||||
status.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If verify_file_checksum is not enabled, we only verify the
|
||||
// checksum function name. If it does not match, fail the ingestion.
|
||||
// If matches, we trust the ingested checksum information and store
|
||||
// in the Manifest.
|
||||
for (size_t i = 0; i < files_to_ingest_.size(); i++) {
|
||||
if (files_checksum_func_names[i] != file_checksum_gen->Name()) {
|
||||
status = Status::InvalidArgument(
|
||||
"Checksum function name does not match with the checksum "
|
||||
"function name of this DB");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"Sst file checksum verification of file: %s failed: %s",
|
||||
external_files_paths[i].c_str(), status.ToString().c_str());
|
||||
break;
|
||||
}
|
||||
files_to_ingest_[i].file_checksum = files_checksums[i];
|
||||
files_to_ingest_[i].file_checksum_func_name =
|
||||
files_checksum_func_names[i];
|
||||
}
|
||||
}
|
||||
} else if (files_checksums.size() != files_checksum_func_names.size() ||
|
||||
(files_checksums.size() == files_checksum_func_names.size() &&
|
||||
files_checksums.size() != 0)) {
|
||||
// The checksum or checksum function name vector are not both empty
|
||||
// and they are incomplete.
|
||||
status = Status::InvalidArgument(
|
||||
"The checksum information of ingested sst files are nonempty and "
|
||||
"the size of checksums or the size of the checksum function "
|
||||
"names "
|
||||
"does not match with the number of ingested sst files");
|
||||
ROCKS_LOG_WARN(
|
||||
db_options_.info_log,
|
||||
"The ingested sst files checksum information is incomplete: %s",
|
||||
status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The following is duplicated with Cleanup().
|
||||
if (!status.ok()) {
|
||||
// We failed, remove all files that we copied into the db
|
||||
@@ -245,6 +372,11 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = GenerateChecksumForIngestedFile(&f);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// We use the import time as the ancester time. This is the time the data
|
||||
// is written to the database.
|
||||
int64_t temp_current_time = 0;
|
||||
@@ -255,11 +387,11 @@ Status ExternalSstFileIngestionJob::Run() {
|
||||
static_cast<uint64_t>(temp_current_time);
|
||||
}
|
||||
|
||||
edit_.AddFile(
|
||||
f.picked_level, f.fd.GetNumber(), f.fd.GetPathId(), f.fd.GetFileSize(),
|
||||
f.smallest_internal_key, f.largest_internal_key, f.assigned_seqno,
|
||||
f.assigned_seqno, false, kInvalidBlobFileNumber, oldest_ancester_time,
|
||||
current_time, kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
edit_.AddFile(f.picked_level, f.fd.GetNumber(), f.fd.GetPathId(),
|
||||
f.fd.GetFileSize(), f.smallest_internal_key,
|
||||
f.largest_internal_key, f.assigned_seqno, f.assigned_seqno,
|
||||
false, kInvalidBlobFileNumber, oldest_ancester_time,
|
||||
current_time, f.file_checksum, f.file_checksum_func_name);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -687,6 +819,31 @@ Status ExternalSstFileIngestionJob::AssignGlobalSeqnoForIngestedFile(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
IOStatus ExternalSstFileIngestionJob::GenerateChecksumForIngestedFile(
|
||||
IngestedFileInfo* file_to_ingest) {
|
||||
if (db_options_.file_checksum_gen_factory == nullptr ||
|
||||
need_generate_file_checksum_ == false ||
|
||||
ingestion_options_.write_global_seqno == false) {
|
||||
// If file_checksum_gen_factory is not set, we are not able to generate
|
||||
// the checksum. if write_global_seqno is false, it means we will use
|
||||
// file checksum generated during Prepare(). This step will be skipped.
|
||||
return IOStatus::OK();
|
||||
}
|
||||
std::string file_checksum, file_checksum_func_name;
|
||||
IOStatus io_s = GenerateOneFileChecksum(
|
||||
fs_, file_to_ingest->internal_file_path,
|
||||
db_options_.file_checksum_gen_factory.get(), &file_checksum,
|
||||
&file_checksum_func_name,
|
||||
ingestion_options_.verify_checksums_readahead_size,
|
||||
db_options_.allow_mmap_reads);
|
||||
if (!io_s.ok()) {
|
||||
return io_s;
|
||||
}
|
||||
file_to_ingest->file_checksum = file_checksum;
|
||||
file_to_ingest->file_checksum_func_name = file_checksum_func_name;
|
||||
return IOStatus::OK();
|
||||
}
|
||||
|
||||
bool ExternalSstFileIngestionJob::IngestedFileFitInLevel(
|
||||
const IngestedFileInfo* file_to_ingest, int level) {
|
||||
if (level == 0) {
|
||||
|
||||
@@ -63,6 +63,10 @@ struct IngestedFileInfo {
|
||||
// ingestion_options.move_files is false by default, thus copy_file is true
|
||||
// by default.
|
||||
bool copy_file = true;
|
||||
// The checksum of ingested file
|
||||
std::string file_checksum;
|
||||
// The name of checksum function that generate the checksum
|
||||
std::string file_checksum_func_name;
|
||||
};
|
||||
|
||||
class ExternalSstFileIngestionJob {
|
||||
@@ -90,6 +94,8 @@ class ExternalSstFileIngestionJob {
|
||||
|
||||
// Prepare the job by copying external files into the DB.
|
||||
Status Prepare(const std::vector<std::string>& external_files_paths,
|
||||
const std::vector<std::string>& files_checksums,
|
||||
const std::vector<std::string>& files_checksum_func_names,
|
||||
uint64_t next_file_number, SuperVersion* sv);
|
||||
|
||||
// Check if we need to flush the memtable before running the ingestion job
|
||||
@@ -148,6 +154,8 @@ class ExternalSstFileIngestionJob {
|
||||
// Set the file global sequence number to `seqno`
|
||||
Status AssignGlobalSeqnoForIngestedFile(IngestedFileInfo* file_to_ingest,
|
||||
SequenceNumber seqno);
|
||||
// Generate the file checksum and store in the IngestedFileInfo
|
||||
IOStatus GenerateChecksumForIngestedFile(IngestedFileInfo* file_to_ingest);
|
||||
|
||||
// Check if `file_to_ingest` can fit in level `level`
|
||||
// REQUIRES: Mutex held
|
||||
@@ -175,6 +183,9 @@ class ExternalSstFileIngestionJob {
|
||||
// Set in ExternalSstFileIngestionJob::Prepare(), if true all files are
|
||||
// ingested in L0
|
||||
bool files_overlap_{false};
|
||||
// Set in ExternalSstFileIngestionJob::Prepare(), if true and DB
|
||||
// file_checksum_gen_factory is set, DB will generate checksum each file.
|
||||
bool need_generate_file_checksum_{true};
|
||||
};
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "file/filename.h"
|
||||
#include "port/port.h"
|
||||
#include "port/stack_trace.h"
|
||||
#include "rocksdb/sst_file_reader.h"
|
||||
#include "rocksdb/sst_file_writer.h"
|
||||
#include "test_util/fault_injection_test_env.h"
|
||||
#include "test_util/testutil.h"
|
||||
@@ -2393,6 +2394,41 @@ TEST_F(ExternalSSTFileTest, IngestFileWrittenWithCompressionDictionary) {
|
||||
ASSERT_EQ(1, num_compression_dicts);
|
||||
}
|
||||
|
||||
// Very slow, not worth the cost to run regularly
|
||||
TEST_F(ExternalSSTFileTest, DISABLED_HugeBlockChecksum) {
|
||||
int max_checksum = static_cast<int>(kxxHash64);
|
||||
for (int i = 0; i <= max_checksum; ++i) {
|
||||
BlockBasedTableOptions table_options;
|
||||
table_options.checksum = static_cast<ChecksumType>(i);
|
||||
Options options = CurrentOptions();
|
||||
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
||||
|
||||
SstFileWriter sst_file_writer(EnvOptions(), options);
|
||||
|
||||
// 2^32 - 1, will lead to data block with more than 2^32 bytes
|
||||
size_t huge_size = port::kMaxUint32;
|
||||
|
||||
std::string f = sst_files_dir_ + "f.sst";
|
||||
ASSERT_OK(sst_file_writer.Open(f));
|
||||
{
|
||||
Random64 r(123);
|
||||
std::string huge(huge_size, 0);
|
||||
for (size_t j = 0; j + 7 < huge_size; j += 8) {
|
||||
EncodeFixed64(&huge[j], r.Next());
|
||||
}
|
||||
ASSERT_OK(sst_file_writer.Put("Huge", huge));
|
||||
}
|
||||
|
||||
ExternalSstFileInfo f_info;
|
||||
ASSERT_OK(sst_file_writer.Finish(&f_info));
|
||||
ASSERT_GT(f_info.file_size, uint64_t{huge_size} + 10);
|
||||
|
||||
SstFileReader sst_file_reader(options);
|
||||
ASSERT_OK(sst_file_reader.Open(f));
|
||||
ASSERT_OK(sst_file_reader.VerifyChecksum());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, IngestFilesIntoMultipleColumnFamilies_Success) {
|
||||
std::unique_ptr<FaultInjectionTestEnv> fault_injection_env(
|
||||
new FaultInjectionTestEnv(env_));
|
||||
@@ -2801,7 +2837,7 @@ TEST_P(ExternalSSTFileTest, IngestFilesTriggerFlushingWithTwoWriteQueue) {
|
||||
GenerateAndAddExternalFile(options, data);
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresents) {
|
||||
TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
constexpr size_t kValueSize = 8;
|
||||
@@ -2842,6 +2878,61 @@ TEST_P(ExternalSSTFileTest, DeltaEncodingWhileGlobalSeqnoPresents) {
|
||||
ASSERT_EQ(value, Get(key2));
|
||||
}
|
||||
|
||||
TEST_P(ExternalSSTFileTest,
|
||||
DeltaEncodingWhileGlobalSeqnoPresentIteratorSwitch) {
|
||||
// Regression test for bug where global seqno corrupted the shared bytes
|
||||
// buffer when switching from reverse iteration to forward iteration.
|
||||
constexpr size_t kValueSize = 8;
|
||||
Options options = CurrentOptions();
|
||||
|
||||
Random rnd(301);
|
||||
std::string value(RandomString(&rnd, kValueSize));
|
||||
|
||||
std::string key0 = "aa";
|
||||
std::string key1 = "ab";
|
||||
// Make the prefix of key2 is same with key1 add zero seqno. The tail of every
|
||||
// key is composed as (seqno << 8 | value_type), and here `1` represents
|
||||
// ValueType::kTypeValue
|
||||
std::string key2 = "ab";
|
||||
PutFixed64(&key2, PackSequenceAndType(0, kTypeValue));
|
||||
key2 += "cdefghijkl";
|
||||
std::string key3 = key2 + "_";
|
||||
|
||||
// Write some key to make global seqno larger than zero
|
||||
ASSERT_OK(Put(key0, value));
|
||||
|
||||
std::string fname = sst_files_dir_ + "test_file";
|
||||
rocksdb::SstFileWriter writer(EnvOptions(), options);
|
||||
ASSERT_OK(writer.Open(fname));
|
||||
|
||||
// key0 is a dummy to ensure the turnaround point (key1) comes from Prev
|
||||
// cache rather than block (restart keys are pinned in block).
|
||||
ASSERT_OK(writer.Put(key0, value));
|
||||
ASSERT_OK(writer.Put(key1, value));
|
||||
ASSERT_OK(writer.Put(key2, value));
|
||||
ASSERT_OK(writer.Put(key3, value));
|
||||
|
||||
ExternalSstFileInfo info;
|
||||
ASSERT_OK(writer.Finish(&info));
|
||||
|
||||
ASSERT_OK(dbfull()->IngestExternalFile({info.file_path},
|
||||
IngestExternalFileOptions()));
|
||||
ReadOptions read_opts;
|
||||
// Prevents Seek() when switching directions, which circumvents the bug.
|
||||
read_opts.total_order_seek = true;
|
||||
Iterator* iter = db_->NewIterator(read_opts);
|
||||
// Scan backwards to key2. File iterator will then be positioned at key1.
|
||||
iter->Seek(key3);
|
||||
ASSERT_EQ(key3, iter->key());
|
||||
iter->Prev();
|
||||
ASSERT_EQ(key2, iter->key());
|
||||
// Scan forwards and make sure key3 is present. Previously key3 would be
|
||||
// corrupted by the global seqno from key1.
|
||||
iter->Next();
|
||||
ASSERT_EQ(key3, iter->key());
|
||||
delete iter;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ExternalSSTFileTest, ExternalSSTFileTest,
|
||||
testing::Values(std::make_tuple(false, false),
|
||||
std::make_tuple(false, true),
|
||||
|
||||
+6
-2
@@ -97,8 +97,11 @@ FlushJob::FlushJob(const std::string& dbname, ColumnFamilyData* cfd,
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri)
|
||||
Env::Priority thread_pri, const std::string& db_id,
|
||||
const std::string& db_session_id)
|
||||
: dbname_(dbname),
|
||||
db_id_(db_id),
|
||||
db_session_id_(db_session_id),
|
||||
cfd_(cfd),
|
||||
db_options_(db_options),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
@@ -393,7 +396,8 @@ Status FlushJob::WriteLevel0Table() {
|
||||
mutable_cf_options_.paranoid_file_checks, cfd_->internal_stats(),
|
||||
TableFileCreationReason::kFlush, &io_s, event_logger_,
|
||||
job_context_->job_id, Env::IO_HIGH, &table_properties_, 0 /* level */,
|
||||
creation_time, oldest_key_time, write_hint, current_time);
|
||||
creation_time, oldest_key_time, write_hint, current_time, db_id_,
|
||||
db_session_id_);
|
||||
if (!io_s.ok()) {
|
||||
io_status_ = io_s;
|
||||
}
|
||||
|
||||
+4
-1
@@ -72,7 +72,8 @@ class FlushJob {
|
||||
CompressionType output_compression, Statistics* stats,
|
||||
EventLogger* event_logger, bool measure_io_stats,
|
||||
const bool sync_output_directory, const bool write_manifest,
|
||||
Env::Priority thread_pri);
|
||||
Env::Priority thread_pri, const std::string& db_id = "",
|
||||
const std::string& db_session_id = "");
|
||||
|
||||
~FlushJob();
|
||||
|
||||
@@ -103,6 +104,8 @@ class FlushJob {
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
const std::string& dbname_;
|
||||
const std::string db_id_;
|
||||
const std::string db_session_id_;
|
||||
ColumnFamilyData* cfd_;
|
||||
const ImmutableDBOptions& db_options_;
|
||||
const MutableCFOptions& mutable_cf_options_;
|
||||
|
||||
@@ -282,7 +282,6 @@ TEST_F(FlushJobTest, FlushMemTablesSingleColumnFamily) {
|
||||
assert(memtable_ids.size() == num_mems);
|
||||
uint64_t smallest_memtable_id = memtable_ids.front();
|
||||
uint64_t flush_memtable_id = smallest_memtable_id + num_mems_to_flush - 1;
|
||||
|
||||
FlushJob flush_job(dbname_, versions_->GetColumnFamilySet()->GetDefault(),
|
||||
db_options_, *cfd->GetLatestMutableCFOptions(),
|
||||
&flush_memtable_id, env_options_, versions_.get(), &mutex_,
|
||||
|
||||
@@ -84,9 +84,9 @@ class ForwardLevelIterator : public InternalIterator {
|
||||
prefix_extractor_, /*table_reader_ptr=*/nullptr,
|
||||
/*file_read_hist=*/nullptr, TableReaderCaller::kUserIterator,
|
||||
/*arena=*/nullptr, /*skip_filters=*/false, /*level=*/-1,
|
||||
/*max_file_size_for_l0_meta_pin=*/0,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_);
|
||||
/*largest_compaction_key=*/nullptr, allow_unprepared_value_);
|
||||
file_iter_->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
valid_ = false;
|
||||
if (!range_del_agg.IsEmpty()) {
|
||||
@@ -686,9 +686,9 @@ void ForwardIterator::RebuildIterators(bool refresh_sv) {
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
MaxFileSizeForL0MetaPin(sv_->mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_));
|
||||
/*largest_compaction_key=*/nullptr, allow_unprepared_value_));
|
||||
}
|
||||
BuildLevelIterators(vstorage);
|
||||
current_ = nullptr;
|
||||
@@ -764,9 +764,9 @@ void ForwardIterator::RenewIterators() {
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
MaxFileSizeForL0MetaPin(svnew->mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_));
|
||||
/*largest_compaction_key=*/nullptr, allow_unprepared_value_));
|
||||
}
|
||||
|
||||
for (auto* f : l0_iters_) {
|
||||
@@ -830,9 +830,9 @@ void ForwardIterator::ResetIncompleteIterators() {
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kUserIterator, /*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/-1,
|
||||
MaxFileSizeForL0MetaPin(sv_->mutable_cf_options),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value_);
|
||||
/*largest_compaction_key=*/nullptr, allow_unprepared_value_);
|
||||
l0_iters_[i]->SetPinnedItersMgr(pinned_iters_mgr_);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class ImportColumnFamilyTest : public DBTestBase {
|
||||
ImportColumnFamilyTest() : DBTestBase("/import_column_family_test") {
|
||||
sst_files_dir_ = dbname_ + "/sst_files/";
|
||||
DestroyAndRecreateExternalSSTFilesDir();
|
||||
export_files_dir_ = test::TmpDir(env_) + "/export";
|
||||
export_files_dir_ = test::PerThreadDBPath(env_, "export");
|
||||
import_cfh_ = nullptr;
|
||||
import_cfh2_ = nullptr;
|
||||
metadata_ptr_ = nullptr;
|
||||
|
||||
@@ -281,6 +281,7 @@ bool Reader::ReadMore(size_t* drop_size, int *error) {
|
||||
// Last read was a full read, so this is a trailer to skip
|
||||
buffer_.clear();
|
||||
Status status = file_->Read(kBlockSize, &buffer_, backing_store_);
|
||||
TEST_SYNC_POINT_CALLBACK("LogReader::ReadMore:AfterReadFile", &status);
|
||||
end_of_buffer_offset_ += buffer_.size();
|
||||
if (!status.ok()) {
|
||||
buffer_.clear();
|
||||
|
||||
@@ -147,6 +147,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);
|
||||
crc = crc32c::Mask(crc); // Adjust for storage
|
||||
TEST_SYNC_POINT_CALLBACK("LogWriter::EmitPhysicalRecord:BeforeEncodeChecksum",
|
||||
&crc);
|
||||
EncodeFixed32(buf, crc);
|
||||
|
||||
// Write the header and the payload
|
||||
|
||||
@@ -724,6 +724,7 @@ static bool SaveValue(void* arg, const char* entry) {
|
||||
return false;
|
||||
}
|
||||
case kTypeDeletion:
|
||||
case kTypeDeletionWithTimestamp:
|
||||
case kTypeSingleDeletion:
|
||||
case kTypeRangeDeletion: {
|
||||
if (*(s->merge_in_progress)) {
|
||||
@@ -929,8 +930,18 @@ void MemTable::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
|
||||
if (found_final_value) {
|
||||
iter->value->PinSelf();
|
||||
range->AddValueSize(iter->value->size());
|
||||
range->MarkKeyDone(iter);
|
||||
RecordTick(moptions_.statistics, MEMTABLE_HIT);
|
||||
if (range->GetValueSize() > read_options.value_size_soft_limit) {
|
||||
// Set all remaining keys in range to Abort
|
||||
for (auto range_iter = range->begin(); range_iter != range->end();
|
||||
++range_iter) {
|
||||
range->MarkKeyDone(range_iter);
|
||||
*(range_iter->s) = Status::Aborted();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PERF_COUNTER_ADD(get_from_memtable_count, 1);
|
||||
|
||||
+11
-13
@@ -43,22 +43,20 @@ void MemTableListVersion::UnrefMemTable(autovector<MemTable*>* to_delete,
|
||||
}
|
||||
|
||||
MemTableListVersion::MemTableListVersion(
|
||||
size_t* parent_memtable_list_memory_usage, MemTableListVersion* old)
|
||||
size_t* parent_memtable_list_memory_usage, const MemTableListVersion& old)
|
||||
: max_write_buffer_number_to_maintain_(
|
||||
old->max_write_buffer_number_to_maintain_),
|
||||
old.max_write_buffer_number_to_maintain_),
|
||||
max_write_buffer_size_to_maintain_(
|
||||
old->max_write_buffer_size_to_maintain_),
|
||||
old.max_write_buffer_size_to_maintain_),
|
||||
parent_memtable_list_memory_usage_(parent_memtable_list_memory_usage) {
|
||||
if (old != nullptr) {
|
||||
memlist_ = old->memlist_;
|
||||
for (auto& m : memlist_) {
|
||||
m->Ref();
|
||||
}
|
||||
memlist_ = old.memlist_;
|
||||
for (auto& m : memlist_) {
|
||||
m->Ref();
|
||||
}
|
||||
|
||||
memlist_history_ = old->memlist_history_;
|
||||
for (auto& m : memlist_history_) {
|
||||
m->Ref();
|
||||
}
|
||||
memlist_history_ = old.memlist_history_;
|
||||
for (auto& m : memlist_history_) {
|
||||
m->Ref();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,7 +602,7 @@ void MemTableList::InstallNewVersion() {
|
||||
} else {
|
||||
// somebody else holds the current version, we need to create new one
|
||||
MemTableListVersion* version = current_;
|
||||
current_ = new MemTableListVersion(¤t_memory_usage_, current_);
|
||||
current_ = new MemTableListVersion(¤t_memory_usage_, *version);
|
||||
current_->Ref();
|
||||
version->Unref();
|
||||
}
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ struct FlushJobInfo;
|
||||
class MemTableListVersion {
|
||||
public:
|
||||
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
|
||||
MemTableListVersion* old = nullptr);
|
||||
const MemTableListVersion& old);
|
||||
explicit MemTableListVersion(size_t* parent_memtable_list_memory_usage,
|
||||
int max_write_buffer_number_to_maintain,
|
||||
int64_t max_write_buffer_size_to_maintain);
|
||||
|
||||
+8
-2
@@ -183,6 +183,8 @@ class Repairer {
|
||||
}
|
||||
// Just create a DBImpl temporarily so we can reuse NewDB()
|
||||
DBImpl* db_impl = new DBImpl(db_options_, dbname_);
|
||||
// Also use this temp DBImpl to get a session id
|
||||
db_impl->GetDbSessionId(db_session_id_);
|
||||
status = db_impl->NewDB();
|
||||
delete db_impl;
|
||||
}
|
||||
@@ -229,6 +231,7 @@ class Repairer {
|
||||
};
|
||||
|
||||
std::string const dbname_;
|
||||
std::string db_session_id_;
|
||||
Env* const env_;
|
||||
const EnvOptions env_options_;
|
||||
const DBOptions db_options_;
|
||||
@@ -435,7 +438,9 @@ class Repairer {
|
||||
CompressionOptions(), false, nullptr /* internal_stats */,
|
||||
TableFileCreationReason::kRecovery, &io_s, nullptr /* event_logger */,
|
||||
0 /* job_id */, Env::IO_HIGH, nullptr /* table_properties */,
|
||||
-1 /* level */, current_time, write_hint);
|
||||
-1 /* level */, current_time, 0 /* oldest_key_time */, write_hint,
|
||||
0 /* file_creation_time */, "DB Repairer" /* db_id */,
|
||||
db_session_id_);
|
||||
ROCKS_LOG_INFO(db_options_.info_log,
|
||||
"Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s",
|
||||
log, counter, meta.fd.GetNumber(),
|
||||
@@ -528,7 +533,8 @@ class Repairer {
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
/*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
|
||||
TableReaderCaller::kRepair, /*arena=*/nullptr, /*skip_filters=*/false,
|
||||
/*level=*/-1, /*smallest_compaction_key=*/nullptr,
|
||||
/*level=*/-1, /*max_file_size_for_l0_meta_pin=*/0,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
ParsedInternalKey parsed;
|
||||
|
||||
+20
-11
@@ -97,7 +97,8 @@ Status TableCache::GetTableReader(
|
||||
bool sequential_mode, bool record_read_stats, HistogramImpl* file_read_hist,
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
const SliceTransform* prefix_extractor, bool skip_filters, int level,
|
||||
bool prefetch_index_and_filter_in_cache) {
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
std::string fname =
|
||||
TableFileName(ioptions_.cf_paths, fd.GetNumber(), fd.GetPathId());
|
||||
std::unique_ptr<FSRandomAccessFile> file;
|
||||
@@ -123,7 +124,9 @@ Status TableCache::GetTableReader(
|
||||
s = ioptions_.table_factory->NewTableReader(
|
||||
TableReaderOptions(ioptions_, prefix_extractor, file_options,
|
||||
internal_comparator, skip_filters, immortal_tables_,
|
||||
level, fd.largest_seqno, block_cache_tracer_),
|
||||
false /* force_direct_prefetch */, level,
|
||||
fd.largest_seqno, block_cache_tracer_,
|
||||
max_file_size_for_l0_meta_pin),
|
||||
std::move(file_reader), fd.GetFileSize(), table_reader,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
TEST_SYNC_POINT("TableCache::GetTableReader:0");
|
||||
@@ -144,8 +147,8 @@ Status TableCache::FindTable(const FileOptions& file_options,
|
||||
const SliceTransform* prefix_extractor,
|
||||
const bool no_io, bool record_read_stats,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level,
|
||||
bool prefetch_index_and_filter_in_cache) {
|
||||
int level, bool prefetch_index_and_filter_in_cache,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
PERF_TIMER_GUARD_WITH_ENV(find_table_nanos, ioptions_.env);
|
||||
Status s;
|
||||
uint64_t number = fd.GetNumber();
|
||||
@@ -169,7 +172,8 @@ Status TableCache::FindTable(const FileOptions& file_options,
|
||||
s = GetTableReader(file_options, internal_comparator, fd,
|
||||
false /* sequential mode */, record_read_stats,
|
||||
file_read_hist, &table_reader, prefix_extractor,
|
||||
skip_filters, level, prefetch_index_and_filter_in_cache);
|
||||
skip_filters, level, prefetch_index_and_filter_in_cache,
|
||||
max_file_size_for_l0_meta_pin);
|
||||
if (!s.ok()) {
|
||||
assert(table_reader == nullptr);
|
||||
RecordTick(ioptions_.statistics, NO_FILE_ERRORS);
|
||||
@@ -193,6 +197,7 @@ InternalIterator* TableCache::NewIterator(
|
||||
RangeDelAggregator* range_del_agg, const SliceTransform* prefix_extractor,
|
||||
TableReader** table_reader_ptr, HistogramImpl* file_read_hist,
|
||||
TableReaderCaller caller, Arena* arena, bool skip_filters, int level,
|
||||
size_t max_file_size_for_l0_meta_pin,
|
||||
const InternalKey* smallest_compaction_key,
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value) {
|
||||
PERF_TIMER_GUARD(new_table_iterator_nanos);
|
||||
@@ -210,7 +215,9 @@ InternalIterator* TableCache::NewIterator(
|
||||
s = FindTable(file_options, icomparator, fd, &handle, prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
!for_compaction /* record_read_stats */, file_read_hist,
|
||||
skip_filters, level);
|
||||
skip_filters, level,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
max_file_size_for_l0_meta_pin);
|
||||
if (s.ok()) {
|
||||
table_reader = GetTableReaderFromHandle(handle);
|
||||
}
|
||||
@@ -371,7 +378,7 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
GetContext* get_context,
|
||||
const SliceTransform* prefix_extractor,
|
||||
HistogramImpl* file_read_hist, bool skip_filters,
|
||||
int level) {
|
||||
int level, size_t max_file_size_for_l0_meta_pin) {
|
||||
auto& fd = file_meta.fd;
|
||||
std::string* row_cache_entry = nullptr;
|
||||
bool done = false;
|
||||
@@ -396,10 +403,12 @@ Status TableCache::Get(const ReadOptions& options,
|
||||
Cache::Handle* handle = nullptr;
|
||||
if (!done && s.ok()) {
|
||||
if (t == nullptr) {
|
||||
s = FindTable(
|
||||
file_options_, internal_comparator, fd, &handle, prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
true /* record_read_stats */, file_read_hist, skip_filters, level);
|
||||
s = FindTable(file_options_, internal_comparator, fd, &handle,
|
||||
prefix_extractor,
|
||||
options.read_tier == kBlockCacheTier /* no_io */,
|
||||
true /* record_read_stats */, file_read_hist, skip_filters,
|
||||
level, true /* prefetch_index_and_filter_in_cache */,
|
||||
max_file_size_for_l0_meta_pin);
|
||||
if (s.ok()) {
|
||||
t = GetTableReaderFromHandle(handle);
|
||||
}
|
||||
|
||||
+7
-4
@@ -72,7 +72,8 @@ class TableCache {
|
||||
const FileMetaData& file_meta, RangeDelAggregator* range_del_agg,
|
||||
const SliceTransform* prefix_extractor, TableReader** table_reader_ptr,
|
||||
HistogramImpl* file_read_hist, TableReaderCaller caller, Arena* arena,
|
||||
bool skip_filters, int level, const InternalKey* smallest_compaction_key,
|
||||
bool skip_filters, int level, size_t max_file_size_for_l0_meta_pin,
|
||||
const InternalKey* smallest_compaction_key,
|
||||
const InternalKey* largest_compaction_key, bool allow_unprepared_value);
|
||||
|
||||
// If a seek to internal key "k" in specified file finds an entry,
|
||||
@@ -91,7 +92,7 @@ class TableCache {
|
||||
GetContext* get_context,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
HistogramImpl* file_read_hist = nullptr, bool skip_filters = false,
|
||||
int level = -1);
|
||||
int level = -1, size_t max_file_size_for_l0_meta_pin = 0);
|
||||
|
||||
// Return the range delete tombstone iterator of the file specified by
|
||||
// `file_meta`.
|
||||
@@ -135,7 +136,8 @@ class TableCache {
|
||||
const bool no_io = false, bool record_read_stats = true,
|
||||
HistogramImpl* file_read_hist = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true);
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0);
|
||||
|
||||
// Get TableReader from a cache handle.
|
||||
TableReader* GetTableReaderFromHandle(Cache::Handle* handle);
|
||||
@@ -200,7 +202,8 @@ class TableCache {
|
||||
std::unique_ptr<TableReader>* table_reader,
|
||||
const SliceTransform* prefix_extractor = nullptr,
|
||||
bool skip_filters = false, int level = -1,
|
||||
bool prefetch_index_and_filter_in_cache = true);
|
||||
bool prefetch_index_and_filter_in_cache = true,
|
||||
size_t max_file_size_for_l0_meta_pin = 0);
|
||||
|
||||
// Create a key prefix for looking up the row cache. The prefix is of the
|
||||
// format row_cache_id + fd_number + seq_no. Later, the user key can be
|
||||
|
||||
+434
-222
@@ -86,6 +86,86 @@ class VersionBuilder::Rep {
|
||||
std::unordered_map<uint64_t, FileMetaData*> added_files;
|
||||
};
|
||||
|
||||
class BlobFileMetaDataDelta {
|
||||
public:
|
||||
bool IsEmpty() const {
|
||||
return !shared_meta_ && !additional_garbage_count_ &&
|
||||
!additional_garbage_bytes_ && newly_linked_ssts_.empty() &&
|
||||
newly_unlinked_ssts_.empty();
|
||||
}
|
||||
|
||||
std::shared_ptr<SharedBlobFileMetaData> GetSharedMeta() const {
|
||||
return shared_meta_;
|
||||
}
|
||||
|
||||
uint64_t GetAdditionalGarbageCount() const {
|
||||
return additional_garbage_count_;
|
||||
}
|
||||
|
||||
uint64_t GetAdditionalGarbageBytes() const {
|
||||
return additional_garbage_bytes_;
|
||||
}
|
||||
|
||||
const std::unordered_set<uint64_t>& GetNewlyLinkedSsts() const {
|
||||
return newly_linked_ssts_;
|
||||
}
|
||||
|
||||
const std::unordered_set<uint64_t>& GetNewlyUnlinkedSsts() const {
|
||||
return newly_unlinked_ssts_;
|
||||
}
|
||||
|
||||
void SetSharedMeta(std::shared_ptr<SharedBlobFileMetaData> shared_meta) {
|
||||
assert(!shared_meta_);
|
||||
assert(shared_meta);
|
||||
|
||||
shared_meta_ = std::move(shared_meta);
|
||||
}
|
||||
|
||||
void AddGarbage(uint64_t count, uint64_t bytes) {
|
||||
additional_garbage_count_ += count;
|
||||
additional_garbage_bytes_ += bytes;
|
||||
}
|
||||
|
||||
void LinkSst(uint64_t sst_file_number) {
|
||||
assert(newly_linked_ssts_.find(sst_file_number) ==
|
||||
newly_linked_ssts_.end());
|
||||
|
||||
// Reconcile with newly unlinked SSTs on the fly. (Note: an SST can be
|
||||
// linked to and unlinked from the same blob file in the case of a trivial
|
||||
// move.)
|
||||
auto it = newly_unlinked_ssts_.find(sst_file_number);
|
||||
|
||||
if (it != newly_unlinked_ssts_.end()) {
|
||||
newly_unlinked_ssts_.erase(it);
|
||||
} else {
|
||||
newly_linked_ssts_.emplace(sst_file_number);
|
||||
}
|
||||
}
|
||||
|
||||
void UnlinkSst(uint64_t sst_file_number) {
|
||||
assert(newly_unlinked_ssts_.find(sst_file_number) ==
|
||||
newly_unlinked_ssts_.end());
|
||||
|
||||
// Reconcile with newly linked SSTs on the fly. (Note: an SST can be
|
||||
// linked to and unlinked from the same blob file in the case of a trivial
|
||||
// move.)
|
||||
auto it = newly_linked_ssts_.find(sst_file_number);
|
||||
|
||||
if (it != newly_linked_ssts_.end()) {
|
||||
newly_linked_ssts_.erase(it);
|
||||
} else {
|
||||
newly_unlinked_ssts_.emplace(sst_file_number);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<SharedBlobFileMetaData> shared_meta_;
|
||||
uint64_t additional_garbage_count_ = 0;
|
||||
uint64_t additional_garbage_bytes_ = 0;
|
||||
std::unordered_set<uint64_t> newly_linked_ssts_;
|
||||
std::unordered_set<uint64_t> newly_unlinked_ssts_;
|
||||
};
|
||||
|
||||
const FileOptions& file_options_;
|
||||
const ImmutableCFOptions* const ioptions_;
|
||||
TableCache* table_cache_;
|
||||
@@ -93,19 +173,21 @@ class VersionBuilder::Rep {
|
||||
VersionSet* version_set_;
|
||||
int num_levels_;
|
||||
LevelState* levels_;
|
||||
// Store states of levels larger than num_levels_. We do this instead of
|
||||
// Store sizes of levels larger than num_levels_. We do this instead of
|
||||
// storing them in levels_ to avoid regression in case there are no files
|
||||
// on invalid levels. The version is not consistent if in the end the files
|
||||
// on invalid levels don't cancel out.
|
||||
std::map<int, std::unordered_set<uint64_t>> invalid_levels_;
|
||||
std::unordered_map<int, size_t> invalid_level_sizes_;
|
||||
// Whether there are invalid new files or invalid deletion on levels larger
|
||||
// than num_levels_.
|
||||
bool has_invalid_levels_;
|
||||
// Current levels of table files affected by additions/deletions.
|
||||
std::unordered_map<uint64_t, int> table_file_levels_;
|
||||
FileComparator level_zero_cmp_;
|
||||
FileComparator level_nonzero_cmp_;
|
||||
|
||||
// Metadata for all blob files affected by the series of version edits.
|
||||
std::map<uint64_t, std::shared_ptr<BlobFileMetaData>> changed_blob_files_;
|
||||
// Metadata delta for all blob files affected by the series of version edits.
|
||||
std::map<uint64_t, BlobFileMetaDataDelta> blob_file_meta_deltas_;
|
||||
|
||||
public:
|
||||
Rep(const FileOptions& file_options, const ImmutableCFOptions* ioptions,
|
||||
@@ -150,14 +232,12 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<BlobFileMetaData> GetBlobFileMetaData(
|
||||
uint64_t blob_file_number) const {
|
||||
auto changed_it = changed_blob_files_.find(blob_file_number);
|
||||
if (changed_it != changed_blob_files_.end()) {
|
||||
const auto& meta = changed_it->second;
|
||||
assert(meta);
|
||||
|
||||
return meta;
|
||||
bool IsBlobFileInVersion(uint64_t blob_file_number) const {
|
||||
auto delta_it = blob_file_meta_deltas_.find(blob_file_number);
|
||||
if (delta_it != blob_file_meta_deltas_.end()) {
|
||||
if (delta_it->second.GetSharedMeta()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
assert(base_vstorage_);
|
||||
@@ -166,39 +246,28 @@ class VersionBuilder::Rep {
|
||||
|
||||
auto base_it = base_blob_files.find(blob_file_number);
|
||||
if (base_it != base_blob_files.end()) {
|
||||
const auto& meta = base_it->second;
|
||||
assert(meta);
|
||||
assert(base_it->second);
|
||||
assert(base_it->second->GetSharedMeta());
|
||||
|
||||
return meta;
|
||||
return true;
|
||||
}
|
||||
|
||||
return std::shared_ptr<BlobFileMetaData>();
|
||||
return false;
|
||||
}
|
||||
|
||||
Status CheckConsistencyOfOldestBlobFileReference(
|
||||
const VersionStorageInfo* vstorage, uint64_t blob_file_number) const {
|
||||
assert(vstorage);
|
||||
using ExpectedLinkedSsts =
|
||||
std::unordered_map<uint64_t, BlobFileMetaData::LinkedSsts>;
|
||||
|
||||
// TODO: remove this check once we actually start recoding metadata for
|
||||
// blob files in the MANIFEST.
|
||||
if (vstorage->GetBlobFiles().empty()) {
|
||||
return Status::OK();
|
||||
}
|
||||
static void UpdateExpectedLinkedSsts(
|
||||
uint64_t table_file_number, uint64_t blob_file_number,
|
||||
ExpectedLinkedSsts* expected_linked_ssts) {
|
||||
assert(expected_linked_ssts);
|
||||
|
||||
if (blob_file_number == kInvalidBlobFileNumber) {
|
||||
return Status::OK();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (!meta) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number
|
||||
<< " is not part of this version";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
(*expected_linked_ssts)[blob_file_number].emplace(table_file_number);
|
||||
}
|
||||
|
||||
Status CheckConsistency(VersionStorageInfo* vstorage) {
|
||||
@@ -209,9 +278,13 @@ class VersionBuilder::Rep {
|
||||
return Status::OK();
|
||||
}
|
||||
#endif
|
||||
// Make sure the files are sorted correctly and that the oldest blob file
|
||||
// reference for each table file points to a valid blob file in this
|
||||
// version.
|
||||
// Make sure the files are sorted correctly and that the links between
|
||||
// table files and blob files are consistent. The latter is checked using
|
||||
// the following mapping, which is built using the forward links
|
||||
// (table file -> blob file), and is subsequently compared with the inverse
|
||||
// mapping stored in the BlobFileMetaData objects.
|
||||
ExpectedLinkedSsts expected_linked_ssts;
|
||||
|
||||
for (int level = 0; level < num_levels_; level++) {
|
||||
auto& level_files = vstorage->LevelFiles(level);
|
||||
|
||||
@@ -220,19 +293,14 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
|
||||
assert(level_files[0]);
|
||||
Status s = CheckConsistencyOfOldestBlobFileReference(
|
||||
vstorage, level_files[0]->oldest_blob_file_number);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
UpdateExpectedLinkedSsts(level_files[0]->fd.GetNumber(),
|
||||
level_files[0]->oldest_blob_file_number,
|
||||
&expected_linked_ssts);
|
||||
for (size_t i = 1; i < level_files.size(); i++) {
|
||||
assert(level_files[i]);
|
||||
s = CheckConsistencyOfOldestBlobFileReference(
|
||||
vstorage, level_files[i]->oldest_blob_file_number);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
UpdateExpectedLinkedSsts(level_files[i]->fd.GetNumber(),
|
||||
level_files[i]->oldest_blob_file_number,
|
||||
&expected_linked_ssts);
|
||||
|
||||
auto f1 = level_files[i - 1];
|
||||
auto f2 = level_files[i];
|
||||
@@ -242,7 +310,6 @@ class VersionBuilder::Rep {
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency0", &pair);
|
||||
#endif
|
||||
if (!level_zero_cmp_(f1, f2)) {
|
||||
fprintf(stderr, "L0 files are not sorted properly");
|
||||
return Status::Corruption("L0 files are not sorted properly");
|
||||
}
|
||||
|
||||
@@ -251,11 +318,6 @@ class VersionBuilder::Rep {
|
||||
SequenceNumber external_file_seqno = f2->fd.smallest_seqno;
|
||||
if (!(external_file_seqno < f1->fd.largest_seqno ||
|
||||
external_file_seqno == 0)) {
|
||||
fprintf(stderr,
|
||||
"L0 file with seqno %" PRIu64 " %" PRIu64
|
||||
" vs. file with global_seqno %" PRIu64 "\n",
|
||||
f1->fd.smallest_seqno, f1->fd.largest_seqno,
|
||||
external_file_seqno);
|
||||
return Status::Corruption(
|
||||
"L0 file with seqno " +
|
||||
NumberToString(f1->fd.smallest_seqno) + " " +
|
||||
@@ -265,11 +327,6 @@ class VersionBuilder::Rep {
|
||||
NumberToString(f1->fd.GetNumber()));
|
||||
}
|
||||
} else if (f1->fd.smallest_seqno <= f2->fd.smallest_seqno) {
|
||||
fprintf(stderr,
|
||||
"L0 files seqno %" PRIu64 " %" PRIu64 " vs. %" PRIu64
|
||||
" %" PRIu64 "\n",
|
||||
f1->fd.smallest_seqno, f1->fd.largest_seqno,
|
||||
f2->fd.smallest_seqno, f2->fd.largest_seqno);
|
||||
return Status::Corruption(
|
||||
"L0 files seqno " + NumberToString(f1->fd.smallest_seqno) +
|
||||
" " + NumberToString(f1->fd.largest_seqno) + " " +
|
||||
@@ -284,7 +341,6 @@ class VersionBuilder::Rep {
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency1", &pair);
|
||||
#endif
|
||||
if (!level_nonzero_cmp_(f1, f2)) {
|
||||
fprintf(stderr, "L%d files are not sorted properly", level);
|
||||
return Status::Corruption("L" + NumberToString(level) +
|
||||
" files are not sorted properly");
|
||||
}
|
||||
@@ -292,9 +348,6 @@ class VersionBuilder::Rep {
|
||||
// Make sure there is no overlap in levels > 0
|
||||
if (vstorage->InternalComparator()->Compare(f1->largest,
|
||||
f2->smallest) >= 0) {
|
||||
fprintf(stderr, "L%d have overlapping ranges %s vs. %s\n", level,
|
||||
(f1->largest).DebugString(true).c_str(),
|
||||
(f2->smallest).DebugString(true).c_str());
|
||||
return Status::Corruption(
|
||||
"L" + NumberToString(level) + " have overlapping ranges " +
|
||||
(f1->largest).DebugString(true) + " vs. " +
|
||||
@@ -307,17 +360,27 @@ class VersionBuilder::Rep {
|
||||
// Make sure that all blob files in the version have non-garbage data.
|
||||
const auto& blob_files = vstorage->GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const uint64_t blob_file_number = pair.first;
|
||||
const auto& blob_file_meta = pair.second;
|
||||
assert(blob_file_meta);
|
||||
|
||||
if (blob_file_meta->GetGarbageBlobCount() >=
|
||||
blob_file_meta->GetTotalBlobCount()) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_meta->GetBlobFileNumber()
|
||||
oss << "Blob file #" << blob_file_number
|
||||
<< " consists entirely of garbage";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
if (blob_file_meta->GetLinkedSsts() !=
|
||||
expected_linked_ssts[blob_file_number]) {
|
||||
std::ostringstream oss;
|
||||
oss << "Links are inconsistent between table files and blob file #"
|
||||
<< blob_file_number;
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
}
|
||||
|
||||
Status ret_s;
|
||||
@@ -326,73 +389,26 @@ class VersionBuilder::Rep {
|
||||
return ret_s;
|
||||
}
|
||||
|
||||
Status CheckConsistencyForDeletes(VersionEdit* /*edit*/, uint64_t number,
|
||||
int level) {
|
||||
#ifdef NDEBUG
|
||||
if (!base_vstorage_->force_consistency_checks()) {
|
||||
// Dont run consistency checks in release mode except if
|
||||
// explicitly asked to
|
||||
return Status::OK();
|
||||
}
|
||||
#endif
|
||||
// a file to be deleted better exist in the previous version
|
||||
bool found = false;
|
||||
for (int l = 0; !found && l < num_levels_; l++) {
|
||||
const std::vector<FileMetaData*>& base_files =
|
||||
base_vstorage_->LevelFiles(l);
|
||||
for (size_t i = 0; i < base_files.size(); i++) {
|
||||
FileMetaData* f = base_files[i];
|
||||
if (f->fd.GetNumber() == number) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if the file did not exist in the previous version, then it
|
||||
// is possibly moved from lower level to higher level in current
|
||||
// version
|
||||
for (int l = level + 1; !found && l < num_levels_; l++) {
|
||||
auto& level_added = levels_[l].added_files;
|
||||
auto got = level_added.find(number);
|
||||
if (got != level_added.end()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// maybe this file was added in a previous edit that was Applied
|
||||
if (!found) {
|
||||
auto& level_added = levels_[level].added_files;
|
||||
auto got = level_added.find(number);
|
||||
if (got != level_added.end()) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fprintf(stderr, "not found %" PRIu64 "\n", number);
|
||||
return Status::Corruption("not found " + NumberToString(number));
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool CheckConsistencyForNumLevels() {
|
||||
bool CheckConsistencyForNumLevels() const {
|
||||
// Make sure there are no files on or beyond num_levels().
|
||||
if (has_invalid_levels_) {
|
||||
return false;
|
||||
}
|
||||
for (auto& level : invalid_levels_) {
|
||||
if (level.second.size() > 0) {
|
||||
|
||||
for (const auto& pair : invalid_level_sizes_) {
|
||||
const size_t level_size = pair.second;
|
||||
if (level_size != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ApplyBlobFileAddition(const BlobFileAddition& blob_file_addition) {
|
||||
const uint64_t blob_file_number = blob_file_addition.GetBlobFileNumber();
|
||||
|
||||
auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (meta) {
|
||||
if (IsBlobFileInVersion(blob_file_number)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number << " already added";
|
||||
|
||||
@@ -423,12 +439,8 @@ class VersionBuilder::Rep {
|
||||
blob_file_addition.GetChecksumMethod(),
|
||||
blob_file_addition.GetChecksumValue(), deleter);
|
||||
|
||||
constexpr uint64_t garbage_blob_count = 0;
|
||||
constexpr uint64_t garbage_blob_bytes = 0;
|
||||
auto new_meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
changed_blob_files_.emplace(blob_file_number, std::move(new_meta));
|
||||
blob_file_meta_deltas_[blob_file_number].SetSharedMeta(
|
||||
std::move(shared_meta));
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
@@ -436,84 +448,181 @@ class VersionBuilder::Rep {
|
||||
Status ApplyBlobFileGarbage(const BlobFileGarbage& blob_file_garbage) {
|
||||
const uint64_t blob_file_number = blob_file_garbage.GetBlobFileNumber();
|
||||
|
||||
auto meta = GetBlobFileMetaData(blob_file_number);
|
||||
if (!meta) {
|
||||
if (!IsBlobFileInVersion(blob_file_number)) {
|
||||
std::ostringstream oss;
|
||||
oss << "Blob file #" << blob_file_number << " not found";
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
assert(meta->GetBlobFileNumber() == blob_file_number);
|
||||
blob_file_meta_deltas_[blob_file_number].AddGarbage(
|
||||
blob_file_garbage.GetGarbageBlobCount(),
|
||||
blob_file_garbage.GetGarbageBlobBytes());
|
||||
|
||||
auto new_meta = BlobFileMetaData::Create(
|
||||
meta->GetSharedMeta(),
|
||||
meta->GetGarbageBlobCount() + blob_file_garbage.GetGarbageBlobCount(),
|
||||
meta->GetGarbageBlobBytes() + blob_file_garbage.GetGarbageBlobBytes());
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
changed_blob_files_[blob_file_number] = std::move(new_meta);
|
||||
int GetCurrentLevelForTableFile(uint64_t file_number) const {
|
||||
auto it = table_file_levels_.find(file_number);
|
||||
if (it != table_file_levels_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
assert(base_vstorage_);
|
||||
return base_vstorage_->GetFileLocation(file_number).GetLevel();
|
||||
}
|
||||
|
||||
uint64_t GetOldestBlobFileNumberForTableFile(int level,
|
||||
uint64_t file_number) const {
|
||||
assert(level < num_levels_);
|
||||
|
||||
const auto& added_files = levels_[level].added_files;
|
||||
|
||||
auto it = added_files.find(file_number);
|
||||
if (it != added_files.end()) {
|
||||
const FileMetaData* const meta = it->second;
|
||||
assert(meta);
|
||||
|
||||
return meta->oldest_blob_file_number;
|
||||
}
|
||||
|
||||
assert(base_vstorage_);
|
||||
const FileMetaData* const meta =
|
||||
base_vstorage_->GetFileMetaDataByNumber(file_number);
|
||||
assert(meta);
|
||||
|
||||
return meta->oldest_blob_file_number;
|
||||
}
|
||||
|
||||
Status ApplyFileDeletion(int level, uint64_t file_number) {
|
||||
assert(level != VersionStorageInfo::FileLocation::Invalid().GetLevel());
|
||||
|
||||
const int current_level = GetCurrentLevelForTableFile(file_number);
|
||||
|
||||
if (level != current_level) {
|
||||
if (level >= num_levels_) {
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Cannot delete table file #" << file_number << " from level "
|
||||
<< level << " since it is ";
|
||||
if (current_level ==
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel()) {
|
||||
oss << "not in the LSM tree";
|
||||
} else {
|
||||
oss << "on level " << current_level;
|
||||
}
|
||||
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
if (level >= num_levels_) {
|
||||
assert(invalid_level_sizes_[level] > 0);
|
||||
--invalid_level_sizes_[level];
|
||||
|
||||
table_file_levels_[file_number] =
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
const uint64_t blob_file_number =
|
||||
GetOldestBlobFileNumberForTableFile(level, file_number);
|
||||
|
||||
if (blob_file_number != kInvalidBlobFileNumber &&
|
||||
IsBlobFileInVersion(blob_file_number)) {
|
||||
blob_file_meta_deltas_[blob_file_number].UnlinkSst(file_number);
|
||||
}
|
||||
|
||||
auto& level_state = levels_[level];
|
||||
|
||||
auto& add_files = level_state.added_files;
|
||||
auto add_it = add_files.find(file_number);
|
||||
if (add_it != add_files.end()) {
|
||||
UnrefFile(add_it->second);
|
||||
add_files.erase(add_it);
|
||||
}
|
||||
|
||||
auto& del_files = level_state.deleted_files;
|
||||
assert(del_files.find(file_number) == del_files.end());
|
||||
del_files.emplace(file_number);
|
||||
|
||||
table_file_levels_[file_number] =
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel();
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status ApplyFileAddition(int level, const FileMetaData& meta) {
|
||||
assert(level != VersionStorageInfo::FileLocation::Invalid().GetLevel());
|
||||
|
||||
const uint64_t file_number = meta.fd.GetNumber();
|
||||
|
||||
const int current_level = GetCurrentLevelForTableFile(file_number);
|
||||
|
||||
if (current_level !=
|
||||
VersionStorageInfo::FileLocation::Invalid().GetLevel()) {
|
||||
if (level >= num_levels_) {
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Cannot add table file #" << file_number << " to level " << level
|
||||
<< " since it is already in the LSM tree on level " << current_level;
|
||||
return Status::Corruption("VersionBuilder", oss.str());
|
||||
}
|
||||
|
||||
if (level >= num_levels_) {
|
||||
++invalid_level_sizes_[level];
|
||||
table_file_levels_[file_number] = level;
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
auto& level_state = levels_[level];
|
||||
|
||||
auto& del_files = level_state.deleted_files;
|
||||
auto del_it = del_files.find(file_number);
|
||||
if (del_it != del_files.end()) {
|
||||
del_files.erase(del_it);
|
||||
}
|
||||
|
||||
FileMetaData* const f = new FileMetaData(meta);
|
||||
f->refs = 1;
|
||||
|
||||
auto& add_files = level_state.added_files;
|
||||
assert(add_files.find(file_number) == add_files.end());
|
||||
add_files.emplace(file_number, f);
|
||||
|
||||
const uint64_t blob_file_number = f->oldest_blob_file_number;
|
||||
|
||||
if (blob_file_number != kInvalidBlobFileNumber &&
|
||||
IsBlobFileInVersion(blob_file_number)) {
|
||||
blob_file_meta_deltas_[blob_file_number].LinkSst(file_number);
|
||||
}
|
||||
|
||||
table_file_levels_[file_number] = level;
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Apply all of the edits in *edit to the current state.
|
||||
Status Apply(VersionEdit* edit) {
|
||||
Status s = CheckConsistency(base_vstorage_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
// Delete files
|
||||
const auto& del = edit->GetDeletedFiles();
|
||||
for (const auto& del_file : del) {
|
||||
const auto level = del_file.first;
|
||||
const auto number = del_file.second;
|
||||
if (level < num_levels_) {
|
||||
levels_[level].deleted_files.insert(number);
|
||||
s = CheckConsistencyForDeletes(edit, number, level);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
auto exising = levels_[level].added_files.find(number);
|
||||
if (exising != levels_[level].added_files.end()) {
|
||||
UnrefFile(exising->second);
|
||||
levels_[level].added_files.erase(exising);
|
||||
}
|
||||
} else {
|
||||
if (invalid_levels_[level].erase(number) == 0) {
|
||||
// Deleting an non-existing file on invalid level.
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
{
|
||||
const Status s = CheckConsistency(base_vstorage_);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new files
|
||||
for (const auto& new_file : edit->GetNewFiles()) {
|
||||
const int level = new_file.first;
|
||||
if (level < num_levels_) {
|
||||
FileMetaData* f = new FileMetaData(new_file.second);
|
||||
f->refs = 1;
|
||||
|
||||
assert(levels_[level].added_files.find(f->fd.GetNumber()) ==
|
||||
levels_[level].added_files.end());
|
||||
levels_[level].deleted_files.erase(f->fd.GetNumber());
|
||||
levels_[level].added_files[f->fd.GetNumber()] = f;
|
||||
} else {
|
||||
uint64_t number = new_file.second.fd.GetNumber();
|
||||
auto& lvls = invalid_levels_[level];
|
||||
if (lvls.count(number) == 0) {
|
||||
lvls.insert(number);
|
||||
} else {
|
||||
// Creating an already existing file on invalid level.
|
||||
has_invalid_levels_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note: we process the blob file related changes first because the
|
||||
// table file addition/deletion logic depends on the blob files
|
||||
// already being there.
|
||||
|
||||
// Add new blob files
|
||||
for (const auto& blob_file_addition : edit->GetBlobFileAdditions()) {
|
||||
s = ApplyBlobFileAddition(blob_file_addition);
|
||||
const Status s = ApplyBlobFileAddition(blob_file_addition);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
@@ -521,13 +630,96 @@ class VersionBuilder::Rep {
|
||||
|
||||
// Increase the amount of garbage for blob files affected by GC
|
||||
for (const auto& blob_file_garbage : edit->GetBlobFileGarbages()) {
|
||||
s = ApplyBlobFileGarbage(blob_file_garbage);
|
||||
const Status s = ApplyBlobFileGarbage(blob_file_garbage);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
// Delete table files
|
||||
for (const auto& deleted_file : edit->GetDeletedFiles()) {
|
||||
const int level = deleted_file.first;
|
||||
const uint64_t file_number = deleted_file.second;
|
||||
|
||||
const Status s = ApplyFileDeletion(level, file_number);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
// Add new table files
|
||||
for (const auto& new_file : edit->GetNewFiles()) {
|
||||
const int level = new_file.first;
|
||||
const FileMetaData& meta = new_file.second;
|
||||
|
||||
const Status s = ApplyFileAddition(level, meta);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
static BlobFileMetaData::LinkedSsts ApplyLinkedSstChanges(
|
||||
const BlobFileMetaData::LinkedSsts& base,
|
||||
const std::unordered_set<uint64_t>& newly_linked,
|
||||
const std::unordered_set<uint64_t>& newly_unlinked) {
|
||||
BlobFileMetaData::LinkedSsts result(base);
|
||||
|
||||
for (uint64_t sst_file_number : newly_unlinked) {
|
||||
assert(result.find(sst_file_number) != result.end());
|
||||
|
||||
result.erase(sst_file_number);
|
||||
}
|
||||
|
||||
for (uint64_t sst_file_number : newly_linked) {
|
||||
assert(result.find(sst_file_number) == result.end());
|
||||
|
||||
result.emplace(sst_file_number);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData> CreateMetaDataForNewBlobFile(
|
||||
const BlobFileMetaDataDelta& delta) {
|
||||
auto shared_meta = delta.GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
|
||||
assert(delta.GetNewlyUnlinkedSsts().empty());
|
||||
|
||||
auto meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), delta.GetNewlyLinkedSsts(),
|
||||
delta.GetAdditionalGarbageCount(), delta.GetAdditionalGarbageBytes());
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
static std::shared_ptr<BlobFileMetaData>
|
||||
GetOrCreateMetaDataForExistingBlobFile(
|
||||
const std::shared_ptr<BlobFileMetaData>& base_meta,
|
||||
const BlobFileMetaDataDelta& delta) {
|
||||
assert(base_meta);
|
||||
assert(!delta.GetSharedMeta());
|
||||
|
||||
if (delta.IsEmpty()) {
|
||||
return base_meta;
|
||||
}
|
||||
|
||||
auto shared_meta = base_meta->GetSharedMeta();
|
||||
assert(shared_meta);
|
||||
|
||||
auto linked_ssts = ApplyLinkedSstChanges(base_meta->GetLinkedSsts(),
|
||||
delta.GetNewlyLinkedSsts(),
|
||||
delta.GetNewlyUnlinkedSsts());
|
||||
|
||||
auto meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), std::move(linked_ssts),
|
||||
base_meta->GetGarbageBlobCount() + delta.GetAdditionalGarbageCount(),
|
||||
base_meta->GetGarbageBlobBytes() + delta.GetAdditionalGarbageBytes());
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
void AddBlobFileIfNeeded(
|
||||
@@ -536,7 +728,8 @@ class VersionBuilder::Rep {
|
||||
assert(vstorage);
|
||||
assert(meta);
|
||||
|
||||
if (meta->GetGarbageBlobCount() < meta->GetTotalBlobCount()) {
|
||||
if (meta->GetGarbageBlobCount() < meta->GetTotalBlobCount() ||
|
||||
!meta->GetLinkedSsts().empty()) {
|
||||
vstorage->AddBlobFile(meta);
|
||||
}
|
||||
}
|
||||
@@ -551,42 +744,41 @@ class VersionBuilder::Rep {
|
||||
auto base_it = base_blob_files.begin();
|
||||
const auto base_it_end = base_blob_files.end();
|
||||
|
||||
auto changed_it = changed_blob_files_.begin();
|
||||
const auto changed_it_end = changed_blob_files_.end();
|
||||
auto delta_it = blob_file_meta_deltas_.begin();
|
||||
const auto delta_it_end = blob_file_meta_deltas_.end();
|
||||
|
||||
while (base_it != base_it_end && changed_it != changed_it_end) {
|
||||
while (base_it != base_it_end && delta_it != delta_it_end) {
|
||||
const uint64_t base_blob_file_number = base_it->first;
|
||||
const uint64_t changed_blob_file_number = changed_it->first;
|
||||
const uint64_t delta_blob_file_number = delta_it->first;
|
||||
|
||||
const auto& base_meta = base_it->second;
|
||||
const auto& changed_meta = changed_it->second;
|
||||
|
||||
assert(base_meta);
|
||||
assert(changed_meta);
|
||||
|
||||
if (base_blob_file_number < changed_blob_file_number) {
|
||||
if (base_blob_file_number < delta_blob_file_number) {
|
||||
const auto& base_meta = base_it->second;
|
||||
assert(base_meta);
|
||||
assert(base_meta->GetGarbageBlobCount() <
|
||||
base_meta->GetTotalBlobCount());
|
||||
|
||||
vstorage->AddBlobFile(base_meta);
|
||||
|
||||
++base_it;
|
||||
} else if (changed_blob_file_number < base_blob_file_number) {
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
} else if (delta_blob_file_number < base_blob_file_number) {
|
||||
// Note: blob file numbers are strictly increasing over time and
|
||||
// once blob files get marked obsolete, they never reappear. Thus,
|
||||
// this case is not possible.
|
||||
assert(false);
|
||||
|
||||
++changed_it;
|
||||
++delta_it;
|
||||
} else {
|
||||
assert(base_blob_file_number == changed_blob_file_number);
|
||||
assert(base_meta->GetSharedMeta() == changed_meta->GetSharedMeta());
|
||||
assert(base_meta->GetGarbageBlobCount() <=
|
||||
changed_meta->GetGarbageBlobCount());
|
||||
assert(base_meta->GetGarbageBlobBytes() <=
|
||||
changed_meta->GetGarbageBlobBytes());
|
||||
assert(base_blob_file_number == delta_blob_file_number);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
const auto& base_meta = base_it->second;
|
||||
const auto& delta = delta_it->second;
|
||||
|
||||
auto meta = GetOrCreateMetaDataForExistingBlobFile(base_meta, delta);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, meta);
|
||||
|
||||
++base_it;
|
||||
++changed_it;
|
||||
++delta_it;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,12 +791,14 @@ class VersionBuilder::Rep {
|
||||
++base_it;
|
||||
}
|
||||
|
||||
while (changed_it != changed_it_end) {
|
||||
const auto& changed_meta = changed_it->second;
|
||||
assert(changed_meta);
|
||||
while (delta_it != delta_it_end) {
|
||||
const auto& delta = delta_it->second;
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, changed_meta);
|
||||
++changed_it;
|
||||
auto meta = CreateMetaDataForNewBlobFile(delta);
|
||||
|
||||
AddBlobFileIfNeeded(vstorage, meta);
|
||||
|
||||
++delta_it;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,7 +865,8 @@ class VersionBuilder::Rep {
|
||||
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
const SliceTransform* prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
assert(table_cache_ != nullptr);
|
||||
|
||||
size_t table_cache_capacity = table_cache_->get_cache()->GetCapacity();
|
||||
@@ -740,7 +935,7 @@ class VersionBuilder::Rep {
|
||||
file_meta->fd, &file_meta->table_reader_handle, prefix_extractor,
|
||||
false /*no_io */, true /* record_read_stats */,
|
||||
internal_stats->GetFileReadHist(level), false, level,
|
||||
prefetch_index_and_filter_in_cache);
|
||||
prefetch_index_and_filter_in_cache, max_file_size_for_l0_meta_pin);
|
||||
if (file_meta->table_reader_handle != nullptr) {
|
||||
// Load table_reader
|
||||
file_meta->fd.table_reader = table_cache_->GetTableReaderFromHandle(
|
||||
@@ -766,12 +961,28 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
|
||||
void MaybeAddFile(VersionStorageInfo* vstorage, int level, FileMetaData* f) {
|
||||
if (levels_[level].deleted_files.count(f->fd.GetNumber()) > 0) {
|
||||
const uint64_t file_number = f->fd.GetNumber();
|
||||
|
||||
const auto& level_state = levels_[level];
|
||||
|
||||
const auto& del_files = level_state.deleted_files;
|
||||
const auto del_it = del_files.find(file_number);
|
||||
|
||||
if (del_it != del_files.end()) {
|
||||
// f is to-be-deleted table file
|
||||
vstorage->RemoveCurrentStats(f);
|
||||
} else {
|
||||
assert(ioptions_);
|
||||
vstorage->AddFile(level, f, ioptions_->info_log);
|
||||
const auto& add_files = level_state.added_files;
|
||||
const auto add_it = add_files.find(file_number);
|
||||
|
||||
// Note: if the file appears both in the base version and in the added
|
||||
// list, the added FileMetaData supersedes the one in the base version.
|
||||
if (add_it != add_files.end() && add_it->second != f) {
|
||||
vstorage->RemoveCurrentStats(f);
|
||||
} else {
|
||||
assert(ioptions_);
|
||||
vstorage->AddFile(level, f, ioptions_->info_log);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -799,10 +1010,11 @@ Status VersionBuilder::SaveTo(VersionStorageInfo* vstorage) {
|
||||
Status VersionBuilder::LoadTableHandlers(
|
||||
InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache, bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor) {
|
||||
return rep_->LoadTableHandlers(internal_stats, max_threads,
|
||||
prefetch_index_and_filter_in_cache,
|
||||
is_initial_load, prefix_extractor);
|
||||
const SliceTransform* prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin) {
|
||||
return rep_->LoadTableHandlers(
|
||||
internal_stats, max_threads, prefetch_index_and_filter_in_cache,
|
||||
is_initial_load, prefix_extractor, max_file_size_for_l0_meta_pin);
|
||||
}
|
||||
|
||||
BaseReferencedVersionBuilder::BaseReferencedVersionBuilder(
|
||||
|
||||
@@ -42,7 +42,8 @@ class VersionBuilder {
|
||||
Status LoadTableHandlers(InternalStats* internal_stats, int max_threads,
|
||||
bool prefetch_index_and_filter_in_cache,
|
||||
bool is_initial_load,
|
||||
const SliceTransform* prefix_extractor);
|
||||
const SliceTransform* prefix_extractor,
|
||||
size_t max_file_size_for_l0_meta_pin);
|
||||
|
||||
private:
|
||||
class Rep;
|
||||
|
||||
+482
-17
@@ -4,8 +4,11 @@
|
||||
// (found in the LICENSE.Apache file in the root directory).
|
||||
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "db/version_edit.h"
|
||||
#include "db/version_set.h"
|
||||
#include "logging/logging.h"
|
||||
@@ -54,7 +57,7 @@ class VersionBuilderTest : public testing::Test {
|
||||
return InternalKey(ukey, smallest_seq, kTypeValue);
|
||||
}
|
||||
|
||||
void Add(int level, uint32_t file_number, const char* smallest,
|
||||
void Add(int level, uint64_t file_number, const char* smallest,
|
||||
const char* largest, uint64_t file_size = 0, uint32_t path_id = 0,
|
||||
SequenceNumber smallest_seq = 100, SequenceNumber largest_seq = 100,
|
||||
uint64_t num_entries = 0, uint64_t num_deletions = 0,
|
||||
@@ -80,13 +83,15 @@ class VersionBuilderTest : public testing::Test {
|
||||
|
||||
void AddBlob(uint64_t blob_file_number, uint64_t total_blob_count,
|
||||
uint64_t total_blob_bytes, std::string checksum_method,
|
||||
std::string checksum_value, uint64_t garbage_blob_count,
|
||||
uint64_t garbage_blob_bytes) {
|
||||
std::string checksum_value,
|
||||
BlobFileMetaData::LinkedSsts linked_ssts,
|
||||
uint64_t garbage_blob_count, uint64_t garbage_blob_bytes) {
|
||||
auto shared_meta = SharedBlobFileMetaData::Create(
|
||||
blob_file_number, total_blob_count, total_blob_bytes,
|
||||
std::move(checksum_method), std::move(checksum_value));
|
||||
auto meta = BlobFileMetaData::Create(
|
||||
std::move(shared_meta), garbage_blob_count, garbage_blob_bytes);
|
||||
auto meta =
|
||||
BlobFileMetaData::Create(std::move(shared_meta), std::move(linked_ssts),
|
||||
garbage_blob_count, garbage_blob_bytes);
|
||||
|
||||
vstorage_.AddBlobFile(std::move(meta));
|
||||
}
|
||||
@@ -367,6 +372,242 @@ TEST_F(VersionBuilderTest, ApplyDeleteAndSaveTo) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileDeletionIncorrectLevel) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
|
||||
Add(level, file_number, smallest, largest);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int incorrect_level = 3;
|
||||
|
||||
edit.DeleteFile(incorrect_level, file_number);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot delete table file #2345 from level 3 since "
|
||||
"it is on level 1"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileDeletionNotInLSMTree) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int level = 3;
|
||||
constexpr uint64_t file_number = 1234;
|
||||
|
||||
edit.DeleteFile(level, file_number);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot delete table file #1234 from level 3 since "
|
||||
"it is not in the LSM tree"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileDeletionAndAddition) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr SequenceNumber smallest_seq = 100;
|
||||
constexpr SequenceNumber largest_seq = 500;
|
||||
constexpr uint64_t num_entries = 0;
|
||||
constexpr uint64_t num_deletions = 0;
|
||||
constexpr bool sampled = false;
|
||||
constexpr SequenceNumber smallest_seqno = 1;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
|
||||
Add(level, file_number, smallest, largest, file_size, path_id, smallest_seq,
|
||||
largest_seq, num_entries, num_deletions, sampled, smallest_seqno,
|
||||
largest_seqno);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit deletion;
|
||||
|
||||
deletion.DeleteFile(level, file_number);
|
||||
|
||||
ASSERT_OK(builder.Apply(&deletion));
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
addition.AddFile(level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest, smallest_seq),
|
||||
GetInternalKey(largest, largest_seq), smallest_seqno,
|
||||
largest_seqno, marked_for_compaction, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
ASSERT_EQ(new_vstorage.GetFileLocation(file_number).GetLevel(), level);
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyInBase) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
|
||||
Add(level, file_number, smallest, largest);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int new_level = 2;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit.AddFile(new_level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
const Status s = builder.Apply(&edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot add table file #2345 to level 2 since it is "
|
||||
"already in the LSM tree on level 1"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileAdditionAlreadyApplied) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr int level = 3;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
edit.AddFile(level, file_number, path_id, file_size, GetInternalKey(smallest),
|
||||
GetInternalKey(largest), smallest_seqno, largest_seqno,
|
||||
marked_for_compaction, kInvalidBlobFileNumber,
|
||||
kUnknownOldestAncesterTime, kUnknownFileCreationTime,
|
||||
kUnknownFileChecksum, kUnknownFileChecksumFuncName);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
VersionEdit other_edit;
|
||||
|
||||
constexpr int new_level = 2;
|
||||
|
||||
other_edit.AddFile(new_level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
const Status s = builder.Apply(&other_edit);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(std::strstr(s.getState(),
|
||||
"Cannot add table file #2345 to level 2 since it is "
|
||||
"already in the LSM tree on level 3"));
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyFileAdditionAndDeletion) {
|
||||
constexpr int level = 1;
|
||||
constexpr uint64_t file_number = 2345;
|
||||
constexpr uint32_t path_id = 0;
|
||||
constexpr uint64_t file_size = 10000;
|
||||
constexpr char smallest[] = "bar";
|
||||
constexpr char largest[] = "foo";
|
||||
constexpr SequenceNumber smallest_seqno = 100;
|
||||
constexpr SequenceNumber largest_seqno = 1000;
|
||||
constexpr bool marked_for_compaction = false;
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit addition;
|
||||
|
||||
addition.AddFile(level, file_number, path_id, file_size,
|
||||
GetInternalKey(smallest), GetInternalKey(largest),
|
||||
smallest_seqno, largest_seqno, marked_for_compaction,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
ASSERT_OK(builder.Apply(&addition));
|
||||
|
||||
VersionEdit deletion;
|
||||
|
||||
deletion.DeleteFile(level, file_number);
|
||||
|
||||
ASSERT_OK(builder.Apply(&deletion));
|
||||
|
||||
constexpr bool force_consistency_checks = false;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
ASSERT_FALSE(new_vstorage.GetFileLocation(file_number).IsValid());
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, ApplyBlobFileAddition) {
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
@@ -406,6 +647,7 @@ TEST_F(VersionBuilderTest, ApplyBlobFileAddition) {
|
||||
ASSERT_EQ(new_meta->GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(new_meta->GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(new_meta->GetChecksumValue(), checksum_value);
|
||||
ASSERT_TRUE(new_meta->GetLinkedSsts().empty());
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobCount(), 0);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobBytes(), 0);
|
||||
}
|
||||
@@ -422,7 +664,8 @@ TEST_F(VersionBuilderTest, ApplyBlobFileAdditionAlreadyInBase) {
|
||||
constexpr uint64_t garbage_blob_bytes = 456789;
|
||||
|
||||
AddBlob(blob_file_number, total_blob_count, total_blob_bytes, checksum_method,
|
||||
checksum_value, garbage_blob_count, garbage_blob_bytes);
|
||||
checksum_value, BlobFileMetaData::LinkedSsts(), garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
@@ -481,7 +724,8 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileInBase) {
|
||||
constexpr uint64_t garbage_blob_bytes = 456789;
|
||||
|
||||
AddBlob(blob_file_number, total_blob_count, total_blob_bytes, checksum_method,
|
||||
checksum_value, garbage_blob_count, garbage_blob_bytes);
|
||||
checksum_value, BlobFileMetaData::LinkedSsts(), garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
|
||||
const auto meta =
|
||||
GetBlobFileMetaData(vstorage_.GetBlobFiles(), blob_file_number);
|
||||
@@ -523,6 +767,7 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileInBase) {
|
||||
ASSERT_EQ(new_meta->GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(new_meta->GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(new_meta->GetChecksumValue(), checksum_value);
|
||||
ASSERT_TRUE(new_meta->GetLinkedSsts().empty());
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobCount(),
|
||||
garbage_blob_count + new_garbage_blob_count);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobBytes(),
|
||||
@@ -580,6 +825,7 @@ TEST_F(VersionBuilderTest, ApplyBlobFileGarbageFileAdditionApplied) {
|
||||
ASSERT_EQ(new_meta->GetTotalBlobBytes(), total_blob_bytes);
|
||||
ASSERT_EQ(new_meta->GetChecksumMethod(), checksum_method);
|
||||
ASSERT_EQ(new_meta->GetChecksumValue(), checksum_value);
|
||||
ASSERT_TRUE(new_meta->GetLinkedSsts().empty());
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobCount(), garbage_blob_count);
|
||||
ASSERT_EQ(new_meta->GetGarbageBlobBytes(), garbage_blob_bytes);
|
||||
}
|
||||
@@ -620,8 +866,8 @@ TEST_F(VersionBuilderTest, SaveBlobFilesTo) {
|
||||
|
||||
AddBlob(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(), garbage_blob_count,
|
||||
garbage_blob_bytes);
|
||||
/* checksum_value */ std::string(), BlobFileMetaData::LinkedSsts(),
|
||||
garbage_blob_count, garbage_blob_bytes);
|
||||
}
|
||||
|
||||
EnvOptions env_options;
|
||||
@@ -710,7 +956,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
/* checksum_value */ std::string(), BlobFileMetaData::LinkedSsts{1},
|
||||
/* garbage_blob_count */ 500, /* garbage_blob_bytes */ 300000);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -759,9 +1005,9 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFiles) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesNotInVersion) {
|
||||
// Initialize base version. The table file points to a blob file that is
|
||||
// not in this version.
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesInconsistentLinks) {
|
||||
// Initialize base version. Links between the table file and the blob file
|
||||
// are inconsistent.
|
||||
|
||||
Add(/* level */ 1, /* file_number */ 1, /* smallest */ "150",
|
||||
/* largest */ "200", /* file_size */ 100,
|
||||
@@ -773,7 +1019,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesNotInVersion) {
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
/* checksum_value */ std::string(), BlobFileMetaData::LinkedSsts{1},
|
||||
/* garbage_blob_count */ 500, /* garbage_blob_bytes */ 300000);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -793,8 +1039,9 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesNotInVersion) {
|
||||
|
||||
const Status s = builder.SaveTo(&new_vstorage);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(
|
||||
std::strstr(s.getState(), "Blob file #256 is not part of this version"));
|
||||
ASSERT_TRUE(std::strstr(
|
||||
s.getState(),
|
||||
"Links are inconsistent between table files and blob file #16"));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
@@ -813,7 +1060,7 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
/* checksum_value */ std::string(), BlobFileMetaData::LinkedSsts{1},
|
||||
/* garbage_blob_count */ 1000, /* garbage_blob_bytes */ 1000000);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
@@ -839,6 +1086,224 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbageLinkedSsts) {
|
||||
// Initialize base version, with a table file pointing to a blob file
|
||||
// that has no garbage at this point.
|
||||
|
||||
Add(/* level */ 1, /* file_number */ 1, /* smallest */ "150",
|
||||
/* largest */ "200", /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ 100, /* largest_seq */ 100,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ 100, /* largest_seqno */ 100,
|
||||
/* oldest_blob_file_number */ 16);
|
||||
|
||||
AddBlob(/* blob_file_number */ 16, /* total_blob_count */ 1000,
|
||||
/* total_blob_bytes */ 1000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(), BlobFileMetaData::LinkedSsts{1},
|
||||
/* garbage_blob_count */ 0, /* garbage_blob_bytes */ 0);
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
// Mark the entire blob file garbage but do not remove the linked SST.
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
edit.AddBlobFileGarbage(/* blob_file_number */ 16,
|
||||
/* garbage_blob_count */ 1000,
|
||||
/* garbage_blob_bytes */ 1000000);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
|
||||
// Save to a new version in order to trigger consistency checks.
|
||||
constexpr bool force_consistency_checks = true;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
const Status s = builder.SaveTo(&new_vstorage);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
ASSERT_TRUE(
|
||||
std::strstr(s.getState(), "Blob file #16 consists entirely of garbage"));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, MaintainLinkedSstsForBlobFiles) {
|
||||
// Initialize base version. Table files 1..10 are linked to blob files 1..5,
|
||||
// while table files 11..20 are not linked to any blob files.
|
||||
|
||||
for (uint64_t i = 1; i <= 10; ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setw(2) << std::setfill('0') << i;
|
||||
|
||||
const std::string key = oss.str();
|
||||
|
||||
Add(/* level */ 1, /* file_number */ i, /* smallest */ key.c_str(),
|
||||
/* largest */ key.c_str(), /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ i * 100, /* largest_seq */ i * 100,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ i * 100,
|
||||
/* largest_seqno */ i * 100,
|
||||
/* oldest_blob_file_number */ ((i - 1) % 5) + 1);
|
||||
}
|
||||
|
||||
for (uint64_t i = 1; i <= 5; ++i) {
|
||||
AddBlob(/* blob_file_number */ i, /* total_blob_count */ 2000,
|
||||
/* total_blob_bytes */ 2000000,
|
||||
/* checksum_method */ std::string(),
|
||||
/* checksum_value */ std::string(),
|
||||
BlobFileMetaData::LinkedSsts{i, i + 5},
|
||||
/* garbage_blob_count */ 1000, /* garbage_blob_bytes */ 1000000);
|
||||
}
|
||||
|
||||
for (uint64_t i = 11; i <= 20; ++i) {
|
||||
std::ostringstream oss;
|
||||
oss << std::setw(2) << std::setfill('0') << i;
|
||||
|
||||
const std::string key = oss.str();
|
||||
|
||||
Add(/* level */ 1, /* file_number */ i, /* smallest */ key.c_str(),
|
||||
/* largest */ key.c_str(), /* file_size */ 100,
|
||||
/* path_id */ 0, /* smallest_seq */ i * 100, /* largest_seq */ i * 100,
|
||||
/* num_entries */ 0, /* num_deletions */ 0,
|
||||
/* sampled */ false, /* smallest_seqno */ i * 100,
|
||||
/* largest_seqno */ i * 100, kInvalidBlobFileNumber);
|
||||
}
|
||||
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
{
|
||||
const auto& blob_files = vstorage_.GetBlobFiles();
|
||||
ASSERT_EQ(blob_files.size(), 5);
|
||||
|
||||
const std::vector<BlobFileMetaData::LinkedSsts> expected_linked_ssts{
|
||||
{1, 6}, {2, 7}, {3, 8}, {4, 9}, {5, 10}};
|
||||
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
const auto meta =
|
||||
GetBlobFileMetaData(blob_files, /* blob_file_number */ i + 1);
|
||||
ASSERT_NE(meta, nullptr);
|
||||
ASSERT_EQ(meta->GetLinkedSsts(), expected_linked_ssts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
VersionEdit edit;
|
||||
|
||||
// Add an SST that references a blob file.
|
||||
edit.AddFile(
|
||||
/* level */ 1, /* file_number */ 21, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("21", 2100),
|
||||
/* largest */ GetInternalKey("21", 2100), /* smallest_seqno */ 2100,
|
||||
/* largest_seqno */ 2100, /* marked_for_compaction */ false,
|
||||
/* oldest_blob_file_number */ 1, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
// Add an SST that does not reference any blob files.
|
||||
edit.AddFile(
|
||||
/* level */ 1, /* file_number */ 22, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("22", 2200),
|
||||
/* largest */ GetInternalKey("22", 2200), /* smallest_seqno */ 2200,
|
||||
/* largest_seqno */ 2200, /* marked_for_compaction */ false,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
// Delete a file that references a blob file.
|
||||
edit.DeleteFile(/* level */ 1, /* file_number */ 6);
|
||||
|
||||
// Delete a file that does not reference any blob files.
|
||||
edit.DeleteFile(/* level */ 1, /* file_number */ 16);
|
||||
|
||||
// Trivially move a file that references a blob file. Note that we save
|
||||
// the original BlobFileMetaData object so we can check that no new object
|
||||
// gets created.
|
||||
auto meta3 =
|
||||
GetBlobFileMetaData(vstorage_.GetBlobFiles(), /* blob_file_number */ 3);
|
||||
|
||||
edit.DeleteFile(/* level */ 1, /* file_number */ 3);
|
||||
edit.AddFile(/* level */ 2, /* file_number */ 3, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("03", 300),
|
||||
/* largest */ GetInternalKey("03", 300),
|
||||
/* smallest_seqno */ 300,
|
||||
/* largest_seqno */ 300, /* marked_for_compaction */ false,
|
||||
/* oldest_blob_file_number */ 3, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
// Trivially move a file that does not reference any blob files.
|
||||
edit.DeleteFile(/* level */ 1, /* file_number */ 13);
|
||||
edit.AddFile(/* level */ 2, /* file_number */ 13, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("13", 1300),
|
||||
/* largest */ GetInternalKey("13", 1300),
|
||||
/* smallest_seqno */ 1300,
|
||||
/* largest_seqno */ 1300, /* marked_for_compaction */ false,
|
||||
kInvalidBlobFileNumber, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
// Add one more SST file that references a blob file, then promptly
|
||||
// delete it in a second version edit before the new version gets saved.
|
||||
// This file should not show up as linked to the blob file in the new version.
|
||||
edit.AddFile(/* level */ 1, /* file_number */ 23, /* path_id */ 0,
|
||||
/* file_size */ 100, /* smallest */ GetInternalKey("23", 2300),
|
||||
/* largest */ GetInternalKey("23", 2300),
|
||||
/* smallest_seqno */ 2300,
|
||||
/* largest_seqno */ 2300, /* marked_for_compaction */ false,
|
||||
/* oldest_blob_file_number */ 5, kUnknownOldestAncesterTime,
|
||||
kUnknownFileCreationTime, kUnknownFileChecksum,
|
||||
kUnknownFileChecksumFuncName);
|
||||
|
||||
VersionEdit edit2;
|
||||
|
||||
edit2.DeleteFile(/* level */ 1, /* file_number */ 23);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder builder(env_options, &ioptions_, table_cache, &vstorage_,
|
||||
version_set);
|
||||
|
||||
ASSERT_OK(builder.Apply(&edit));
|
||||
ASSERT_OK(builder.Apply(&edit2));
|
||||
|
||||
constexpr bool force_consistency_checks = true;
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, &vstorage_,
|
||||
force_consistency_checks);
|
||||
|
||||
ASSERT_OK(builder.SaveTo(&new_vstorage));
|
||||
|
||||
{
|
||||
const auto& blob_files = new_vstorage.GetBlobFiles();
|
||||
ASSERT_EQ(blob_files.size(), 5);
|
||||
|
||||
const std::vector<BlobFileMetaData::LinkedSsts> expected_linked_ssts{
|
||||
{1, 21}, {2, 7}, {3, 8}, {4, 9}, {5, 10}};
|
||||
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
const auto meta =
|
||||
GetBlobFileMetaData(blob_files, /* blob_file_number */ i + 1);
|
||||
ASSERT_NE(meta, nullptr);
|
||||
ASSERT_EQ(meta->GetLinkedSsts(), expected_linked_ssts[i]);
|
||||
}
|
||||
|
||||
// Make sure that no new BlobFileMetaData got created for the blob file
|
||||
// affected by the trivial move.
|
||||
ASSERT_EQ(GetBlobFileMetaData(blob_files, /* blob_file_number */ 3), meta3);
|
||||
}
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForFileDeletedTwice) {
|
||||
Add(0, 1U, "150", "200", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
#include "util/string_util.h"
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
// The unknown file checksum.
|
||||
const std::string kUnknownFileChecksum("");
|
||||
// The unknown sst file checksum function name.
|
||||
const std::string kUnknownFileChecksumFuncName("Unknown");
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@ constexpr uint64_t kFileNumberMask = 0x3FFFFFFFFFFFFFFF;
|
||||
constexpr uint64_t kUnknownOldestAncesterTime = 0;
|
||||
constexpr uint64_t kUnknownFileCreationTime = 0;
|
||||
|
||||
extern const std::string kUnknownFileChecksum;
|
||||
extern const std::string kUnknownFileChecksumFuncName;
|
||||
|
||||
extern uint64_t PackFileNumberAndPathId(uint64_t number, uint64_t path_id);
|
||||
|
||||
// A copyable structure contains information needed to read data from an SST
|
||||
|
||||
+21
-10
@@ -27,12 +27,17 @@ VersionEditHandler::VersionEditHandler(
|
||||
assert(version_set_ != nullptr);
|
||||
}
|
||||
|
||||
Status VersionEditHandler::Iterate(log::Reader& reader, std::string* db_id) {
|
||||
void VersionEditHandler::Iterate(log::Reader& reader, Status* log_read_status,
|
||||
std::string* db_id) {
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
assert(log_read_status);
|
||||
assert(log_read_status->ok());
|
||||
|
||||
size_t recovered_edits = 0;
|
||||
Status s = Initialize();
|
||||
while (reader.ReadRecord(&record, &scratch) && s.ok()) {
|
||||
while (s.ok() && reader.ReadRecord(&record, &scratch) &&
|
||||
log_read_status->ok()) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
@@ -70,13 +75,15 @@ Status VersionEditHandler::Iterate(log::Reader& reader, std::string* db_id) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!log_read_status->ok()) {
|
||||
s = *log_read_status;
|
||||
}
|
||||
|
||||
CheckIterationResult(reader, &s);
|
||||
|
||||
if (!s.ok()) {
|
||||
status_ = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionEditHandler::Initialize() {
|
||||
@@ -404,18 +411,19 @@ Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
bool is_initial_load) {
|
||||
assert(cfd != nullptr);
|
||||
assert(!cfd->IsDropped());
|
||||
Status s;
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
assert(builder_iter->second != nullptr);
|
||||
VersionBuilder* builder = builder_iter->second->version_builder();
|
||||
assert(builder);
|
||||
s = builder->LoadTableHandlers(
|
||||
Status s = builder->LoadTableHandlers(
|
||||
cfd->internal_stats(),
|
||||
version_set_->db_options_->max_file_opening_threads,
|
||||
prefetch_index_and_filter_in_cache, is_initial_load,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
if (s.IsPathNotFound() && no_error_if_table_files_missing_) {
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
if ((s.IsPathNotFound() || s.IsCorruption()) &&
|
||||
no_error_if_table_files_missing_) {
|
||||
s = Status::OK();
|
||||
}
|
||||
if (!s.ok() && !version_set_->db_options_->paranoid_checks) {
|
||||
@@ -546,9 +554,11 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
const std::string fpath =
|
||||
MakeTableFileName(cfd->ioptions()->cf_paths[0].path, file_num);
|
||||
s = version_set_->VerifyFileMetadata(fpath, meta);
|
||||
if (s.IsPathNotFound() || s.IsNotFound()) {
|
||||
if (s.IsPathNotFound() || s.IsNotFound() || s.IsCorruption()) {
|
||||
missing_files.insert(file_num);
|
||||
s = Status::OK();
|
||||
} else if (!s.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool missing_info = !version_edit_params_.has_log_number_ ||
|
||||
@@ -556,8 +566,9 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
!version_edit_params_.has_last_sequence_;
|
||||
|
||||
// Create version before apply edit
|
||||
if (!missing_info && ((!missing_files.empty() && !prev_has_missing_files) ||
|
||||
(missing_files.empty() && force_create_version))) {
|
||||
if (s.ok() && !missing_info &&
|
||||
((!missing_files.empty() && !prev_has_missing_files) ||
|
||||
(missing_files.empty() && force_create_version))) {
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
auto* builder = builder_iter->second->version_builder();
|
||||
|
||||
@@ -40,7 +40,8 @@ class VersionEditHandler {
|
||||
|
||||
virtual ~VersionEditHandler() {}
|
||||
|
||||
Status Iterate(log::Reader& reader, std::string* db_id);
|
||||
void Iterate(log::Reader& reader, Status* log_read_status,
|
||||
std::string* db_id);
|
||||
|
||||
const Status& status() const { return status_; }
|
||||
|
||||
|
||||
+139
-62
@@ -10,6 +10,7 @@
|
||||
#include "db/version_set.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
@@ -19,6 +20,7 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "compaction/compaction.h"
|
||||
#include "db/internal_stats.h"
|
||||
#include "db/log_reader.h"
|
||||
@@ -50,6 +52,7 @@
|
||||
#include "table/table_reader.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "test_util/sync_point.h"
|
||||
#include "util/cast_util.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/stop_watch.h"
|
||||
#include "util/string_util.h"
|
||||
@@ -365,6 +368,7 @@ class FilePickerMultiGet {
|
||||
range_(range),
|
||||
batch_iter_(range->begin()),
|
||||
batch_iter_prev_(range->begin()),
|
||||
upper_key_(range->begin()),
|
||||
maybe_repeat_key_(false),
|
||||
current_level_range_(*range, range->begin(), range->end()),
|
||||
current_file_range_(*range, range->begin(), range->end()),
|
||||
@@ -481,9 +485,19 @@ class FilePickerMultiGet {
|
||||
}
|
||||
if (cmp_largest == 0) {
|
||||
// cmp_largest is 0, which means the next key will not be in this
|
||||
// file, so stop looking further. Also don't increment megt_iter_
|
||||
// as we may have to look for this key in the next file if we don't
|
||||
// find it in this one
|
||||
// file, so stop looking further. However, its possible there are
|
||||
// duplicates in the batch, so find the upper bound for the batch
|
||||
// in this file (upper_key_) by skipping past the duplicates. We
|
||||
// leave batch_iter_ as is since we may have to pick up from there
|
||||
// for the next file, if this file has a merge value rather than
|
||||
// final value
|
||||
upper_key_ = batch_iter_;
|
||||
++upper_key_;
|
||||
while (upper_key_ != current_level_range_.end() &&
|
||||
user_comparator_->Compare(batch_iter_->ukey, upper_key_->ukey) ==
|
||||
0) {
|
||||
++upper_key_;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if (curr_level_ == 0) {
|
||||
@@ -503,6 +517,12 @@ class FilePickerMultiGet {
|
||||
*fd = f;
|
||||
*file_index = curr_file_index;
|
||||
*is_last_key_in_file = cmp_largest == 0;
|
||||
if (!*is_last_key_in_file) {
|
||||
// If the largest key in the batch overlapping the file is not the
|
||||
// largest key in the file, upper_ley_ would not have been updated so
|
||||
// update it here
|
||||
upper_key_ = batch_iter_;
|
||||
}
|
||||
return file_hit;
|
||||
}
|
||||
|
||||
@@ -524,7 +544,7 @@ class FilePickerMultiGet {
|
||||
// file regardless for all keys not found yet
|
||||
if (current_level_range_.CheckKeyDone(batch_iter_) ||
|
||||
curr_level_ == 0) {
|
||||
++batch_iter_;
|
||||
batch_iter_ = upper_key_;
|
||||
}
|
||||
}
|
||||
// batch_iter_prev_ will become the start key for the next file
|
||||
@@ -544,18 +564,20 @@ class FilePickerMultiGet {
|
||||
&is_last_key_in_file)) {
|
||||
search_ended_ = !PrepareNextLevel();
|
||||
} else {
|
||||
MultiGetRange::Iterator upper_key = batch_iter_;
|
||||
if (is_last_key_in_file) {
|
||||
// Since cmp_largest is 0, batch_iter_ still points to the last key
|
||||
// that falls in this file, instead of the next one. Increment
|
||||
// upper_key so we can set the range properly for SST MultiGet
|
||||
++upper_key;
|
||||
++(fp_ctx_array_[batch_iter_.index()].curr_index_in_curr_level);
|
||||
// the file index for all keys between batch_iter_ and upper_key_
|
||||
auto tmp_iter = batch_iter_;
|
||||
while (tmp_iter != upper_key_) {
|
||||
++(fp_ctx_array_[tmp_iter.index()].curr_index_in_curr_level);
|
||||
++tmp_iter;
|
||||
}
|
||||
maybe_repeat_key_ = true;
|
||||
}
|
||||
// Set the range for this file
|
||||
current_file_range_ =
|
||||
MultiGetRange(next_file_range, batch_iter_prev_, upper_key);
|
||||
MultiGetRange(next_file_range, batch_iter_prev_, upper_key_);
|
||||
returned_file_level_ = curr_level_;
|
||||
hit_file_level_ = curr_level_;
|
||||
is_hit_file_last_in_level_ =
|
||||
@@ -607,6 +629,7 @@ class FilePickerMultiGet {
|
||||
// key found in the previous SST file, in order to serve as the start of
|
||||
// the batch key range for the next SST file
|
||||
MultiGetRange::Iterator batch_iter_prev_;
|
||||
MultiGetRange::Iterator upper_key_;
|
||||
bool maybe_repeat_key_;
|
||||
MultiGetRange current_level_range_;
|
||||
MultiGetRange current_file_range_;
|
||||
@@ -626,7 +649,7 @@ class FilePickerMultiGet {
|
||||
if (fp_ctx_array_[mget_iter.index()].curr_index_in_curr_level <
|
||||
curr_file_level_->num_files) {
|
||||
batch_iter_prev_ = current_level_range_.begin();
|
||||
batch_iter_ = current_level_range_.begin();
|
||||
upper_key_ = batch_iter_ = current_level_range_.begin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -721,7 +744,7 @@ class FilePickerMultiGet {
|
||||
}
|
||||
if (level_contains_keys) {
|
||||
batch_iter_prev_ = current_level_range_.begin();
|
||||
batch_iter_ = current_level_range_.begin();
|
||||
upper_key_ = batch_iter_ = current_level_range_.begin();
|
||||
return true;
|
||||
}
|
||||
curr_level_++;
|
||||
@@ -984,7 +1007,8 @@ class LevelIterator final : public InternalIterator {
|
||||
read_options_, file_options_, icomparator_, *file_meta.file_metadata,
|
||||
range_del_agg_, prefix_extractor_,
|
||||
nullptr /* don't need reference to table */, file_read_hist_, caller_,
|
||||
/*arena=*/nullptr, skip_filters_, level_, smallest_compaction_key,
|
||||
/*arena=*/nullptr, skip_filters_, level_,
|
||||
/*max_file_size_for_l0_meta_pin=*/0, smallest_compaction_key,
|
||||
largest_compaction_key, allow_unprepared_value_);
|
||||
}
|
||||
|
||||
@@ -1579,10 +1603,9 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
mutable_cf_options_.prefix_extractor.get(), nullptr,
|
||||
cfd_->internal_stats()->GetFileReadHist(0),
|
||||
TableReaderCaller::kUserIterator, arena,
|
||||
/*skip_filters=*/false, /*level=*/0,
|
||||
/*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value));
|
||||
/*largest_compaction_key=*/nullptr, allow_unprepared_value));
|
||||
}
|
||||
if (should_sample) {
|
||||
// Count ones for every L0 files. This is done per iterator creation
|
||||
@@ -1604,8 +1627,8 @@ void Version::AddIteratorsForLevel(const ReadOptions& read_options,
|
||||
mutable_cf_options_.prefix_extractor.get(), should_sample_file_read(),
|
||||
cfd_->internal_stats()->GetFileReadHist(level),
|
||||
TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
|
||||
range_del_agg, /*largest_compaction_key=*/nullptr,
|
||||
allow_unprepared_value));
|
||||
range_del_agg,
|
||||
/*compaction_boundaries=*/nullptr, allow_unprepared_value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1639,7 +1662,7 @@ Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
|
||||
mutable_cf_options_.prefix_extractor.get(), nullptr,
|
||||
cfd_->internal_stats()->GetFileReadHist(0),
|
||||
TableReaderCaller::kUserIterator, &arena,
|
||||
/*skip_filters=*/false, /*level=*/0,
|
||||
/*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false));
|
||||
@@ -1743,6 +1766,8 @@ Version::Version(ColumnFamilyData* column_family_data, VersionSet* vset,
|
||||
refs_(0),
|
||||
file_options_(file_opt),
|
||||
mutable_cf_options_(mutable_cf_options),
|
||||
max_file_size_for_l0_meta_pin_(
|
||||
MaxFileSizeForL0MetaPin(mutable_cf_options_)),
|
||||
version_number_(version_number) {}
|
||||
|
||||
void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
@@ -1806,11 +1831,11 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||
fp.IsHitFileLastInLevel()),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel(), max_file_size_for_l0_meta_pin_);
|
||||
// TODO: examine the behavior for corrupted key
|
||||
if (timer_enabled) {
|
||||
PERF_COUNTER_BY_LEVEL_ADD(get_from_table_nanos, timer.ElapsedNanos(),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
}
|
||||
if (!status->ok()) {
|
||||
return;
|
||||
@@ -1932,6 +1957,7 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
&storage_info_.level_files_brief_, storage_info_.num_non_empty_levels_,
|
||||
&storage_info_.file_indexer_, user_comparator(), internal_comparator());
|
||||
FdWithKeyRange* f = fp.GetNextFile();
|
||||
Status s;
|
||||
|
||||
while (f != nullptr) {
|
||||
MultiGetRange file_range = fp.CurrentFileRange();
|
||||
@@ -1939,17 +1965,17 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
GetPerfLevel() >= PerfLevel::kEnableTimeExceptForMutex &&
|
||||
get_perf_context()->per_level_perf_context_enabled;
|
||||
StopWatchNano timer(env_, timer_enabled /* auto_start */);
|
||||
Status s = table_cache_->MultiGet(
|
||||
s = table_cache_->MultiGet(
|
||||
read_options, *internal_comparator(), *f->file_metadata, &file_range,
|
||||
mutable_cf_options_.prefix_extractor.get(),
|
||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||
fp.IsHitFileLastInLevel()),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
// TODO: examine the behavior for corrupted key
|
||||
if (timer_enabled) {
|
||||
PERF_COUNTER_BY_LEVEL_ADD(get_from_table_nanos, timer.ElapsedNanos(),
|
||||
fp.GetCurrentLevel());
|
||||
fp.GetHitFileLevel());
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// TODO: Set status for individual keys appropriately
|
||||
@@ -1960,7 +1986,8 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
return;
|
||||
}
|
||||
uint64_t batch_size = 0;
|
||||
for (auto iter = file_range.begin(); iter != file_range.end(); ++iter) {
|
||||
for (auto iter = file_range.begin(); s.ok() && iter != file_range.end();
|
||||
++iter) {
|
||||
GetContext& get_context = *iter->get_context;
|
||||
Status* status = iter->s;
|
||||
// The Status in the KeyContext takes precedence over GetContext state
|
||||
@@ -2006,7 +2033,12 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
PERF_COUNTER_BY_LEVEL_ADD(user_key_return_count, 1,
|
||||
fp.GetHitFileLevel());
|
||||
file_range.AddValueSize(iter->value->size());
|
||||
file_range.MarkKeyDone(iter);
|
||||
if (file_range.GetValueSize() > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
case GetContext::kDeleted:
|
||||
// Use empty error message for speed
|
||||
@@ -2028,14 +2060,14 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
}
|
||||
}
|
||||
RecordInHistogram(db_statistics_, SST_BATCH_SIZE, batch_size);
|
||||
if (file_picker_range.empty()) {
|
||||
if (!s.ok() || file_picker_range.empty()) {
|
||||
break;
|
||||
}
|
||||
f = fp.GetNextFile();
|
||||
}
|
||||
|
||||
// Process any left over keys
|
||||
for (auto iter = range->begin(); iter != range->end(); ++iter) {
|
||||
for (auto iter = range->begin(); s.ok() && iter != range->end(); ++iter) {
|
||||
GetContext& get_context = *iter->get_context;
|
||||
Status* status = iter->s;
|
||||
Slice user_key = iter->lkey->user_key();
|
||||
@@ -2060,12 +2092,23 @@ void Version::MultiGet(const ReadOptions& read_options, MultiGetRange* range,
|
||||
nullptr /* result_operand */, true);
|
||||
if (LIKELY(iter->value != nullptr)) {
|
||||
iter->value->PinSelf();
|
||||
range->AddValueSize(iter->value->size());
|
||||
range->MarkKeyDone(iter);
|
||||
if (range->GetValueSize() > read_options.value_size_soft_limit) {
|
||||
s = Status::Aborted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
range->MarkKeyDone(iter);
|
||||
*status = Status::NotFound(); // Use an empty error message for speed
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = range->begin(); iter != range->end(); ++iter) {
|
||||
range->MarkKeyDone(iter);
|
||||
*(iter->s) = s;
|
||||
}
|
||||
}
|
||||
|
||||
bool Version::IsFilterSkipped(int level, bool is_file_last_in_level) {
|
||||
@@ -2087,6 +2130,9 @@ void VersionStorageInfo::GenerateLevelFilesBrief() {
|
||||
void Version::PrepareApply(
|
||||
const MutableCFOptions& mutable_cf_options,
|
||||
bool update_stats) {
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"Version::PrepareApply:forced_check",
|
||||
reinterpret_cast<void*>(&storage_info_.force_consistency_checks_));
|
||||
UpdateAccumulatedStats(update_stats);
|
||||
storage_info_.UpdateNumNonEmptyLevels();
|
||||
storage_info_.CalculateBaseBytes(*cfd_->ioptions(), mutable_cf_options);
|
||||
@@ -2388,6 +2434,11 @@ void VersionStorageInfo::ComputeCompactionScore(
|
||||
// compaction score for the whole DB. Adding other levels as if
|
||||
// they are L0 files.
|
||||
for (int i = 1; i < num_levels(); i++) {
|
||||
// Its possible that a subset of the files in a level may be in a
|
||||
// compaction, due to delete triggered compaction or trivial move.
|
||||
// In that case, the below check may not catch a level being
|
||||
// compacted as it only checks the first file. The worst that can
|
||||
// happen is a scheduled compaction thread will find nothing to do.
|
||||
if (!files_[i].empty() && !files_[i][0]->being_compacted) {
|
||||
num_sorted_runs++;
|
||||
}
|
||||
@@ -2610,6 +2661,12 @@ void VersionStorageInfo::AddFile(int level, FileMetaData* f, Logger* info_log) {
|
||||
#endif
|
||||
f->refs++;
|
||||
level_files->push_back(f);
|
||||
|
||||
const uint64_t file_number = f->fd.GetNumber();
|
||||
|
||||
assert(file_locations_.find(file_number) == file_locations_.end());
|
||||
file_locations_.emplace(file_number,
|
||||
FileLocation(level, level_files->size() - 1));
|
||||
}
|
||||
|
||||
void VersionStorageInfo::AddBlobFile(
|
||||
@@ -3870,7 +3927,8 @@ Status VersionSet::ProcessManifestWrites(
|
||||
cfd->internal_stats(), 1 /* max_threads */,
|
||||
true /* prefetch_index_and_filter_in_cache */,
|
||||
false /* is_initial_load */,
|
||||
mutable_cf_options_ptrs[i]->prefix_extractor.get());
|
||||
mutable_cf_options_ptrs[i]->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*mutable_cf_options_ptrs[i]));
|
||||
if (!s.ok()) {
|
||||
if (db_options_->paranoid_checks) {
|
||||
break;
|
||||
@@ -4389,24 +4447,26 @@ Status VersionSet::GetCurrentManifestPath(const std::string& dbname,
|
||||
if (dbname.back() != '/') {
|
||||
manifest_path->push_back('/');
|
||||
}
|
||||
*manifest_path += fname;
|
||||
manifest_path->append(fname);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status VersionSet::ReadAndRecover(
|
||||
log::Reader* reader, AtomicGroupReadBuffer* read_buffer,
|
||||
log::Reader& reader, AtomicGroupReadBuffer* read_buffer,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>& name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>&
|
||||
builders,
|
||||
VersionEditParams* version_edit_params, std::string* db_id) {
|
||||
assert(reader != nullptr);
|
||||
Status* log_read_status, VersionEditParams* version_edit_params,
|
||||
std::string* db_id) {
|
||||
assert(read_buffer != nullptr);
|
||||
assert(log_read_status != nullptr);
|
||||
Status s;
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
size_t recovered_edits = 0;
|
||||
while (reader->ReadRecord(&record, &scratch) && s.ok()) {
|
||||
while (s.ok() && reader.ReadRecord(&record, &scratch) &&
|
||||
log_read_status->ok()) {
|
||||
VersionEdit edit;
|
||||
s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
@@ -4450,6 +4510,9 @@ Status VersionSet::ReadAndRecover(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!log_read_status->ok()) {
|
||||
s = *log_read_status;
|
||||
}
|
||||
if (!s.ok()) {
|
||||
// Clear the buffer if we fail to decode/apply an edit.
|
||||
read_buffer->Clear();
|
||||
@@ -4496,8 +4559,7 @@ Status VersionSet::Recover(
|
||||
db_options_->log_readahead_size));
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>
|
||||
builders;
|
||||
VersionBuilderMap builders;
|
||||
|
||||
// add default column family
|
||||
auto default_cf_iter = cf_name_to_options.find(kDefaultColumnFamilyName);
|
||||
@@ -4519,12 +4581,13 @@ Status VersionSet::Recover(
|
||||
VersionEditParams version_edit_params;
|
||||
{
|
||||
VersionSet::LogReporter reporter;
|
||||
reporter.status = &s;
|
||||
Status log_read_status;
|
||||
reporter.status = &log_read_status;
|
||||
log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
|
||||
true /* checksum */, 0 /* log_number */);
|
||||
AtomicGroupReadBuffer read_buffer;
|
||||
s = ReadAndRecover(&reader, &read_buffer, cf_name_to_options,
|
||||
column_families_not_found, builders,
|
||||
s = ReadAndRecover(reader, &read_buffer, cf_name_to_options,
|
||||
column_families_not_found, builders, &log_read_status,
|
||||
&version_edit_params, db_id);
|
||||
current_manifest_file_size = reader.GetReadOffset();
|
||||
assert(current_manifest_file_size != 0);
|
||||
@@ -4597,7 +4660,8 @@ Status VersionSet::Recover(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
true /* is_initial_load */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
if (!s.ok()) {
|
||||
if (db_options_->paranoid_checks) {
|
||||
return s;
|
||||
@@ -4789,21 +4853,20 @@ Status VersionSet::TryRecoverFromOneManifest(
|
||||
db_options_->log_readahead_size));
|
||||
}
|
||||
|
||||
assert(s.ok());
|
||||
VersionSet::LogReporter reporter;
|
||||
reporter.status = &s;
|
||||
log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
|
||||
/*checksum=*/true, /*log_num=*/0);
|
||||
{
|
||||
VersionEditHandlerPointInTime handler_pit(read_only, column_families,
|
||||
const_cast<VersionSet*>(this));
|
||||
VersionEditHandlerPointInTime handler_pit(read_only, column_families,
|
||||
const_cast<VersionSet*>(this));
|
||||
|
||||
s = handler_pit.Iterate(reader, db_id);
|
||||
handler_pit.Iterate(reader, &s, db_id);
|
||||
|
||||
assert(nullptr != has_missing_table_file);
|
||||
*has_missing_table_file = handler_pit.HasMissingFiles();
|
||||
}
|
||||
assert(nullptr != has_missing_table_file);
|
||||
*has_missing_table_file = handler_pit.HasMissingFiles();
|
||||
|
||||
return s;
|
||||
return handler_pit.status();
|
||||
}
|
||||
|
||||
Status VersionSet::ListColumnFamilies(std::vector<std::string>* column_families,
|
||||
@@ -4945,7 +5008,19 @@ Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
|
||||
}
|
||||
|
||||
if (first_nonempty_level > 0) {
|
||||
new_files_list[new_levels - 1] = vstorage->LevelFiles(first_nonempty_level);
|
||||
auto& new_last_level = new_files_list[new_levels - 1];
|
||||
|
||||
new_last_level = vstorage->LevelFiles(first_nonempty_level);
|
||||
|
||||
for (size_t i = 0; i < new_last_level.size(); ++i) {
|
||||
const FileMetaData* const meta = new_last_level[i];
|
||||
assert(meta);
|
||||
|
||||
const uint64_t file_number = meta->fd.GetNumber();
|
||||
|
||||
vstorage->file_locations_[file_number] =
|
||||
VersionStorageInfo::FileLocation(new_levels - 1, i);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] vstorage -> files_;
|
||||
@@ -5445,7 +5520,8 @@ uint64_t VersionSet::ApproximateSize(const SizeApproximationOptions& options,
|
||||
static_cast<uint64_t>(total_full_size * margin)) {
|
||||
total_full_size += total_intersecting_size / 2;
|
||||
} else {
|
||||
// Estimate for all the first files, at each level
|
||||
// Estimate for all the first files (might also be last files), at each
|
||||
// level
|
||||
for (const auto file_ptr : first_files) {
|
||||
total_full_size += ApproximateSize(v, *file_ptr, start, end, caller);
|
||||
}
|
||||
@@ -5621,13 +5697,14 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
for (size_t i = 0; i < flevel->num_files; i++) {
|
||||
list[num++] = cfd->table_cache()->NewIterator(
|
||||
read_options, file_options_compactions,
|
||||
cfd->internal_comparator(),
|
||||
*flevel->files[i].file_metadata, range_del_agg,
|
||||
c->mutable_cf_options()->prefix_extractor.get(),
|
||||
cfd->internal_comparator(), *flevel->files[i].file_metadata,
|
||||
range_del_agg, c->mutable_cf_options()->prefix_extractor.get(),
|
||||
/*table_reader_ptr=*/nullptr,
|
||||
/*file_read_hist=*/nullptr, TableReaderCaller::kCompaction,
|
||||
/*arena=*/nullptr,
|
||||
/*skip_filters=*/false, /*level=*/static_cast<int>(which),
|
||||
/*skip_filters=*/false,
|
||||
/*level=*/static_cast<int>(c->level(which)),
|
||||
MaxFileSizeForL0MetaPin(*c->mutable_cf_options()),
|
||||
/*smallest_compaction_key=*/nullptr,
|
||||
/*largest_compaction_key=*/nullptr,
|
||||
/*allow_unprepared_value=*/false);
|
||||
@@ -5641,7 +5718,7 @@ InternalIterator* VersionSet::MakeInputIterator(
|
||||
/*should_sample=*/false,
|
||||
/*no per level latency histogram=*/nullptr,
|
||||
TableReaderCaller::kCompaction, /*skip_filters=*/false,
|
||||
/*level=*/static_cast<int>(which), range_del_agg,
|
||||
/*level=*/static_cast<int>(c->level(which)), range_del_agg,
|
||||
c->boundaries(which));
|
||||
}
|
||||
}
|
||||
@@ -5910,8 +5987,7 @@ Status ReactiveVersionSet::Recover(
|
||||
// In recovery, nobody else can access it, so it's fine to set it to be
|
||||
// initialized earlier.
|
||||
default_cfd->set_initialized();
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>
|
||||
builders;
|
||||
VersionBuilderMap builders;
|
||||
std::unordered_map<int, std::string> column_families_not_found;
|
||||
builders.insert(
|
||||
std::make_pair(0, std::unique_ptr<BaseReferencedVersionBuilder>(
|
||||
@@ -5919,7 +5995,7 @@ Status ReactiveVersionSet::Recover(
|
||||
|
||||
manifest_reader_status->reset(new Status());
|
||||
manifest_reporter->reset(new LogReporter());
|
||||
static_cast<LogReporter*>(manifest_reporter->get())->status =
|
||||
static_cast_with_check<LogReporter>(manifest_reporter->get())->status =
|
||||
manifest_reader_status->get();
|
||||
Status s = MaybeSwitchManifest(manifest_reporter->get(), manifest_reader);
|
||||
log::Reader* reader = manifest_reader->get();
|
||||
@@ -5928,10 +6004,9 @@ Status ReactiveVersionSet::Recover(
|
||||
VersionEdit version_edit;
|
||||
while (s.ok() && retry < 1) {
|
||||
assert(reader != nullptr);
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
s = ReadAndRecover(reader, &read_buffer_, cf_name_to_options,
|
||||
column_families_not_found, builders, &version_edit);
|
||||
s = ReadAndRecover(*reader, &read_buffer_, cf_name_to_options,
|
||||
column_families_not_found, builders,
|
||||
manifest_reader_status->get(), &version_edit);
|
||||
if (s.ok()) {
|
||||
bool enough = version_edit.has_next_file_number_ &&
|
||||
version_edit.has_log_number_ &&
|
||||
@@ -5958,7 +6033,8 @@ Status ReactiveVersionSet::Recover(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
true /* is_initial_load */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
if (!s.ok()) {
|
||||
enough = false;
|
||||
if (s.IsPathNotFound()) {
|
||||
@@ -6234,7 +6310,8 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
cfd->internal_stats(), db_options_->max_file_opening_threads,
|
||||
false /* prefetch_index_and_filter_in_cache */,
|
||||
false /* is_initial_load */,
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get());
|
||||
cfd->GetLatestMutableCFOptions()->prefix_extractor.get(),
|
||||
MaxFileSizeForL0MetaPin(*cfd->GetLatestMutableCFOptions()));
|
||||
TEST_SYNC_POINT_CALLBACK(
|
||||
"ReactiveVersionSet::ApplyOneVersionEditToBuilder:"
|
||||
"AfterLoadTableHandlers",
|
||||
|
||||
+70
-5
@@ -283,6 +283,58 @@ class VersionStorageInfo {
|
||||
return files_[level];
|
||||
}
|
||||
|
||||
class FileLocation {
|
||||
public:
|
||||
FileLocation() = default;
|
||||
FileLocation(int level, size_t position)
|
||||
: level_(level), position_(position) {}
|
||||
|
||||
int GetLevel() const { return level_; }
|
||||
size_t GetPosition() const { return position_; }
|
||||
|
||||
bool IsValid() const { return level_ >= 0; }
|
||||
|
||||
bool operator==(const FileLocation& rhs) const {
|
||||
return level_ == rhs.level_ && position_ == rhs.position_;
|
||||
}
|
||||
|
||||
bool operator!=(const FileLocation& rhs) const { return !(*this == rhs); }
|
||||
|
||||
static FileLocation Invalid() { return FileLocation(); }
|
||||
|
||||
private:
|
||||
int level_ = -1;
|
||||
size_t position_ = 0;
|
||||
};
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
FileLocation GetFileLocation(uint64_t file_number) const {
|
||||
const auto it = file_locations_.find(file_number);
|
||||
|
||||
if (it == file_locations_.end()) {
|
||||
return FileLocation::Invalid();
|
||||
}
|
||||
|
||||
assert(it->second.GetLevel() < num_levels_);
|
||||
assert(it->second.GetPosition() < files_[it->second.GetLevel()].size());
|
||||
assert(files_[it->second.GetLevel()][it->second.GetPosition()]);
|
||||
assert(files_[it->second.GetLevel()][it->second.GetPosition()]
|
||||
->fd.GetNumber() == file_number);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
FileMetaData* GetFileMetaDataByNumber(uint64_t file_number) const {
|
||||
auto location = GetFileLocation(file_number);
|
||||
|
||||
if (!location.IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return files_[location.GetLevel()][location.GetPosition()];
|
||||
}
|
||||
|
||||
// REQUIRES: This version has been saved (see VersionSet::SaveTo)
|
||||
using BlobFiles = std::map<uint64_t, std::shared_ptr<BlobFileMetaData>>;
|
||||
const BlobFiles& GetBlobFiles() const { return blob_files_; }
|
||||
@@ -461,6 +513,11 @@ class VersionStorageInfo {
|
||||
// in increasing order of keys
|
||||
std::vector<FileMetaData*>* files_;
|
||||
|
||||
// Map of all table files in version. Maps file number to (level, position on
|
||||
// level).
|
||||
using FileLocations = std::unordered_map<uint64_t, FileLocation>;
|
||||
FileLocations file_locations_;
|
||||
|
||||
// Map of blob files in version by number.
|
||||
BlobFiles blob_files_;
|
||||
|
||||
@@ -746,6 +803,8 @@ class Version {
|
||||
int refs_; // Number of live refs to this version
|
||||
const FileOptions file_options_;
|
||||
const MutableCFOptions mutable_cf_options_;
|
||||
// Cached value to avoid recomputing it on every read.
|
||||
const size_t max_file_size_for_l0_meta_pin_;
|
||||
|
||||
// A version number that uniquely represents this version. This is
|
||||
// used for debugging and logging purposes only.
|
||||
@@ -1106,6 +1165,10 @@ class VersionSet {
|
||||
void SetIOStatusOK() { io_status_ = IOStatus::OK(); }
|
||||
|
||||
protected:
|
||||
using VersionBuilderMap =
|
||||
std::unordered_map<uint32_t,
|
||||
std::unique_ptr<BaseReferencedVersionBuilder>>;
|
||||
|
||||
struct ManifestWriter;
|
||||
|
||||
friend class Version;
|
||||
@@ -1117,7 +1180,9 @@ class VersionSet {
|
||||
struct LogReporter : public log::Reader::Reporter {
|
||||
Status* status;
|
||||
virtual void Corruption(size_t /*bytes*/, const Status& s) override {
|
||||
if (this->status->ok()) *this->status = s;
|
||||
if (status->ok()) {
|
||||
*status = s;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1148,13 +1213,14 @@ class VersionSet {
|
||||
const VersionEdit* edit);
|
||||
|
||||
Status ReadAndRecover(
|
||||
log::Reader* reader, AtomicGroupReadBuffer* read_buffer,
|
||||
log::Reader& reader, AtomicGroupReadBuffer* read_buffer,
|
||||
const std::unordered_map<std::string, ColumnFamilyOptions>&
|
||||
name_to_options,
|
||||
std::unordered_map<int, std::string>& column_families_not_found,
|
||||
std::unordered_map<
|
||||
uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>& builders,
|
||||
VersionEditParams* version_edit, std::string* db_id = nullptr);
|
||||
Status* log_read_status, VersionEditParams* version_edit,
|
||||
std::string* db_id = nullptr);
|
||||
|
||||
// REQUIRES db mutex
|
||||
Status ApplyOneVersionEditToBuilder(
|
||||
@@ -1283,8 +1349,7 @@ class ReactiveVersionSet : public VersionSet {
|
||||
std::unique_ptr<log::FragmentBufferedReader>* manifest_reader);
|
||||
|
||||
private:
|
||||
std::unordered_map<uint32_t, std::unique_ptr<BaseReferencedVersionBuilder>>
|
||||
active_version_builders_;
|
||||
VersionBuilderMap active_version_builders_;
|
||||
AtomicGroupReadBuffer read_buffer_;
|
||||
// Number of version edits to skip by ReadAndApply at the beginning of a new
|
||||
// MANIFEST created by primary.
|
||||
|
||||
+30
-8
@@ -372,19 +372,19 @@ TEST_F(VersionStorageInfoTest, EstimateLiveDataSize) {
|
||||
Add(2, 3U, "6", "8", 1U); // Partial overlap with last level
|
||||
Add(3, 4U, "1", "9", 1U); // Contains range of last level
|
||||
Add(4, 5U, "4", "5", 1U); // Inside range of last level
|
||||
Add(4, 5U, "6", "7", 1U); // Inside range of last level
|
||||
Add(5, 6U, "4", "7", 10U);
|
||||
Add(4, 6U, "6", "7", 1U); // Inside range of last level
|
||||
Add(5, 7U, "4", "7", 10U);
|
||||
ASSERT_EQ(10U, vstorage_.EstimateLiveDataSize());
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, EstimateLiveDataSize2) {
|
||||
Add(0, 1U, "9", "9", 1U); // Level 0 is not ordered
|
||||
Add(0, 1U, "5", "6", 1U); // Ignored because of [5,6] in l1
|
||||
Add(1, 1U, "1", "2", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 2U, "3", "4", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 3U, "5", "6", 1U);
|
||||
Add(2, 4U, "2", "3", 1U);
|
||||
Add(3, 5U, "7", "8", 1U);
|
||||
Add(0, 2U, "5", "6", 1U); // Ignored because of [5,6] in l1
|
||||
Add(1, 3U, "1", "2", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 4U, "3", "4", 1U); // Ignored because of [2,3] in l2
|
||||
Add(1, 5U, "5", "6", 1U);
|
||||
Add(2, 6U, "2", "3", 1U);
|
||||
Add(3, 7U, "7", "8", 1U);
|
||||
ASSERT_EQ(4U, vstorage_.EstimateLiveDataSize());
|
||||
}
|
||||
|
||||
@@ -421,6 +421,28 @@ TEST_F(VersionStorageInfoTest, GetOverlappingInputs) {
|
||||
1, {"i", 0, kTypeValue}, {"j", 0, kTypeValue}));
|
||||
}
|
||||
|
||||
TEST_F(VersionStorageInfoTest, FileLocationAndMetaDataByNumber) {
|
||||
Add(0, 11U, "1", "2", 5000U);
|
||||
Add(0, 12U, "1", "2", 5000U);
|
||||
|
||||
Add(2, 7U, "1", "2", 8000U);
|
||||
|
||||
ASSERT_EQ(vstorage_.GetFileLocation(11U),
|
||||
VersionStorageInfo::FileLocation(0, 0));
|
||||
ASSERT_NE(vstorage_.GetFileMetaDataByNumber(11U), nullptr);
|
||||
|
||||
ASSERT_EQ(vstorage_.GetFileLocation(12U),
|
||||
VersionStorageInfo::FileLocation(0, 1));
|
||||
ASSERT_NE(vstorage_.GetFileMetaDataByNumber(12U), nullptr);
|
||||
|
||||
ASSERT_EQ(vstorage_.GetFileLocation(7U),
|
||||
VersionStorageInfo::FileLocation(2, 0));
|
||||
ASSERT_NE(vstorage_.GetFileMetaDataByNumber(7U), nullptr);
|
||||
|
||||
ASSERT_FALSE(vstorage_.GetFileLocation(999U).IsValid());
|
||||
ASSERT_EQ(vstorage_.GetFileMetaDataByNumber(999U), nullptr);
|
||||
}
|
||||
|
||||
class VersionStorageInfoTimestampTest : public VersionStorageInfoTestBase {
|
||||
public:
|
||||
VersionStorageInfoTimestampTest()
|
||||
|
||||
+21
-3
@@ -908,7 +908,14 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
PutLengthPrefixedSlice(&b->rep_, key);
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSlice(&b->rep_, key);
|
||||
} else {
|
||||
PutVarint32(&b->rep_,
|
||||
static_cast<uint32_t>(key.size() + b->timestamp_size_));
|
||||
b->rep_.append(key.data(), key.size());
|
||||
b->rep_.append(b->timestamp_size_, '\0');
|
||||
}
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
@@ -930,7 +937,11 @@ Status WriteBatchInternal::Delete(WriteBatch* b, uint32_t column_family_id,
|
||||
b->rep_.push_back(static_cast<char>(kTypeColumnFamilyDeletion));
|
||||
PutVarint32(&b->rep_, column_family_id);
|
||||
}
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
if (0 == b->timestamp_size_) {
|
||||
PutLengthPrefixedSliceParts(&b->rep_, key);
|
||||
} else {
|
||||
PutLengthPrefixedSlicePartsWithPadding(&b->rep_, key, b->timestamp_size_);
|
||||
}
|
||||
b->content_flags_.store(b->content_flags_.load(std::memory_order_relaxed) |
|
||||
ContentFlags::HAS_DELETE,
|
||||
std::memory_order_relaxed);
|
||||
@@ -1546,7 +1557,14 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
return seek_status;
|
||||
}
|
||||
|
||||
auto ret_status = DeleteImpl(column_family_id, key, Slice(), kTypeDeletion);
|
||||
ColumnFamilyData* cfd = cf_mems_->current();
|
||||
assert(!cfd || cfd->user_comparator());
|
||||
const size_t ts_sz = (cfd && cfd->user_comparator())
|
||||
? cfd->user_comparator()->timestamp_size()
|
||||
: 0;
|
||||
const ValueType delete_type =
|
||||
(0 == ts_sz) ? kTypeDeletion : kTypeDeletionWithTimestamp;
|
||||
auto ret_status = DeleteImpl(column_family_id, key, Slice(), delete_type);
|
||||
// optimize for non-recovery mode
|
||||
if (UNLIKELY(!ret_status.IsTryAgain() && rebuilding_trx_ != nullptr)) {
|
||||
assert(!write_after_commit_);
|
||||
|
||||
Loaded 100 of 283 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user